Skip to content

Install linters for Python code in vim with ALE

Louis Maddox edited this page Jul 30, 2022 · 29 revisions

Motivation

I had previously tried to use linters in vim without success, before finding "ALE" a tool with a "fixer" feature which lints on save, much like Go does (for imports, similar to Python's isort).

About ALE

From its README:

ALE offers support for fixing code with command line tools in a non-blocking manner with the :ALEFix feature, supporting tools in many languages, like prettier, eslint, autopep8, and more.

  • There are a ton of these: recently I've used flake8-bugbear and pandas-vet.
  • My standard set of linters that themselves modify code is: isort (reorder imports), black (enforce standard code style), and autoflake8 (remove unused imports)

It also claims to be efficient

One of ALE's general missions is that you won't pay for the features that you don't use.

The work is done in the background upon opening new buffers or editing files.

Options are documented in the vim help file

  • :help ale-options for global options
  • :help ale-integration-options for specific linters

Requirements

ALE requires vim 8+ (mine is 8.1 at time of writing, in 2022)

To install plugins for Vim I use Pathogen (see the README for other plugin managers). The requirement is one line in .vimrc, which should already be there if you've used it before.

"ensure you have the line execute pathogen#infect() in your ~/.vimrc file"

Installing ALE with Pathogen

cd ~/.vim/bundle
git clone https://github.com/dense-analysis/ale.git

Installing linters with ALE

  1. Put this in .vimrc (to automatically fix files when you save them):
" Set this variable to 1 to fix files when you save them.
let g:ale_fix_on_save = 1
  1. Then define a List in an ftplugin file at ~/.vim/ftplugin/python.vim, (after mkdir -p ~/.vim/ftplugin in my case), similar to the suggestion here [but with the standard line length]:
" Fix files with isort, and then black.
let b:ale_fixers = {'python': ['isort', 'black']}
let g:ale_python_isort_options = '--profile black -l 88'

💡 Note: typically you'll find the defaults to use here in pyproject.toml (e.g.) or .pre-commit-config.yaml (e.g.)

Using linters with ALE

You 'fix' files with the linters installed in this way by calling ALEFix if you set the ale_fix_on_save option to 0 (off).

Troubleshooting

Linters vs. fixers

Linters and fixers are not the same! In my case I'm more interested in fixers (for now). Read carefully (I missed this distinction at first).

Identifying installed/enable/active linters

To see the current config when editing a file activated for ALE (i.e. in a language you installed it for), run :ALEInfo. For me this gives:

 Current Filetype: python
Available Linters: ['bandit', 'cspell', 'flake8', 'flakehell', 'jedils', 'mypy', 'prospector',
'pycodestyle', 'pydocstyle', 'pyflakes', 'pylama', 'pylint', 'pylsp', 'pyre', 'pyright', 'unimport',
'vulture']
  Enabled Linters: ['flake8', 'mypy', 'pylint', 'pyright']
  Ignored Linters: []
 Suggested Fixers:
  'add_blank_lines_for_python_control_statements' - Add blank lines before control statements.
  'autoflake' - Fix flake issues with autoflake.
  'autoimport' - Fix import issues with autoimport.
  'autopep8' - Fix PEP8 issues with autopep8.
  'black' - Fix PEP8 issues with black.
  'isort' - Sort Python imports with isort.
  'pyflyby' - Tidy Python imports with pyflyby.
  'remove_trailing_lines' - Remove all blank lines at the end of a file.
  'reorder-python-imports' - Sort Python imports with reorder-python-imports.
  'trim_whitespace' - Remove all trailing whitespace characters at the end of every line.
  'yapf' - Fix Python files with yapf.

⚠️ There are more running than were not configured to run, and the ones I did configure to run aren't running!

If you scroll down to the bottom of the :ALEInfo output you see the cause: these are all run as arguments to /bin/bash -c:

  Command History:
(finished - exit code 127) ['/bin/bash', '-c', '''pylint'' --version']
<<<OUTPUT STARTS>>>
/bin/bash: pylint: command not found
<<<OUTPUT ENDS>>>
(executable check - success) flake8
(finished - exit code 0) ['/bin/bash', '-c', 'cd ''/home/louis/dev/testing/ale'' && ''flake8'' --version']
<<<OUTPUT STARTS>>>
4.0.1 (mccabe: 0.6.1, pycodestyle: 2.8.0, pyflakes: 2.4.0) CPython 3.9.7 on Linux
<<<OUTPUT ENDS>>>
(executable check - success) mypy
(finished - exit code 1) ['/bin/bash', '-c', 'cd ''/home/louis/dev/testing/ale'' && ''mypy'' --show-column-numbers --shadow-file ''/home/louis/dev/testing/ale/namedconstant.py'' ''/tmp/v6fBvfM/1/namedconstant.py'' ''/home/louis/dev/testing/ale/namedconstant.py''']
<<<OUTPUT STARTS>>>
namedconstant.py:1:1: error: Cannot find implementation or library stub for module named "aenum"
namedconstant.py:1:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
namedconstant.py:2:1: error: Cannot find implementation or library stub for module named "classprop_util"
Found 2 errors in 1 file (checked 1 source file)
<<<OUTPUT ENDS>>>
(executable check - failure) pylint
(executable check - failure) pyright-langserver
(finished - exit code 1) ['/bin/bash', '-c', 'cd ''/home/louis/dev/testing/ale'' && ''flake8'' --format=default --stdin-display-name ''/home/louis/dev/testing/ale/namedconstant.py'' - < ''/tmp/v6fBvfM/2/namedconstant.py''']
<<<OUTPUT STARTS>>>
/home/louis/dev/testing/ale/namedconstant.py:4:1: E302 expected 2 blank lines, found 1
/home/louis/dev/testing/ale/namedconstant.py:25:5: E265 block comment should start with '# '

To debug this, the first step was to switch off the ones that were configured to run by setting

let g:ale_linters_ignore = ["flake8", "mypy", "pylint", "pyright"]

Now the :ALEInfo's Command History section was empty. When I saved a file however, the edits were still made: just any feedback from them was silenced. In a way desirable, but in another (not a controlled usage) very much not!

I wanted to disable these not just ignore their warnings and errors. This is controlled with:

" Only run linters named in ale_linters settings.
let g:ale_linters_explicit = 1
  • TODO: debug black and isort being 'suggested' but not 'available' or 'enabled'
    • I presume the ftplugin file is not being read...?

Quietening ALE

ALE does not attempt to 'fade into the background' of development, but instead commandeers the vim UI as a linting readout. This is very different to the behaviour of the vim-go plugin (which simply "does its job and gets out of your way").

By default, ALE uses the strongest visual cues available to direct attention. As an initial user this is off-putting (I came for automatic rewriting, not to recreate an IDE).

Each to their own, but below I outline how to disable this behaviour in ALE.

Freezing the sign gutter

The default behaviour for ALE is to introduce a "sign gutter" along the left hand side of your code, that shifts the entire code file along -- quite visually disruptive.

The relevant part of the docs here show these are the 'sign gutter' containing ale_sign_error (default: '>>') and ale_sign_warning (default: '--'). These cannot be set to the empty string.

You can either keep the gutter open at all times with

let g:ale_sign_column_always = 1

Turning off gutter background highlighting

The sign gutter uses red background highlighting for errors and orange for warnings, which can be turned off with the following in ~/.vimrc:

" Set this in your vimrc file to disable sign gutter highlighting
highlight clear ALEErrorSign
highlight clear ALEWarningSign

This leaves just the grey gutter background and black background where there is a sign/warning.

Turning off code background highlighting

The code itself is also marked at the position of errors, not just in the gutter beside it.

To turn this off:

" Set this in your vimrc file to disable highlighting
let g:ale_set_highlights = 0

Hiding the gutter

Another option would be to hide the sign gutter entirely

  • TODO: how to turn off the sign gutter?
Clone this wiki locally