2018, Aug 13 - Dimitri Merejkowsky License: CC By 4.0
In my last article[1] I explained why I decided to no longer use pylint.
In a nutshell, most pylint warning are now caught by other linters, and all that's left are some warnings.
In an other article[2] I mentioned flake8[3] briefly, saying that I preferred a simple bash script to drive the execution of the various linters.
3: http://flake8.pycqa.org/en/latest/
I was concerned that flake8 did not include pylint (now I no longer care of course). Also, I did not like the fact that flake8 forces a specific version for all the linters it runs.
Well, I was wrong (again).
First off, I thought flake8 only combined the pyflakes and pycodestyle linters and nothing else. . Well, it also includes mccabe[4] by default.
4: https://pypi.org/project/mccabe/
Also, as explained in the FAQ[5], there are significant advantages in having the versions of the linters frozen this way.
5: http://flake8.pycqa.org/en/latest/faq.html#why-does-flake8-use-ranges-for-its-dependencies
Here are some other features I overlooked:
The last time I upgraded pylint, I only got one new warning. It was on a line looking like this:
my_set = set([elem for elem in my_list if some_condition(elem)])
The intent here is to build a unique set from a list of elements that satisfy a given condition.
pylint emitted the following warning:
R1718: Consider using a set comprehension (consider-using-set-comprehension)
Indeed, the code can also be written like this, using a *set comprehension* instead:
my_set = {elem for elem in my_list if some_condition(elem)}
Advantages:
Well, there is already a flake8 plugin called flake8-comprehension[6] that deals with these kind of issues.
6: https://pypi.org/project/flake8-comprehensions/
In fact, there are a bunch of flake8 plugins[7] available!
7: https://pypi.org/search/?q=flake8-
Plus, adding a new flake8 plugin is as easy as running `pipenv install --dev <plugin name>` and nothing else has to change :)
Itamar Turner-Trauring , in the comment section[8] on dev.to gave an interesting example:
8: https://dev.to/dmerejkowsky/bye-bye-pylint-4chh
# Note: I've taken the liberty of making the code a bit less abstract def greet(prefix, name): print(prefix, name) greeters = list() prefixes = ["Hi", "Hello", "Howdy"] for prefix in prefixes: greeters.append(lambda x: greet(prefix, x)) for greeter in greeters: greeter("world")
You may think the following code would print:
Hi world Hello world Howdy world
but instead it prints:
Howdy world Howdy world Howdy world
This has to do with how the closures work in Python, and the bug is indeed caught by pylint:
$ pylint example.py W0640: Cell variable `prefix` defined in loop (cell-var-from-loop)
I did not find a flake8 plugin that could catch this bug right away, but I found flake8-bugbear[9], a plugin to "find likely bugs and design problems".
9: https://github.com/PyCQA/flake8-bugbear
The plugin is well-written, well-tested and easy to contribute to.
I've already tried porting some pylint warnings to flake8-bugbear[10] and so far it has been much easier than I thought.
10: https://github.com/PyCQA/flake8-bugbear/pull/51
Therefore, next time I find a bug that could have been caught by inspecting the AST (which is what both flake8-bugbear and pylint do), I know how to write or contribute to a flake8 plugin in order to automatically catch it during CI.
Thus, I can slowly build a complete replacement for pylint, with just the warnings I care about, and without the configuration issues and false positives.
Bright future indeed!
----