Automatic Python Linting and Formatting in Neovim

Outside of languages that more or less require a specific IDE (looking at you, Java), I typically prefer to write code in Neovim. It’s fast, it’s lean, and with a little bit of configuration to get an LSP running, it gives the exact same experience as something like VS Code but with much better editing and a fraction of the resource footprint.

Overall I’m pretty happy with my Neovim config, but I realized recently that something was still missing when I’m writing code in Python. With Conquer of Completion, I have coc-pyright running nicely, but two things were off. First, I use pylint to lint my code. This tells me when I do things that aren’t the Pythonic way, even though they may work. For example, I recently wrote something like:

some_value: str = os.getenv(“some_key”)
if some_value.startswith(“t”):
    other_value: bool = True
else:
    other_value: bool = False

pylint gave me a warning that I should simplify this with bool().

some_value: str = os.getenv(“some_key”)
other_value: bool = bool(some_value.startswith(“t”))

I wouldn’t have thought of that on my own, so pylint does a great job of keeping my code clean and Pythonic. It will also give me warnings if a particular class has too many members, for example, to make me think about whether or not I’m trying to do too much with a single class and need to split it up. Likewise, it’ll complain if a given function or method has too many lines or too many paths, forcing me to reconsider if what I’m doing is the best way to handle things.

The other tool I use is black. black will automatically format my code to very opinionated standards. If a line is too long, for example, black will automatically split it into multiple lines. If I have a list that spans multiple lines, black will ensure that the last item in the list still has a comma after it. It’s nice for keeping code neat and up to standards so that it’s easy for anyone to look at.

When using something like VS Code, these tools run automatically in the editor. Pylint will pop up warnings in real-time after I write a line of code, and black will automatically format my code every time I hit save; that doesn’t happen automatically in Neovim. I could use the tools manually from the CLI, but obviously tabbing to a different terminal and manually executing two commands isn’t the most convenient experience. Plus, trying to then tab back to the code and address pylint errors by line number (which will change as soon as I start fixing any of them) is also annoying.

Knowing there had to be some way to automate this, I started digging around online. I quickly found a lot of references to changing some simple CoC settings in the coc-settings.json file. My immediate problem was that it was difficult to figure out where that file actually lived for me to update it. Finally I stumbled across the fact that looking for the file on the filesystem was completely unnecessary because I could open it with a Neovim command:

:CocConfig

This just opens the file for me, and then I could paste in the few settings I had found online:

{
    "python.linting.pylintEnabled": true,
    "python.formatting.provider": "black",
    "coc.preferences.formatOnSave": true,
}

Sure enough, after these changes I ran a few quick tests and confirmed that pylint was giving me the linting I needed right in Neovim, and black was automatically formatting my code every time I saved a file.