PYLINT(1)
💅 PYLINT(1) 🎀
okay bestie, this is the Pylint manual page — literally the whole glow-up guide for pylint(1). Pull up a chair, grab a strawberry, we're getting into it. 🍓
🦋 NAME 🦋
pylint — Pylint Documentation, and yes the badge wall is part of the fit. Here's the whole stack verbatim, no cap:
[image] <https://github.com/pylint-dev/pylint/actions>[image] <https://codecov.io/gh/pylint-dev/pylint>[image: PyPI Package version]
[image] <https://pypi.python.org/pypi/pylint>[image: Documentation Status]
[image] <https://pylint.readthedocs.io/en/latest/?badge=latest>[image] <https://github.com/ambv/black>[image] <https://github.com/pylint-dev/pylint>[image: pre-commit.ci status]
[image] <https://results.pre-commit.ci/latest/github/pylint-dev/pylint/main>[image: CII Best Practices] [image] <https://bestpractices.coreinfrastructure.org/projects/6328>[image: OpenSSF Scorecard] [image] <https://api.securityscorecards.dev/projects/github.com/PyCQA/pylint>[image: Discord] [image] <https://discord.gg/qYxpadCgkx>
✨ WHAT IS PYLINT? ✨
Pylint is a static code analyser for Python 2 or 3 (the Wikipedia flex is here: <https://en.wikipedia.org/wiki/Static_code_analysis>). The latest version supports Python 3.10.0 and above, periodt.
So what does it actually do? It analyses your code without actually running it. Iconic. It checks for errors, enforces a coding standard, looks for code smells (<https://martinfowler.com/bliki/CodeSmell.html>), and can make suggestions about how the code could be refactored. It's giving "your most honest friend who also happens to be a compiler nerd." 💅
🌸 INSTALL 🌸
For command line use, pylint is installed with:
pip install pylint
Or if you want to also check spelling with enchant (heads up: you might need to install the enchant C library first — <https://pyenchant.github.io/pyenchant/install.html#installing-the-enchant-c-library>):
pip install pylint[spelling]
It can also be integrated in most editors or IDEs. More information can be found in the documentation: <https://pylint.readthedocs.io/en/latest/user_guide/installation/index.html>.
🧚 WHAT DIFFERENTIATES PYLINT? 🧚
Okay so this is the part where Pylint is lowkey a genius. It is not trusting your typing and is inferring the actual values of nodes (for a start, because there was no typing when pylint started off) using its internal code representation (astroid).
Like, if your code is import logging as argparse, Pylint can check and know that argparse.error(...) is in fact a logging call and not an argparse call. The way that works?? Obsessed. This makes pylint slower, but it also lets pylint find more issues if your code is not fully typed.
[inference] is the killer feature that keeps us using [pylint] in our project despite how painfully slow it is. — Realist pylint user <https://github.com/charliermarsh/ruff/issues/970#issuecomment-1381067064>, 2022
pylint, not afraid of being a little slower than it already is, is also a lot more thorough than other linters. There are more checks — including some opinionated ones that are deactivated by default but can be enabled using configuration. Main character energy fr fr. 👑
💖 HOW TO USE PYLINT 💖
Pylint isn't smarter than you: it may warn you about things that you have conscientiously done or check for some things that you don't care about. During adoption, especially in a legacy project where pylint was never enforced, it's best to start with the --errors-only flag, then disable convention and refactor messages with --disable=C,R, and progressively re-evaluate and re-enable messages as your priorities evolve. Slow and steady wins the vibe check. 🫶
Pylint is highly configurable and permits to write plugins in order to add your own checks (for example, for internal libraries or an internal rule). Pylint also has an ecosystem of existing plugins for popular frameworks and third-party libraries.
Note: Pylint supports the Python standard library out of the box. Third-party libraries are not always supported, so a plugin might be needed. A good place to start is PyPI, which often returns a plugin by searching forpylint <library>.pylint-pydantic(<https://pypi.org/project/pylint-pydantic>),pylint-django(<https://github.com/pylint-dev/pylint-django>) andpylint-sonarjson(<https://github.com/cnescatlab/pylint-sonarjson-catlab>) are examples of such plugins. More information about plugins and how to load them can be found at plugins (<https://pylint.readthedocs.io/en/latest/development_guide/how_tos/plugins.html#plugins>).
🌈 ADVISED LINTERS ALONGSIDE PYLINT 🌈
Projects that you might want to use alongside pylint include:
ruff(<https://github.com/astral-sh/ruff>) — really fast, with builtin auto-fix and a large number of checks taken from popular linters, but implemented in rustflake8(<https://github.com/PyCQA/flake8>) — a framework to implement your own checks in python using ast directlymypy(<https://github.com/python/mypy>) — typing checkspyright(<https://github.com/microsoft/pyright>) /pylance— typing checkspyre(<https://github.com/facebook/pyre-check>) — typing checksbandit(<https://github.com/PyCQA/bandit>) — security oriented checksblack(<https://github.com/psf/black>) — auto-formattingisort(<https://pycqa.github.io/isort/>) — auto-formattingautoflake(<https://github.com/myint/autoflake>) — automated removal of unused imports or variablespyupgrade(<https://github.com/asottile/pyupgrade>) — automated upgrade to newer python syntaxpydocstringformatter(<https://github.com/DanielNoord/pydocstringformatter>) — automated pep257
🎀 ADDITIONAL TOOLS INCLUDED IN PYLINT 🎀
Pylint ships with two additional tools, and they're both cute:
pyreverse(<https://pylint.readthedocs.io/en/latest/additional_tools/pyreverse/index.html>) — standalone tool that generates package and class diagrams.symilar(<https://pylint.readthedocs.io/en/latest/additional_tools/symilar/index.html>) — duplicate code finder that is also integrated in pylint.
💜 CONTRIBUTING 💜
We welcome all forms of contributions such as updates for documentation, new code, checking issues for duplicates or telling us that we can close them, confirming that issues still exist, creating issues because you found a bug or want a feature (<https://pylint.readthedocs.io/en/latest/contact.html#bug-reports-feedback>), etc. Everything is much appreciated!
Please follow the code of conduct (<https://github.com/pylint-dev/pylint/blob/main/CODE_OF_CONDUCT.md>) and check the Contributor Guides (<https://pylint.readthedocs.io/en/latest/development_guide/contribute.html>) if you want to make a code contribution.
🦋 SHOW YOUR USAGE 🦋
You can place this badge in your README to let others know your project uses pylint.
[image] <https://github.com/pylint-dev/pylint>.UNINDENT
Learn how to add a badge to your documentation in the badge documentation: <https://pylint.readthedocs.io/en/latest/user_guide/installation/badge.html>.
👑 LICENSE 👑
pylint is, with a few exceptions listed below, GPLv2 (<https://github.com/pylint-dev/pylint/blob/main/LICENSE>).
The icon files are licensed under the CC BY-SA 4.0 license (<https://creativecommons.org/licenses/by-sa/4.0/>):
doc/logo.png(<https://raw.githubusercontent.com/pylint-dev/pylint/main/doc/logo.png>)doc/logo.svg(<https://raw.githubusercontent.com/pylint-dev/pylint/main/doc/logo.svg>)
🌟 SUPPORT 🌟
Please check the contact information: <https://pylint.readthedocs.io/en/latest/contact.html>.
[image: Tidelift] [image]— Professional support for pylint is available as part of the Tidelift Subscription (<https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme>). Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools.
🍓 Tutorial 🍓
This tutorial is all about approaching coding standards with little or no knowledge of in-depth programming or the code standards themselves. It's the equivalent of skipping the manual and jumping right in. Lives were changed. 😭
The command line prompt for these examples is:
tutor Desktop$
⭐ Getting Started ⭐
Running Pylint with the --help arguments will give you an idea of the arguments available. Do that now, i.e.:
pylint --help
A couple of the options that we'll focus on here are:
Commands:
--help-msg=<msg-id>
--generate-toml-config
Messages control:
--disable=<msg-ids>
Reports:
--reports=<y or n>
--output-format=<format>
If you need more detail, you can also ask for an even longer help message:
pylint --long-help
Pay attention to the last bit of this longer help output. This gives you a hint of what Pylint is going to pick on:
Output:
Using the default text output, the message format is :
MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
There are 5 kind of message types :
* (C) convention, for programming standard violation
* (R) refactor, for bad code smell
* (W) warning, for python specific problems
* (E) error, for probable bugs in the code
* (F) fatal, if an error occurred which prevented pylint from doing
further processing.
When Pylint is first run on a fresh piece of code, a common complaint is that it is too noisy. The default configuration enforces a lot of warnings. We'll use some of the options we noted above to make it suit your preferences a bit better. 🫶
🌸 Your First Pylint'ing 🌸
We'll use a basic Python script with black (<https://github.com/psf/black>) already applied on it, as fodder for our tutorial. The starting code we will use is called simplecaesar.py and is here in its entirety:
#!/usr/bin/env python3
import string
shift = 3
choice = input("would you like to encode or decode?")
word = input("Please enter text")
letters = string.ascii_letters + string.punctuation + string.digits
encoded = ""
if choice == "encode":
for letter in word:
if letter == " ":
encoded = encoded + " "
else:
x = letters.index(letter) + shift
encoded = encoded + letters[x]
if choice == "decode":
for letter in word:
if letter == " ":
encoded = encoded + " "
else:
x = letters.index(letter) - shift
encoded = encoded + letters[x]
print(encoded)
Ok bestie, let's get started 🫶 If we run this:
tutor Desktop$ pylint simplecaesar.py
************* Module simplecaesar
simplecaesar.py:1:0: C0114: Missing module docstring (missing-module-docstring)
simplecaesar.py:5:0: C0103: Constant name "shift" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:8:0: C0103: Constant name "letters" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:9:0: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:13:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:15:12: C0103: Constant name "x" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:16:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:20:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:22:12: C0103: Constant name "x" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:23:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
-----------------------------------
Your code has been rated at 4.74/10
We can see the second line is:
"simplecaesar.py:1:0: C0114: Missing module docstring (missing-module-docstring)"
Which basically means that line 1 at column 0 violates the convention C0114. Another piece of information is the message symbol between the parens, missing-module-docstring.
Want to read up a bit more about that? Go back to the command line and try this 💅
tutor Desktop$ pylint --help-msg=missing-module-docstring
:missing-module-docstring (C0114): *Missing module docstring*
Used when a module has no docstring.Empty modules do not require a docstring.
This message belongs to the basic checker.
That one was a bit of a no-brainer, but we can also run into error messages where we are unfamiliar with the underlying code theory. 😭
✨ The Next Step ✨
Now that we got some configuration stuff out of the way, let's see what we can do with the remaining warnings. If we add a docstring to describe what the code is meant to do, that will help. There are invalid-name messages that we will get to later. Here is the updated code:
#!/usr/bin/env python3
"""This script prompts a user to enter a message to encode or decode
using a classic Caesar shift substitution (3 letter shift)"""
import string
shift = 3
choice = input("would you like to encode or decode?")
word = input("Please enter text")
letters = string.ascii_letters + string.punctuation + string.digits
encoded = ""
if choice == "encode":
for letter in word:
if letter == " ":
encoded = encoded + " "
else:
x = letters.index(letter) + shift
encoded = encoded + letters[x]
if choice == "decode":
for letter in word:
if letter == " ":
encoded = encoded + " "
else:
x = letters.index(letter) - shift
encoded = encoded + letters[x]
print(encoded)
Here is what happens when we run it:
tutor Desktop$ pylint simplecaesar.py
************* Module simplecaesar
simplecaesar.py:8:0: C0103: Constant name "shift" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:11:0: C0103: Constant name "letters" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:12:0: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:16:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:18:12: C0103: Constant name "x" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:19:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:23:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:25:12: C0103: Constant name "x" doesn't conform to UPPER_CASE naming style (invalid-name)
simplecaesar.py:26:12: C0103: Constant name "encoded" doesn't conform to UPPER_CASE naming style (invalid-name)
------------------------------------------------------------------
Your code has been rated at 5.26/10 (previous run: 4.74/10, +0.53)
Nice! 🎉 Pylint told us how much our code rating has improved since our last run, and we're down to just the invalid-name messages.
There are fairly well defined conventions around naming things like instance variables, functions, classes, etc. The conventions focus on the use of UPPERCASE and lowercase as well as the characters that separate multiple words in the name. This lends itself well to checking via a regular expression, thus the should match (([A-Z_][A-Z1-9_]*)|(__.*__))$.
In this case Pylint is telling us that those variables appear to be constants and should be all UPPERCASE. This is an in-house convention that has lived with Pylint since its inception. You too can create your own in-house naming conventions — but for the purpose of this tutorial, we want to stick to the PEP 8 standard (https://peps.python.org/pep-0008/>). In this case, the variables we declared should follow the convention of all lowercase. The appropriate rule would be something like: "should match [a-z_][a-z0-9_]{2,30}$". Notice the lowercase letters in the regular expression (a-z versus A-Z). 👑
If we run that rule using a --const-rgx='[a-z\_][a-z0-9\_]{2,30}$' option, it will now be quite quiet:
tutor Desktop$ pylint simplecaesar.py --const-rgx='[a-z\_][a-z0-9\_]{2,30}$'
************* Module simplecaesar
simplecaesar.py:18:12: C0103: Constant name "x" doesn't conform to '[a-z\\_][a-z0-9\\_]{2,30}$' pattern (invalid-name)
simplecaesar.py:25:12: C0103: Constant name "x" doesn't conform to '[a-z\\_][a-z0-9\\_]{2,30}$' pattern (invalid-name)
------------------------------------------------------------------
Your code has been rated at 8.95/10 (previous run: 5.26/10, +3.68)
You can read up on regular expressions at https://docs.python.org/library/re.html or use a website to help you at https://regex101.com/ 🌸
Tip: 💡 It would really be a pain to specify that regex on the command line all the time, particularly if we're using many other options. That's what a configuration file is for. We can configure our Pylint to store our options for us so we don't have to declare them on the command line. Using a configuration file is a nice way of formalizing your rules and quickly sharing them with others. Invokingpylint --generate-toml-configwill create a sample.tomlsection with all the options set and explained in comments. This can then be added to yourpyproject.tomlfile or any other.tomlfile pointed to with the--rcfileoption. 🎀
🌈 INSTALLATION 🌈
Pylint can be installed:
- As a command line tool (
#installation) - Integrated in your editor/ide (
#ide-integration) - As a pre-commit hook (
#pre-commit-integration) - For multiple python interpreters in your continuous integration (
#continuous-integration)
🧚 Command line installation 🧚
Pylint is installable using a package manager. Your package manager will find a version that works with your interpreter. We recommend pip:
pip install pylint
Or if you want to also check spelling with enchant (you might need to install the enchant C library — https://pyenchant.github.io/pyenchant/install.html#installing-the-enchant-c-library):
pip install pylint[spelling]
The newest pylint supports all Python interpreters that are not past end of life. ✨
We recommend to use the latest interpreter because we rely on the ast builtin module that gets better with each new Python interpreter. For example a Python 3.6 interpreter can't analyse 3.8 syntax (amongst others, because of the new walrus operator) while a 3.8 interpreter can also deal with Python 3.6. See using pylint with multiple interpreters (#continuous-integration) for more details.
Note: 📌 You can also use conda or your system package manager on debian based OS. These package managers lag a little behind as they are maintained by a separate entity on a slower release cycle. Not me being patient about it, just facts.
conda install pylint
sudo apt-get install pylint
🦋 Editor and IDE integration 🦋
Pylint can be integrated in various editors and IDE's. Below you can find tutorials for some of the most common ones. Slay, honestly. 💅
- Eclipse —
https://www.pydev.org/manual_adv_pylint.html - Emacs —
https://www.emacswiki.org/emacs/PythonProgrammingInEmacs - Eric IDE —
https://eric-ide.python-projects.org/in the Project > Check menu, - gedit —
https://launchpad.net/gedit-pylint-2(another option for gedit:https://wiki.gnome.org/Apps/Gedit/PylintPlugin) - Flymake —
#pylint-in-flymake - Jupyter Notebook —
https://github.com/nbQA-dev/nbQA - Komodo —
https://mateusz.loskot.net/post/2006/01/15/running-pylint-from-komodo/ - Pycharm —
https://stackoverflow.com/a/46409649/2519059 - PyDev —
https://www.pydev.org/manual_adv_pylint.html - pyscripter —
https://github.com/pyscripter/pyscripterin the Tool -> Tools menu. - Spyder —
https://docs.spyder-ide.org/current/panes/pylint.htmlin the View -> Panes -> Static code analysis - Sublime Text —
https://packagecontrol.io/packages/SublimeLinter-pylint - TextMate —
#pylint-in-textmate - Vim —
https://www.vim.org/scripts/script.php?script_id=891 - Visual Studio Code —
https://code.visualstudio.com/docs/python/lintingin the Preferences -> Settings menu - Visual Studio Code Pylint Extension —
https://marketplace.visualstudio.com/items?itemName=ms-python.pylint - Visual Studio —
https://docs.microsoft.com/visualstudio/python/code-pylintin the Python > Run PyLint command on a project's context menu. - WingIDE —
https://wingware.com/doc/warnings/external-checkers
🦋 Using Pylint through Flymake in Emacs
Warning:epylintwas deprecated in 2.16.0 and targeted for deletion in 3.0.0. All emacs and flymake related files were removed and their support will now happen in an external repository:https://github.com/emacsorphanage/pylint. 💀
🎀 Integrate Pylint with TextMate
Bestie, install Pylint in the usual way first:
pip install pylint
Then install the Python bundle for TextMate (https://github.com/textmate/python.tmbundle):
- select TextMate > Preferences
- select the Bundles tab
- find and tick the Python bundle in the list
You should now see it in Bundles > Python. ✨
In Preferences, select the Variables tab. If a TM_PYCHECKER variable is not already listed, add it, with the value pylint.
The default keyboard shortcut to run the syntax checker is Control-Shift-V — open a .py file in Textmate, and try it.
You should see the output in a new window:
PyCheckMate 1.2 – Pylint 1.4.4
No config file found, using default configuration
Then all is well, and most likely Pylint will have expressed some opinions about your Python code (or will exit with 0 if your code already conforms to its expectations). 🫶
If you receive a message:
Please install PyChecker, PyFlakes, Pylint, PEP 8 or flake8 for more
extensive code checking.
That means that Pylint wasn't found — which is likely an issue with command paths. TextMate needs be looking for Pylint on the right paths. 🙈
Check where Pylint has been installed, using which:
$ which pylint
/usr/local/bin/pylint
The output will tell you where Pylint can be found; in this case, in /usr/local/bin.
- select TextMate > Preferences
- select the Variables tab
- find and check that a
PATHvariable exists, and that it contains the appropriate path (if the path to Pylint were/usr/local/bin/pylintas above, then the variable would need to contain/usr/local/bin). An actual example in this case might be$PATH:/opt/local/bin:/usr/local/bin:/usr/texbin, which includes other paths.
... and try running Pylint again. 💅
🧚 Pre-commit integration
pylint can be used as a pre-commit hook. We however discourage it, because pylint — due to its speed — is more suited to a continuous integration job or a git pre-push hook, especially if your repository is large. 😴
Since pylint needs to import modules and dependencies to work correctly, the hook only works with a local installation of pylint (in your environment). It means it can't be used with pre-commit.ci, and you will need to add the following to your .pre-commit-config.yaml:
.. sourcecode:: yaml
ci: skip: [pylint]
Another limitation is that pylint should analyse all your code at once in order to best infer the actual values that result from calls. If only some of the files are given, pylint might miss a particular value's type and produce inferior inference for the subset. Since pre-commit slices the files given to it in order to parallelize the processing, the result can be degraded. It can also be unexpectedly different when the file set changes because the new slicing can change the inference. Thus the require_serial option should be set to true if correctness and determinism are more important than parallelization to you. 🧠
If you installed pylint locally it can be added to .pre-commit-config.yaml as follows:
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]
require_serial: true
args:
[
"-rn", # Only display messages
"-sn", # Don't display the score
]
You can use args to pass command line arguments as described in the Tutorial. A hook with more arguments could look something like this:
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]
args:
[
"-rn", # Only display messages
"-sn", # Don't display the score
"--rcfile=pylintrc", # Link to your config file
"--load-plugins=pylint.extensions.docparams", # Load an extension
]
🌈 Installation with multiple interpreters
It's possible to analyse code written for older or multiple interpreters by using the py-version option and setting it to the oldest supported interpreter of your code. For example you can check that there are no f-strings in Python 3.5 code using Python 3.8 with an up-to-date pylint — even if Python 3.5 is past end of life (EOL) and the version of pylint you use is not compatible with it. 🕰️
We do not guarantee that py-version will work for all EOL Python interpreters indefinitely (for anything before Python 3.5, it probably won't). If a newer version does not work for you, the best available pylint might be an old version that works with your old interpreter — but without the bug fixes and features of later versions. 💀
👑 Show your usage
You can place this badge in your README to let others know your project uses pylint.
[image]
https://github.com/pylint-dev/pylint
Use the badge in your project's README.md (or any other Markdown file):
[](https://github.com/pylint-dev/pylint)
Use the badge in your project's README.rst (or any other rst file):
.. image:: https://img.shields.io/badge/linting-pylint-yellowgreen
:target: https://github.com/pylint-dev/pylint
If you use GitHub Actions, and one of your CI workflows begins with name: pylint, you can use GitHub's workflow status badges (https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge#using-the-workflow-file-name) to show an up-to-date indication of whether pushes to your default branch pass pylint. For more detailed information, check the documentation. ⭐
🍓 Upgrading pylint
You should probably set the version of pylint in your development environment in order to choose when you actually upgrade pylint's warnings. pylint is following semver versioning. But we can't guarantee that the output between versions will stay the same. What this means is that:
- In patch release (
1.2.3), we only fix false positives and crashes.
You can expect less messages if you set the minor and upgrade to a new patch version. But still, if you enable useless-suppression it still means you can get a new useless-suppression when a false positive that you disabled is now fixed. Also, if a library you're using was upgraded and is understood better or worse than the previous one, you could get new messages too. 🌀
- In minor releases (
1.2.0), we add checks, remove checks, drop python interpreters past end of life, upgradeastroidminor/major versions and fix false negatives.
You can expect a lot more change in output — the main one being new checks.
- In major releases (
1.0.0) we change everything else (pylintoptions, json output, dev API...) while still trying to minimize disruption.
You could have to change the command you're launching or the plugin and editor integration you're using. 😭
🚀 USAGE
💜 Running Pylint
On module packages or directories
pylint is meant to be called from the command line. The usage is
pylint [options] modules_or_packages
By default the pylint command only accepts a list of python modules and packages. On versions below 2.15, specifying a directory that is not an explicit package (with __init__.py) results in an error:
pylint mydir
************* Module mydir
mydir/__init__.py:1:0: F0010: error while code parsing: Unable to load file mydir/__init__.py:
[Errno 2] No such file or directory: 'mydir/__init__.py' (parse-error)
Thus, on versions before 2.15 using the --recursive=y option allows for linting a namespace package:
pylint --recursive=y mydir mymodule mypackage
This option makes pylint attempt to discover all modules (files ending with .py extension) and all explicit packages (all directories containing a __init__.py file). 🔍
pylint will not import this package or module, but it does use Python internals to locate them and as such is subject to the same rules and configuration. You should pay attention to your PYTHONPATH, since it is a common error to analyze an installed version of a module instead of the development version. 🫠
On files
It is also possible to analyze Python files, with a few restrictions. As a convenience, you can give it a file name if it's possible to guess a module name from the file's path using the python path. Some examples:
pylint mymodule.pyshould always work since the current working directory is automatically added on top of the python pathpylint directory/mymodule.pywill work if:directoryis a python package (i.e. has an__init__.pyfile), an implicit namespace package, or ifdirectoryis in the python path.
🌸 With implicit namespace packages
If the analyzed sources use implicit namespace packages (PEP 420), the source root(s) should be specified using the --source-roots option. Otherwise, the package names are detected incorrectly, since implicit namespace packages don't contain an __init__.py. 💅
🎀 Globbing support
It is also possible to specify both directories and files using globbing patterns:
pylint [options] packages/*/src
⚡ Command line options
First of all, we have two basic (but useful) options. 👑
--version— show program's version number and exit-h,--help— show help about the command line options
pylint is architected around several checkers. You can disable a specific checker, or some of its messages, or message categories, by specifying --disable=<symbol>. If you want to enable only some checkers or some message symbols, first use --disable=all then --enable=<symbol> — with <symbol> being a comma-separated list of checker names and message symbols. See the list of available features for a description of provided checkers with their functionalities. The --disable and --enable options can be used with comma-separated lists mixing checkers, message ids and categories like -d C,W,no-error,design. 🔥
It is possible to disable all messages with --disable=all. This is useful to enable only a few checkers or a few messages by first disabling everything, and then re-enabling only what you need. ✨
Each checker has, like, its own little pile of specific options — and those can take a yes/no value, an integer, a Python regular expression, or a comma-separated list of values (those are usually there so you can override a regex in special cases, iykyk). For the full list of options, just hit --help ✨
Specifying every single option that matches your setup and your coding standards is so tedious, honestly, so there's a config file for setting default values. You can point at one on the command line with the --rcfile option. Otherwise Pylint goes hunting for a config file in this order and uses the first one it finds:
pylintrcin the current working directorypylintrc.tomlin the current working directory, as long as it has at least onetool.pylint.section.pylintrcin the current working directory.pylintrc.tomlin the current working directory, as long as it has at least onetool.pylint.sectionpyproject.tomlin the current working directory, as long as it has at least onetool.pylint.section. Thepyproject.tomlhas to prepend section names withtool.pylint., like[tool.pylint.'MESSAGES CONTROL']. Those can also be passed in on the command line.setup.cfgin the current working directory, as long as it has at least onepylint.sectiontox.iniin the current working directory, as long as it has at least onepylint.section- Pylint searches for the
pyproject.tomlfile up the directory hierarchy until it's found, or a.git/.hgdirectory is found, or the file system root is approached - If the current working directory is inside a Python package, Pylint searches up the hierarchy of Python packages until it finds a
pylintrcfile. This is the whole "coding standards on a module-by-module basis" thing 💅 Of course, a directory only counts as a Python package if it contains an__init__.pyfile. - The file named by the environment variable
PYLINTRC - if you have a home directory which isn't
/root:.pylintrcin your home directory.config/pylintrcin your home directory
/etc/pylintrc
The --generate-toml-config option will generate a commented configuration file on standard output based on the current configuration and then exit. That includes:
- Any configuration file found as explained above
- Options appearing before
--generate-toml-configon the Pylint command line
And of course you can also just start from the default values and hand-tune the config yourself 🎀
Other useful global options include:
--ignore=<file[,file...]>— files or directories to skip. These should be base names, not paths, bestie.--output-format=<format>— pick the output format (text,json,custom).--msg-template=<template>— modify the text output message template.--list-msgs— generate pylint's messages.--list-msgs-enabled— display a list of which messages are enabled and which are disabled with the given configuration.--full-documentation— generate pylint's full documentation, in reST format.
Parallel execution
You can speed up Pylint's execution, no cap. If the computer you're running on has more than one CPU, the work of checking all the files can be spread across all the cores through Pylint's sub-processes 🚀
This is exposed via the -j command-line parameter. If the number you give is 0, then the total number of CPUs gets autodetected and used.
Example:
pylint -j 4 mymodule1.py mymodule2.py mymodule3.py mymodule4.py
This spawns 4 parallel Pylint sub-processes, where each provided module gets checked in parallel. Problems discovered by checkers are not displayed immediately — they show up right after a module finishes being checked.
You can also do your own parallelization by launching pylint multiple times on subsets of your files (like pre-commit does with the default require_serial=false). But be aware, though: pylint should analyse all your code at once to best infer the actual values that result from calls. If only some of the files are given, pylint might miss a particular value's type and produce inferior inference for that subset. It can also come out unexpectedly different when the file set changes, because the new slicing can change the inference. So don't do this if correctness and determinism are important to you 🙈
Exit codes
Pylint returns bit-encoded exit codes, so it's giving math but make it diagnostics:
0— no error1— fatal message issued2— error message issued4— warning message issued8— refactor message issued16— convention message issued32— usage error
For example, an exit code of 20 means there was at least one warning message (4) and at least one convention message (16) and nothing else 🔥
Pylint output
Output options
Output by default goes to stdout. The simplest way to output to a file is with the --output=<filename> option.
The default format for the output is raw text. You can change that by passing pylint the --output-format=<value> option. Possible values are:
textparseablecolorizedjson2: improved json formatjson: old json formatmsvs: visual studiogithub: GitHub action messages (https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)
Multiple output formats can be used at the same time by passing a comma-separated list of formats to --output-format. That output can be redirected to a file by giving a filename after a colon 🦋
For example, to save a json report to somefile.json and print a colorized report to stdout at the same time:
--output-format=json:somefile.json,colorized
Custom message formats
You can customize the exact way information is displayed using the --msg-template=<format string> option. The format string uses the Python new format syntax (https://docs.python.org/2/library/string.html#formatstrings) and the following fields are available:
path — relative path to the file
abspath — absolute path to the file
line — line number
column — column number
end_line — line number of the end of the node
end_column — column number of the end of the node
module — module name
obj — object within the module (if any)
msg — text of the message
msg_id — the message code (eg. I0011)
symbol — symbolic name of the message (eg. locally-disabled)
C — one letter indication of the message category
category — fullname of the message category
For example, the former (pre 1.0) default format can be obtained with:
pylint --msg-template='{msg_id}:{line:3d},{column}: {obj}: {msg}'
A few other examples:
- the default format:
{path}:{line}:{column}: {msg_id}: {msg} ({symbol}) - Visual Studio compatible format (former
'msvs'output format):{path}({line}): [{msg_id}{obj}] {msg} - Parseable (Emacs and all, former
'parseable'output format) format:{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}
The --msg-template option can only be combined with text-based reporters (--output-format either unspecified or one of: parseable, colorized or msvs). If both --output-format and --msg-template are specified, the --msg-template option takes precedence over the default line format defined by the reporter class.
If end_line or end_column are None, they'll be represented as an empty string by the default TextReporter 💖
Source code analysis section
For each python module, Pylint first displays a few '*' characters followed by the name of the module. Then comes a number of messages with the following format:
MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE
You can get another output format — useful since it's recognized by most editors or other development tools — using the --output-format=parseable option.
The message type can be:
[I]nformationalmessages that Pylint emits (do not contribute to your analysis score)[R]efactorfor a "good practice" metric violation[C]onventionfor coding standard violation[W]arningfor stylistic problems, or minor programming issues[E]rrorfor important programming issues (i.e. most probably bug)[F]atalfor errors which prevented further processing
Sometimes the line of code which caused the error is displayed with a caret pointing to the error. This may be generalized in future versions of Pylint 👑
Example (extracted from a run of Pylint on itself...):
************* Module pylint.checkers.format
W: 50: Too long line (86/80)
W:108: Operator not followed by a space
print >>sys.stderr, 'Unable to match %r', line
^
W:141: Too long line (81/80)
W: 74:searchall: Unreachable code
W:171:FormatChecker.process_tokens: Redefining built-in (type)
W:150:FormatChecker.process_tokens: Too many local variables (20/15)
W:150:FormatChecker.process_tokens: Too many branches (13/12)
💅 Reports section 💅
Okay so after the analysis messages, Pylint can ALSO flex by showing you a whole set of reports — each one zooms in on a different aspect of your project, like how many messages you got per category, or your module dependencies. You turn these on with --reports=y or the cute shortcut -ry ✨
For example, the metrics report gives you the tea from the current run:
- how many modules got processed
- for each module, the percentage of errors and warnings
- the total number of errors and warnings
- the percentage of classes, functions and modules that have docstrings — plus a glow-up comparison from the previous run 📈
- the percentage of classes, functions and modules with a correct name (per the coding standard) — also with a comparison from the previous run
- a list of external dependencies it found in the code, and where they show up
💯 Score section 💯
And finally, Pylint gives your code a global evaluation score out of a max of 10.0 — like a vibe check but with math 🎀
You can mute that whole thing with --score=n, or the shorthand -sn.
And if you're feeling rebellious, you can override the evaluation formula itself with --evaluation=<python_expression>.
📬 Messages 📬
👀 Messages overview 👀
Pylint can emit the following messages:
💀 Fatal 💀
All messages in the fatal category:
astroid-error / F0002
Message emitted:
%s: %s
Description:
Used when an unexpected error occurred while building the Astroid representation. This is usually accompanied by a traceback. Please report such errors !
Additional details:
This is a message linked to an internal problem in pylint. There's nothing to change in your code, but maybe in pylint's configuration or installation.
Created by the main checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py
config-parse-error / F0011
Message emitted:
error while parsing the configuration: %s
Description:
Used when an exception occurred while parsing a pylint configuration file.
Additional details:
This is a message linked to a problem in your configuration, not your code.
Created by the main checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py
fatal / F0001
Message emitted:
%s
Description:
Used when an error occurred preventing the analysis of a module (unable to find it for instance).
Additional details:
This is a message linked to an internal problem in pylint. There's nothing to change in your code.
Created by the main checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py
method-check-failed / F0202
Message emitted:
Unable to check methods signature (%s / %s)
Description:
Used when Pylint has been unable to check methods signature compatibility for an unexpected reason. Please report this kind if you don't make sense of it.
Additional details:
This is a message linked to an internal problem in pylint. There's nothing to change in your code.
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
parse-error / F0010
Message emitted:
error while code parsing: %s
Description:
Used when an exception occurred while building the Astroid representation which could be handled by astroid.
Additional details:
This is a message linked to an internal problem in pylint. There's nothing to change in your code.
Created by the main checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py
All renamed messages in the fatal category:
old-import-error / F0401
old-import-error has been renamed. The new message can be found at:
import-error / E0401
Message emitted:
Unable to import %s
Description:
Used when pylint has been unable to import a module.
Problematic code:
from patlib import Path # [import-error]
Correct code:
from pathlib import Path
Additional details:
This can happen if you're importing a package that is not installed in your environment, or if you made a typo.
The solution is to install the package via pip/setup.py/wheel or fix the typo.
Created by the imports checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py
🚨 Error 🚨
All messages in the error category:
abstract-class-instantiated / E0110
Message emitted:
Abstract class %r with abstract methods instantiated
Description:
Used when an abstract class with abc.ABCMeta as metaclass has abstract methods and is instantiated.
Problematic code:
import abc
class Animal(abc.ABC):
@abc.abstractmethod
def make_sound(self):
pass
sheep = Animal() # [abstract-class-instantiated]
Correct code:
import abc
class Animal(abc.ABC):
@abc.abstractmethod
def make_sound(self):
pass
class Sheep(Animal):
def make_sound(self):
print("bhaaaaa")
sheep = Sheep()
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py
access-member-before-definition / E0203
Message emitted:
Access to member %r before its definition line %s
Description:
Used when an instance member is accessed before it's actually assigned.
Problematic code:
class Unicorn:
def __init__(self, fluffiness_level):
if self.fluffiness_level > 9000: # [access-member-before-definition]
print("It's OVER-FLUFFYYYY ! *crush glasses*")
self.fluffiness_level = fluffiness_level
Correct code:
class Unicorn:
def __init__(self, fluffiness_level):
self.fluffiness_level = fluffiness_level
if self.fluffiness_level > 9000:
print("It's OVER-FLUFFYYYY ! *crush glasses*")
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
assigning-non-slot / E0237
Message emitted:
Assigning to attribute %r not defined in class slots
Description:
Used when assigning to an attribute not defined in the class slots.
Problematic code:
class Student:
__slots__ = ("name",)
def __init__(self, name, surname):
self.name = name
self.surname = surname # [assigning-non-slot]
self.setup()
def setup(self):
pass
Correct code:
class Student:
__slots__ = ("name", "surname")
def __init__(self, name, surname):
self.name = name
self.surname = surname
self.setup()
def setup(self):
pass
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
assignment-from-no-return / E1111
Message emitted:
Assigning result of a function call, where the function has no return
Description:
Used when an assignment is done on a function call but the inferred function doesn't return anything.
Problematic code:
def add(x, y):
print(x + y)
value = add(10, 10) # [assignment-from-no-return]
Correct code:
def add(x, y):
return x + y
value = add(10, 10)
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
assignment-from-none / E1128
Message emitted:
Assigning result of a function call, where the function returns None
Description:
Used when an assignment is done on a function call but the inferred function returns nothing but None.
Problematic code:
def function():
return None
f = function() # [assignment-from-none]
Correct code:
def function():
return None
f = function() if function() else 1
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
async-context-manager-with-regular-with / E1145
Message emitted:
Context manager '%s' is async and should be used with 'async with'.
Description:
Used when an async context manager is used with a regular with statement instead of async with.
Problematic code:
from contextlib import asynccontextmanager
@asynccontextmanager
async def async_context():
yield
with async_context(): # [async-context-manager-with-regular-with]
print("This will cause an error at runtime")
Correct code:
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def async_context():
yield
async def main():
async with async_context():
print("This works correctly")
Related links:
- PEP 492 - Coroutines with
asyncandawaitsyntax —https://peps.python.org/pep-0492/ contextlib.asynccontextmanager—https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
await-outside-async / E1142
Message emitted:
'await' should be used within an async function
Description:
Emitted when await is used outside an async function.
Problematic code:
import asyncio
def main():
await asyncio.sleep(1) # [await-outside-async]
Correct code:
import asyncio
async def main():
await asyncio.sleep(1)
Related links:
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
bad-configuration-section / E0014
Message emitted:
Out-of-place setting encountered in top level configuration-section '%s' : '%s'
Description:
Used when we detect a setting in the top level of a toml configuration that shouldn't be there.
Additional details:
This error was raised when we encountered an unexpected value type in a toml configuration between pylint 2.12 and pylint 2.14 (before the argparse refactor).
Created by the main checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py
bad-except-order / E0701
Message emitted:
Bad except clauses order (%s)
Description:
...and that's exactly why the order matters, bestie! If you don't fix the order, some exceptions may literally not be caught by the most specific handler. 😭
Problematic code:
try:
print(int(input()))
except Exception:
raise
except TypeError: # [bad-except-order]
# This block cannot be reached since TypeError exception
# is caught by previous exception handler.
raise
Correct code:
try:
print(int(input()))
except TypeError:
raise
except Exception:
raise
Created by the exceptions checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py 🫶
💜 bad-exception-cause / E0705 💜
Message emitted:
Exception cause set to something which is not an exception, nor None
Description:
Used when using the syntax "raise ... from ...", where the exception cause is not an exception, nor None. Like girl, that's not a cause, that's just... a value. 💀
Problematic code:
def divide(x, y):
result = 0
try:
result = x / y
except ZeroDivisionError:
# +1: [bad-exception-cause]
raise ValueError(f"Division by zero when dividing {x} by {y} !") from result
return result
Correct code:
def divide(x, y):
result = 0
try:
result = x / y
except ZeroDivisionError as exc:
raise ValueError(f"Division by zero when dividing {x} by {y} !") from exc
return result
Related links:
- The raise statement —
https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement - Explicit Exception Chaining —
https://peps.python.org/pep-3134/#explicit-exception-chainingper PEP 3134
Created by the exceptions checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py ✨
🎀 bad-format-character / E1300 🎀
Message emitted:
Unsupported format character %r (%#02x) at index %d
Description:
Used when an unsupported format character is used in a format string. Ma'am, that character does not exist, please pick a real one. 🙈
Problematic code:
print("%s %z" % ("hello", "world")) # [bad-format-character]
Correct code:
print("%s %s" % ("hello", "world"))
Additional details:
This check is currently only active for "old-style" string formatting as seen in the examples. See Issue #6085 — https://github.com/pylint-dev/pylint/issues/6085 — for more information. (iykyk, old-style is a whole vibe and not in the good way 💅)
Related links:
- Format String Syntax —
https://docs.python.org/3/library/string.html#formatstrings - PyFormat —
https://pyformat.info/
Created by the string checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py 🌸
⭐ bad-plugin-value / E0013 ⭐
Message emitted:
Plugin '%s' is impossible to load, is it installed ? ('%s')
Description:
Used when a bad value is used in 'load-plugins'.
Additional details:
One of your pylint plugins cannot be loaded. There's nothing to change in your code, but your pylint configuration or installation has an issue. Not me debugging your code when it's your config the whole time 😭
For example, there might be a typo. The following config:
[MAIN]
load-plugins = pylint.extensions.bad_biultin
Should be:
[MAIN]
load-plugins = pylint.extensions.bad_builtin
Or the plugin you added is not importable in your environment.
Created by the main checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py 💜
🦋 bad-reversed-sequence / E0111 🦋
Message emitted:
The first reversed() argument is not a sequence
Description:
Used when the first argument to reversed() builtin isn't a sequence (does not implement __reversed__, nor __getitem__ and __len__
Problematic code:
reversed({1, 2, 3, 4}) # [bad-reversed-sequence]
Correct code:
reversed([1, 2, 3, 4])
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py 🫧
🍓 bad-str-strip-call / E1310 🍓
Message emitted:
Suspicious argument in %s.%s call
Description:
The argument to a str.{l,r,}strip call contains a duplicate character,
Problematic code:
hello_world.py:
"Hello World".strip("Hello") # [bad-str-strip-call]
# >>> ' World'
remove_abc_from_both_side.py:
"abcbc def bacabc".strip("abcbc ") # [bad-str-strip-call]
# >>> 'def'
Correct code:
hello_world.py:
"Hello World".strip("Helo")
# >>> ' World'
remove_abc_from_both_side.py:
"abcbc def bacabc".strip("abc ")
# >>> 'def'
Additional details:
A common misconception is that str.strip('Hello') removes the substring 'Hello' from the beginning and end of the string. This is not the case. From the documentation — https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.strip:
The chars argument is not a prefix or suffix; rather, all combinations of its values are stripped
Duplicated characters in the str.strip call, besides not having any effect on the actual result, may indicate this misunderstanding. Bestie it's giving "I didn't read the docs" and honestly same, but now we know 💅
Related links:
- Documentation:
str.strip([chars])—https://docs.python.org/3/library/stdtypes.html?highlight=strip#str.strip
Created by the string checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py 🌺
🔥 bad-string-format-type / E1307 🔥
Message emitted:
Argument %r does not match format type %r
Description:
Used when a type required by format string is not suitable for actual argument type. The format said one thing, you handed it another, and now everyone's looking at each other 😭
Problematic code:
print("%d" % "1") # [bad-string-format-type]
Correct code:
print("%d" % 1)
Additional details:
This check is currently only active for "old-style" string formatting as seen in the examples. See Issue #6085 — https://github.com/pylint-dev/pylint/issues/6163 — for more information.
Related links:
- Format String Syntax —
https://docs.python.org/3/library/string.html#formatstrings - PyFormat —
https://pyformat.info/
Created by the string checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py ✨
👑 bad-super-call / E1003 👑
Message emitted:
Bad first argument %r given to super()
Description:
Used when another argument than the current class is given as first argument of the super builtin. Girl who is that... that's not your class 💀
Problematic code:
class Animal:
pass
class Tree:
pass
class Cat(Animal):
def __init__(self):
super(Tree, self).__init__() # [bad-super-call]
super(Animal, self).__init__()
Correct code:
class Animal:
pass
class Tree:
pass
class Cat(Animal):
def __init__(self):
super(Animal, self).__init__()
Additional details:
In Python 2.7, super() has to be called with its own class and self as arguments (super(Cat, self)), which can lead to a mix up of parent and child class in the code.
In Python 3 the recommended way is to call super() without arguments (see also super-with-arguments).
One exception is calling super() on a non-direct parent class. This can be used to get a method other than the default method returned by the mro().
Related links:
- Documentation for
super()—https://docs.python.org/3/library/functions.html#super
Created by the newstyle checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/newstyle.py 🦋
🌈 bare-name-capture-pattern / E1901 🌈
Message emitted:
The name capture `case %s` makes the remaining patterns unreachable. Use a dotted name (for example an enum) to fix this.
Description:
Emitted when a name capture pattern is used in a match statement and there are case statements below it. It ate all the other cases, no crumbs, nothing left for them 😭
Problematic code:
red = 0
green = 1
blue = 2
def func(color):
match color:
case red: # [bare-name-capture-pattern]
print("I see red!")
case green: # [bare-name-capture-pattern]
print("Grass is green")
case blue:
print("I'm feeling the blues :(")
Correct code:
from enum import Enum
class Color(Enum):
RED = 0
GREEN = 1
BLUE = 2
def func(color: Color) -> None:
match color:
case Color.RED:
print("I see red!")
case Color.GREEN:
print("Grass is green")
case Color.BLUE:
print("I'm feeling the blues :(")
Related links:
Created by the match_statements checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/match_statements_checker.py 🎀
🧚 bidirectional-unicode / E2502 🧚
Message emitted:
Contains control characters that can permit obfuscated code executed differently than displayed
Description:
bidirectional unicode are typically not displayed characters required to display right-to-left (RTL) script (i.e. Chinese, Japanese, Arabic, Hebrew, ...) correctly. So can you trust this code? Are you sure it displayed correctly in all editors? If you did not write it or your language is not RTL, remove the special characters, as they could be used to trick you into executing code, that does something else than what it looks like. More Information: https://en.wikipedia.org/wiki/Bidirectional_text https://trojansource.codes/
🚨 Okay this one is actually serious, bestie. This is the Trojan Source thing. Read the links. For real. 🔥
Problematic code:
# +1: [bidirectional-unicode]
example = "x" * 100 # "x" is assigned
Correct code:
example = "x[U+2194]" * 100
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py 💜
💀 broken-collections-callable / E6005 💀
Message emitted:
'collections.abc.Callable' inside Optional and Union is broken in 3.9.0 / 3.9.1 (use 'typing.Callable' instead)
Description:
collections.abc.Callable inside Optional and Union is broken in Python 3.9.0 and 3.9.1. Use typing.Callable for these cases instead. https://bugs.python.org/issue42965
Problematic code:
from collections.abc import Callable
from typing import Optional
def func() -> Optional[Callable[[int], None]]: # [broken-collections-callable]
...
Correct code:
from typing import Callable, Optional
def func() -> Optional[Callable[[int], None]]: ...
Configuration file:
[main]
py-version=3.9
load-plugins=pylint.extensions.typing
Related links:
broken-noreturn / E6004
Okay so this one is about typing.NoReturn being straight-up broken inside compound types in Python 3.7.0 and 3.7.1 — literally only those two micro-versions, which is so specific and so annoying. If you're not depending on runtime introspection, the move is to use a string annotation instead, e.g. Callable[..., 'NoReturn']. Full tea is here: bpo-42965 — https://bugs.python.org/issue42965.
Note: this message is emitted by the optionaltypingchecker, which requires thepylint.extensions.typingplugin to be loaded. 👑
Message emitted:
'NoReturn' inside compound types is broken in 3.7.0 / 3.7.1
Description:
typing.NoReturn inside compound types is broken in Python 3.7.0 and 3.7.1. If not dependent on runtime introspection, use string annotation instead. E.g. Callable[..., 'NoReturn']. https://bugs.python.org/issue34921
Problematic code:
from typing import NoReturn, Union
def exploding_apple(apple) -> Union[None, NoReturn]: # [broken-noreturn]
print(f"{apple} is about to explode")
Correct code:
from typing import NoReturn
def exploding_apple(apple) -> NoReturn:
print(f"{apple} is about to explode")
raise Exception("{apple} exploded !")
Configuration file:
[main]
py-version=3.7
load-plugins=pylint.extensions.typing
Created by the typing checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/typing.py. 🫶
catching-non-exception / E0712
Message emitted:
Catching an exception which doesn't inherit from Exception: %s
Description:
Used when a class which doesn't inherit from Exception is used as an exception in an except clause. Like bestie, you can't catch something that isn't an exception, that's not how any of this works 😭
Problematic code:
class FooError:
pass
try:
1 / 0
except FooError: # [catching-non-exception]
pass
Correct code:
class FooError(Exception):
pass
try:
1 / 0
except FooError:
pass
Created by the exceptions checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py. 💅
class-variable-slots-conflict / E0242
Message emitted:
Value %r in slots conflicts with class variable
Description:
Used when a value in __slots__ conflicts with a class variable, property or method. It's giving "I put the same name in two places and now everything is chaos" 🌀
Problematic code:
class Person:
# +1: [class-variable-slots-conflict, class-variable-slots-conflict, class-variable-slots-conflict]
__slots__ = ("age", "name", "say_hi")
name = None
def __init__(self, age, name):
self.age = age
self.name = name
@property
def age(self):
return self.age
def say_hi(self):
print(f"Hi, I'm {self.name}.")
Correct code:
class Person:
__slots__ = ("_age", "name")
def __init__(self, age, name):
self._age = age
self.name = name
@property
def age(self):
return self._age
def say_hi(self):
print(f"Hi, I'm {self.name}.")
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py. ✨
declare-non-slot / E0245
Message emitted:
No such name %r in __slots__
Description:
Raised when a type annotation on a class is absent from the list of names in __slots__, and __slots__ does not contain a __dict__ entry. So you promised a slot that doesn't exist, and pylint is calling you out 🙈
Problematic code:
class Student:
__slots__ = ("name",)
name: str
surname: str # [declare-non-slot]
Correct code:
class Student:
__slots__ = ("name", "surname")
name: str
surname: str
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py. 🎀
dict-iter-missing-items / E1141
Message emitted:
Unpacking a dictionary in iteration without calling .items()
Description:
Emitted when trying to iterate through a dict without calling .items(). Bestie. You cannot unpack a dict like that. Please. 💀
Problematic code:
data = {"Paris": 2_165_423, "New York City": 8_804_190, "Tokyo": 13_988_129}
for city, population in data: # [dict-iter-missing-items]
print(f"{city} has population {population}.")
Correct code:
data = {"Paris": 2_165_423, "New York City": 8_804_190, "Tokyo": 13_988_129}
for city, population in data.items():
print(f"{city} has population {population}.")
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py. 🦋
duplicate-argument-name / E0108
Message emitted:
Duplicate argument name %r in function definition
Description:
Duplicate argument names in function definitions are syntax errors. The language itself said no, so this isn't even a style thing, it's a "your code does not run" thing 😭
Problematic code:
def get_fruits(apple, banana, apple): # [duplicate-argument-name]
pass
Correct code:
def get_fruits(apple, banana, orange):
pass
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py. 🍓
duplicate-bases / E0241
Message emitted:
Duplicate bases for class %r
Description:
Duplicate use of base classes in derived classes raise TypeErrors. You can't inherit from the same class twice, it's not a vibe 🚫
Problematic code:
class Animal:
pass
class Cat(Animal, Animal): # [duplicate-bases]
pass
Correct code:
class Animal:
pass
class Bird(Animal):
pass
class Cat(Animal):
pass
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py. 🧚
format-needs-mapping / E1303
Message emitted:
Expected mapping for format string, not %s
Description:
Used when a format string that uses named conversion specifiers is used with an argument that is not a mapping. Named specifiers want a mapping, that's the whole deal 💅
Problematic code:
print("%(x)d %(y)d" % [1, 2]) # [format-needs-mapping]
Correct code:
print("%(x)d %(y)d" % {"x": 1, "y": 2})
Created by the string checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py. 🌸
function-redefined / E0102
Message emitted:
%s already defined line %s
Description:
Used when a function / class / method is redefined. One definition is enough, you're not a reboot franchise 🔁
Problematic code:
def get_email():
pass
def get_email(): # [function-redefined]
pass
Correct code:
def get_email():
pass
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py. 👑
inconsistent-mro / E0240
Message emitted:
Inconsistent method resolution order for class %r
Description:
Used when a class has an inconsistent method resolution order. Your inheritance graph is doing gymnastics and Python is not entertained 🤸
Problematic code:
class A:
pass
class B(A):
pass
class C(A, B): # [inconsistent-mro]
pass
Correct code:
class A:
pass
class B(A):
pass
class C(B): # or 'B, A' or 'A' but not 'A, B'
pass
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py. 🌀
inherit-non-class / E0239
Message emitted:
Inheriting %r, which is not a class.
Description:
Used when a class inherits from something which is not a class. Like inheriting from bool. That's not a class you inherit from, bestie, that's a builtin type 😭
Problematic code:
class Fruit(bool): # [inherit-non-class]
pass
Correct code:
class Fruit:
def __bool__(self):
pass
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py. ⭐
init-is-generator / E0100
Message emitted:
__init__ method is a generator
Description:
Used when the special class method __init__ is turned into a generator by a yield in its body. __init__ is not allowed to be a generator, that's just the rules 🤷♀️
Problematic code:
class Fruit:
def __init__(self, worms): # [init-is-generator]
yield from worms
apple = Fruit(["Fahad", "Anisha", "Tabatha"])
Correct code:
class Fruit:
def __init__(self, worms):
self.__worms = worms
def worms(self):
yield from self.__worms
apple = Fruit(["Fahad", "Anisha", "Tabatha"])
for worm in apple.worms():
pass
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py. 🐛
invalid-all-format / E0605
Message emitted:
Invalid format for __all__, must be tuple or list
Description:
Used when __all__ has an invalid format. A bare string is not a list of names, even though it feels like it should be 💀
Problematic code:
__all__ = "CONST" # [invalid-all-format]
CONST = 42
Correct code:
__all__ = ("CONST",)
CONST = 42
Created by the variables checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py. 🎀
invalid-all-object / E0604
Message emitted:
Invalid object %r in __all__, must contain only strings
Description:
Used when an invalid (non-string) object occurs in __all__.
Problematic code:
__all__ = (
None, # [invalid-all-object]
Fruit,
Worm,
)
class Fruit:
pass
class Worm:
pass
Correct code:
__all__ = ["Fruit", "Worm"]
class Fruit:
pass
class Worm:
pass
Additional details:
From The Python Language Reference – The import statement https://docs.python.org/3/reference/simple_stmts.html#the-import-statement:
"The public names defined by a module are determined by checking the module's namespace for a variable named __all__; if defined, it must be a sequence of strings which are names defined or imported by that module."
Related links:
- PEP 8 – Style Guide for Python Code —
https://peps.python.org/pep-0008/#module-level-dunder-names
Created by the variables checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py. 💖
invalid-bool-returned / E0304
Message emitted:
__bool__ does not return bool
Description:
Used when a __bool__ method returns something which is not a bool. If you call it __bool__ it better return an actual bool, that's the contract bestie ✨
Problematic code:
class CustomBool:
"""__bool__ returns an int"""
def __bool__(self): # [invalid-bool-returned]
return 1
Correct code:
class CustomBool:
"""__bool__ returns `bool`"""
def __bool__(self):
return True
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-bytes-returned / E0308 🧊
Message emitted:
__bytes__ does not return bytes
Description:
Fires when your __bytes__ method hands back something that is not bytes. Rude, honestly.
Problematic code:
class CustomBytes:
"""__bytes__ returns <type 'str'>"""
def __bytes__(self): # [invalid-bytes-returned]
return "123"
Correct code:
class CustomBytes:
"""__bytes__ returns <type 'bytes'>"""
def __bytes__(self):
return b"some bytes"
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-character-backspace / E2510 ⌫
Message emitted:
Invalid unescaped character backspace, use "\b" instead.
Description:
Moves the cursor back, so the character after it will overwrite the character before. Sneaky little gremlin behaviour, bestie.
Problematic code:
STRING = "Invalid character backspace" # [invalid-character-backspace]
Correct code:
STRING = "Valid character backspace \b"
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py
invalid-character-carriage-return / E2511 ↩️
Message emitted:
Invalid unescaped character carriage-return, use "\r" instead.
Description:
Moves the cursor to the start of line, subsequent characters overwrite the start of the line. It's giving "text file pretending to be a printer."
Correct code:
STRING = "Valid carriage return: \r"
Additional details:
This message exists because one of our checkers is very generic, but it's never going to raise during normal use as it's a syntax-error that would prevent the python ast (and thus pylint) from constructing a code representation of the file.
You could encounter it by feeding a properly constructed node directly to the checker.
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py
invalid-character-esc / E2513 🚨
Message emitted:
Invalid unescaped character esc, use "\x1B" instead.
Description:
Commonly initiates escape codes which allow arbitrary control of the terminal. Yeah. Terminal takeover energy. Not the cute kind.
Problematic code:
STRING = "Invalid escape character " # [invalid-character-esc]
Correct code:
STRING = "Valid escape character \x1b"
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py
invalid-character-nul / E2514 🕳️
Message emitted:
Invalid unescaped character nul, use "\0" instead.
Description:
Mostly end of input for python.
Additional details:
There's no need to use end-of-string characters. String objects maintain their own length. Like, they know how long they are, they don't need you micromanaging 💅
Related links:
- Null terminator in python —
https://stackoverflow.com/a/24410304/2519059
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py
invalid-character-sub / E2512 🛑
Message emitted:
Invalid unescaped character sub, use "\x1A" instead.
Description:
Ctrl+Z "End of text" on Windows. Some programs (such as type) ignore the rest of the file after it. So your code literally vanishes from their view. Iconic in the worst way 😭
Problematic code:
STRING = "Invalid character sub " # [invalid-character-sub]
Correct code:
STRING = "Valid character sub x1A"
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py
invalid-character-zero-width-space / E2515 👻
Message emitted:
Invalid unescaped character zero-width-space, use "\u200B" instead.
Description:
Invisible space character could hide real code execution. That's the jumpscare of the whole unicode category, fr fr.
Problematic code:
STRING = "Invalid character zero-width-space " # [invalid-character-zero-width-space]
Correct code:
STRING = "Valid character zero-width-space u200B"
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py
invalid-class-object / E0243 🍎
Message emitted:
Invalid assignment to '__class__'. Should be a class definition but got a '%s'
Description:
Used when an invalid object is assigned to a __class__ property. Only a class is permitted. Like, the guest list has exactly one name on it and it's "a class."
Problematic code:
class Apple:
pass
Apple.__class__ = 1 # [invalid-class-object]
Correct code:
class Apple:
pass
class RedDelicious:
pass
Apple.__class__ = RedDelicious
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
invalid-enum-extension / E0244 🎨
Message emitted:
Extending inherited Enum class "%s"
Description:
Used when a class tries to extend an inherited Enum class. Doing so will raise a TypeError at runtime. So it's not just a vibe thing — it actually blows up.
Problematic code:
from enum import Enum
class Color(Enum):
ORANGE = 1
CHERRY = 2
class Fruit(Color): # [invalid-enum-extension]
APPLE = 3
Correct code:
from enum import Enum
class Color(Enum):
ORANGE = 1
CHERRY = 2
class Fruit(Enum):
ORANGE = 1
CHERRY = 2
APPLE = 3
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
invalid-envvar-value / E1507 🌱
Message emitted:
%s does not support %s type argument
Description:
Env manipulation functions support only string type arguments. See https://docs.python.org/3/library/os.html#os.getenv. Strings only bestie, the environment does not do math.
Problematic code:
import os
os.getenv(1) # [invalid-envvar-value]
Correct code:
import os
os.getenv("1")
Created by the stdlib checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py
invalid-field-call / E3701 🗂️
Message emitted:
Invalid usage of field(), %s
Description:
The dataclasses.field() specifier should only be used as the value of an assignment within a dataclass, or within the make_dataclass() function. You can't just call it wherever, it's not a free-for-all 🫶
Problematic code:
from dataclasses import dataclass, field
@dataclass
class C:
a: float
b: float
c: float
field(init=False) # [invalid-field-call]
def __post_init__(self):
self.c = self.a + self.b
print(field(init=False)) # [invalid-field-call]
Correct code:
from dataclasses import dataclass, field, make_dataclass
C = make_dataclass(
"C",
[("x", int), "y", ("z", int, field(default=5))],
namespace={"add_one": lambda self: self.x + 1},
)
@dataclass
class C:
a: float
b: float
c: float = field(init=False)
def __post_init__(self):
self.c = self.a + self.b
Created by the dataclass checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/dataclass_checker.py
invalid-format-returned / E0311 🎀
Message emitted:
__format__ does not return str
Description:
Used when a __format__ method returns something which is not a string. It's literally called format, it has one job.
Problematic code:
class CustomFormat:
"""__format__ returns <type 'int'>"""
def __format__(self, format_spec): # [invalid-format-returned]
return 1
Correct code:
class CustomFormat:
"""__format__ returns <type 'str'>"""
def __format__(self, format_spec):
return "hello!"
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-getnewargs-ex-returned / E0313 📦
Message emitted:
__getnewargs_ex__ does not return a tuple containing (tuple, dict)
Description:
Used when a __getnewargs_ex__ method returns something which is not of the form tuple(tuple, dict). Nested shape matters, bestie. It's a tuple inside a tuple, plus a dict, and pylint will absolutely check.
Problematic code:
class CustomGetNewArgsEx:
"""__getnewargs_ex__ returns tuple with incorrect arg length"""
def __getnewargs_ex__(self): # [invalid-getnewargs-ex-returned]
return (tuple(1), dict(x="y"), 1)
Correct code:
class CustomGetNewArgsEx:
"""__getnewargs_ex__ returns <type 'tuple'>"""
def __getnewargs_ex__(self):
return ((1,), {"2": 2})
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-getnewargs-returned / E0312 🎁
Message emitted:
__getnewargs__ does not return a tuple
Description:
Used when a __getnewargs__ method returns something which is not a tuple. Just. A. Tuple. That's the whole assignment.
Problematic code:
class CustomGetNewArgs:
"""__getnewargs__ returns an integer"""
def __getnewargs__(self): # [invalid-getnewargs-returned]
return 1
Correct code:
class CustomGetNewArgs:
"""__getnewargs__ returns <type 'tuple'>"""
def __getnewargs__(self):
return (1, 2)
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-hash-returned / E0309 🔑
Message emitted:
__hash__ does not return int
Description:
Used when a __hash__ method returns something which is not an integer. Returning a dict here is genuinely unhinged behaviour and I respect the audacity but no 💀
Problematic code:
class CustomHash:
"""__hash__ returns dict"""
def __hash__(self): # [invalid-hash-returned]
return {}
Correct code:
class CustomHash:
"""__hash__ returns `int`"""
def __hash__(self):
return 19
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-index-returned / E0305 🔢
Message emitted:
__index__ does not return int
Description:
Used when an __index__ method returns something which is not an integer. It's an index. Integers. That's it, that's the post.
Problematic code:
class CustomIndex:
"""__index__ returns a dict"""
def __index__(self): # [invalid-index-returned]
return {"19": "19"}
Correct code:
class CustomIndex:
"""__index__ returns <type 'int'>"""
def __index__(self):
return 19
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-length-hint-returned / E0310
Message emitted: __length_hint__ does not return non-negative integer
Description: fires when a __length_hint__ method hands back something that isn't a non-negative integer. no cap, that's just not it 💅
Problematic code:
class CustomLengthHint:
"""__length_hint__ returns non-int"""
def __length_hint__(self): # [invalid-length-hint-returned]
return 3.0
Correct code:
class CustomLengthHint:
"""__length_hint__ returns <type 'int'>"""
def __length_hint__(self):
return 10
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-length-returned / E0303
Message emitted: __len__ does not return non-negative integer
Description: yep, __len__ gave back something that isn't a non-negative integer. bestie, lengths can't be negative, that's literally the whole vibe 😭
Problematic code:
class FruitBasket:
def __init__(self, fruits):
self.fruits = ["Apple", "Banana", "Orange"]
def __len__(self): # [invalid-length-returned]
return -len(self.fruits)
Correct code:
class FruitBasket:
def __init__(self, fruits):
self.fruits = ["Apple", "Banana", "Orange"]
def __len__(self):
return len(self.fruits)
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-match-args-definition / E1902
Message emitted: `__match_args__` must be a tuple of strings.
Description: emitted if __match_args__ isn't a tuple of strings — which is what match needs. list girlies, this is not your era 🙈
Problematic code:
class Book:
__match_args__ = ["title", "year"] # [invalid-match-args-definition]
def __init__(self, title, year):
self.title = title
self.year = year
Correct code:
class Book:
__match_args__ = ("title", "year")
def __init__(self, title, year):
self.title = title
self.year = year
Related links:
- Python documentation —
https://docs.python.org/3/reference/datamodel.html#customizing-positional-arguments-in-class-pattern-matching
Created by the match_statements checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/match_statements_checker.py
invalid-metaclass / E1139
Message emitted: Invalid metaclass %r used
Description: emitted whenever we can clock that a class is using something as a metaclass that might just be invalid for metaclass duty. it's giving "wrong person for the job" 💀
Problematic code:
class Apple(metaclass=int): # [invalid-metaclass]
pass
Correct code:
class Plant:
pass
class Apple(Plant):
pass
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
invalid-repr-returned / E0306
Message emitted: __repr__ does not return str
Description: used when a __repr__ method returns something that isn't a string. we said str, not "whatever felt right" 🎀
Problematic code:
class CustomRepr:
"""__repr__ returns <type 'int'>"""
def __repr__(self): # [invalid-repr-returned]
return 1
Correct code:
class CustomRepr:
"""__repr__ returns <type 'str'>"""
def __repr__(self):
return "apples"
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-sequence-index / E1126
Message emitted: Sequence index is not an int, slice, or instance with __index__
Description: used when a sequence type gets indexed with an invalid type. the valid types are ints, slices, and objects with an __index__ method. that's the guest list, and "apple" is not on it 🚫
Problematic code:
fruits = ["apple", "banana", "orange"]
print(fruits["apple"]) # [invalid-sequence-index]
Correct code:
fruits = ["apple", "banana", "orange"]
print(fruits[0])
Additional details: be careful with [True] or [False] as a sequence index, because True and False will respectively be evaluated as 1 and 0 — so you'll quietly get the second element of the list and the first one, no error, no warning, just vibes 🫠
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
invalid-slice-index / E1127
Message emitted: Slice index is not an int, None, or instance with __index__
Description: used when a slice index isn't an integer, None, or an object with an __index__ method. quotes around a number do not make it a number, bestie 🌸
Problematic code:
LETTERS = ["a", "b", "c", "d"]
FIRST_THREE = LETTERS[:"3"] # [invalid-slice-index]
Correct code:
LETTERS = ["a", "b", "c", "d"]
FIRST_THREE = LETTERS[:3]
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
invalid-slice-step / E1144
Message emitted: Slice step cannot be 0
Description: used when a slice step is 0 and the object doesn't implement a custom __getitem__ method. zero steps = zero progress, iconic of nothing 🔥
Problematic code:
LETTERS = ["a", "b", "c", "d"]
LETTERS[::0] # [invalid-slice-step]
Correct code:
LETTERS = ["a", "b", "c", "d"]
LETTERS[::2]
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
invalid-slots / E0238
Message emitted: Invalid __slots__ object
Description: used when an invalid __slots__ shows up in a class. only a string, an iterable, or a sequence is permitted — 42 is none of those, bestie 😭
Problematic code:
class Person: # [invalid-slots]
__slots__ = 42
Correct code:
class Person:
__slots__ = ("name", "age")
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
invalid-slots-object / E0236
Message emitted: Invalid object %r in __slots__, must contain only non empty strings
Description: used when an invalid (non-string) object shows up inside __slots__. it's a slots list, not a junk drawer 🎀
Problematic code:
class Person:
__slots__ = ("name", 3) # [invalid-slots-object]
Correct code:
class Person:
__slots__ = ("name", "surname")
Related links:
- Documentation for
__slots__—https://docs.python.org/3/reference/datamodel.html#slots
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
invalid-star-assignment-target / E0113
Message emitted: Starred assignment target must be in a list or tuple
Description: emitted when a star expression is used as a starred assignment target. the star has to live inside a list or tuple, it can't just be out there on its own main character arc ✨
Problematic code:
*fruit = ["apple", "banana", "orange"] # [invalid-star-assignment-target]
Correct code:
fruit = ["apple", "banana", "orange"]
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py
invalid-str-returned / E0307
Message emitted: __str__ does not return str
Description: used when a __str__ method returns something that isn't a string. 1 is not a string, no matter how confidently you return it 💀
Problematic code:
class CustomStr:
"""__str__ returns int"""
def __str__(self): # [invalid-str-returned]
return 1
Correct code:
class CustomStr:
"""__str__ returns <type 'str'>"""
def __str__(self):
return "oranges"
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
invalid-unary-operand-type / E1130
Message emitted: %s
Description: emitted when a unary operand gets used on an object that doesn't support that type of operation. the object said "not my era" and honestly, respect 🫶
Problematic code:
cherries = 10
eaten_cherries = int
cherries = -eaten_cherries # [invalid-unary-operand-type]
Correct code:
cherries = 10
eaten_cherries = 2
cherries -= eaten_cherries
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
invalid-unicode-codec / E2501
Message emitted: UTF-16 and UTF-32 aren't backward compatible. Use UTF-8 instead
Description: for compatibility, use UTF-8 instead of UTF-16/UTF-32. see also https://bugs.python.org/issue1503789 for a history of this issue. and https://softwareengineering.stackexchange.com/questions/102205/ for some possible problems when using UTF-16, for instance. UTF-8 is the moment, fr fr 🌈
Additional details: this message is a placeholder for a potential future issue with unicode codecs.
Created by the unicode_checker checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py
logging-format-truncated / E1201
Message emitted: Logging format string ends in middle of conversion specifier
Description: used when a logging statement's format string terminates before the end of a conversion specifier. a lone % is just hanging there like "…and?" 😭
Problematic code:
import logging
import sys
logging.warning("Python version: %", sys.version) # [logging-format-truncated]
Correct code:
import logging
import sys
logging.warning("Python version: %s", sys.version)
Created by the logging checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/logging.py
logging-too-few-args / E1206
Message emitted: Not enough arguments for logging format string
Description: used when a logging format string gets too few arguments. two %s but one arg? the math is not mathing 🧮💅
Problematic code:
import logging
try:
function()
except Exception as e:
logging.error("%s error occurred: %s", e) # [logging-too-few-args]
raise
Correct code:
import logging
try:
function()
except Exception as e:
logging.error("%s error occurred: %s", type(e), e)
raise
Created by the logging checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/logging.py
logging-too-many-args / E1205
Message emitted: Too many arguments for logging format string
Description: used when a logging format string gets too many arguments. one %s, two args — someone's not being used and they know it 🙈
Problematic code:
import logging
try:
function()
except Exception as e:
logging.error("Error occurred: %s", type(e), e) # [logging-too-many-args]
raise
Correct code:
import logging
💅 logging-unsupported-format / E1200
Message emitted:
Unsupported logging format character %r (%#02x) at index %d
Description: you used a format character in a logging statement's format string that... isn't a thing. Unsupported. Not a vibe. 🚫
Problematic code:
import logging
logging.info("%s %y !", "Hello", "World") # [logging-unsupported-format]
Correct code:
import logging
logging.info("%s %s !", "Hello", "World")
Created by the logging checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/logging.py 🫶
🌸 method-hidden / E0202
Message emitted:
An attribute defined in %s line %s hides this method
Description: this fires when a class defines a method that gets hidden — either by an instance attribute coming down from an ancestor class, or by something some client code set on it. Your method is literally being overshadowed, giving background-character energy. 😭
Problematic code:
class Fruit:
def __init__(self, vitamins):
self.vitamins = vitamins
def vitamins(self): # [method-hidden]
pass
Correct code:
class Fruit:
def __init__(self, vitamins):
self.vitamins = vitamins
def antioxidants(self):
pass
Created by the classes checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py ✨
🦋 misplaced-bare-raise / E0704
Message emitted:
The raise statement is not inside an except clause
Description: ok so this one's a real one. It fires when a bare raise isn't sitting inside an except clause — which is an error, because there's no active exception to re-raise. Nothing to re-raise, bestie. 💀
There is one exception to this rule: a bare raise inside a finally clause. That one might actually work — as long as an exception got raised inside the try block. But it's still a code smell and you should not be relying on it. Consider yourself warned, ngl.
Problematic code:
def validate_positive(x):
if x <= 0:
raise # [misplaced-bare-raise]
Correct code:
def validate_positive(x):
if x <= 0:
raise ValueError(f"{x} is not positive")
Created by the exceptions checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py 🎀
🍓 misplaced-format-function / E0119
Message emitted:
format function is not called on str
Description: emitted when .format gets called on something that is not a str. Classic example: doing print("value: {}").format(123) instead of print("value: {}".format(123)). Like — the parentheses went to the wrong place, and now nothing is formatting. This is probably not what you intended to do. 🙈
Problematic code:
print("Value: {}").format("Car") # [misplaced-format-function]
Correct code:
print("Value: {}".format("Car"))
Created by the basic checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py 💜
🧚 missing-format-string-key / E1304
Message emitted:
Missing key %r in format string dictionary
Description: fires when a format string using named conversion specifiers gets handed a dictionary that doesn't have all the keys the format string is asking for. You promised %(orange_price)d and then didn't deliver. Rude, honestly. 🍊
Problematic code:
# +1: [missing-format-string-key]
fruit_prices = """
Apple: %(apple_price)d ¤
Orange: %(orange_price)d ¤
""" % {
"apple_price": 42
}
Correct code:
fruit_prices = """
Apple: %(apple_price)d ¤
Orange: %(orange_price)d ¤
""" % {
"apple_price": 42,
"orange_price": 87,
}
Created by the string checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py 🎀
⭐ missing-kwoa / E1125
Message emitted:
Missing mandatory keyword argument %r in %s call
Description: a function call skipped a mandatory keyword-only argument. You had one job. 😤
Problematic code:
def target(pos, *, keyword):
return pos + keyword
def not_forwarding_kwargs(*args, **kwargs):
target(*args) # [missing-kwoa]
Correct code:
def target(pos, *, keyword):
return pos + keyword
def not_forwarding_kwargs(*args, **kwargs):
target(*args, **kwargs)
Created by the typecheck checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py 🔥
🌈 mixed-format-string / E1302
Message emitted:
Mixing named and unnamed conversion specifiers in format string
Description: used when a format string has both named specifiers (like '%(foo)d') and unnamed ones (like '%d') in the same string. It also fires when a named conversion specifier contains * for the minimum field width and/or the precision. Pick a lane, format string. 🛣️
Problematic code:
print("x=%(x)d, y=%d" % (0, 1)) # [mixed-format-string]
Correct code:
only_named.py:
print("x=%(x)d, y=%(y)d" % {"x": 0, "y": 1})
only_ordered.py:
print("x=%d, y=%d" % (0, 1))
Created by the string checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py ✨
💀 modified-iterating-dict / E4702
Message emitted:
Iterated dict '%s' is being modified inside for loop body, iterate
through a copy of it instead.
Description: emitted when items get added to or removed from a dict while you're iterating through it. Doing that raises a RuntimeError, bestie — this is not a vibe, this is a crash. 😭
Problematic code:
fruits = {"apple": 1, "orange": 2, "mango": 3}
i = 0
for fruit in fruits:
fruits["apple"] = i # [modified-iterating-dict]
i += 1
Correct code:
fruits = {"apple": 1, "orange": 2, "mango": 3}
i = 0
for fruit in fruits.copy():
fruits["apple"] = i
i += 1
Created by the modified_iteration checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/modified_iterating_checker.py 🦋
🎀 modified-iterating-set / E4703
Message emitted:
Iterated set '%s' is being modified inside for loop body, iterate
through a copy of it instead.
Description: same energy as the dict one, but for a set. Items being added or removed while you're iterating through it → RuntimeError. Do not do this. Copy first. 💅
Problematic code:
fruits = {"apple", "orange", "mango"}
for fruit in fruits:
fruits.add(fruit + "yum") # [modified-iterating-set]
Correct code:
fruits = {"apple", "orange", "mango"}
for fruit in fruits.copy():
fruits.add(fruit + "yum")
Created by the modified_iteration checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/modified_iterating_checker.py 👑
🦋 multiple-class-sub-patterns / E1904
Message emitted:
Multiple sub-patterns for attribute %s
Description: emitted when there's more than one sub-pattern for a specific attribute inside a class pattern. Nobody needs two of the same thing, babe. 🫶
Problematic code:
class Book:
__match_args__ = ("title", "year")
def __init__(self, title, year):
self.title = title
self.year = year
def func(item: Book):
match item:
case Book("abc", title="abc"): # [multiple-class-sub-patterns]
...
case Book(year=2000, year=2001): # [multiple-class-sub-patterns]
...
Correct code:
class Book:
__match_args__ = ("title", "year")
def __init__(self, title, year):
self.title = title
self.year = year
def func(item: Book):
match item:
case Book(title="abc"):
...
case Book(year=2000):
...
Related links:
- Python documentation:
https://docs.python.org/3/reference/compound_stmts.html#class-patterns
Created by the match_statements checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/match_statements_checker.py ⭐
😭 no-member / E1101
Message emitted:
%s %r has no %r member%s
Description: used when a variable gets accessed for a member that does not exist. Ghost member. Not real. 💀
Problematic code:
from pathlib import Path
directories = Path(".").mothers # [no-member]
class Cat:
def meow(self):
print("Meow")
Cat().roar() # [no-member]
Correct code:
from pathlib import Path
directories = Path(".").parents
class Cat:
def meow(self):
print("Meow")
Cat().meow()
Additional details:
So you got the dreaded no-member error. Deep breath. 💖 There's a possibility that any one of these is happening:
- pylint found a bug in your code
- You're launching pylint without the dependencies installed in its environment
- pylint would need to lint a C extension module and is refraining to do so
- pylint does not understand dynamically generated code
Linting C extension modules is not supported out of the box — especially since pylint has no way to get an AST object out of the extension module.
But! pylint actually has a mechanism you might use if you do want to analyze C extensions. There's a flag called extension-pkg-allow-list (formerly extension-pkg-whitelist) that lets you tell pylint to import that module and build an AST from that imported module:
$ pylint --extension-pkg-allow-list=your_c_extension
⚠️ Be aware though: using this flag means extensions get loaded into the active Python interpreter and may run arbitrary code, which you may not want. This is exactly why we disable loading C extensions by default.
If you don't want the hassle of passing C extension modules with that flag every single time, you can enable unsafe-load-any-extension in your configuration file — that builds AST objects from all the C extensions pylint encounters:
$ pylint --unsafe-load-any-extension=y
Alternatively — since pylint emits a separate error for attributes that can't be found in C extensions, c-extension-no-member — you can just disable that error for your project. ✨
And if something is generated dynamically, pylint won't be able to understand the code from your library (c-extension or not — doesn't matter). In that case you can specify generated attributes with the generated-members option. For example, if cv2.LINE_AA and sphinx.generated_member create false positives for no-member, you can do:
$ pylint --generated-member=cv2.LINE_AA,sphinx.generated_member
Created by the typecheck checker — source at https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py 🔥
🌸 no-method-argument / E0211
Message emitted:
Method %r has no argument
Description:
Used when a method that should have the bound instance as its first argument just... has no argument defined at all. Like, bestie, where is self? 😭
Problematic code:
class Person:
def print_greeting(): # [no-method-argument]
print("hello")
Correct code:
class Person:
def print_greeting(self):
print("hello")
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
no-name-in-module / E0611
Message emitted: No name %r in module %r
Description: Used when a name cannot be found in a module. She looked everywhere and the name is simply not in there 💀
Problematic code:
from os import pizza # [no-name-in-module]
Correct code:
from os import path
Created by the variables checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py
no-self-argument / E0213
Message emitted: Method %r should have "self" as first argument
Description: Used when a method has an attribute different from "self" as its first argument. Pylint treats this as a full-on error because this convention is so common that you genuinely should not break it. Like, it's giving "everyone agreed on this in 1991" energy 👑
Problematic code:
class Fruit:
def __init__(this, name): # [no-self-argument]
this.name = name
Correct code:
class Fruit:
def __init__(self, name):
self.name = name
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py
no-value-for-parameter / E1120
Message emitted: No value for argument %s in %s call
Description: Used when a function call passes too few arguments. Girl, she's hungry, feed her the parameters 🍓
Problematic code:
def add(x, y):
return x + y
add(1) # [no-value-for-parameter]
Correct code:
def add(x, y):
return x + y
add(1, 2)
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
non-iterator-returned / E0301
Message emitted: __iter__ returns non-iterator
Description: Used when an __iter__ method returns something that is not an iterable (i.e. has no __next__ method). Close but not quite, icon 💅
Problematic code:
import random
class GenericAstrology:
def __init__(self, signs, predictions):
self.signs = signs
self.predictions = predictions
def __iter__(self): # [non-iterator-returned]
self.index = 0
self.number_of_prediction = len(self.predictions)
return self
SIGNS = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra"]
PREDICTIONS = ["good things", "bad thing", "existential dread"]
for sign, prediction in GenericAstrology(SIGNS, PREDICTIONS):
print(f"{sign} : {prediction} today")
Correct code:
import random
class GenericAstrology:
def __init__(self, signs, predictions):
self.signs = signs
self.predictions = predictions
def __iter__(self):
self.index = 0
self.number_of_prediction = len(self.predictions)
return self
def __next__(self):
if self.index == len(self.signs):
raise StopIteration
self.index += 1
prediction_index = random.randint(0, self.number_of_prediction - 1)
return self.signs[self.index - 1], self.predictions[prediction_index]
SIGNS = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra"]
PREDICTIONS = ["good things", "bad thing", "existential dread"]
for sign, prediction in GenericAstrology(SIGNS, PREDICTIONS):
print(f"{sign} : {prediction} today")
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py
nonexistent-operator / E0107
Message emitted: Use of the non-existent %s operator
Description: Used when you attempt to use the C-style pre-increment or pre-decrement operator -- and ++, which literally does not exist in Python. Sweetie, this is not C, we don't do that here 🙈
Problematic code:
i = 0
while i <= 10:
print(i)
++i # [nonexistent-operator]
Correct code:
i = 0
while i <= 10:
print(i)
i += 1
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py
nonlocal-and-global / E0115
Message emitted: Name %r is nonlocal and global
Description: Emitted when a name is both nonlocal and global. Pick a lane, bestie, you cannot be both 😭
Problematic code:
NUMBER = 42
def update_number(number): # [nonlocal-and-global]
global NUMBER
nonlocal NUMBER
NUMBER = number
print(f"New global number is: {NUMBER}")
update_number(24)
Correct code:
NUMBER = 42
def update_number(number):
global NUMBER
NUMBER = number
print(f"New global number is: {NUMBER}")
update_number(24)
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py
nonlocal-without-binding / E0117
Message emitted: nonlocal name %s found without binding
Description: Emitted when a nonlocal variable doesn't have an attached name somewhere up in the parent scopes. She's nonlocal but she has no home 🥺
Problematic code:
class Fruit:
def get_color(self):
nonlocal colors # [nonlocal-without-binding]
Correct code:
class Fruit:
colors = ["red", "green"]
def get_color(self):
nonlocal colors
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py
not-a-mapping / E1134
Message emitted: Non-mapping value %s is used in a mapping context
Description: Used when a non-mapping value is used in a place where a mapping is expected. You promised me a dict and handed me a list, respectfully what 💀
Problematic code:
def print_colors(**colors):
print(colors)
print_colors(**list("red", "black")) # [not-a-mapping]
Correct code:
def print_colors(**colors):
print(colors)
print_colors(**dict(red=1, black=2))
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
not-an-iterable / E1133
Message emitted: Non-iterable value %s is used in an iterating context
Description: Used when a non-iterable value is used in a place where an iterable is expected. You cannot for-loop over the number ten, queen, that's not a vibe ✨
Problematic code:
for i in 10: # [not-an-iterable]
pass
Correct code:
for i in "10":
pass
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
not-async-context-manager / E1701
Message emitted: Async context manager '%s' doesn't implement __aenter__ and __aexit__.
Description: Used when an async context manager is used with an object that does not implement the async context management protocol. Imagine showing up to an async party without your __aenter__ and __aexit__ — embarrassing 🫶
Problematic code:
class ContextManager:
def __enter__(self):
pass
def __exit__(self, *exc):
pass
async def foo():
async with ContextManager(): # [not-async-context-manager]
pass
Correct code:
class AsyncContextManager:
def __aenter__(self):
pass
def __aexit__(self, *exc):
pass
async def foo():
async with AsyncContextManager():
pass
Additional details:
Async context manager doesn't implement __aenter__ and __aexit__. It can't be emitted when using Python < 3.5.
Created by the async checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/async_checker.py
not-callable / E1102
Message emitted: %s is not callable
Description: Used when an object being called has been inferred to be a non-callable object. Ma'am, that is a number, you cannot call it 📞
Problematic code:
NUMBER = 42
print(NUMBER()) # [not-callable]
Correct code:
NUMBER = 42
print(NUMBER)
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
not-context-manager / E1129
Message emitted: Context manager '%s' doesn't implement __enter__ and __exit__.
Description: Used when an instance in a with statement doesn't implement the context manager protocol (__enter__/__exit__). Half a context manager is not a context manager, periodt 💅
Problematic code:
class MyContextManager:
def __enter__(self):
pass
with MyContextManager() as c: # [not-context-manager]
pass
Correct code:
class MyContextManager:
def __enter__(self):
pass
def __exit__(self, *exc):
pass
with MyContextManager() as c:
pass
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py
not-in-loop / E0103
Message emitted: %r not properly in loop
Description: Used when break or continue keywords are used outside a loop. You cannot continue something that never started, bestie 😭
Problematic code:
def print_even_numbers():
for i in range(100):
if i % 2 == 0:
print(i)
else:
continue # [not-in-loop]
Correct code:
def print_even_numbers():
for i in range(100):
if i % 2:
continue
print(i)
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py
notimplemented-raised / E0711
Message emitted: NotImplemented raised - should raise NotImplementedError
Description: Used when NotImplemented is raised instead of NotImplementedError. Bestie those are two entirely different girls 🙈
Problematic code:
class Worm:
def bore(self):
raise NotImplemented # [notimplemented-raised]
Correct code:
class Worm:
def bore(self):
raise NotImplementedError
Created by the exceptions checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py
positional-only-arguments-expected / E3102
Message emitted: `%s()` got some positional-only arguments passed as keyword arguments: %s
Description: Emitted when positional-only arguments have been passed as keyword arguments. Remove the keywords for the affected arguments in the function call. She said positional only and she meant it 🔥
Problematic code:
(…continuing the cube example from above ✨)
def cube(n, /):
"""Takes in a number n, returns the cube of n"""
return n**3
cube(n=2) # [positional-only-arguments-expected]
Correct code, bestie:
def cube(n, /):
"""Takes in a number n, returns the cube of n"""
return n**3
cube(2)
Related links:
- PEP 570 —
https://peps.python.org/pep-570/
Created by the method_args checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/method_args.py.
possibly-used-before-assignment / E0606 🔥
Message emitted:
Possibly using variable %r before assignment
Description:
Emitted when a local variable is accessed before its assignment took place in both branches of an if/else switch.
Problematic code:
def check_lunchbox(items: list[str]):
if not items:
empty = True
print(empty) # [possibly-used-before-assignment]
Correct code:
def check_lunchbox(items: list[str]):
empty = False
if not items:
empty = True
print(empty)
Additional details:
Okay so you can use assert_never to mark exhaustive choices — it's giving "I've covered everything, trust me":
from typing import assert_never
def handle_date_suffix(suffix):
if suffix == "d":
...
elif suffix == "m":
...
elif suffix == "y":
...
else:
assert_never(suffix)
if suffix in "dmy":
handle_date_suffix(suffix)
Or, instead of assert_never(), you can call a function with a return annotation of Never or NoReturn. And here's the spicy bit 👀 — unlike in the general case, where by design pylint ignores type annotations and does its own static analysis, here pylint treats these special annotations like a disable comment.
Pylint currently allows repeating the same test like this, even though this lets some error cases through, as pylint does not assess the intervening code:
if guarded():
var = 1
# what if code here affects the result of guarded()?
if guarded():
print(var)
But this exception is limited to the repeating the exact same test. This one warns ⚠️:
if guarded():
var = 1
if guarded() or other_condition:
print(var) # [possibly-used-before-assignment]
If you find this surprising, consider that pylint, as a static analysis tool, does not know if guarded() is deterministic or talks to a database. For variables (e.g. guarded versus guarded()), this is less of an issue, so in this case, possibly-used-before-assignment acts more like a future-proofing style preference than an error, per se.
Created by the variables checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py.
potential-index-error / E0643 🍓
Message emitted:
Invalid index for iterable length
Description:
Emitted when an index used on an iterable goes beyond the length of that iterable. Off-the-end energy, not it 💀
Problematic code:
print([1, 2, 3][3]) # [potential-index-error]
Correct code:
print([1, 2, 3][2])
Created by the variables checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py.
raising-bad-type / E0702 🎀
Message emitted:
Raising %s while only classes or instances are allowed
Description:
Used when something which is neither a class nor an instance is raised (i.e. a TypeError will be raised).
Problematic code:
class FasterThanTheSpeedOfLightError(ZeroDivisionError):
def __init__(self):
super().__init__("You can't go faster than the speed of light !")
def calculate_speed(distance: float, time: float) -> float:
try:
return distance / time
except ZeroDivisionError as e:
raise None # [raising-bad-type]
Correct code:
class FasterThanTheSpeedOfLightError(ZeroDivisionError):
def __init__(self):
super().__init__("You can't go faster than the speed of light !")
def calculate_speed(distance: float, time: float) -> float:
try:
return distance / time
except ZeroDivisionError as e:
raise FasterThanTheSpeedOfLightError() from e
Created by the exceptions checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py.
raising-non-exception / E0710 😭
Message emitted:
Raising a class which doesn't inherit from BaseException
Description:
Used when a class which doesn't inherit from BaseException is raised.
Problematic code:
raise str # [raising-non-exception]
Correct code:
raise Exception("Goodbye world !")
Created by the exceptions checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py.
redundant-keyword-arg / E1124 💅
Message emitted:
Argument %r passed by position and keyword in %s call
Description:
Used when a function call would result in assigning multiple values to a function parameter — one value from a positional argument and one from a keyword argument. Pick a lane, bestie 😤
Problematic code:
def square(x):
return x * x
square(5, x=4) # [redundant-keyword-arg]
Correct code:
only_arg.py:
def square(x):
return x * x
square(5)
only_kwarg.py:
def square(x):
return x * x
square(x=4)
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py.
relative-beyond-top-level / E0402 🦋
Message emitted:
Attempted relative import beyond top-level package
Description:
Used when a relative import tries to access too many levels in the current package. You went too many dots deep, girlie 🙈
Problematic code:
from ................antigravity import NGField # [relative-beyond-top-level]
Correct code:
absolute_import.py:
from physic.antigravity import NGField
fix_the_relative_import.py:
# Right number of dots in the import: you needed 15 dots, not 16, duh.
# from ...............antigravity import NGField
Additional details:
Absolute imports were strongly preferred, historically. Relative imports allow you to reorganize packages without changing any code, but these days refactoring tools and IDEs allow you to do that at almost no cost anyway if the imports are explicit/absolute. Therefore, absolute imports are often still preferred over relative ones.
Related links:
- Absolute vs. explicit relative import of Python module —
https://stackoverflow.com/a/16748366/2519059 - Withdraw anti-recommendation of relative imports from documentation —
https://bugs.python.org/msg118031
Created by the imports checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py.
repeated-keyword / E1132 💀
Message emitted:
Got multiple values for keyword argument %r in function call
Description:
Emitted when a function call got multiple values for a keyword. Same keyword twice? The audacity 😳
Problematic code:
def func(a, b, c):
return a, b, c
func(1, 2, c=3, **{"c": 4}) # [repeated-keyword]
func(1, 2, **{"c": 3}, **{"c": 4}) # [repeated-keyword]
Correct code:
def func(a, b, c):
return a, b, c
func(1, 2, c=3)
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py.
return-arg-in-generator / E0106 🧚
Message emitted:
Return with argument inside generator
Description:
Used when a "return" statement with an argument is found in a generator function or method (e.g. with some "yield" statements).
Correct code:
def yield_numbers():
for number in range(10):
yield number
return "I am now allowed!" # This was not allowed in Python 3.3 and earlier.
Additional details:
This is a message that isn't going to be raised for python > 3.3. It was raised for code like:
def interrogate_until_you_find_jack(pirates):
for pirate in pirates:
if pirate == "Captain Jack Sparrow":
return "Arrr! We've found our captain!"
yield pirate
Which is now valid and equivalent to the previously expected:
def interrogate_until_you_find_jack(pirates):
for pirate in pirates:
if pirate == "Captain Jack Sparrow":
raise StopIteration("Arrr! We've found our captain!")
yield pirate
Related links:
- PEP380 —
https://peps.python.org/pep-0380/ - Stackoverflow explanation —
https://stackoverflow.com/a/16780113/2519059
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py.
return-in-init / E0101 🌸
Message emitted:
Explicit return in __init__
Description:
Used when the special class method __init__ has an explicit return value. __init__ returns None, that's the whole vibe, don't fight it 🫶
Problematic code:
class Sum:
def __init__(self, a, b): # [return-in-init]
return a + b
Correct code:
class Sum:
def __init__(self, a, b) -> None:
self.result = a + b
Related links:
__init__method documentation —https://docs.python.org/3/reference/datamodel.html#object.__init__
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py.
return-outside-function / E0104 ✨
Message emitted:
Return outside function
Description:
Used when a "return" statement is found outside a function or method.
Problematic code:
return 42 # [return-outside-function]
Correct code:
def get_the_answer():
return 42
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py.
singledispatch-method / E1519 🎀
Message emitted:
singledispatch decorator should not be used with methods, use singledispatchmethod instead.
Description:
singledispatch should decorate functions and not class/instance methods. Use singledispatchmethod for those cases. Right tool for the right job, iykyk 💅
Problematic code:
from functools import singledispatch
class Board:
@singledispatch # [singledispatch-method]
def convert_position(self, position):
pass
@convert_position.register # [singledispatch-method]
def _(self, position: str) -> tuple:
position_a, position_b = position.split(",")
return (int(position_a), int(position_b))
@convert_position.register # [singledispatch-method]
def _(self, position: tuple) -> str:
return f"{position[0]},{position[1]}"
Correct code:
from functools import singledispatch
@singledispatch
def convert_position(position):
print(position)
@convert_position.register
def _(position: str) -> tuple:
position_a, position_b = position.split(",")
return (int(position_a), int(position_b))
@convert_position.register
def _(position: tuple) -> str:
return f"{position[0]},{position[1]}"
Created by the stdlib checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py). 🧚
💅 singledispatchmethod-function / E1520
Message emitted: singledispatchmethod decorator should not be used with functions, use singledispatch instead.
Description: Bestie, singledispatchmethod is for class/instance methods — not plain functions. If you're decorating functions, use singledispatch instead. That's the whole vibe. ✨
Problematic code:
from functools import singledispatchmethod
@singledispatchmethod # [singledispatchmethod-function]
def convert_position(position):
print(position)
@convert_position.register # [singledispatchmethod-function]
def _(position: str) -> tuple:
position_a, position_b = position.split(",")
return (int(position_a), int(position_b))
@convert_position.register # [singledispatchmethod-function]
def _(position: tuple) -> str:
return f"{position[0]},{position[1]}"
Correct code:
from functools import singledispatchmethod
class Board:
@singledispatchmethod
def convert_position(cls, position):
pass
@singledispatchmethod
@classmethod
def _(cls, position: str) -> tuple:
position_a, position_b = position.split(",")
return (int(position_a), int(position_b))
@singledispatchmethod
@classmethod
def _(cls, position: tuple) -> str:
return f"{position[0]},{position[1]}"
Created by the stdlib checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py). 🫶
🌟 star-needs-assignment-target / E0114
Message emitted: Can use starred expression only in assignment target
Description: This one drops when a star expression isn't being used in an assignment target. Like, the starred thing has to land somewhere, okay? 💫
Problematic code:
stars = *["Sirius", "Arcturus", "Vega"] # [star-needs-assignment-target]
Correct code:
sirius, *arcturus_and_vega = ["Sirius", "Arcturus", "Vega"]
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py). 💖
🚨 syntax-error / E0001
Message emitted: %s
Description: Used when a syntax error is raised for a module.
Problematic code:
fruit_stock = {
'apple': 42,
'orange': 21 # [syntax-error]
'banana': 12
}
Correct code:
fruit_stock = {"apple": 42, "orange": 21, "banana": 12}
Additional details: Python's ast builtin module cannot parse your code if there's a syntax error, so if there's a syntax error other messages won't be available at all. No cap. 😭
Related links:
- Why can't pylint recover from a syntax error ? —
https://stackoverflow.com/a/78419051/2519059
Created by the main checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py). 🎀
🍓 too-few-format-args / E1306
Message emitted: Not enough arguments for format string
Description: Used when a format string that uses unnamed conversion specifiers is given too few arguments.
Problematic code:
print("Today is {0}, so tomorrow will be {1}".format("Monday")) # [too-few-format-args]
Correct code:
print("Today is {0}, so tomorrow will be {1}".format("Monday", "Tuesday"))
Related links:
- String Formatting —
https://docs.python.org/3/library/string.html#formatstrings
Created by the string checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py). 🌸
🌈 too-many-format-args / E1305
Message emitted: Too many arguments for format string
Description: Used when a format string that uses unnamed conversion specifiers is given too many arguments.
Problematic code:
# +1: [too-many-format-args]
print("Today is {0}, so tomorrow will be {1}".format("Monday", "Tuesday", "Wednesday"))
Correct code:
print("Today is {0}, so tomorrow will be {1}".format("Monday", "Tuesday"))
Related links:
- String Formatting —
https://docs.python.org/3/library/string.html#formatstrings
Created by the string checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py). ✨
💀 too-many-function-args / E1121
Message emitted: Too many positional arguments for %s call
Description: Used when a function call passes too many positional arguments. Like, bestie, the function did not ask for all that. 🙈
Problematic code:
class Fruit:
def __init__(self, color):
self.color = color
apple = Fruit("red", "apple", [1, 2, 3]) # [too-many-function-args]
Correct code:
class Fruit:
def __init__(self, color, name):
self.color = color
self.name = name
apple = Fruit("red", "apple")
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py). 💜
🦋 too-many-positional-sub-patterns / E1903
Message emitted: %s expects %d positional sub-patterns (given %d)
Description: Emitted when the number of allowed positional sub-patterns exceeds the number of allowed sub-patterns specified in __match_args__. Pattern matching said "not that many, actually." 🔥
Problematic code:
class Book:
__match_args__ = ("title", "year")
def __init__(self, title, year, author):
self.title = title
self.year = year
self.author = author
def func(item: Book):
match item:
case Book("title", 2000, "author"): # [too-many-positional-sub-patterns]
...
Correct code:
class Book:
__match_args__ = ("title", "year")
def __init__(self, title, year, author):
self.title = title
self.year = year
self.author = author
def func(item: Book):
match item:
case Book("title", 2000, author="author"):
...
Related links:
- Python documentation —
https://docs.python.org/3/reference/compound_stmts.html#class-patterns
Created by the match_statements checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/match_statements_checker.py). 🌟
⭐ too-many-star-expressions / E0112
Message emitted: More than one starred expression in assignment
Description: Emitted when there are more than one starred expressions (*x) in an assignment. This is a SyntaxError. So yeah, not just a vibe check — actual illegal syntax. 😭
Problematic code:
*stars, *constellations = ["Sirius", "Arcturus", "Vega"] # [too-many-star-expressions]
Correct code:
*sirius_and_arcturus, vega = ["Sirius", "Arcturus", "Vega"]
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py). 🎀
🎀 truncated-format-string / E1301
Message emitted: Format string ends in middle of conversion specifier
Description: Used when a format string terminates before the end of a conversion specifier. It's giving "sentence cut off mid—" 💅
Problematic code:
PARG_2 = 1
print("strange format %2" % PARG_2) # [truncated-format-string]
Correct code:
PARG_2 = 1
print(f"strange format {PARG_2}")
Created by the string checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py). 🍓
🧚 undefined-all-variable / E0603
Message emitted: Undefined variable name %r in __all__
Description: Used when an undefined variable name is referenced in __all__. Your export list is lying, bestie. 🙈
Problematic code:
__all__ = ["get_fruit_colour"] # [undefined-all-variable]
def get_fruit_color():
pass
Correct code:
__all__ = ["get_fruit_color"]
def get_fruit_color():
pass
Related links:
Importing * From a Package—https://docs.python.org/3/tutorial/modules.html#importing-from-a-package
Created by the variables checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py). 🦋
🔥 undefined-variable / E0602
Message emitted: Undefined variable %r
Description: Used when an undefined variable is accessed. It's literally not there. The audacity. 💀
Problematic code:
print(number + 2) # [undefined-variable]
Correct code:
number = 3
print(number + 2)
Created by the variables checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py). 👑
🌸 unexpected-keyword-arg / E1123
Message emitted: Unexpected keyword argument %r in %s call
Description: Used when a function call passes a keyword argument that doesn't correspond to one of the function's parameter names. The function said "who?" and honestly same. 😭
Problematic code:
def print_coordinates(x=0, y=0):
print(f"{x=}, {y=}")
print_coordinates(x=1, y=2, z=3) # [unexpected-keyword-arg]
Correct code:
def print_coordinates(x=0, y=0):
print(f"{x=}, {y=}")
print_coordinates(x=1, y=2)
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py). 🫶
💜 unexpected-special-method-signature / E0302
Message emitted: The special method %r expects %s param(s), %d %s given
Description: Emitted when a special method was defined with an invalid number of parameters. If it has too few or too many, it might not work at all. Special methods are picky like that. ✨
Problematic code:
class ContextManager:
def __enter__(self, context): # [unexpected-special-method-signature]
pass
def __exit__(self, type): # [unexpected-special-method-signature]
pass
Correct code:
class ContextManager:
def __enter__(self):
pass
def __exit__(self, type, value, traceback):
pass
Created by the classes checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/special_methods_checker.py). 🧚
🎀 unhashable-member / E1143
Message emitted: '%s' is unhashable and can't be used as a %s in a %s
Description: Emitted when a dict key or set member is not hashable (i.e. doesn't define __hash__ method).
Problematic code:
# Print the number of apples:
print({"apple": 42}[["apple"]]) # [unhashable-member]
Correct code:
# Print the number of apples:
print({"apple": 42}["apple"])
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py).
🧩 unpacking-non-sequence / E0633
Message emitted: Attempting to unpack a non-sequence%s
Description: Used when something which is not a sequence is used in an unpack assignment.
Problematic code:
a, b, c = 1 # [unpacking-non-sequence]
Correct code:
a, b, c = 1, 2, 3
Created by the variables checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py).
🧩 unrecognized-inline-option / E0011
Message emitted: Unrecognized file option %r
Description: Used when an unknown inline option is encountered.
Problematic code:
# +1: [unrecognized-inline-option]
# pylint:applesoranges=1
Correct code:
# pylint: enable=too-many-public-methods
Created by the main checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py).
🧩 unrecognized-option / E0015
Message emitted: Unrecognized option found: %s
Description: Used when we detect an option that we do not recognize.
Additional details: One of your options is not recognized. There's nothing to change in your code, but your pylint configuration or the way you launch pylint needs to be modified.
For example, this message would be raised when invoking pylint with pylint --unknown-option=yes test.py. Or you might be launching pylint with the following toml configuration:
[tool.pylint]
jars = "10"
When the following should be used:
[tool.pylint]
jobs = "10"
This warning was released in pylint 2.14: bad options were silently failing before.
Created by the main checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py).
🧩 unsubscriptable-object / E1136
Message emitted: Value '%s' is unsubscriptable
Description: Emitted when a subscripted value doesn't support subscription (i.e. doesn't define __getitem__ method or __class_getitem__ for a class).
Problematic code:
class Fruit:
pass
Fruit()[1] # [unsubscriptable-object]
Correct code:
class Fruit:
def __init__(self):
self.colors = ["red", "orange", "yellow"]
def __getitem__(self, idx):
return self.colors[idx]
Fruit()[1]
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py).
🧩 unsupported-assignment-operation / E1137
Message emitted: %r does not support item assignment
Description: Emitted when an object does not support item assignment (i.e. doesn't define __setitem__ method).
Problematic code:
def pick_fruits(fruits):
for fruit in fruits:
print(fruit)
pick_fruits(["apple"])[0] = "orange" # [unsupported-assignment-operation]
Correct code:
def pick_fruits(fruits):
for fruit in fruits:
print(fruit)
return []
pick_fruits(["apple"])[0] = "orange"
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py).
🧩 unsupported-binary-operation / E1131
Message emitted: %s
Description: Emitted when a binary arithmetic operation between two operands is not supported.
Problematic code:
drink = "water" | None # [unsupported-binary-operation]
result = [] | None # [unsupported-binary-operation]
Correct code:
masked = 0b111111 & 0b001100
result = 0xAEFF | 0x0B99
Configuration file:
[main]
py-version=3.9
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py).
🧩 unsupported-delete-operation / E1138
Message emitted: %r does not support item deletion
Description: Emitted when an object does not support item deletion (i.e. doesn't define __delitem__ method).
Problematic code:
FRUITS = ("apple", "orange", "berry")
del FRUITS[0] # [unsupported-delete-operation]
Correct code:
FRUITS = ["apple", "orange", "berry"]
del FRUITS[0]
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py).
🧩 unsupported-membership-test / E1135
Message emitted: Value '%s' doesn't support membership test
Description: Emitted when an instance in membership test expression doesn't implement membership protocol (__contains__/__iter__/__getitem__).
Problematic code:
class Fruit:
pass
apple = "apple" in Fruit() # [unsupported-membership-test]
Correct code:
class Fruit:
FRUITS = ["apple", "orange"]
def __contains__(self, name):
return name in self.FRUITS
apple = "apple" in Fruit()
Created by the typecheck checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py).
🧩 used-before-assignment / E0601
Message emitted: Using variable %r before assignment
Description: Emitted when a local variable is accessed before its assignment took place. Assignments in try blocks are assumed not to have occurred when evaluating associated except/finally blocks. Assignments in except blocks are assumed not to have occurred when evaluating statements outside the block, except when the associated try block contains a return statement.
Problematic code:
print(hello) # [used-before-assignment]
hello = "Hello World !"
Correct code:
hello = "Hello World !"
print(hello)
Created by the variables checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py).
🧩 used-prior-global-declaration / E0118
Message emitted: Name %r is used prior to global declaration
Description: Emitted when a name is used prior a global declaration, which results in an error since Python 3.6.
Problematic code:
TOMATO = "black cherry"
def update_tomato():
print(TOMATO) # [used-prior-global-declaration]
global TOMATO
TOMATO = "cherry tomato"
Correct code:
TOMATO = "black cherry"
def update_tomato():
global TOMATO
TOMATO = "moneymaker"
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py).
🧩 yield-inside-async-function / E1700
Message emitted: Yield inside async function
Description: Used when an yield or yield from statement is found inside an async function.
Problematic code:
async def foo():
yield from [1, 2, 3] # [yield-inside-async-function]
Correct code:
async def foo():
def _inner_foo():
yield from [1, 2, 3]
async def foo():
yield 42
Additional details: The message can't be emitted when using Python < 3.5.
Related links:
- PEP 525 —
https://peps.python.org/pep-0525/
Created by the async checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/async_checker.py).
🧩 yield-outside-function / E0105
Message emitted: Yield outside function
Description: Used when a "yield" statement is found outside a function or method.
Problematic code:
for i in range(10):
yield i # [yield-outside-function]
Correct code:
def one_to_ten():
for i in range(10):
yield i
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py).
All renamed messages in the error category:
bad-context-manager/E0235—'bad-context-manager' has been renamed. The new message can be found at:bad-exception-context/E0703—'bad-exception-context' has been renamed. The new message can be found at:bad-option-value/E0012—'bad-option-value' has been renamed. The new message can be found at:
🧩 useless-option-value / R0022
Message emitted: Useless option value for '%s', %s
Description: Used when a value for an option that is now deleted from pylint is encountered.
Problematic code:
"""'bad-continuation' was removed from pylint in https://github.com/pylint-dev/pylint/pull/3571"""
# pylint: disable=bad-continuation # [useless-option-value]
Correct code:
"""'bad-continuation' was removed from pylint in https://github.com/pylint-dev/pylint/pull/3571"""
Additional details: You can disable this check if you don't want to cleanup your configuration of old messages.
Created by the main checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py).
🧩 unknown-option-value / W0012
Message emitted: Unknown option value for '%s', expected a valid pylint message and got '%s'
Description: Used when an unknown value is encountered for an option.
Problematic code:
# pylint: disable=missnig-docstring # [unknown-option-value]
Correct code:
# pylint: disable=missing-docstring
Created by the main checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py).
maybe-no-member/E1103—'maybe-no-member' has been renamed. The new message can be found at:old-non-iterator-returned-2/E0234—'old-non-iterator-returned-2' has been renamed. The new message can be found at:old-unbalanced-tuple-unpacking/E0632—'old-unbalanced-tuple-unpacking' has been renamed. The new message can be found at:
🧩 unbalanced-tuple-unpacking / W0632
Message emitted: Possible unbalanced tuple unpacking with sequence %s: left side has %d label%s, right side has %d value%s
Description: Used when there is an unbalanced tuple unpacking in assignment.
Problematic code:
fruits = ("orange", "apple", "strawberry", "peer")
orange, apple, strawberry = fruits # [unbalanced-tuple-unpacking]
Correct code:
fruits = ("orange", "apple", "strawberry", "peer")
orange, apple, *remaining_fruits = fruits
Related links:
- PEP 3132 - Extended Iterable Unpacking —
https://peps.python.org/pep-3132/
Created by the variables checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py).
unhashable-dict-key/E1140—'unhashable-dict-key' has been renamed. The new message can be found at:
⚠️ Warning
okay bestie, buckle up — these are literally all the messages in the warning category:
🧚 abstract-method / W0223
Message emitted:
Method %r is abstract in class %r but is not overridden in child class
%r
Description: this one fires when an abstract method (like, one that does raise NotImplementedError) doesn't get overridden in a concrete class. no cap, the subclass is just vibing with no implementation.
Problematic code:
abstract_method.py:
import abc
class WildAnimal:
@abc.abstractmethod
def make_sound(self):
pass
class Panther(WildAnimal): # [abstract-method]
pass
function_raising_not_implemented_error.py:
class Pet:
def make_sound(self):
raise NotImplementedError
class Cat(Pet): # [abstract-method]
pass
Correct code:
abstract_method.py:
import abc
class WildAnimal:
@abc.abstractmethod
def make_sound(self):
pass
class Panther(WildAnimal):
def make_sound(self):
print("MEEEOW")
function_raising_not_implemented_error.py:
class Pet:
def make_sound(self):
raise NotImplementedError
class Cat(Pet):
def make_sound(self):
print("Meeeow")
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py 💅
🌸 anomalous-backslash-in-string / W1401
Message emitted:
Anomalous backslash in string: '%s'. String constant might be missing an
r prefix.
Description: used when a backslash is in a literal string but not as an escape. it's giving "sir that backslash is doing nothing".
Problematic code:
string = "\z" # [syntax-error]
Correct code:
double_escape.py:
string = "\\z"
existing_escape_sequence.py:
string = "\t"
r_prefix.py:
string = r"\z"
Additional details: \z is same as \\z because there's no escape sequence for z. But it is not clear for the reader of the code.
The only reason this is demonstrated to raise syntax-error is because pylint's CI now runs on Python 3.12, where this truly raises a SyntaxError. We hope to address this discrepancy in the documentation in the future.
Related links:
- String and Bytes literals —
https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals - Long form stackoverflow explanation —
https://stackoverflow.com/a/19030982/2519059
Created by the string checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py 🎀
🦋 anomalous-unicode-escape-in-string / W1402
Message emitted:
Anomalous Unicode escape in byte string: '%s'. String constant might be
missing an r or u prefix.
Description: used when an escape like u is encountered in a byte string where it has no effect. the escape is literally just standing there, not escaping anything 😭
Problematic code:
print(b"\u%b" % b"0394") # [syntax-error]
Correct code:
print(b"\\u%b" % b"0394")
Created by the string checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py ✨
💜 arguments-differ / W0221
Message emitted:
%s %s %r method
Description: used when a method has a different number of arguments than in the implemented interface or in an overridden method. Extra arguments with default values are ignored. (so default-valued extras get a pass, ok?)
Problematic code:
class Drink:
def mix(self, fluid_one, fluid_two):
return fluid_one + fluid_two
class Cocktail(Drink):
def mix(self, fluid_one, fluid_two, alcoholic_fluid): # [arguments-differ]
return fluid_one + fluid_two + alcoholic_fluid
Correct code:
add_option_in_base_class.py:
"""
Here we assume that drink and cocktail are the same thing and should actually
inherit from each over. We also assume that 'Drink' are 'Cocktail' without
alcohol (we added the alcohol option in the base class).
This permit to not have to modify the cocktails calls downstream but the case where
an alcohol is mixed in a soft drink will need to be handled.
"""
class Drink:
def mix(self, fluid_one, fluid_two, alcoholic_fluid=None):
# if alcoholic_fluid is not None:
# raise Exception(f"This soft drink has {alcoholic_fluid} in it !")
return fluid_one + fluid_two
class Cocktail(Drink):
def mix(self, fluid_one, fluid_two, alcoholic_fluid):
return fluid_one + fluid_two + alcoholic_fluid
default_value.py:
"""
Here we assume that drink and cocktail are the same thing and should actually
inherit from each over. We also assume that any Cocktail can be treated like
a Drink (if you add beer to it).
This permit to not have to modify the calls downstream and causes the least
amount of disturbance at the cost of making cocktails beer-based implicitly.
"""
class Drink:
def mix(self, fluid_one, fluid_two):
return fluid_one + fluid_two
class Cocktail(Drink):
def mix(self, fluid_one, fluid_two, alcoholic_fluid="Beer"):
return fluid_one + fluid_two + alcoholic_fluid
no_inheritance.py:
"""
Here we assume that 'Drink' and 'Cocktail' are different things and should
not be treated together like if they were the same thing.
This will force some downstream changes and force the API user to make a
conscious decision about the alcoholic content of its drink when using the
API. For example, it's impossible to create a mojito with beer without
explicitly wanting to, or to add an alcohol to a soft-drink.
"""
class Drink:
def mix(self, fluid_one, fluid_two):
return fluid_one + fluid_two
class Cocktail:
def mix(self, fluid_one, fluid_two, alcoholic_fluid):
return fluid_one + fluid_two + alcoholic_fluid
Additional details: argument-differ denotes an issue with the Liskov Substitution Principle. This means that the code in question violates an important design principle which does not have one single solution. We recommend to search online for the best solution in your case.
To give some examples of potential solutions:
- Add the argument to the parent class
- Remove the inheritance completely
- Add default arguments to the child class
Related links:
- Liskov Substitution Principle —
https://en.wikipedia.org/wiki/Liskov_substitution_principle
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py 👑
🌈 arguments-out-of-order / W1114
Message emitted:
Positional arguments appear to be out of order
Description: emitted when the caller's argument names fully match the parameter names in the function signature but do not have the same order. like bestie, you had the names right and then shuffled them 😭
Problematic code:
def function_3_args(first_argument, second_argument, third_argument):
"""Three arguments function"""
return first_argument, second_argument, third_argument
def args_out_of_order():
first_argument = 1
second_argument = 2
third_argument = 3
function_3_args( # [arguments-out-of-order]
first_argument, third_argument, second_argument
)
Correct code:
def function_3_args(first_argument, second_argument, third_argument):
"""Three arguments function"""
return first_argument, second_argument, third_argument
def args_out_of_order():
first_argument = 1
second_argument = 2
third_argument = 3
function_3_args(first_argument, second_argument, third_argument)
Created by the typecheck checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py 🫶
🍓 arguments-renamed / W0237
Message emitted:
%s %s %r method
Description: used when a method parameter has a different name than in the implemented interface or in an overridden method. same energy, different name — and pylint is not letting it slide.
Problematic code:
class Fruit:
def brew(self, ingredient_name: str):
print(f"Brewing a {type(self)} with {ingredient_name}")
class Apple(Fruit): ...
class Orange(Fruit):
def brew(self, flavor: str): # [arguments-renamed]
print(f"Brewing an orange with {flavor}")
for fruit, ingredient_name in [[Orange(), "thyme"], [Apple(), "cinnamon"]]:
fruit.brew(ingredient_name=ingredient_name)
Correct code:
class Fruit:
def brew(self, ingredient_name: str):
print(f"Brewing a {type(self)} with {ingredient_name}")
class Apple(Fruit): ...
class Orange(Fruit):
def brew(self, ingredient_name: str):
print(f"Brewing an orange with {ingredient_name}")
for fruit, ingredient_name in [[Orange(), "thyme"], [Apple(), "cinnamon"]]:
fruit.brew(ingredient_name=ingredient_name)
Created by the classes checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py 💖
⭐ assert-on-string-literal / W0129
Message emitted:
Assert statement has a string literal as its first argument. The assert
will %s fail.
Description: used when an assert statement has a string literal as its first argument, which will cause the assert to always pass. (which is... not the vibe you want from a test 🫠)
Problematic code:
def test_division():
a = 9 / 3
assert "No ZeroDivisionError were raised" # [assert-on-string-literal]
Correct code:
def test_division():
a = 9 / 3
assert a == 3
Additional details: directly asserting a string literal will always pass. The solution is to test something that could fail, or not assert at all.
For unittest assertions there is the similar redundant-unittest-assert / W1503 message (#redundant-unittest-assert).
Related links:
- Tests without assertion —
https://stackoverflow.com/a/137418/2519059 - Testing that there is no error raised —
https://stackoverflow.com/questions/20274987 - Parametrizing conditional raising —
https://docs.pytest.org/en/latest/example/parametrize.html#parametrizing-conditional-raising
Created by the basic checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py 🌟
💀 assert-on-tuple / W0199
Message emitted:
Assert called on a populated tuple. Did you mean 'assert x,y'?
Description: a call of assert on a tuple will always evaluate to true if the tuple is not empty, and will always evaluate to false if it is. non-empty tuple = truthy, bestie, that's just how it is.
Problematic code:
assert (1, None) # [assert-on-tuple]
Correct code:
x, y = (1, None)
assert x
assert y
Additional details:
Directly asserting a non-empty tuple will always pass. The fix is literally just: test something that could actually fail, or don't assert at all. 🫶
For unittest assertions there's the similar redundant-unittest-assert / W1503 message, iykyk.
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py).
💅 attribute-defined-outside-init / W0201
Message emitted:
Attribute %r defined outside __init__
Description:
Used when an instance attribute is defined outside the __init__ method.
Problematic code:
class Student:
def register(self):
self.is_registered = True # [attribute-defined-outside-init]
Correct code:
class Student:
def __init__(self):
self.is_registered = False
def register(self):
self.is_registered = True
Created by the classes checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py).
🚫 bad-builtin / W0141
Message emitted:
Used builtin function %s
Description:
Used when a disallowed builtin function is used (see the bad-function option). Usual disallowed functions are the ones like map, or filter, where Python offers now some cleaner alternative like list comprehension.
Problematic code:
numbers = list(map(lambda x: 2 * x, [1, 2, 3])) # [bad-builtin]
print(numbers)
Correct code:
numbers = [2 * x for x in [1, 2, 3]]
print(numbers)
Configuration file:
[MAIN]
load-plugins = pylint.extensions.bad_builtin
Note: this message is emitted by the optionaldeprecated_builtinschecker, which requires thepylint.extensions.bad_builtinplugin to be loaded.
Created by the deprecated_builtins checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/bad_builtin.py).
😵💫 bad-chained-comparison / W3601
Message emitted:
Suspicious %s-part chained comparison using semantically incompatible operators (%s)
Description:
Used when there is a chained comparison where one expression is part of two comparisons that belong to different semantic groups ("<" does not mean the same thing as "is", chaining them in "0 < x is None" is probably a mistake).
Problematic code:
parrot.py:
shop = {
# animal: (specie, descriptions)
"parrot": ("Norvegian blue", ("restin'", "remarkable", "beautiful plumage")),
}
if "parrot" in shop is "restin'": # [bad-chained-comparison]
print("Hellooooo, Pooolllllyyy ! WAAAAKEEY, WAKKEEEY !")
xor.py:
def xor_check(*, left=None, right=None):
if left is None != right is None: # [bad-chained-comparison]
raise ValueError(
"Either both left= and right= need to be provided or none should."
)
Correct code:
parrot.py:
shop = {
# animal: (specie, descriptions)
"parrot": ("Norvegian blue", ("restin'", "remarkable", "beautiful plumage")),
}
if "parrot" in shop and "restin'" in shop["parrot"][1]:
print("Hellooooo, Pooolllllyyy ! WAAAAKEEY, WAKKEEEY !")
xor.py:
def xor_check(*, left=None, right=None):
if (left is None) != (right is None):
raise ValueError(
"Either both left= and right= need to be provided or none should."
)
Related links:
- Comparison Chaining —
https://docs.python.org/3/reference/expressions.html#comparisons
Created by the bad-chained-comparison checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/bad_chained_comparison.py).
✨ bad-dunder-name / W3201
Message emitted:
Bad or misspelled dunder method name %s.
Description:
Used when a dunder method is misspelled or defined with a name not within the predefined list of dunder names.
Problematic code:
class Apples:
def _init_(self): # [bad-dunder-name]
pass
def __hello__(self): # [bad-dunder-name]
print("hello")
Correct code:
class Apples:
def __init__(self):
pass
def hello(self):
print("hello")
Configuration file:
[MAIN]
load-plugins=pylint.extensions.dunder
Note: this message is emitted by the optionaldunderchecker, which requires thepylint.extensions.dunderplugin to be loaded.
Created by the dunder checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/dunder.py).
💀 bad-format-string / W1302
Message emitted:
Invalid format string
Description:
Used when a PEP 3101 format string is invalid.
Problematic code:
print("{a[0] + a[1]}".format(a=[0, 1])) # [bad-format-string]
Correct code:
print("{a[0]} + {a[1]}".format(a=[0, 1]))
Related links:
- Format String Syntax —
https://docs.python.org/3/library/string.html#formatstrings - PyFormat —
https://pyformat.info/
Created by the string checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py).
🎀 bad-format-string-key / W1300
Message emitted:
Format string dictionary key should be a string, not %s
Description:
Used when a format string that uses named conversion specifiers is used with a dictionary whose keys are not all strings.
Problematic code:
print("%(one)d" % {"one": 1, 2: 2}) # [bad-format-string-key]
Correct code:
print("%(one)d, %(two)d" % {"one": 1, "two": 2})
Additional details:
This check only works for old-style string formatting using the '%' operator.
This check only works if the dictionary with the values to be formatted is defined inline. Passing a variable will not trigger the check as the other keys in this dictionary may be used in other contexts, while an inline defined dictionary is clearly only intended to hold the values that should be formatted.
Created by the string checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py).
📐 bad-indentation / W0311
Message emitted:
Bad indentation. Found %s %s, expected %s
Description:
Used when an unexpected number of indentation's tabulations or spaces has been found.
Problematic code:
if input():
print('yes') # [bad-indentation]
Correct code:
if input():
print("yes")
Additional details:
The option --indent-string can be used to set the indentation unit for this check.
Created by the format checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py).
🔓 bad-open-mode / W1501
Message emitted:
"%s" is not a valid mode for open.
Description:
Python supports: r, w, a[, x] modes with b, +, and U (only with r) options. See https://docs.python.org/3/library/functions.html#open
Problematic code:
def open_and_get_content(file_path):
with open(file_path, "rwx") as file: # [bad-open-mode]
return file.read()
Correct code:
def open_and_get_content(file_path):
with open(file_path, "r") as file:
return file.read()
Created by the stdlib checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py).
🐺 bad-staticmethod-argument / W0211
Message emitted:
Static method with %r as first argument
Description:
Used when a static method has "self" or a value specified in valid-classmethod-first-arg option or valid-metaclass-classmethod-first-arg option as first argument.
Problematic code:
class Wolf:
@staticmethod
def eat(self): # [bad-staticmethod-argument]
pass
Correct code:
class Wolf:
@staticmethod
def eat(sheep):
pass
Created by the classes checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py).
🧵 bad-thread-instantiation / W1506
Message emitted:
threading.Thread needs the target function
Description:
The warning is emitted when a threading.Thread class is instantiated without the target function being passed as a kwarg or as a second argument. By default, the first parameter is the group param, not the target param.
Problematic code:
import threading
def thread_target(n):
print(n**2)
thread = threading.Thread(lambda: None) # [bad-thread-instantiation]
thread.start()
Correct code:
import threading
def thread_target(n):
print(n**2)
thread = threading.Thread(target=thread_target, args=(10,))
thread.start()
Created by the stdlib checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py).
🙈 bare-except / W0702
Message emitted:
No exception type(s) specified
Description:
A bare except: clause will catch SystemExit and KeyboardInterrupt exceptions, making it harder to interrupt a program with Control-C, and can disguise other problems. If you want to catch all exceptions that signal program errors, use except Exception: (bare except is equivalent to except BaseException:).
Problematic code:
try:
import platform_specific_module
except: # [bare-except]
platform_specific_module = None
Correct code:
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
Additional details:
A good rule of thumb is to limit use of bare 'except' clauses to two cases: - If the exception handler will be printing out or logging the traceback; at least the user will be aware that an error has occurred. - If the code needs to do some cleanup work, but then lets the exception propagate upwards with raise. try...finally can be a better way to handle this case.
Related links:
- Programming recommendation in PEP8 —
https://peps.python.org/pep-0008/#programming-recommendations - PEP 760 – No More Bare Excepts (Rejected) —
https://peps.python.org/pep-0760/ - Discussion about PEP 760 —
https://discuss.python.org/t/pep-760-no-more-bare-excepts/67182
Created by the exceptions checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py).
⚠️ binary-op-exception / W0711
Message emitted:
Exception to catch is the result of a binary "%s" operation
Description:
okay so this check is basically the “did you actually mean that?” of exception handling. It fires when the exception you're catching is shaped like except A or B:. If you actually meant to catch multiple, rewrite it as except (A, B): — no cap.
Problematic code:
try:
1 / 0
except ZeroDivisionError or ValueError: # [binary-op-exception]
pass
Correct code:
try:
1 / 0
except (ZeroDivisionError, ValueError):
pass
Created by the exceptions checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py).
🦋 boolean-datetime / W1502 🦋
Message emitted: Using datetime.time in a boolean context.
Description: using datetime.time in a boolean context can hide subtle bugs when the time it represents matches midnight UTC. This behaviour was fixed in Python 3.5. See https://bugs.python.org/issue13936 for reference. 💅
Problematic code:
import datetime
if datetime.time(): # [boolean-datetime]
print("It is time.")
if datetime.datetime.now().time(): # [boolean-datetime]
print("Now or never.")
Correct code:
import datetime
time_now_utc = datetime.datetime.now(tz=datetime.UTC).time()
if time_now_utc > datetime.time(6, 0):
print("Daytime!")
if time_now_utc < datetime.time(6, 0):
print("Nighttime!")
Configuration file:
[main]
py-version=3.4
Related links:
- Python bug tracker:
https://bugs.python.org/issue13936
Created by the stdlib checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py).
🎀 break-in-finally / W0137 🎀
Message emitted: 'break' discouraged inside 'finally' clause
Description: emitted when the break keyword is found inside a finally clause. This will raise a SyntaxWarning starting in Python 3.14. ⚠️
Problematic code:
while True:
try:
pass
finally:
break # [break-in-finally]
Correct code:
while True:
try:
pass
except ValueError:
pass
else:
break
Related links:
- Python 3 docs 'finally' clause:
https://docs.python.org/3/reference/compound_stmts.html#finally-clause - PEP 765 - Disallow return/break/continue that exit a finally block:
https://peps.python.org/pep-0765/
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py).
🌸 broad-exception-caught / W0718 🌸
Message emitted: Catching too general exception %s
Description: if you use a naked except Exception: clause, you might end up catching exceptions other than the ones you expect to catch. This can hide bugs or make it harder to debug programs when unrelated errors are hidden. 😭
Problematic code:
try:
import platform_specific_module
except Exception: # [broad-exception-caught]
platform_specific_module = None
Correct code:
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
Additional details: for example, you're trying to import a library with required system dependencies and you catch everything instead of only import errors, you will miss the error message telling you that your code could work if you had installed the system dependencies. fr fr.
Related links:
- Should I always specify an exception type in 'except' statements?:
https://stackoverflow.com/a/14797508/2519059
Created by the exceptions checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py).
💜 broad-exception-raised / W0719 💜
Message emitted: Raising too general exception: %s
Description: raising exceptions that are too generic forces you to catch exceptions generically too. It will force you to use a naked except Exception: clause. You might then end up catching exceptions other than the ones you expect to catch. This can hide bugs or make it harder to debug programs when unrelated errors are hidden. 💀
Problematic code:
def small_apple(apple, length):
if len(apple) < length:
raise Exception("Apple is too small!") # [broad-exception-raised]
print(f"{apple} is proper size.")
Correct code:
def small_apple(apple, length):
if len(apple) < length:
raise ValueError("Apple is too small!")
print(f"{apple} is proper size.")
Related links:
- Programming recommendation in PEP8:
https://peps.python.org/pep-0008/#programming-recommendations
Created by the exceptions checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py).
🦋 cell-var-from-loop / W0640 🦋
Message emitted: Cell variable %s defined in loop
Description: a variable used in a closure is defined in a loop. This will result in all closures using the same value for the closed-over variable. main character energy? no bestie, this is a bug. 😭
Problematic code:
def teacher_greeting(names):
greetings = []
for name in names:
def greet():
# do something
print(f"Hello, {name}!") # [cell-var-from-loop]
if name.isalpha():
greetings.append(greet)
for greet in greetings:
# the "name" variable is evaluated when the function is called here,
# which is the last value it had in the loop - "Not-A-Name"
greet()
teacher_greeting(["Graham", "John", "Terry", "Eric", "Terry", "Michael"])
# "Hello, Michael!"
# "Hello, Michael!"
# "Hello, Michael!"
# "Hello, Michael!"
# "Hello, Michael!"
Correct code:
functools.partial.py:
import functools
def teacher_greeting(names):
greetings = []
for name in names:
if name.isalpha():
# "name" is evaluated when the partial is created here, so this
# does not do lazy evaluation
greetings.append(functools.partial(print, f"Hello, {name}!"))
for greet in greetings:
# `partial`s are called like functions, but you've already passed the
# arguments to them
greet()
teacher_greeting(["Graham", "John", "Terry", "Eric", "Terry", "Michael"])
# "Hello, Graham!"
# "Hello, John!"
# "Hello, Eric!"
# "Hello, Terry!"
# "Hello, Michael!"
new_function.py:
def teacher_greeting(names):
def greet(name):
# do something
print(f"Hello, {name}!")
for name in names:
if name.isalpha():
# we're passing the value of "name" to the function here
greet(name)
teacher_greeting(["Graham", "John", "Terry", "Eric", "Terry", "Michael"])
# "Hello, Graham!"
# "Hello, John!"
# "Hello, Eric!"
# "Hello, Terry!"
# "Hello, Michael!"
Related links:
- Stackoverflow discussion:
https://stackoverflow.com/questions/25314547/cell-var-from-loop-warning-from-pylint
Created by the variables checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py).
🎀 comparison-with-callable / W0143 🎀
Message emitted: Comparing against a callable, did you omit the parenthesis?
Description: this message is emitted when pylint detects that a comparison with a callable was made, which might suggest that some parenthesis were omitted, resulting in potential unwanted behaviour. the way that missing () can ruin your whole vibe 😭
Problematic code:
def function_returning_a_fruit() -> str:
return "orange"
def is_an_orange(fruit: str = "apple"):
# apple == <function function_returning_a_fruit at 0x7f343ff0a1f0>
return fruit == function_returning_a_fruit # [comparison-with-callable]
Correct code:
def function_returning_a_fruit() -> str:
return "orange"
def is_an_orange(fruit: str = "apple"):
# apple == orange
return fruit == function_returning_a_fruit()
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/comparison_checker.py).
🌸 confusing-with-statement / W0124 🌸
Message emitted: Following "as" with another context manager looks like a tuple.
Description: emitted when a with statement component returns multiple values and uses name binding with as only for a part of those values, as in with ctx() as a, b. This can be misleading, since it's not clear if the context manager returns a tuple or if the node without a name binding is another context manager. 🙈
Problematic code:
with open("file.txt", "w") as fh1, fh2: # [confusing-with-statement]
pass
Correct code:
with open("file.txt", "w", encoding="utf8") as fh1:
with open("file.txt", "w", encoding="utf8") as fh2:
pass
Created by the basic checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py).
💅 consider-ternary-expression / W0160 💅
Message emitted: Consider rewriting as a ternary expression
Description: multiple assign statements spread across if/else blocks can be rewritten with a single assignment and ternary expression. it's giving “one line, cleaner vibe” ✨
Problematic code:
x, y = input(), input()
if x >= y: # [consider-ternary-expression]
maximum = x
else:
maximum = y
Correct code:
x, y = input(), input()
maximum = x if x >= y else y
Configuration file:
[MAIN]
load-plugins=pylint.extensions.consider_ternary_expression
Note: this message is emitted by the optional 'consider_ternary_expression' checker (#pylint-extensions-consider-ternary-expression), which requires thepylint.extensions.consider_ternary_expressionplugin to be loaded. 🫶
Created by the consider_ternary_expression checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/consider_ternary_expression.py).
🌈 contextmanager-generator-missing-cleanup / W0135 🌈
Message emitted: The context used in function %r will not be exited.
Description: used when a contextmanager is used inside a generator function and the cleanup is not handled. lowkey a leak, bestie. 💀
Problematic code:
import contextlib
@contextlib.contextmanager
def cm():
contextvar = "acquired context"
print("cm enter")
yield contextvar
print("cm exit")
def genfunc_with_cm():
with cm() as context: # [contextmanager-generator-missing-cleanup]
yield context * 2
Correct code:
import contextlib
@contextlib.contextmanager def good_cm_except(): contextvar = "acquired context" print("good cm enter") try: yield contextvar except GeneratorExit: print("good cm exit")
def genfunc_with_cm(): with good_cm_except() as context: yield context * 2
def genfunc_with_discard(): with good_cm_except(): yield "discarded"
@contextlib.contextmanager def good_cm_yield_none(): print("good cm enter") yield print("good cm exit")
def genfunc_with_none_yield(): with good_cm_yield_none() as var: print(var) yield "constant yield"
@contextlib.contextmanager def good_cm_finally(): contextvar = "acquired context" print("good cm enter") try: yield contextvar finally: print("good cm exit")
def good_cm_finally_genfunc(): with good_cm_finally() as context: yield context * 2
@contextlib.contextmanager def good_cm_no_cleanup(): contextvar = "acquired context" print("cm enter") yield contextvar
def good_cm_no_cleanup_genfunc(): with good_cm_no_cleanup() as context: yield context * 2
**Additional details:** ok bestie, so instantiating and using a contextmanager inside a generator function can lowkey result in unexpected behavior if you're expecting the context to only be available for the generator function. In the case that the generator is not closed or destroyed, then the context manager is held suspended as is 😭.
This message warns on the generator function instead of the contextmanager function because the ways to use a contextmanager are many. A contextmanager can be used as a decorator (which immediately has `__enter__`/`__exit__` applied), and the use of `as ...` or discarding the return value also implies whether the context needs cleanup or not. So for this message, warning the invoker of the contextmanager is important 💅.
The check can create false positives if `yield` is used inside an `if`-`else` block without custom cleanup. Use `pylint: disable` for these.
from contextlib import contextmanager
@contextmanager def good_cm_no_cleanup(): contextvar = "acquired context" print("cm enter") if some_condition: yield contextvar else: yield contextvar
def good_cm_no_cleanup_genfunc(): # pylint: disable-next=contextmanager-generator-missing-cleanup with good_cm_no_cleanup() as context: yield context * 2
**Related links:** 🫶
- **Rationale** — `https://discuss.python.org/t/preventing-yield-inside-certain-context-managers/1091`
- **CPython Issue** — `https://github.com/python/cpython/issues/81924#issuecomment-1093830682`
Created by the **basic** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/function_checker.py` 🌸
---
### <pink>continue-in-finally</pink> / `W0136` 💅
**Message emitted:**
`'continue' discouraged inside 'finally' clause`
**Description:**
Emitted when the `continue` keyword is found inside a finally clause. This will raise a `SyntaxWarning` starting in Python 3.14 ⚠️.
**Problematic code:** 🙈
while True: try: pass finally: continue # [continue-in-finally]
**Correct code:** ✨
while True: try: pass except ValueError: pass else: continue
**Related links:** 🫶
- **Python 3 docs 'finally' clause** — `https://docs.python.org/3/reference/compound_stmts.html#finally-clause`
- **PEP 765 - Disallow return/break/continue that exit a finally block** — `https://peps.python.org/pep-0765/`
Created by the **basic** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py` 🌈
---
### <pink>dangerous-default-value</pink> / `W0102` 💀
**Message emitted:**
`Dangerous default value %s as argument`
**Description:**
Used when a mutable value as list or dictionary is detected in a default value for an argument 😬.
**Problematic code:** 🙈
def whats_on_the_telly(penguin=[]): # [dangerous-default-value] penguin.append("property of the zoo") return penguin
**Correct code:** ✨
def whats_on_the_telly(penguin=None): if penguin is None: penguin = [] penguin.append("property of the zoo") return penguin
**Additional details:** With a mutable default value, with each call the default value is modified, i.e.:
whats_on_the_telly() # ["property of the zoo"] whats_on_the_telly() # ["property of the zoo", "property of the zoo"] whats_on_the_telly() # ["property of the zoo", "property of the zoo", "property of the zoo"]
Created by the **basic** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py` 🎀
---
### <pink>deprecated-argument</pink> / `W4903` 🦋
**Message emitted:**
`Using deprecated argument %s of method %s()`
**Description:**
The argument is marked as deprecated and will be removed in the future 👀.
**Problematic code:** 🙈
int(x=1) # [deprecated-argument]
**Correct code:** ✨
int(1)
**Configuration file:** (none shown here, bestie 💀)
**Additional details:** The actual replacement needs to be studied on a case by case basis by reading the deprecation warning or the release notes 📝.
Created by the **stdlib** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py` ⭐
---
### <pink>deprecated-attribute</pink> / `W4906` 🧚
**Message emitted:**
`Using deprecated attribute %r`
**Description:**
The attribute is marked as deprecated and will be removed in the future 👀.
**Problematic code:** 🙈
from configparser import ParsingError
err = ParsingError("filename") source = err.filename # [deprecated-attribute]
**Correct code:** ✨
from configparser import ParsingError
err = ParsingError("filename") source = err.source
**Additional details:** The actual replacement needs to be studied on a case by case basis by reading the deprecation warning or the release notes 📝.
Created by the **stdlib** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py` 💜
---
### <pink>deprecated-class</pink> / `W4904` 🌸
**Message emitted:**
`Using deprecated class %s of module %s`
**Description:**
The class is marked as deprecated and will be removed in the future 👀.
**Problematic code:** 🙈
from collections import Iterable # [deprecated-class]
**Correct code:** ✨
from collections.abc import Iterable
**Additional details:** The actual replacement needs to be studied on a case by case basis by reading the deprecation warning or the release notes 📝.
Created by the **stdlib** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py` 🫶
---
### <pink>deprecated-decorator</pink> / `W4905` 🎀
**Message emitted:**
`Using deprecated decorator %s()`
**Description:**
The decorator is marked as deprecated and will be removed in the future 👀.
**Problematic code:** 🙈
import abc
class Animal: @abc.abstractclassmethod # [deprecated-decorator] def breath(cls): pass
**Correct code:** ✨
import abc
class Animal: @abc.classmethod @abc.abstractmethod def breath(cls): pass
**Configuration file:**
[main] py-version = 3.3
**Additional details:** The actual replacement needs to be studied on a case by case basis by reading the deprecation warning or the release notes 📝.
Created by the **stdlib** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py` 🌈
---
### <pink>deprecated-method</pink> / `W4902` 🦋
**Message emitted:**
`Using deprecated method %s()`
**Description:**
The method is marked as deprecated and will be removed in the future 👀.
**Problematic code:** 🙈
import logging
logging.warn("I'm coming, world !") # [deprecated-method]
**Correct code:** ✨
import logging
logging.warning("I'm coming, world !")
**Additional details:** The actual replacement needs to be studied on a case by case basis by reading the deprecation warning or the release notes 📝.
Created by the **stdlib** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py` ⭐
---
### <pink>deprecated-module</pink> / `W4901` 🍓
**Message emitted:**
`Deprecated module %r`
**Description:**
A module marked as deprecated is imported 👀.
**Problematic code:** 🙈
import distutils # [deprecated-module]
import whatever_you_want # [deprecated-module]
**Correct code:** ✨
import setuptools import whatever_replacement_you_want
**Configuration file:**
[main] py-version=3.7 deprecated-modules=whatever_you_want
**Additional details:** The actual replacement needs to be studied on a case by case basis by reading the deprecation warning or the release notes 📝.
Created by the **imports** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py` 💜
---
### <pink>deprecated-typing-alias</pink> / `W6001` 🧚
**Message emitted:**
`'%s' is deprecated, use '%s' instead`
**Description:**
Emitted when a deprecated typing alias is used 👀.
**Problematic code:** 🙈
import typing
item_to_number_of_item: typing.Dict[str, int] # [deprecated-typing-alias]
**Correct code:** ✨
item_to_number_of_item: dict[str, int]
**Configuration file:**
[main] load-plugins = pylint.extensions.typing
**Note:** This message is emitted by the optional `typing` checker (the `#pylint-extensions-typing` one, iykyk), which requires the `pylint.extensions.typing` plugin to be loaded 🫶.
Created by the **typing** checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/typing.py` 🌸
---
### <pink>differing-param-doc</pink> / `W9017` 🎀
**Message emitted:**
`"%s" differing in parameter documentation`
**Description:**
Please check parameter names in declarations 👀.
**Problematic code:** 🙈
def add(x, y): # [differing-param-doc] """Add two numbers.
:param int x: x value. :param int z: z value. """
return x + y
**Correct code:** ✨
def add(x, y): """Add two numbers.
:param int x: x value. :param int y: y value. """
return x + y
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.docparams
**Note:** This message is emitted by the optional `parameter_documentation` checker (the `#pylint-extensions-docparams` one, iykyk), which requires the `pylint.extensions.docparams` plugin to be loaded 🫶.
### `differing-type-doc` / `W9018`
**Message emitted:**
`"%s" differing in parameter type documentation`
**Description:** please double-check the parameter names in your type declarations, bestie — the names in the docstring type block gotta actually match the ones in the signature 💅
**Problematic code:**
def add(x: int, y: int): # [differing-type-doc] """Add two numbers.
:param int xy: x value. :param str y: y value. """
return x + y
**Correct code:**
def add(x, y): """Add two numbers.
:param int x: x value. :param int y: y value. """
return x + y
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.docparams
> **Heads up:** this message is emitted by the optional `parameter_documentation` checker (the one at `#pylint-extensions-docparams`), which requires the `pylint.extensions.docparams` plugin to be loaded. No plugin, no message. Periodt ✨
Created by the `parameter_documentation` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py`
---
### `duplicate-except` / `W0705`
**Message emitted:**
`Catching previously caught exception type %s`
**Description:** used when an `except` catches a type that was already caught by a previous handler. Like girl, the first `except` already got it — the second one is literally never gonna run 😭
**Problematic code:**
try: 1 / 0 except ZeroDivisionError: pass except ZeroDivisionError: # [duplicate-except] pass
**Correct code:**
try: 1 / 0 except ZeroDivisionError: pass
Created by the `exceptions` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py`
---
### `duplicate-key` / `W0109`
**Message emitted:**
`Duplicate key %r in dictionary`
**Description:** used when a dictionary expression binds the same key multiple times. Bestie, the second one just overwrites the first — that's not a dictionary, that's a heartbreak 💔
**Problematic code:**
test_score = {"Mathematics": 85, "Biology": 90, "Mathematics": 75} # [duplicate-key]
**Correct code:**
test_score = {"Mathematics": 85, "Biology": 90, "History": 75}
**Related links:**
- Python Dictionaries — `https://docs.python.org/3/tutorial/datastructures.html#dictionaries`
- Mapping Types — dict — `https://docs.python.org/3/library/stdtypes.html#typesmapping`
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`
---
### `duplicate-string-formatting-argument` / `W1308`
**Message emitted:**
`Duplicate string formatting argument %r, consider passing as named argument`
**Description:** used when we detect that a string formatting is repeating an argument instead of using named string arguments. It's giving copy-paste energy, and not the cute kind 🙈
**Problematic code:**
pylint: disable=missing-docstring, consider-using-f-string
SEE = "see 👀" SEA = "sea 🌊"
+1: [duplicate-string-formatting-argument,duplicate-string-formatting-argument]
CONST = """ A sailor went to {}, {}, {} To {} what he could {}, {}, {} But all that he could {}, {}, {} Was the bottom of the deep blue {}, {}, {}! """.format( SEA, SEA, SEA, SEE, SEE, SEE, SEE, SEE, SEE, SEE, SEA, SEA, SEA, )
**Correct code:**
pylint: disable=missing-docstring, consider-using-f-string
SEE = "see 👀" SEA = "sea 🌊"
CONST = """ A sailor went to {sea}, {sea}, {sea} To {see} what he could {see}, {see}, {see} But all that he could {see}, {see}, {see} Was the bottom of the deep blue {sea}, {sea}, {sea}! """.format( sea=SEA, see=SEE )
Created by the `string` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py`
---
### `duplicate-value` / `W0130`
**Message emitted:**
`Duplicate value %r in set`
**Description:** this message is emitted when a set contains the same value two or more times. A set said "no duplicates" and then duplicated itself. The audacity 💀
**Problematic code:**
incorrect_set = {"value1", 23, 5, "value1"} # [duplicate-value]
**Correct code:**
correct_set = {"value1", 23, 5}
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`
---
### `eq-without-hash` / `W1641`
**Message emitted:**
`Implementing __eq__ without also implementing __hash__`
**Description:** used when a class implements `__eq__` but not `__hash__`. Objects get `None` as their default `__hash__` implementation if they also implement `__eq__` — so lowkey your object becomes unhashable, and that's a whole vibe killer 😭
**Problematic code:**
class Fruit: # [eq-without-hash] def __init__(self) -> None: self.name = "apple"
def __eq__(self, other: object) -> bool: return isinstance(other, Fruit) and other.name == self.name
**Correct code:**
class Fruit: def __init__(self) -> None: self.name = "apple"
def __eq__(self, other: object) -> bool: return isinstance(other, Fruit) and other.name == self.name
def __hash__(self) -> int: return hash(self.name)
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.eq_without_hash,
> **Heads up:** this message is emitted by the optional `eq-without-hash` checker (see `#pylint-extensions-eq-without-hash`), which requires the `pylint.extensions.eq_without_hash` plugin to be loaded 🫶
Created by the `eq-without-hash` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/eq_without_hash.py`
---
### `eval-used` / `W0123`
**Message emitted:**
`Use of eval`
**Description:** used when you use the `"eval"` function, to discourage its usage. Consider using `ast.literal_eval` for safely evaluating strings containing Python expressions from untrusted sources. Girl, `eval` will run *anything* — that is NOT the energy you want around untrusted input 🔥
**Problematic code:**
eval("[1, 2, 3]") # [eval-used]
**Correct code:**
from ast import literal_eval
literal_eval("[1, 2, 3]")
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`
---
### `exec-used` / `W0122`
**Message emitted:**
`Use of exec`
**Description:** raised when the `'exec'` statement is used. It's dangerous to use this function for a user input, and it's also slower than actual code in general. This doesn't mean you should never use it, but you should consider alternatives first and restrict the functions available. Ngl, this one is main-character-with-a-red-flag energy 🚩
**Problematic code:**
username = "Ada" code_to_execute = f"""input('Enter code to be executed please, {username}: ')""" program = exec(code_to_execute) # [exec-used] exec(program) # [exec-used]
**Correct code:**
def get_user_code(name): return input(f"Enter code to be executed please, {name}: ")
username = "Ada"
If the globals dictionary does not contain a value for the key __builtins__,
all builtins are allowed. You need to be explicit about it being disallowed.
allowed_globals = {"__builtins__": {}} allowed_locals = {}
pylint: disable-next=exec-used
exec(get_user_code(username), allowed_globals, allowed_locals)
**Additional details:**
The available methods and variables used in `exec()` may introduce a security hole. You can restrict the use of these variables and methods by passing optional globals and locals parameters (dictionaries) to the `exec()` method.
However, use of `exec()` is still insecure if you allow some functions like `__import__` or `open`. For example, consider the following call that writes a file to the user's system and then execute code unrestrained by the `allowed_globals`, or `allowed_locals` parameters:
import textwrap
def forbid_print(*args): raise ValueError("This is raised when a print is used")
allowed_globals = { "__builtins__": { "__import__": __builtins__.__import__, "open": __builtins__.open, "print": forbid_print, } }
exec( textwrap.dedent( """ import textwrap
with open("nefarious.py", "w") as f: f.write(textwrap.dedent(''' def connive(): print("Here's some code as nefarious as imaginable") '''))
import nefarious
nefarious.connive() # This will NOT raise a ValueError """ ), allowed_globals, )
The import is used only for readability of the example in this case but it could import a dangerous functions:
- `subprocess.run('echo "print(\"Hello, World!\")" > nefarious.py'`
- `pathlib.Path("nefarious.py").write_file("print(\"Hello, World!\")")`
- `os.system('echo "print(\"Hello, World!\")" > nefarious.py')`
- `logging.basicConfig(filename='nefarious.py'); logging.error('print("Hello, World!")')`
- etc.
**Related links:**
- Be careful with exec and eval in Python — `https://lucumr.pocoo.org/2011/2/1/exec-in-python/`
- Python documentation — `https://docs.python.org/3/library/functions.html#exec`
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`
---
### `expression-not-assigned` / `W0106`
**Message emitted:**
`Expression "%s" is assigned to nothing`
**Description:** used when an expression that is not a function call is assigned to nothing. Probably something else was intended — like bestie, you did the math and then threw the answer in the trash 🗑️
**Problematic code:**
str(42) == "42" # [expression-not-assigned]
**Correct code:**
are_equal: bool = str(42) == "42"
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`
---
### `f-string-without-interpolation` / `W1309`
**Message emitted:**
`Using an f-string that does not have any interpolated variables`
**Description:** used when we detect an f-string that does not use any interpolation variables, in which case it can be either a normal string or a bug in the code. That `f` is doing absolutely nothing, girl is unemployed 😭
**Problematic code:**
x = 1 y = 2 print(f"x + y = x + y") # [f-string-without-interpolation]
**Correct code:**
x = 1 y = 2 print(f"{x} + {y} = {x + y}")
Created by the `string` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py`
---
### `fixme` / `W0511`
**Message emitted:**
`%s`
**Description:** used when a warning note as `FIXME` or `XXX` is detected. The comment equivalent of a sticky note that says "deal with me later" — and we all know later never comes 🙈
**Problematic code:**
TODO: We should fix this at some point # [fixme]
**Correct code:**
`bug_tracker.py`:
The issue was added to the bug tracker: no longer need the comment
`fixed.py`:
The issue was fixed: no longer need the comment
**`no_fix.py`:**
We no longer want to fix this: no longer need the comment
**Additional details:** You can use regular expressions and the `notes-rgx` option to create some constraints for this message. See the following issue `https://github.com/pylint-dev/pylint/issues/2874` for some examples.
Created by the <green>miscellaneous</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/misc.py`).
### 🐛 forgotten-debug-statement / W1515
> **Message emitted:** `Leaving functions creating breakpoints in production code is not recommended`
**Description:** Calls to `breakpoint()`, `sys.breakpointhook()` and `pdb.set_trace()` should be removed from code that is not actively being debugged. <pink>Like, if you're not literally debugging right now, those calls need to go, bestie.</pink>
**Problematic code:**
import pdb
def find_the_treasure(clues): for clue in clues: pdb.set_trace() # [forgotten-debug-statement] if "treasure" in clue: return True return False
treasure_hunt = [ "Dead Man's Chest", "X marks the spot", "The treasure is buried near the palm tree", ] find_the_treasure(treasure_hunt)
**Correct code:**
def find_the_treasure(clues): for clue in clues: if "treasure" in clue: return True return False
treasure_hunt = [ "Dead Man's Chest", "X marks the spot", "The treasure is buried near the palm tree", ] find_the_treasure(treasure_hunt)
Created by the <green>stdlib</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py`).
### 🧩 format-combined-specification / W1305
> **Message emitted:** `Format string contains both automatic field numbering and manual field specification`
**Description:** Used when a PEP 3101 format string contains both automatic field numbering (e.g. `'{}'`) and manual field specification (e.g. `'{0}'`). <purple>Pick a lane, bestie — mixing them does not slay.</purple>
**Problematic code:**
print("{} {1}".format("hello", "world")) # [format-combined-specification]
**Correct code:**
**`index_formatting.py`:**
print("{0} {1}".format("hello", "world"))
**`order_formatting.py`:**
print("{} {}".format("hello", "world"))
Created by the <green>string</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py`).
### 🎀 format-string-without-interpolation / W1310
> **Message emitted:** `Using formatting for a string that does not have any interpolated variables`
**Description:** Used when we detect a string that does not have any interpolation variables — it's giving either a normal string without formatting or a bug in the code. <yellow>Either way, we're flagging it.</yellow>
**Problematic code:**
print("number".format(1)) # [format-string-without-interpolation]
**Correct code:**
print("number: {}".format(1))
Created by the <green>string</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py`).
### 🌍 global-at-module-level / W0604
> **Message emitted:** `Using the global statement at the module level`
**Description:** Used when you use the `"global"` statement at the module level since it has no effect. <blue>Not me seeing `global` at module level when it's literally doing nothing — no cap. The way that has zero effect is wild.</blue>
**Problematic code:**
price = 25 global price # [global-at-module-level]
**Correct code:**
price = 25
**Related links:**
- Official Python FAQ - global and local — `https://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python`
- PEP 3104 - Access to Names in Outer Scopes — `https://peps.python.org/pep-3104/`
- Python global statement — `https://docs.python.org/3/reference/simple_stmts.html#global`
Created by the <green>variables</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`).
### 👑 global-statement / W0603
> **Message emitted:** `Using the global statement`
**Description:** Used when you use the `"global"` statement to update a global variable. Pylint discourages its usage. That doesn't mean you cannot use it! <purple>Pylint is lowkey not a fan of this move, but it's not saying you *can't* — it's just not the vibe.</purple>
**Problematic code:**
var = 1
def foo(): global var # [global-statement] var = 10 print(var)
foo() print(var)
**Correct code:**
var = 1
def foo(): print(var) return 10
var = foo() print(var)
Created by the <green>variables</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`).
### 🍅 global-variable-not-assigned / W0602
> **Message emitted:** `Using global for %r but no assignment is done`
**Description:** When a variable defined in the global scope is modified in an inner scope, the `'global'` keyword is required in the inner scope only if there is an assignment operation done in the inner scope. <mint>No assignment in the inner scope? Then you don't need `global` there, <rainbow>periodt</rainbow>.</mint>
**Problematic code:**
TOMATO = "black cherry"
def update_tomato(): global TOMATO # [global-variable-not-assigned] print(TOMATO)
**Correct code:**
TOMATO = "black cherry"
def update_tomato(): global TOMATO TOMATO = "moneymaker"
Created by the <green>variables</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`).
### 🌱 global-variable-undefined / W0601
> **Message emitted:** `Global variable %r undefined at the module level`
**Description:** Used when a variable is defined through the `"global"` statement but the variable is not defined in the module scope. <red>You declared a variable via `global`, but it isn't actually defined at module scope — awkward.</red>
**Problematic code:**
def update_tomato(): global TOMATO # [global-variable-undefined] TOMATO = "moneymaker"
**Correct code:**
TOMATO = "black cherry"
def update_tomato(): global TOMATO TOMATO = "moneymaker"
Created by the <green>variables</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`).
### 🚩 implicit-flag-alias / W0213
> **Message emitted:** `Flag member %(overlap)s shares bit positions with %(sources)s`
**Description:** Used when multiple integer values declared within an `enum.IntFlag` class share a common bit position. <hotpink>Overlap central — two members are sharing the same bit position, and that's not iconic.</hotpink>
**Problematic code:**
from enum import IntFlag
class FilePermissions(IntFlag): READ = 1 WRITE = 2 EXECUTE = 3 # [implicit-flag-alias]
**Correct code:**
from enum import IntFlag
class FilePermissions(IntFlag): READ = 1 WRITE = 2 EXECUTE = 4
Created by the <green>classes</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`).
### 🧵 implicit-str-concat / W1404
> **Message emitted:** `Implicit string concatenation found in %s`
**Description:** String literals are implicitly concatenated in a literal iterable definition : maybe a comma is missing ? <yellow>Highkey maybe a comma ghosted you? The string literals are getting implicitly concatenated inside a literal iterable definition.</yellow>
**Problematic code:**
**`list.py`:**
x = ["a" "b"] # [implicit-str-concat]
**`open.py`:**
with open("hello.txt" "r") as f: # [implicit-str-concat] print(f.read())
**Correct code:**
**`list.py`:**
x = ["a", "b"]
**`open.py`:**
with open("hello.txt", "r") as f: print(f.read())
**Additional details:** By default, detection of implicit string concatenation of line jumps is disabled. Hence the following code will not trigger this rule:
SEQ = ('a', 'b' 'c')
In order to detect this case, you must enable `check-str-concat-over-line-jumps`:
[STRING_CONSTANT] check-str-concat-over-line-jumps = true
However, the drawback of this setting is that it will trigger false positive for string parameters passed on multiple lines in function calls:
warnings.warn( "rotate() is deprecated and will be removed in a future release. " "Use the rotation() context manager instead.", DeprecationWarning, stacklevel=3, )
No message will be emitted, though, if you clarify the wanted concatenation with parentheses:
warnings.warn( ( "rotate() is deprecated and will be removed in a future release. " "Use the rotation() context manager instead." ), DeprecationWarning, stacklevel=3, )
Created by the <green>string</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py`).
### 🔁 import-self / W0406
> **Message emitted:** `Module import itself`
**Description:** Used when a module is importing itself. <cyan>Ngl, a module importing itself is wild — main character energy gone wrong.</cyan>
**Additional details:** Say you have a file called `my_file.py`. `import-self` would be raised on the following code:
from my_file import a_function # [import-self]
def a_function(): pass
The solution would be to remove the import:
def a_function(): pass
Created by the <green>imports</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py`).
### 🌈 inconsistent-quotes / W1405
> **Message emitted:** `Quote delimiter %s is inconsistent with the rest of the file`
**Description:** Quote delimiters are not used consistently throughout a module (with allowances made for avoiding unnecessary escaping). <pink>Your quotes failed the vibe check — they're not matching the rest of the module, but allowances are made when you're avoiding unnecessary escaping, iykyk.</pink>
**Problematic code:**
import datetime
print('Current year: ', datetime.date.today().strftime("%Y")) # [inconsistent-quotes]
**Correct code:**
import datetime
print("Current year: ", datetime.date.today().strftime("%Y"))
**Configuration file:**
[main] check-quote-consistency=yes
Created by the <green>string</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py`).
### 🔐 invalid-envvar-default / W1508
> **Message emitted:** `%s default type is %s. Expected str or None.`
**Description:** Env manipulation functions return `None` or `str` values. Supplying anything different as a default may cause bugs. See `https://docs.python.org/3/library/os.html#os.getenv`. <red>Fr fr, if you pass anything other than `str` or `None` as the default, you're asking for bugs, bestie.</red>
**Problematic code:**
import os
env = os.getenv("SECRET_KEY", 1) # [invalid-envvar-default]
**Correct code:**
import os
env = os.getenv("SECRET_KEY", "1")
Created by the <green>stdlib</green> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py`).
### 🍓 invalid-format-index / W1307
> **Message emitted:** `Using invalid lookup key %r in format specifier %r`
**Description:** Used when a PEP 3101 format string uses a lookup specifier (`{a[1]}`), but the argument passed for formatting doesn't contain or doesn't have that key as an attribute. <purple>You asked for `{a[1]}`, but the argument you passed doesn't contain that key — or doesn't have it as an attribute — so the lookup is invalid.</purple>
**Problematic code:**
not_enough_fruits = ["apple"] print('The second fruit is a {fruits[1]}'.format(fruits=not_enough_fruits)) # [invalid-format-index]
**Correct code:**
### `invalid-overridden-method` / W0236
> **Message emitted:** `Method %r was expected to be %r, found it instead as %r`
Okay so picture this 💅 — you've got a method in a base class, and a subclass overrides it, but the override doesn't match what the base class was doing. Pylint clocks that and goes "bestie, this could literally cause bugs at runtime." That's the whole vibe check it's running here.
**Problematic code:**
class Fruit: async def bore(self, insect): insect.eat(self)
class Apple(Fruit): def bore(self, insect): # [invalid-overridden-method] insect.eat(self)
**Correct code:**
class Fruit: async def bore(self, insect): insect.eat(self)
class Apple(Fruit): async def bore(self, insect): insect.eat(self)
Created by the `classes` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py> 🍎
---
### `isinstance-second-argument-not-valid-type` / W1116
> **Message emitted:** `Second argument of isinstance is not a type`
If you call `isinstance` and the second thing you hand it isn't an actual type, pylint throws this at you. That's it, that's the whole message. Simple, iconic, no notes ✨
**Problematic code:**
isinstance("apples and oranges", hex) # [isinstance-second-argument-not-valid-type]
**Correct code:**
isinstance("apples and oranges", str)
Created by the `typecheck` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py> 🍊
---
### `keyword-arg-before-vararg` / W1113
> **Message emitted:** `Keyword argument before variable positional arguments list in the definition of %s function`
Okay so here's the situation 🫠 — when you define a keyword argument *before* variable positional arguments, and then someone calls the function with keyword arguments, you can end up passing multiple values for that same parameter. Messy. Not the look.
**Problematic code:**
def func(x=None, *args): # [keyword-arg-before-vararg] return [x, *args]
**Correct code:**
def func(*args, x=None): return [*args, x]
Created by the `typecheck` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py> 🫶
---
### `kwarg-superseded-by-positional-arg` / W1117
> **Message emitted:** `%r will be included in %r since a positional-only parameter with this name already exists`
This one fires when you call a function with a keyword argument whose name matches a positional-only parameter — and the function also has a keyword variadic parameter dict. Basically your kwarg is getting eaten by the positional-only one and you have no idea 😭
**Problematic code:**
def print_name(name="Sarah", /, **kwds): print(name)
print_name(name="Jacob") # [kwarg-superseded-by-positional-arg]
Will print "Sarah"
**Correct code:**
def print_name(name="Sarah", /, **kwds): print(name)
print_name("Jacob")
Will print "Jacob"
Created by the `typecheck` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py> 💀
---
### `logging-format-interpolation` / W1202
> **Message emitted:** `Use %s formatting in logging functions`
Used when a logging statement looks like `logging.<logging method>(format_string.format(format_args...))`. Pylint wants you to use another type of string formatting instead, bestie 💅 You *can* use `%` formatting, but leave the interpolation to the logging function by passing the parameters as arguments. If `logging-fstring-interpolation` is disabled then you can use fstring formatting. And if `logging-not-lazy` is disabled then you can use `%` formatting as normal.
**Problematic code:**
import logging import sys
+1: [logging-format-interpolation]
logging.error("Python version: {}".format(sys.version))
**Correct code:**
import logging import sys
logging.error("Python version: %s", sys.version)
**Additional details:**
Another totally reasonable option is to use an f-string ✨ If that's your moment, you need to enable `logging-format-interpolation` and disable `logging-fstring-interpolation`.
**Related links:**
- logging variable data — <https://docs.python.org/3/howto/logging.html#logging-variable-data>
- Rationale for the message on stackoverflow — <https://stackoverflow.com/a/34634301/2519059>
Created by the `logging` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/logging.py> 🪵
---
### `logging-fstring-interpolation` / W1203
> **Message emitted:** `Use %s formatting in logging functions`
Used when a logging statement looks like `logging.<logging method>(f"...")`. Same deal as before — use another type of string formatting instead 💖 You can use `%` formatting but leave interpolation to the logging function by passing the parameters as arguments. If `logging-format-interpolation` is disabled then you can use `str.format`. And if `logging-not-lazy` is disabled then you can use `%` formatting as normal.
**Problematic code:**
import logging import sys
logging.error(f"Python version: {sys.version}") # [logging-fstring-interpolation]
**Correct code:**
import logging import sys
logging.error("Python version: %s", sys.version)
**Additional details:**
This message lets you allow f-strings in logging and *still* be warned about `logging-format-interpolation`. Best of both worlds, honestly 👑
**Related links:**
- logging variable data — <https://docs.python.org/3/howto/logging.html#logging-variable-data>
- Rationale — <https://stackoverflow.com/questions/34619790>
Created by the `logging` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/logging.py> 🌸
---
### `logging-not-lazy` / W1201
> **Message emitted:** `Use %s formatting in logging functions`
Used when a logging statement is shaped like `logging.<logging method>(format_string % (format_args...))`. Use another type of string formatting instead, okay? 🫶 You can use `%` formatting but leave interpolation to the logging function by passing the parameters as arguments. If `logging-fstring-interpolation` is disabled then you can use fstring formatting. If `logging-format-interpolation` is disabled then you can use `str.format`.
**Problematic code:**
import logging
try: function() except Exception as e: logging.error("Error occurred: %s" % e) # [logging-not-lazy] raise
**Correct code:**
import logging
try: function() except Exception as e: logging.error("Error occurred: %s", e) raise
**Additional details:**
Another reasonable option is to use f-strings ✨ If you want to do that, you need to enable `logging-not-lazy` and disable `logging-fstring-interpolation`.
**Related links:**
- Logging variable data — <https://docs.python.org/3/howto/logging.html#logging-variable-data>
- Rationale for the message on stackoverflow — <https://stackoverflow.com/a/34634301/2519059>
Created by the `logging` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/logging.py> 😭
---
### `lost-exception` / W0150
> **Message emitted:** `%s statement in finally block may swallow exception`
This one's serious, no cap 🔥 It fires when there's a `break` or a `return` inside the `finally` clause of a `try...finally` block — and the consequences are that exceptions raised in the `try` clause get **silently swallowed** instead of being re-raised. Silently. Swallowed. The exception just vanishes into the void 💀
**Problematic code:**
class FasterThanTheSpeedOfLightError(ZeroDivisionError): def __init__(self): super().__init__("You can't go faster than the speed of light !")
def calculate_speed(distance: float, time: float) -> float: try: return distance / time except ZeroDivisionError as e: raise FasterThanTheSpeedOfLightError() from e finally: return 299792458 # [lost-exception]
**Correct code:**
class FasterThanTheSpeedOfLightError(ZeroDivisionError): def __init__(self): super().__init__("You can't go faster than the speed of light !")
def calculate_speed(distance: float, time: float) -> float: try: return distance / time except ZeroDivisionError as e: raise FasterThanTheSpeedOfLightError() from e
Created by the `basic` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py> ⚡
---
### `method-cache-max-size-none` / W1518
> **Message emitted:** `'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self'`
Okay bestie, listen 👀 — when you decorate a method with `lru_cache` or `cache`, the `self` argument gets linked to the function and is therefore **never garbage collected**. Unless your instance will never ever need to be garbage collected (hi, singleton 👋), it's recommended to refactor the code to avoid this pattern, or add a `maxsize` to the cache. The default value for `maxsize` is `128`. Remember that number, there's a quiz later.
**Problematic code:**
import functools
class Fibonnaci: def __init__(self): self.result = []
@functools.lru_cache(maxsize=None) # [method-cache-max-size-none] def fibonacci(self, n): if n in {0, 1}: self.result.append(n) self.result.append(self.fibonacci(n - 1) + self.fibonacci(n - 2))
**Correct code:**
import functools
@functools.cache def cached_fibonacci(n): if n in {0, 1}: return n return cached_fibonacci(n - 1) + cached_fibonacci(n - 2)
class Fibonnaci: def __init__(self): self.result = []
def fibonacci(self, n): self.result.append(cached_fibonacci(n))
Created by the `stdlib` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py> 🌀
---
### `misplaced-future` / W0410
> **Message emitted:** `__future__ import is not the first non docstring statement`
Python `2.5` and greater require the `__future__` import to be the first non docstring statement in the module. That's just the rule, periodt 💅
**Problematic code:**
import sys
from __future__ import print_function # [misplaced-future]
**Correct code:**
from __future__ import print_function
import sys
**Additional details:**
A bare `raise` statement will re-raise the last active exception in the current scope. If the `raise` statement is not in an `except` or `finally` block, a `RuntimeError` will be raised instead.
Created by the `imports` checker — <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py> 🕰️
---
### `missing-any-param-doc` / W9021
> **Message emitted:** `Missing any documentation in "%s"`
Please add parameter and/or type documentation 🥺 That's the ask. That's the whole ask.
**Problematic code:**
def puppies(head, tail): # [missing-any-param-doc] """Print puppy's details.""" print(head, tail)
**Correct code** (the glow-up ✨):
def puppies(head: str, tail: str): """Print puppy's details.
:param head: description of the head of the dog :param tail: description of the tail of the dog """ print(head, tail)
**Configuration file:**
[main] load-plugins = pylint.extensions.docparams accept-no-param-doc = false
> **Note:** this message is emitted by the optional 'parameter_documentation' <#pylint-extensions-docparams> checker, which requires the `pylint.extensions.docparams` plugin to be loaded. No plugin, no message, bestie. 💅
Created by the `parameter_documentation` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py> checker.
---
## <pink>missing-format-argument-key / W1303</pink>
**Message emitted:**
`Missing keyword argument %r for format string`
**Description:**
Fires when a PEP 3101 format string that uses named fields doesn't get one or more of the keywords it's asking for. It's giving "you promised me a `last` and then ghosted me" 😭
**Problematic code:**
print("My name is {first} {last}".format(first="John")) # [missing-format-argument-key]
**Correct code:**
print("My name is {first} {last}".format(first="John", last="Wick"))
**Related links:**
- PEP 3101 <https://peps.python.org/pep-3101/>
- Custom String Formatting <https://docs.python.org/3/library/string.html#custom-string-formatting>
Created by the `string` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py> checker.
---
## <pink>missing-format-attribute / W1306</pink>
**Message emitted:**
`Missing format attribute %r in format specifier %r`
**Description:**
This one pops off when a PEP 3101 format string uses an attribute specifier like `{0.length}`, but the argument you actually passed in for formatting doesn't have that attribute. Rude of the argument, honestly. 🙈
**Problematic code:**
print("{0.real}".format("1")) # [missing-format-attribute]
**Correct code:**
print("{0.real}".format(1))
Created by the `string` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py> checker.
---
## <pink>missing-param-doc / W9015</pink>
**Message emitted:**
`"%s" missing in parameter documentation`
**Description:**
Bestie. Please. Add parameter declarations for **all** the parameters. All of them. Not just the cute ones. 🫶
**Problematic code:**
def integer_sum(a: int, b): # [missing-param-doc] """Returns sum of two integers :param a: first integer """ return a + b
**Correct code:**
def integer_sum(a: int, b: int): """Returns sum of two integers :param a: first integer :param b: second integer """ return a + b
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.docparams
> **Note:** this message is emitted by the optional 'parameter_documentation' <#pylint-extensions-docparams> checker, which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py> checker.
---
## <pink>missing-parentheses-for-call-in-test / W0126</pink>
**Message emitted:**
`Using a conditional statement with potentially wrong function or method call due to missing parentheses`
**Description:**
Emitted when a conditional statement (an `If` or a ternary `if`) looks like it's trying to call a function but forgot the parentheses — so you're not calling the function, you're just vibe-checking the function object itself. Which is always truthy. Which means your `if` is lying to you. 💀
**Problematic code:**
import random
def is_it_a_good_day(): return random.choice([True, False])
if is_it_a_good_day: # [missing-parentheses-for-call-in-test] print("Today is a good day!")
**Correct code:**
import random
def is_it_a_good_day(): return random.choice([True, False])
if is_it_a_good_day(): print("Today is a good day!")
Created by the `basic` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py> checker.
---
## <pink>missing-raises-doc / W9006</pink>
**Message emitted:**
`"%s" not documented as being raised`
**Description:**
Please document the exceptions for **all** the raised exception types. If your function can throw hands, the docstring should say so. 🥊
**Problematic code:**
def integer_sum(a: int, b: int): # [missing-raises-doc] """Returns sum of two integers :param a: first integer :param b: second integer """ if not (isinstance(a, int) and isinstance(b, int)): raise ValueError("Function supports only integer parameters.") return a + b
**Correct code:**
def integer_sum(a: int, b: int): """Returns sum of two integers :param a: first integer :param b: second integer :raises ValueError: One of the parameters is not an integer. """ if not (isinstance(a, int) and isinstance(b, int)): raise ValueError("Function supports only integer parameters.") return a + b
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.docparams
[BASIC] accept-no-raise-doc = no
> **Note:** this message is emitted by the optional 'parameter_documentation' <#pylint-extensions-docparams> checker, which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py> checker.
---
## <pink>missing-return-doc / W9011</pink>
**Message emitted:**
`Missing return documentation`
**Description:**
Please add documentation about what this method returns. Like, what does it *give*? We need to know. 🎁
**Problematic code:**
def integer_sum(a: int, b: int): # [missing-return-doc] """Returns sum of two integers :param a: first integer :param b: second integer """ return a + b
**Correct code:**
def integer_sum(a: int, b: int) -> int: """Returns sum of two integers :param a: first integer :param b: second integer :return: sum of parameters a and b """ return a + b
**Configuration file:**
[main] load-plugins=pylint.extensions.docparams
[Parameter_documentation] accept-no-return-doc=no
**Additional details:**
This message is raised **only** when the parameter `accept-no-return-doc` is set to `no`. So like, it's opt-in drama. 🌸
> **Note:** this message is emitted by the optional 'parameter_documentation' <#pylint-extensions-docparams> checker, which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py> checker.
---
## <pink>missing-return-type-doc / W9012</pink>
**Message emitted:**
`Missing return type documentation`
**Description:**
Please document the *type* returned by this method. Not just that it returns something — what flavour of something. 🍓
**Problematic code:**
def integer_sum(a: int, b: int): # [missing-return-type-doc] """Returns sum of two integers :param a: first integer :param b: second integer :return: sum of parameters a and b """ return a + b
**Correct code:**
def integer_sum(a: int, b: int) -> int: """Returns sum of two integers :param a: first integer :param b: second integer :return: sum of parameters a and b """ return a + b
**Configuration file:**
[main] load-plugins=pylint.extensions.docparams
[Parameter_documentation] accept-no-return-doc=no
**Additional details:**
This message is raised **only** when the parameter `accept-no-return-doc` is set to `no`. Same deal as its sibling up there. 👑
> **Note:** this message is emitted by the optional 'parameter_documentation' <#pylint-extensions-docparams> checker, which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py> checker.
---
## <pink>missing-timeout / W3101</pink>
**Message emitted:**
`Missing timeout argument for method '%s' can cause your program to hang indefinitely`
**Description:**
Used when a method needs a `'timeout'` parameter so you don't end up waiting forever like a text back that's never coming. If no timeout is specified explicitly, the default value is used — and for example, for `'requests'` the program will **never** time out (i.e. hang indefinitely). Not the main character energy you want. 😭
**Problematic code:**
import requests
requests.post("http://localhost") # [missing-timeout]
**Correct code:**
import requests
requests.post("http://localhost", timeout=10)
**Additional details:**
You can add new methods that should have a defined `timeout` argument as qualified names in the `timeout-methods` option, for example:
- `requests.api.get`
- `requests.api.head`
- `requests.api.options`
- `requests.api.patch`
- `requests.api.post`
- `requests.api.put`
- `requests.api.request`
Created by the `method_args` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/method_args.py> checker.
---
## <pink>missing-type-doc / W9016</pink>
**Message emitted:**
`"%s" missing in parameter type documentation`
**Description:**
Please add parameter *type* declarations for all parameters. Every single one. Yes, even the boring one. 🎀
**Problematic code:**
def integer_sum(a: int, b): # [missing-type-doc] """Returns sum of two integers :param a: first integer :param b: second integer """ return a + b
**Correct code:**
def integer_sum(a: int, b: int): """Returns sum of two integers :param a: first integer :param b: second integer """ return a + b
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.docparams
> **Note:** this message is emitted by the optional 'parameter_documentation' <#pylint-extensions-docparams> checker, which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py> checker.
---
## <pink>missing-yield-doc / W9013</pink>
**Message emitted:**
`Missing yield documentation`
**Description:**
Please add documentation about what this generator yields. It's a generator, bestie, it *gives* — tell us what it gives. 🌈
**Problematic code:**
def even_number_under(n: int): # [missing-yield-doc] """Prints even numbers smaller than n. Args: n: Upper limit of even numbers. """ for i in range(n): if i % 2 == 1: continue yield i
**Correct code:**
from typing import Iterator
def even_number_under(n: int) -> Iterator[int]: """Prints even numbers smaller than n. Args: n: Upper limit of even numbers.
...and the docstring wraps up like this, bestie — same code, still exact ✨
Yields: even numbers """ for i in range(n): if i % 2 == 1: continue yield i
Configuration file, copy it exactly 👇
[main] load-plugins=pylint.extensions.docparams
[Parameter_documentation] accept-no-yields-doc=no
**Additional details** 🫶
This message only pops off when `accept-no-yields-doc` is set to `no`. That's the whole vibe check.
> **Note:** this one comes from the optional `parameter_documentation` checker (that's the `pylint-extensions-docparams` one), so you *gotta* have the `pylint.extensions.docparams` plugin loaded or it simply won't show up, no cap.
Created by the `parameter_documentation` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py`.
---
### 🌸 `missing-yield-type-doc` / `W9014` 🌸
**Message emitted:**
> Missing yield type documentation
**Description:**
Please document the type yielded by this method. Like, actually type it out, bestie 💅
**Problematic code:**
def even_number_under(n: int): # [missing-yield-type-doc] """Prints even numbers smaller than n. Args: n: Upper limit of even numbers.
Yields: even numbers """ for i in range(n): if i % 2 == 1: continue yield i
**Correct code:**
from typing import Iterator
def even_number_under(n: int) -> Iterator[int]: """Prints even numbers smaller than n. Args: n: Upper limit of even numbers.
Yields: even numbers """ for i in range(n): if i % 2 == 1: continue yield i
**Configuration file:**
[main] load-plugins=pylint.extensions.docparams
[Parameter_documentation] accept-no-yields-doc=no
**Additional details:**
Only raised when `accept-no-yields-doc` is set to `no`. Iconic gatekeeping honestly 🎀
> **Note:** emitted by the optional `parameter_documentation` checker, which needs the `pylint.extensions.docparams` plugin loaded. No plugin, no message 🦋
Created by the `parameter_documentation` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py`.
---
### 💀 `modified-iterating-list` / `W4701` 💀
**Message emitted:**
> Iterated list '%s' is being modified inside for loop body, consider iterating through a copy of it instead.
**Description:**
Fires when you're adding or removing items from a list *while you're literally iterating through it*. That can give you unexpected behaviour — which is exactly why using a copy is the preferred move. Do not do the chaotic thing, bestie 😭
**Problematic code:**
fruits = ["apple", "orange", "mango"] for fruit in fruits: fruits.append("pineapple") # [modified-iterating-list]
**Correct code:**
fruits = ["apple", "orange", "mango"] for fruit in fruits.copy(): fruits.append("pineapple")
Created by the `modified_iteration` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/modified_iterating_checker.py`.
---
### 🎀 `multiple-constructor-doc` / `W9005` 🎀
**Message emitted:**
> "%s" has constructor parameters documented in class and __init__
**Description:**
Pick one, queen. Please remove the parameter declarations in the class **or** the constructor 👑
**Problematic code:**
class Point: # [multiple-constructor-doc] """Represents a point in the xy-coordinate plane.
:param x: coordinate :param y: coordinate """
def __init__(self, x, y): """Represents a point in the xy-coordinate plane.
:param x: coordinate :param y: coordinate """ self.x = x self.y = y
**Correct code:**
class Point: def __init__(self, x, y): """Represents a point in the xy-coordinate plane.
:param x: x coordinate :param y: y coordinate """ self.x = x self.y = y
**Configuration file:**
[main] load-plugins=pylint.extensions.docparams
[Parameter_documentation] no-docstring-rgx=^(?!__init__$)_
**Additional details:**
Both docstrings are acceptable — just not both at the same time 🫶 it's giving "why not both" but the answer is no.
> **Note:** emitted by the optional `parameter_documentation` checker, which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py`.
---
### ⭐ `named-expr-without-context` / `W0131` ⭐
**Message emitted:**
> Named expression used without context
**Description:**
Emitted if a named expression is used to do a regular, plain-old assignment *outside* a context like `if`, `for`, `while`, or a comprehension. Contextless walrus = not the vibe 🦭
**Problematic code:**
(a := 42) # [named-expr-without-context]
**Correct code:**
if a := 42: print("Success")
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`.
---
### 🧚 `nan-comparison` / `W0177` 🧚
**Message emitted:**
> Comparison %s should be %s
**Description:**
Used when an expression gets compared to NaN values like `numpy.NaN` and `float('nan')`. And like — NaN isn't even equal to itself, so `==` is doing nothing for you, bestie 😭
**Problematic code:**
import numpy as np
def both_nan(x, y) -> bool: return x == np.NaN and y == float("nan") # [nan-comparison, nan-comparison]
**Correct code:**
import numpy as np
def both_nan(x, y) -> bool: return np.isnan(x) and np.isnan(y)
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/comparison_checker.py`.
---
### 🔥 `nested-min-max` / `W3301` 🔥
**Message emitted:**
> Do not use nested call of '%s'; it's possible to do '%s' instead
**Description:**
Nested calls `min(1, min(2, 3))` can be rewritten as `min(1, 2, 3)`. Flatten it, bestie — the compiler isn't your therapist 💅
**Problematic code:**
print(min(1, min(2, 3))) # [nested-min-max]
**Correct code:**
print(min(1, 2, 3))
Created by the `nested_min_max` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/nested_min_max.py`.
---
### 🦋 `non-ascii-file-name` / `W2402` 🦋
**Message emitted:**
> %s name "%s" contains a non-ASCII character.
**Description:**
Under python 3.5, PEP 3131 allows non-ascii identifiers, but not non-ascii file names. Since Python 3.5, even though Python supports UTF-8 files, some editors or tools don't. So like... keep the file names boring, it's safer 🥲
**Problematic code:**
`bàd.py`:
[non-ascii-file-name]
`not_bétter.py`:
[non-ascii-file-name]
**Correct code:**
`__init__.py`:
`bad.py`:
`not_better.py`:
**Related links:**
- PEP 489 — `https://peps.python.org/pep-0489/#export-hook-name`
- PEP 672 — `https://peps.python.org/pep-0672/#confusing-features`
- Python issue 20485 — `https://bugs.python.org/issue20485`
Created by the `nonascii-checker` — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/non_ascii_names.py`.
---
### 💜 `non-parent-init-called` / `W0233` 💜
**Message emitted:**
> __init__ method from a non direct base class %r is called
**Description:**
Used when an `__init__` method is called on a class which is **not** in the direct ancestors for the analysed class. Like, you skipped a whole generation, bestie 😭 that's not how inheritance works.
**Problematic code:**
class Animal: def __init__(self): self.is_multicellular = True
class Vertebrate(Animal): def __init__(self): super().__init__() self.has_vertebrae = True
class Cat(Vertebrate): def __init__(self): Animal.__init__(self) # [non-parent-init-called] self.is_adorable = True
**Correct code:**
class Animal: def __init__(self): self.is_multicellular = True
class Vertebrate(Animal): def __init__(self): super().__init__() self.has_vertebrae = True
class Cat(Vertebrate): def __init__(self): super().__init__() self.is_adorable = True
Created by the `classes` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`.
---
### 🌈 `non-str-assignment-to-dunder-name` / `W1115` 🌈
**Message emitted:**
> Non-string value assigned to __name__
**Description:**
Emitted when a non-string value is assigned to `__name__`. It's a *name*, bestie, so name it with a string 😤
**Problematic code:**
class Fruit: pass
Fruit.__name__ = 1 # [non-str-assignment-to-dunder-name]
**Correct code:**
class Fruit: pass
Fruit.__name__ = "FRUIT"
Created by the `typecheck` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/typecheck.py`.
---
### 🍓 `overlapping-except` / `W0714` 🍓
**Message emitted:**
> Overlapping exceptions (%s)
**Description:**
Used when exceptions in a handler overlap or are identical. Which one caught it?? Nobody knows, that's the problem 💀
**Problematic code:**
def divide_x_by_y(x: float, y: float): try: print(x / y) except (ArithmeticError, FloatingPointError) as e: # [overlapping-except] print(f"There was an issue: {e}")
**Correct code:**
`less_generic_first.py`:
def divide_x_by_y(x: float, y: float): try: print(x / y) except FloatingPointError as e: print(f"There was a FloatingPointError: {e}") except ArithmeticError as e: # FloatingPointError were already caught at this point print(f"There was an OverflowError or a ZeroDivisionError: {e}")
`only_generic.py`:
def divide_x_by_y(x: float, y: float): try: print(x / y) except ArithmeticError as e: print( f"There was an OverflowError, a ZeroDivisionError or a FloatingPointError: {e}" )
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.overlapping_exceptions,
**Related links:**
- Exception hierarchy — `https://docs.python.org/3/library/exceptions.html#exception-hierarchy`
> **Note:** emitted by the optional `overlap-except` checker, which requires the `pylint.extensions.overlapping_exceptions` plugin to be loaded. Load the plugin or get nothing, iykyk 🧚
Created by the `overlap-except` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/overlapping_exceptions.py`.
---
### 👑 `overridden-final-method` / `W0239` 👑
**Message emitted:**
> Method %r overrides a method decorated with typing.final which is defined in class %r
**Description:**
Used when a method decorated with `typing.final` has been overridden. It said `final`. It meant `final`. Respect the boundary, bestie 🚫
**Problematic code:**
from typing import final
*(and yes, the page keeps going — more messages incoming 💖)*
...and the **correct** version — the one that actually passes the vibe check — is this:
from typing import final
class Animal: @final def can_breathe(self): return True
class Cat(Animal): def can_purr(self): return True
**Configuration file:**
[MAIN] py-version=3.8
**Additional details:**
The message can't be emitted when using Python < 3.8.
**Related links:**
- PEP 591 (`https://peps.python.org/pep-0591/`)
Created by the `classes` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`).
---
### 💅 `pointless-exception-statement` / `W0133`
**Message emitted:**
`Exception statement has no effect`
**Description:**
Used when an exception is created without being assigned, raised or returned for subsequent use elsewhere.
**Problematic code:**
Exception("This exception is a statement.") # [pointless-exception-statement]
**Correct code:**
raise Exception("This will raise.")
Created by the `basic` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`).
---
### ✨ `pointless-statement` / `W0104`
**Message emitted:**
`Statement seems to have no effect`
**Description:**
Used when a statement doesn't have (or at least seems to) any effect.
**Problematic code:**
[1, 2, 3] # [pointless-statement]
**Correct code:**
NUMBERS = [1, 2, 3]
print(NUMBERS)
Created by the `basic` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`).
---
### 🎀 `pointless-string-statement` / `W0105`
**Message emitted:**
`String statement has no effect`
**Description:**
Used when a string is used as a statement (which of course has no effect). This is a particular case of `W0104` with its own message so you can easily disable it if you're using those strings as documentation, instead of comments.
**Problematic code:**
"""This is a docstring which describes the module"""
"""This is not a docstring""" # [pointless-string-statement]
**Correct code:**
"""This is a docstring which describes the module"""
This is comment which describes a particular part of the module.
**Related links:**
- Discussion thread re: docstrings on assignments (`https://discuss.python.org/t/docstrings-for-new-type-aliases-as-defined-in-pep-695/39816`)
Created by the `basic` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`).
---
### 🦋 `possibly-unused-variable` / `W0641`
**Message emitted:**
`Possibly unused variable %r`
**Description:**
Used when a variable is defined but might not be used. The possibility comes from the fact that `locals()` might be used, which could consume or not the said variable
**Problematic code:**
def choose_fruits(fruits): print(fruits) color = "red" # [possibly-unused-variable] return locals()
**Correct code:**
def choose_fruits(fruits): current_locals = locals() print(fruits) color = "red" print(color) return current_locals
Created by the `variables` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`).
---
### 🌈 `preferred-module` / `W0407`
**Message emitted:**
`Prefer importing %r instead of %r`
**Description:**
Used when a module imported has a preferred replacement module.
**Problematic code:**
import urllib # [preferred-module]
**Correct code:**
import requests
**Configuration file:**
[IMPORTS] preferred-modules=urllib:requests,
Created by the `imports` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py`).
---
### 🔥 `protected-access` / `W0212`
**Message emitted:**
`Access to a protected member %s of a client class`
**Description:**
Used when a protected member (i.e. class member with a name beginning with an underscore) is accessed outside the class or a descendant of the class where it's defined.
**Problematic code:**
class Worm: def __swallow(self): pass
jim = Worm() jim.__swallow() # [protected-access]
**Correct code:**
class Worm: def __swallow(self): pass
def eat(self): return self.__swallow()
jim = Worm() jim.eat()
Created by the `classes` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`).
---
### 🫶 `raise-missing-from` / `W0707`
**Message emitted:**
`Consider explicitly re-raising using %s'%s from %s'`
**Description:**
Python's exception chaining shows the traceback of the current exception, but also of the original exception. When you raise a new exception after another exception was caught it's likely that the second exception is a friendly re-wrapping of the first exception. In such cases `raise from` provides a better link between the two tracebacks in the final error.
**Problematic code:**
try: 1 / 0 except ZeroDivisionError as e: raise ValueError("Rectangle Area cannot be zero") # [raise-missing-from]
**Correct code:**
try: 1 / 0 except ZeroDivisionError as e: raise ValueError("Rectangle Area cannot be zero") from e
**Related links:**
- PEP 3134 (`https://peps.python.org/pep-3134/`)
Created by the `exceptions` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py`).
---
### 😭 `raising-format-tuple` / `W0715`
**Message emitted:**
`Exception arguments suggest string formatting might be intended`
**Description:**
Used when passing multiple arguments to an exception constructor, the first of them a string literal containing what appears to be placeholders intended for formatting
**Problematic code:**
raise RuntimeError("This looks wrong %s %s", ("a", "b")) # [raising-format-tuple]
**Correct code:**
raise RuntimeError("This looks wrong %s %s" % ("a", "b"))
Created by the `exceptions` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py`).
---
### 🌸 `redeclared-assigned-name` / `W0128`
**Message emitted:**
`Redeclared variable %r in assignment`
**Description:**
Emitted when we detect that a variable was redeclared in the same assignment.
**Problematic code:**
FIRST, FIRST = (1, 2) # [redeclared-assigned-name]
**Correct code:**
FIRST, SECOND = (1, 2)
Created by the `basic` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`).
---
### 👑 `redefined-builtin` / `W0622`
**Message emitted:**
`Redefining built-in %r`
**Description:**
Used when a variable or function override a built-in.
**Problematic code:**
def map(): # [redefined-builtin] pass
**Correct code:**
def map_iterable(): pass
**Additional details:**
The `allowed-redefined-builtins` (`<#variables-options>`) option lets you specify names that are permitted to shadow built-ins.
However, this option is not effective for redefinitions at the module level or for global variables. For example:
Module-Level Redefinitions:
module_level_redefine.py
id = 1 # Shadows the built-in id
Global Variable Redefinitions:
global_variable_redefine.py
def my_func(): global len len = 1 # Shadows the built-in len
**Rationale:**
Shadowing built-ins at the global scope is discouraged because it obscures their behavior throughout the entire module, increasing the risk of subtle bugs when the built-in is needed elsewhere. In contrast, local redefinitions are acceptable as their impact is confined to a specific scope, reducing unintended side effects and simplifying debugging.
Created by the `variables` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`).
---
### 💀 `redefined-loop-name` / `W2901`
**Message emitted:**
`Redefining %r from loop (line %s)`
**Description:**
Used when a loop variable is overwritten in the loop body.
**Problematic code:**
def normalize_names(names): for name in names: name = name.lower() # [redefined-loop-name]
**Correct code:**
def normalize_names(names): for name in names: lowercased_name = name.lower()
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.redefined_loop_name,
> **Note:** This message is emitted by the optional 'redefined-loop-name' (`<#pylint-extensions-redefined-loop-name>`) checker, which requires the `pylint.extensions.redefined_loop_name` plugin to be loaded.
Created by the `redefined-loop-name` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/redefined_loop_name.py`).
---
### 🧚 `redefined-outer-name` / `W0621`
**Message emitted:**
`Redefining name %r from outer scope (line %s)`
**Description:**
Used when a variable's name hides a name defined in an outer scope or except handler.
**Problematic code:**
count = 10
def count_it(count): # [redefined-outer-name] for i in range(count): print(i)
**Correct code:**
count = 10
def count_it(limit): for i in range(limit): print(i)
**Additional details:**
A common issue is that this message is triggered when using pytest fixtures (`https://docs.pytest.org/en/7.1.x/how-to/fixtures.html`):
import pytest
@pytest.fixture def setup(): ...
def test_something(setup): # [redefined-outer-name] ...
One solution to this problem is to explicitly name the fixture:
@pytest.fixture(name="setup") def setup_fixture(): ...
Alternatively pylint plugins like `pylint-pytest` (`https://pypi.org/project/pylint-pytest/`) can be used.
Created by the `variables` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`).
---
### 🍓 `redefined-slots-in-subclass` / `W0244`
**Message emitted:**
`Redefined slots %r in subclass`
**Description:**
Used when a slot is re-defined in a subclass.
**Problematic code:**
class Base: __slots__ = ("a", "b")
class Subclass(Base): __slots__ = ("a", "d") # [redefined-slots-in-subclass]
**Correct code:**
class Base: __slots__ = ("a", "b")
class Subclass(Base): __slots__ = ("d",)
### 🎀 `redundant-returns-doc` / `W9008`
**<pink>Message emitted:</pink>** Redundant returns documentation
**<purple>Description:</purple>** Please remove the `return`/`rtype` documentation from this method.
**<cyan>Problematic code:</cyan>**
def print_fruits(fruits): # [redundant-returns-doc] """Print list of fruits
Returns ------- str """ print(fruits) return None
**<mint>Correct code:</mint>**
def print_fruits(fruits): """Print list of fruits
Returns ------- str """ print(fruits) return ",".join(fruits)
**<yellow>Configuration file:</yellow>**
[MAIN] load-plugins = pylint.extensions.docparams
> **<lavender>Note:</lavender>** This message is emitted by the optional `parameter_documentation` checker (anchor: `#pylint-extensions-docparams`), which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py`.
---
### ✨ `redundant-u-string-prefix` / `W1406`
**<pink>Message emitted:</pink>** `The u prefix for strings is no longer necessary in Python >=3.0`
**<purple>Description:</purple>** This fires when a string has a `u` prefix. Those prefixes were necessary in Python 2 to say “this string is Unicode,” but since Python 3.0 strings are Unicode by default.
**<cyan>Problematic code:</cyan>**
def print_fruit(): print(u"Apple") # [redundant-u-string-prefix]
**<mint>Correct code:</mint>**
def print_fruit(): print("Apple")
Created by the `strings` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py`.
---
### 🦋 `redundant-unittest-assert` / `W1503`
**<pink>Message emitted:</pink>** `Redundant use of %s with constant value %r`
**<purple>Description:</purple>** The first argument of `assertTrue` and `assertFalse` is a condition. If you pass a constant as that parameter, the condition is always true. In that case a warning should be emitted.
**<cyan>Problematic code:</cyan>**
import unittest
class DummyTestCase(unittest.TestCase): def test_dummy(self): self.assertTrue("foo") # [redundant-unittest-assert]
**<mint>Correct code:</mint>**
import unittest
class DummyTestCase(unittest.TestCase): def test_dummy(self): actual = "test_result" self.assertEqual(actual, "expected")
**<yellow>Additional details:</yellow>** Directly asserting a string literal will always pass. The solution is to test something that could fail, or not assert at all.
For assertions using `assert` there are similar messages: `assert-on-string-literal` / `W0129` (anchor: `#assert-on-string-literal`) and `assert-on-tuple` / `W0199` (anchor: `#assert-on-tuple`).
**<hotpink>Related links:</hotpink>**
- `https://stackoverflow.com/a/137418/2519059`
- `https://stackoverflow.com/questions/20274987`
- `https://docs.pytest.org/en/latest/example/parametrize.html#parametrizing-conditional-raising`
Created by the `stdlib` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py`.
---
### 💜 `redundant-yields-doc` / `W9010`
**<pink>Message emitted:</pink>** Redundant yields documentation
**<purple>Description:</purple>** Please remove the `yields` documentation from this method.
**<cyan>Problematic code:</cyan>**
def give_fruits(fruits): # [redundant-yields-doc] """Something about fruits
Yields ------- list fruits """ return fruits
**<mint>Correct code:</mint>**
def give_fruits(fruits): """Something about fruits
Yields ------- str fruit """ for fruit in fruits: yield fruit
**<yellow>Configuration file:</yellow>**
[MAIN] load-plugins = pylint.extensions.docparams
> **<lavender>Note:</lavender>** This message is emitted by the optional `parameter_documentation` checker (anchor: `#pylint-extensions-docparams`), which requires the `pylint.extensions.docparams` plugin to be loaded.
Created by the `parameter_documentation` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py`.
---
### 🌸 `reimported` / `W0404`
**<pink>Message emitted:</pink>** `Reimport %r (imported line %s)`
**<purple>Description:</purple>** Used when a module is imported more than once.
**<cyan>Problematic code:</cyan>**
import re import re # [reimported]
**<mint>Correct code:</mint>**
import re
Created by the `imports` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py`.
---
### 🔥 `return-in-finally` / `W0134`
**<pink>Message emitted:</pink>** `'return' shadowed by the 'finally' clause.`
**<purple>Description:</purple>** Emitted when a `return` statement is found inside a `finally` block. It will overwrite the return value of a function, so it should be avoided.
**<cyan>Problematic code:</cyan>**
def second_favorite(): fruits = ["kiwi", "pineapple"] try: return fruits[1] finally: # because of this return statement, this function will always return "kiwi" return fruits[0] # [return-in-finally]
**<mint>Correct code:</mint>**
def second_favorite(): fruits = ["kiwi", "pineapple"] try: return fruits[1] except KeyError: ...
return fruits[0]
**<hotpink>Related links:</hotpink>**
- `https://docs.python.org/3/reference/compound_stmts.html#finally-clause`
- `https://peps.python.org/pep-0765/`
Created by the `basic` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`.
---
### 🍓 `self-assigning-variable` / `W0127`
**<pink>Message emitted:</pink>** `Assigning the same variable %r to itself`
**<purple>Description:</purple>** Emitted when a variable is assigned to itself.
**<cyan>Problematic code:</cyan>**
year = 2000 year = year # [self-assigning-variable]
**<mint>Correct code:</mint>**
year = 2000
**<hotpink>Related links:</hotpink>**
- `https://docs.python.org/3/reference/simple_stmts.html#assignment-statements`
Created by the `basic` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`.
---
### 🧚 `self-cls-assignment` / `W0642`
**<pink>Message emitted:</pink>** `Invalid assignment to %s in method`
**<purple>Description:</purple>** Invalid assignment to `self` or `cls` in an instance or class method respectively.
**<cyan>Problematic code:</cyan>**
class Fruit: @classmethod def list_fruits(cls): cls = "apple" # [self-cls-assignment]
def print_color(self, *colors): self = "red" # [self-cls-assignment] color = colors[1] print(color)
**<mint>Correct code:</mint>**
class Fruit: @classmethod def list_fruits(cls): fruit = "apple" print(fruit)
def print_color(self, *colors): color = colors[1] print(color)
Created by the `variables` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`.
---
### 🌈 `shadowed-import` / `W0416`
**<pink>Message emitted:</pink>** `Shadowed %r (imported line %s)`
**<purple>Description:</purple>** Used when a module is aliased with a name that shadows another import.
**<cyan>Problematic code:</cyan>**
from pathlib import Path
import FastAPI.Path as Path # [shadowed-import]
**<mint>Correct code:</mint>**
from pathlib import Path
import FastAPI.Path as FastApiPath
Created by the `imports` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py`.
---
### 💅 `shallow-copy-environ` / `W1507`
**<pink>Message emitted:</pink>** `Using copy.copy(os.environ). Use os.environ.copy() instead.`
**<purple>Description:</purple>** `os.environ` is not a `dict` object but a proxy object, so a shallow copy still has effects on the original object. See `https://bugs.python.org/issue15373` for reference.
**<cyan>Problematic code:</cyan>**
import copy import os
copied_env = copy.copy(os.environ) # [shallow-copy-environ]
**<mint>Correct code:</mint>**
import os
copied_env = os.environ.copy()
Created by the `stdlib` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py`.
---
### 👑 `signature-differs` / `W0222`
**<pink>Message emitted:</pink>** `Signature differs from %s %r method`
**<purple>Description:</purple>** Used when a method signature is different than the implemented interface or an overridden method.
**<cyan>Problematic code:</cyan>**
class Animal: def run(self, distance=0): print(f"Ran {distance} km!")
class Dog(Animal): def run(self, distance): # [signature-differs] super(Animal, self).run(distance) print("Fetched that stick, wuff !")
**<mint>Correct code:</mint>**
class Animal: def run(self, distance=0): print(f"Ran {distance} km!")
class Dog(Animal): def run(self, distance=0): super(Animal, self).run(distance) print("Fetched that stick, wuff !")
Created by the `classes` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`.
---
### 🎀 `subclassed-final-class` / `W0240`
**<pink>Message emitted:</pink>** `Class %r is a subclass of a class decorated with typing.final: %r`
**<purple>Description:</purple>** Used when a class decorated with `typing.final` has been subclassed.
**<cyan>Problematic code:</cyan>**
from typing import final
@final class PlatypusData: """General Platypus data."""
average_length = 46 average_body_temperature = 32
class FluorescentPlaytipus(PlatypusData): # [subclassed-final-class] """Playtipus with fluorescent fur."""
**<mint>Correct code:</mint>**
from typing import final
@final class PlatypusData: """General Platypus data."""
average_length = 46 average_body_temperature = 32
def print_average_length_platypus(): output = f"The average length of a platypus is: {PlatypusData.average_length}cm" print(output)
**<yellow>Configuration file:</yellow>**
[MAIN] py-version=3.8
**<yellow>Additional details:</yellow>** This message is emitted when a class decorated with `final` is subclassed — the decorator indicates that the class is not intended to be extended.
Note this message can’t be emitted when using `Python < 3.8`.
**<hotpink>Related links:</hotpink>**
- `https://peps.python.org/pep-0591/#the-final-decorator`
Created by the `classes` checker — source: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`.
### 🧵 `subprocess-popen-preexec-fn` / W1509
**Message emitted:**
Using preexec_fn keyword which may be unsafe in the presence of threads
**Description:**
Okay so `preexec_fn` is *not* safe to use when your app has threads running, periodt. The child process could straight-up deadlock before `exec` even gets called 😭. If you absolutely must use it, keep it trivial — like, minimize how many libraries you call into, fr fr. More deets here: `https://docs.python.org/3/library/subprocess.html#popen-constructor`
**Problematic code:**
import subprocess
def foo(): pass
subprocess.Popen(preexec_fn=foo) # [subprocess-popen-preexec-fn]
**Correct code:**
import subprocess
subprocess.Popen()
Created by the `stdlib` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py`
---
### 🏃 `subprocess-run-check` / W1510
**Message emitted:**
'subprocess.run' used without explicitly defining the value for 'check'.
**Description:**
So the `check` keyword defaults to `False`, which means the process launched by `subprocess.run` can exit with a non-zero exit code and just… fail silently 🙈. Way better to set it explicitly so everyone reading your code knows exactly what the error-handling behavior is. Transparency slay ✨
**Problematic code:**
import subprocess
proc = subprocess.run(["ls"]) # [subprocess-run-check]
**Correct code:**
import subprocess
proc = subprocess.run(["ls"], check=False)
**Related links:**
- subprocess.run documentation — `https://docs.python.org/3/library/subprocess.html#subprocess.run`
Created by the `stdlib` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py`
---
### 👑 `super-init-not-called` / W0231
**Message emitted:**
__init__ method from base class %r is not called
**Description:**
Fires when some ancestor class has an `__init__` method and your derived class just… doesn't call it. Rude, honestly 💅
**Problematic code:**
class Fruit: def __init__(self, name="fruit"): self.name = name print("Creating a {self.name}")
class Apple(Fruit): def __init__(self): # [super-init-not-called] print("Creating an apple")
**Correct code:**
class Fruit: def __init__(self, name="fruit"): self.name = name print("Creating a {self.name}")
class Apple(Fruit): def __init__(self): super().__init__("apple")
Created by the `classes` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`
---
### 🥣 `super-without-brackets` / W0245
**Message emitted:**
Super call without brackets
**Description:**
Used when a call to super does not have brackets — which means it's not an actual call at all and does NOT work how you think it does 😭. Brackets are the whole vibe check here.
**Problematic code:**
class Soup: @staticmethod def temp(): print("Soup is hot!")
class TomatoSoup(Soup): @staticmethod def temp(): super.temp() # [super-without-brackets] print("But tomato soup is even hotter!")
**Correct code:**
class Soup: @staticmethod def temp(): print("Soup is hot!")
class TomatoSoup(Soup): @staticmethod def temp(): super().temp() print("But tomato soup is even hotter!")
Created by the `classes` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`
---
### 🍎 `too-many-try-statements` / W0717
**Message emitted:**
%s
**Description:**
Your try clause is doing the absolute most — it contains too many statements. Bestie, narrow it down 🫶
**Problematic code:**
FRUITS = {"apple": 1, "orange": 10}
def pick_fruit(name): try: # [too-many-try-statements] count = FRUITS[name] count += 1 print(f"Got fruit count {count}") except KeyError: return
**Correct code:**
FRUITS = {"apple": 1, "orange": 10}
def pick_fruit(name): try: count = FRUITS[name] except KeyError: return
count += 1 print(f"Got fruit count {count}")
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.broad_try_clause,
> **Note:** This message is emitted by the optional `broad_try_clause` checker (the `#pylint-extensions-broad-try-clause` section), which requires the `pylint.extensions.broad_try_clause` plugin to be loaded.
Created by the `broad_try_clause` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/broad_try_clause.py`
---
### 💀 `try-except-raise` / W0706
**Message emitted:**
The except handler raises immediately
**Description:**
So this one triggers when an except handler uses `raise` as its first or only operator. Which is genuinely useless — you're just raising the same exception right back 💀. Either remove the `raise` operator or yeet the entire `try-except-raise` block!
**Problematic code:**
try: 1 / 0 except ZeroDivisionError as e: # [try-except-raise] raise
**Correct code:**
`remove_try_except.py`:
The try except might be removed entirely:
1 / 0
`specialized_exception.py`:
Another more detailed exception can be raised:
try: 1 / 0 except ZeroDivisionError as e: raise ValueError("The area of the rectangle cannot be zero") from e
**Additional details:**
Okay but there IS a legitimate use case for re-raising immediately. Like, with this inheritance tree:
+-- ArithmeticError +-- FloatingPointError +-- OverflowError +-- ZeroDivisionError
And this code shows a valid case for re-raising an exception immediately:
def execute_calculation(a, b): try: return some_calculation(a, b) except ZeroDivisionError: raise except ArithmeticError: return float('nan')
The pylint is able to detect this case and does not produce error. Iconic behavior honestly 🎀
Created by the `exceptions` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py`
---
### 🧺 `unbalanced-dict-unpacking` / W0644
**Message emitted:**
Possible unbalanced dict unpacking with %s: left side has %d label%s, right side has %d value%s
**Description:**
Used when there is an unbalanced dict unpacking in assignment or for loop. The counts don't match up and it's giving mismatch energy 😭
**Problematic code:**
FRUITS = {"apple": 2, "orange": 3, "mellon": 10}
for fruit, price in FRUITS.values(): # [unbalanced-dict-unpacking] print(fruit)
**Correct code:**
FRUITS = {"apple": 2, "orange": 3, "mellon": 10}
for fruit, price in FRUITS.items(): print(fruit)
Created by the `variables` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`
---
### 🌀 `undefined-loop-variable` / W0631
**Message emitted:**
Using possibly undefined loop variable %r
**Description:**
Fires when a loop variable (like, one defined by a `for` loop or a list comprehension or a generator expression) gets used *outside* the loop. Which means if the loop never ran… bestie, that variable does not exist 🫠
**Problematic code:**
def find_even_number(numbers): for x in numbers: if x % 2 == 0: break return x # [undefined-loop-variable]
**Correct code:**
def find_even_number(numbers): for x in numbers: if x % 2: return x return None
Created by the `variables` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py`
---
### ✨ `unnecessary-ellipsis` / W2301
**Message emitted:**
Unnecessary ellipsis constant
**Description:**
Used when the ellipsis constant is encountered and can be avoided. A line of code consisting of an ellipsis is unnecessary if there's a docstring on the preceding line or if there's a statement in the same scope. Like, we get it, but also… no 💅
**Problematic code:**
def my_function(): """My docstring""" ... # [unnecessary-ellipsis]
**Correct code:**
def my_function(): """My docstring"""
Created by the `unnecessary_ellipsis` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/ellipsis_checker.py`
---
### 🐑 `unnecessary-lambda` / W0108
**Message emitted:**
Lambda may not be necessary
**Description:**
Used when the body of a lambda expression is a function call on the same argument list as the lambda itself. Like, such lambda expressions are in all but a few cases replaceable with the function being called in the body of the lambda. So you can just… pass the function directly, bestie 🦋
**Problematic code:**
`pandas.py`:
df.apply(lambda x: str(x)) # [unnecessary-lambda]
`print.py`:
function = lambda x: print(x) # [unnecessary-lambda]
function("Hello world !")
**Correct code:**
`pandas.py`:
df.apply(str)
`print.py`:
print("Hello world !")
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`
---
### 🚫 `unnecessary-pass` / W0107
**Message emitted:**
Unnecessary pass statement
**Description:**
Used when a `"pass"` statement can be removed without affecting the behaviour of the code. Extra `pass`? Not the main character, cut it 🌸
**Problematic code:**
class DataEntryError(Exception): """This exception is raised when a user has provided incorrect data."""
pass # [unnecessary-pass]
**Correct code:**
class DataEntryError(Exception): """This exception is raised when a user has provided incorrect data."""
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/pass_checker.py`
---
### 🎀 `unnecessary-semicolon` / W0301
**Message emitted:**
Unnecessary semicolon
**Description:**
Used when a statement is ended by a semi-colon (`";"`), which isn't necessary. That's python, not C ;) 💀
**Problematic code:**
print("Hello World!"); # [unnecessary-semicolon]
**Correct code:**
print("Hello World!")
Created by the `format` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py`
---
### 🕳️ `unreachable` / W0101
**Message emitted:**
Unreachable code
**Description:**
Used when there's some code behind a `"return"` or `"raise"` statement, which will never be accessed. Like it's literally never getting there, the vibes are blocked 😭
**Problematic code:**
def say_hello(): return True print("Hello World!, Outside function.") # [unreachable]
**Correct code:**
def say_hello(): print("Hello World!, Inside function.") return True
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py`
---
### 📂 `unspecified-encoding` / W1514
**Message emitted:**
Using open without explicitly specifying an encoding
**Description:**
...okay bestie, real talk: <pink>it's *way* better to specify an encoding when you open documents</pink>. Like, if you just let the system default do its thing implicitly, that can genuinely cause problems on other operating systems. No cap. See https://peps.python.org/pep-0597/ ✨
Problematic code (🚩 do not do this):
def foo(file_path): with open(file_path) as file: # [unspecified-encoding] contents = file.read()
Correct code (slay 💅):
def foo(file_path): with open(file_path, encoding="utf-8") as file: contents = file.read()
Created by the `stdlib` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/stdlib.py). 🎀
---
### `unused-argument` / `W0613`
Message emitted:
Unused argument %r
Description: this one fires when a function or method argument is just... sitting there. Unused. Iconic behavior of the "dead weight" variety, honestly.
Problematic code:
def print_point(x, y): # [unused-argument] print(f"Point is located at {x},{x}")
Correct code:
def print_point(x, y): print(f"Point is located at {x},{y}")
Created by the `variables` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py). 🦋
---
### `unused-format-string-argument` / `W1304`
Message emitted:
Unused format argument %r
Description: used when a PEP 3101 format string that uses named fields gets handed an argument that the format string literally does not require. Extra. Unnecessary. Not in the group chat. 🙈
Problematic code:
print("{x} {y}".format(x=1, y=2, z=3)) # [unused-format-string-argument]
Correct code:
add_format_target.py:
print("{x} {y} {z}".format(x=1, y=2, z=3))
remove_unused_args.py:
print("{x} {y}".format(x=1, y=2))
Created by the `string` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py). 💖
---
### `unused-format-string-key` / `W1301`
Message emitted:
Unused key %r in format string dictionary
Description: this fires when a format string with named conversion specifiers is used with a dictionary that has keys the format string never asked for. Keys just chilling. Uninvited. 😭
Problematic code:
"The quick %(color)s fox jumps over the lazy dog." % { "color": "brown", "action": "hops", }
-4: [unused-format-string-key]
Correct code:
"The quick %(color)s fox %(action)s over the lazy dog." % { "color": "brown", "action": "hops", }
Created by the `string` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/strings.py). 🎀
---
### `unused-import` / `W0611`
Message emitted:
Unused %s
Description: fires when an imported module or variable is not used. Ghosted imports, basically.
Problematic code:
from logging import getLogger from pathlib import Path # [unused-import]
LOGGER = getLogger(__name__)
Correct code:
from logging import getLogger
LOGGER = getLogger(__name__)
Additional details: by default, this check is **skipped for `__init__.py` files**, because those often contain imports from submodules for the convenience of end users. Like, those imports aren't used *inside* `__init__.py`, but they're doing the work of giving people intuitive import paths for the module's important classes and constants. Main character energy for real. 👑
Related links:
- `--init-import`
Created by the `variables` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py). ⭐
---
### `unused-private-member` / `W0238`
Message emitted:
Unused private member %s.%s
Description: emitted when a private member of a class is defined but never used. It's giving "wrote the whole method and then forgot to call it." 💀
Problematic code:
class Fruit: FRUITS = {"apple": "red", "orange": "orange"}
def __print_color(self): # [unused-private-member] pass
Correct code:
class Fruit: FRUITS = {"apple": "red", "orange": "orange"}
def __print_color(self, name, color): print(f"{name}: {color}")
def print(self): for fruit, color in self.FRUITS.items(): self.__print_color(fruit, color)
Created by the `classes` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py). 🌸
---
### `unused-variable` / `W0612`
Message emitted:
Unused variable %r
Description: fires when a variable is defined but not used. Bestie. You made it. You named it. You never spoke to it again. 🫶
Problematic code:
def print_fruits(): fruit1 = "orange" fruit2 = "apple" # [unused-variable] print(fruit1)
Correct code:
def print_fruits(): fruit1 = "orange" fruit2 = "apple" print(fruit1, fruit2)
Created by the `variables` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py). 🦋
---
### `unused-wildcard-import` / `W0614`
Message emitted:
Unused import(s) %s from wildcard import of %s
Description: used when an imported module or variable is not used from a `'from X import *'` style import. Yes, the chaotic star import. We're checking it. 👀
Problematic code:
from abc import * # [unused-wildcard-import]
class Animal(ABC): ...
Correct code:
from abc import ABC
class Animal(ABC): ...
Created by the `variables` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/variables.py). 💜
---
### `useless-else-on-loop` / `W0120`
Message emitted:
Else clause on loop without a break statement, remove the else and de-indent all the code inside it
Description: loops should only have an `else` clause if they can exit early with a `break` statement. Otherwise, the statements under `else` should be on the same scope as the loop itself. Lowkey the `else` is just lying to you about what it does. 😤
Problematic code:
def find_even_number(numbers): for x in numbers: if x % 2 == 0: return x else: # [useless-else-on-loop] print("Did not find an even number")
Correct code:
def find_even_number(numbers): for x in numbers: if x % 2 == 0: return x print("Did not find an even number")
Created by the `basic` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_error_checker.py). 🔥
---
### `useless-param-doc` / `W9019`
Message emitted:
"%s" useless ignored parameter documentation
Description: please remove the ignored parameter documentation. That's it. That's the whole ask. 🙏
Problematic code:
def say_hello(_new: str) -> str: # [useless-param-doc] """say hello!
:param _new: :return: comment """ return "hello"
Correct code:
def say_hello(_new: str) -> str: """say hello!
:return: comment """ return "hello"
Configuration file:
[MAIN] load-plugins=pylint.extensions.docparams,
> Note: this message is emitted by the optional `parameter_documentation` checker, which requires the `pylint.extensions.docparams` plugin to be loaded. So like, load the plugin or this message simply will not show up. Periodt. 💅
Created by the `parameter_documentation` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py). 🧚
---
### `useless-parent-delegation` / `W0246`
Message emitted:
Useless parent or super() delegation in method %r
Description: used whenever we can detect that an overridden method is useless — it's just relying on parent or `super()` delegation to do exactly the same thing as another method from the MRO. You wrote a whole method to do nothing. Respectfully. 😭
Problematic code:
class Animal: def eat(self, food): print(f"Eating {food}")
class Human(Animal): def eat(self, food): # [useless-parent-delegation] super(Human, self).eat(food)
Correct code:
class Animal: def eat(self, food): print(f"Eating {food}")
class Human(Animal): """There is no need to override 'eat' it has the same signature as the implementation in Animal."""
Related links:
- Stackoverflow explanation for `'useless-super-delegation'` — https://stackoverflow.com/a/51030674/2519059
Created by the `classes` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py). 🌈
---
### `useless-type-doc` / `W9020`
Message emitted:
"%s" useless ignored parameter type documentation
Description: please remove the ignored parameter type documentation. Same vibe as above but for the *type* part. 💅
Problematic code:
def print_fruit(fruit, _): # [useless-type-doc] """docstring ...
Args: fruit (str): A fruit. _ (float): Another argument. """ print(fruit)
Correct code:
def print_fruit(fruit): """docstring ...
Args: fruit (str): A fruit. """ print(fruit)
Configuration file:
[MAIN] load-plugins=pylint.extensions.docparams,
> Note: this message is emitted by the optional `parameter_documentation` checker, which requires the `pylint.extensions.docparams` plugin to be loaded. Yes, again. Load the plugin bestie!! 🫶
Created by the `parameter_documentation` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docparams.py). 🍓
---
### `useless-with-lock` / `W2101`
Message emitted:
'%s()' directly created in 'with' has no effect
Description: used when a new lock instance is created right there in the `with` statement — which has **no effect**. Instead, an existing instance should be used to acquire the lock. Creating it fresh in the `with` defeats the entire point, ngl. 🔒
Problematic code:
import threading
with threading.Lock(): # [useless-with-lock] print("Make your bed.") with threading.Lock(): # [useless-with-lock] print("Sleep in it")
Correct code:
import threading
lock = threading.Lock() with lock: print("Make your bed.") with lock: print("Sleep in it.")
Created by the `threading` checker (https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/threading_checker.py). 🌸
---
### `using-assignment-expression-in-unsupported-version` / `W2605`
Message emitted:
Assignment expression is not supported by all versions included in the py-version setting
Description: used when the `py-version` set by the user is lower than `3.8` and pylint encounters an assignment expression (walrus) operator. 🦭
Problematic code:
import random
+1: [using-assignment-expression-in-unsupported-version]
if zero_or_one := random.randint(0, 1): assert zero_or_one == 1
Correct code:
import random
zero_or_one = random.randint(0, 1) if zero_or_one: assert zero_or_one == 1
Configuration file:
[main] py-version=3.7
Additional details: the assignment expression (walrus) operator (`:=`) was introduced in Python 3.8 — so to use it, please use a more recent version of Python. Like, that's the whole thing. 🐣
Created by the `basic` checker. 💫
### `using-constant-test` / `W0125`
**Message emitted:**
Using a conditional statement with a constant value
**Description:**
Emitted when a conditional statement (If or ternary if) uses a constant value for its test. This might not be what the user intended to do.
**Problematic code:**
if 0: # [using-constant-test] print("This code is never executed.") if 1: # [using-constant-test] print("This code is always executed.")
**Correct code:**
print("This code is always executed.")
Created by the basic <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/basic_checker.py> checker.
---
### `using-exception-groups-in-unsupported-version` / `W2603`
**Message emitted:**
Exception groups are not supported by all versions included in the py-version setting
**Description:**
Used when the py-version set by the user is lower than 3.11 and pylint encounters `except*` or `ExceptionGroup`.
**Problematic code:**
def f(): excs = [OSError("error 1"), SystemError("error 2")] # +1: [using-exception-groups-in-unsupported-version] raise ExceptionGroup("there were problems", excs)
try: # [using-exception-groups-in-unsupported-version] f() except* OSError as e: print("There were OSErrors") except* SystemError as e: print("There were SystemErrors")
**Correct code:**
def f(): raise OSError("error 1")
try: f() except OSError as e: print("There were OSErrors") except SystemError as e: print("There were SystemErrors")
**Configuration file:**
[main] py-version=3.10
**Additional details:**
Exception groups were introduced in Python 3.11; to use it, please use a more recent version of Python.
Created by the unsupported_version <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unsupported_version.py> checker.
---
### `using-f-string-in-unsupported-version` / `W2601`
**Message emitted:**
F-strings are not supported by all versions included in the py-version setting
**Description:**
Used when the py-version set by the user is lower than 3.6 and pylint encounters an f-string.
**Problematic code:**
f"python {3.5} is past end of life" # [using-f-string-in-unsupported-version]
**Correct code:**
"python {} is past end of life".format(3.5)
**Configuration file:**
[main] py-version=3.5
**Additional details:**
f-strings were introduced in Python version 3.6; to use them, please use a more recent version of Python.
Created by the unsupported_version <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unsupported_version.py> checker.
---
### `using-final-decorator-in-unsupported-version` / `W2602`
**Message emitted:**
typing.final is not supported by all versions included in the py-version setting
**Description:**
Used when the py-version set by the user is lower than 3.8 and pylint encounters a `typing.final` decorator.
**Problematic code:**
from typing import final
@final # [using-final-decorator-in-unsupported-version] class Playtypus(Animal): @final # [using-final-decorator-in-unsupported-version] def lay_egg(self): ...
**Correct code:**
class Playtypus(Animal): def lay_egg(self): ...
**Configuration file:**
[main] py-version=3.7
**Additional details:**
The message is emitted when the final decorator is used with a Python version less than 3.8. The final decorator was introduced in Python version 3.8.
**Related links:**
- PEP 591 <https://peps.python.org/pep-0591/#the-final-decorator>
Created by the unsupported_version <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unsupported_version.py> checker.
---
### `using-generic-type-syntax-in-unsupported-version` / `W2604`
**Message emitted:**
Generic type syntax (PEP 695) is not supported by all versions included in the py-version setting
**Description:**
Used when the py-version set by the user is lower than 3.12 and pylint encounters generic type syntax.
**Problematic code:**
type Vector = list[float] # [using-generic-type-syntax-in-unsupported-version]
**Correct code:**
from typing import TypeAlias
Vector: TypeAlias = list[float]
**Configuration file:**
[main] py-version=3.11
**Additional details:**
Generic type syntax was introduced in Python 3.12; to use it, please use a more recent version of Python.
Created by the unsupported_version <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unsupported_version.py> checker.
---
### `using-positional-only-args-in-unsupported-version` / `W2606`
**Message emitted:**
Positional-only arguments are not supported by all versions included in the py-version setting
**Description:**
Used when the py-version set by the user is lower than 3.8 and pylint encounters positional-only arguments.
**Problematic code:**
def add(x, y, /): # [using-positional-only-args-in-unsupported-version] return x + y
**Correct code:**
pylint: disable=missing-function-docstring, missing-module-docstring
def add(x, y): return x + y
**Configuration file:**
[main] py-version=3.7
**Additional details:**
Positional-only arguments were introduced in Python 3.8; to use them, please use a more recent version of Python.
Created by the unsupported_version <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unsupported_version.py> checker.
---
### `while-used` / `W0149`
**Message emitted:**
Used `while` loop
**Description:**
Unbounded `while` loops can often be rewritten as bounded `for` loops. Exceptions can be made for cases such as event loops, listeners, etc.
**Problematic code:**
import requests
def fetch_data(): i = 1 while i < 6: # [while-used] print(f"Attempt {i}...") try: return requests.get("https://example.com/data") except requests.exceptions.RequestException: pass i += 1
**Correct code:**
import requests
def fetch_data(): for i in range(1, 6): print(f"Attempt {i}...") try: return requests.get("https://example.com/data") except requests.exceptions.RequestException: pass
**Configuration file:**
[main] load-plugins=pylint.extensions.while_used
**Related links:**
- Stackoverflow discussion <https://stackoverflow.com/questions/920645/when-to-use-while-or-for-in-python>
> **Note:** This message is emitted by the optional `while_used` <#pylint-extensions-while-used> checker, which requires the `pylint.extensions.while_used` plugin to be loaded.
Created by the while_used <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/while_used.py> checker.
---
### `wildcard-import` / `W0401`
**Message emitted:**
Wildcard import %s
**Description:**
Used when `from module import *` is detected.
**Problematic code:**
from abc import * # [wildcard-import]
**Correct code:**
Either import module or
only import required objects from module.
import abc from abc import ABC, abstractmethod
Created by the imports <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py> checker.
---
### `wrong-exception-operation` / `W0716`
**Message emitted:**
Invalid exception operation. %s
**Description:**
Used when an operation is done against an exception, but the operation is not valid for the exception in question. Usually emitted when having binary operations between exceptions in except handlers.
**Problematic code:**
try: 1 / 0 except ValueError + TypeError: # [wrong-exception-operation] pass
**Correct code:**
try: 1 / 0 except (ValueError, TypeError): pass
Created by the exceptions <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/exceptions.py> checker.
---
All renamed messages in the warning category:
### `broad-except` / `W0703`
`'broad-except'` has been renamed. The new message can be found at:
### `cache-max-size-none` / `W1517`
`'cache-max-size-none'` has been renamed. The new message can be found at:
### `implicit-str-concat-in-sequence` / `W1403`
`'implicit-str-concat-in-sequence'` has been renamed. The new message can be found at:
### `lru-cache-decorating-method` / `W1516`
`'lru-cache-decorating-method'` has been renamed. The new message can be found at:
### `old-assignment-from-none` / `W1111`
`'old-assignment-from-none'` has been renamed. The new message can be found at:
### `old-deprecated-argument` / `W1511`
`'old-deprecated-argument'` has been renamed. The new message can be found at:
### `old-deprecated-class` / `W1512`
`'old-deprecated-class'` has been renamed. The new message can be found at:
### `old-deprecated-decorator` / `W1513`
`'old-deprecated-decorator'` has been renamed. The new message can be found at:
### `old-deprecated-method` / `W1505`
`'old-deprecated-method'` has been renamed. The new message can be found at:
### `old-deprecated-module` / `W0402`
`'old-deprecated-module'` has been renamed. The new message can be found at:
### `old-empty-docstring` / `W0132`
`'old-empty-docstring'` has been renamed. The new message can be found at:
---
### `empty-docstring` / `C0112`
**Message emitted:**
Empty %s docstring
**Description:**
Used when a module, function, class or method has an empty docstring (it would be too easy ;).
**Problematic code:**
def foo(): # [empty-docstring] """"""
**Correct code:**
def foo(): """A dummy description."""
Created by the basic <https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/docstring_checker.py> checker.
---
### `old-missing-param-doc` / `W9003`
`'old-missing-param-doc'` has been renamed. The new message can be found at:
### `old-missing-returns-doc` / `W9007`
`'old-missing-returns-doc'` has been renamed. The new message can be found at:
### `old-missing-type-doc` / `W9004`
`'old-missing-type-doc'` has been renamed. The new message can be found at:
### `old-missing-yields-doc` / `W9009`
`'old-missing-yields-doc'` has been renamed. The new message can be found at:
### `old-non-iterator-returned-1` / `W0234`
`'old-non-iterator-returned-1'` has been renamed. The new message can be found at:
### `old-unidiomatic-typecheck` / W0154
`old-unidiomatic-typecheck` has been renamed 💅 the new message can be found at: … and then the page literally just ends there, no link, no nothing 🙈 that's the whole entry bestie. Point is: renamed, go track down the new one.
### `unidiomatic-typecheck` / C0123
**Message emitted:**
Use isinstance() rather than type() for a typecheck.
**Description:**
The idiomatic way to do an explicit typecheck in Python is `isinstance(x, Y)` and *not* `type(x) == Y` or `type(x) is Y`. That said — and this is important — there are unusual situations where these actually give you different results, so don't come for me if you had a reason 😌
**Problematic code:**
test_score = {"Biology": 95, "History": 80} if type(test_score) is dict: # [unidiomatic-typecheck] pass
**Correct code:**
test_score = {"Biology": 95, "History": 80} if isinstance(test_score, dict): pass
**Related links:**
- Builtin function `type()` — `https://docs.python.org/3/library/functions.html#type`
- Builtin function `isinstance()` — `https://docs.python.org/3/library/functions.html#isinstance`
Created by the `basic` checker — <cyan>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/comparison_checker.py`</cyan> 🫶
### `old-unpacking-non-sequence` / W0633
`old-unpacking-non-sequence` has been renamed 💫 the new message can be found at: (again, the trail goes cold right there 🔥). Renamed, that's the tea.
### `useless-super-delegation` / W0235
`useless-super-delegation` has been renamed ✨ the new message can be found at: … yep, same cliffhanger energy 🙈. It's renamed, bestie.
---
## 💅 Convention
All messages in the convention category, coming at you 👑
### `bad-classmethod-argument` / C0202
**Message emitted:**
Class method %s should have %s as first argument
**Description:**
Fires when a class method's first argument is named something other than whatever you set in the `valid-classmethod-first-arg` option (which defaults to `"cls"` — lowercase, bestie). The whole point is so you can lowkey tell class methods apart from regular instance methods at a glance ✨
**Problematic code:**
class Klass: @classmethod def get_instance(self): # [bad-classmethod-argument] return self()
**Correct code:**
class Klass: @classmethod def get_instance(cls): return cls()
Created by the `classes` checker — <purple>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`</purple> 💜
### `bad-docstring-quotes` / C0198
**Message emitted:**
Bad docstring quotes in %s, expected """, given %s
**Description:**
Emitted when a docstring isn't wrapped in triple double quotes. That's it. That's the rule 🎀
**Problematic code:**
def foo(): # [bad-docstring-quotes] "Docstring." return
**Correct code:**
def foo(): """Docstring.""" return
**Configuration file:**
[main] load-plugins=pylint.extensions.docstyle
**Additional details:**
Straight from PEP 257, no notes:
> `For consistency, always use """triple double quotes""" around docstrings.`
**Related links:**
- PEP 257 – Docstring Conventions — <blue>`https://peps.python.org/pep-0257/#specification`</blue>
> **Note:** this message is emitted by the optional `'docstyle'` checker (anchor: `#pylint-extensions-docstyle`), which requires the `pylint.extensions.docstyle` plugin to be loaded 🌸
Created by the `docstyle` checker — <mint>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docstyle.py`</mint> 🧚
### `bad-file-encoding` / C2503
**Message emitted:**
PEP8 recommends UTF-8 as encoding for Python files
**Description:**
PEP8 says Python files should use UTF-8 as the default encoding, full stop 💅 see `https://peps.python.org/pep-0008/#source-file-encoding` for the receipts.
**Problematic code:**
coding: latin_1 # [bad-file-encoding]
**Correct code:**
(it's literally blank in the manpage — no correct-code sample here, that's just how it is 🙈)
Created by the `unicode_checker` checker — <yellow>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/unicode.py`</yellow> ⭐
### `bad-mcs-classmethod-argument` / C0204
**Message emitted:**
Metaclass class method %s should have %s as first argument
**Description:**
Same vibe as the classmethod one but for **metaclasses** 🦋 it fires when a metaclass class method's first argument doesn't match the value of the `valid-metaclass-classmethod-first-arg` option (default: `"mcs"`). Again — so you can easily tell them apart from regular instance methods, iykyk.
**Problematic code:**
class Meta(type): @classmethod def foo(some): # [bad-mcs-classmethod-argument] pass
**Correct code:**
class Meta(type): @classmethod def foo(mcs): pass
Created by the `classes` checker — <purple>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`</purple> 💜
### `bad-mcs-method-argument` / C0203
**Message emitted:**
Metaclass method %s should have %s as first argument
**Description:**
The non-classmethod twin 👯 fires when a metaclass method's first argument is named differently from the value in the `valid-classmethod-first-arg` option (default `"cls"`) — yes, that's the *classmethod* option, that's what the page says, I don't make the rules. The reason given: it's recommended so you can easily differentiate them from regular instance methods ✨
**Problematic code:**
class Meta(type): def func(some): # [bad-mcs-method-argument] pass
**Correct code:**
class Meta(type): def func(cls): pass
Created by the `classes` checker — <purple>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`</purple> 💜
### `consider-iterating-dictionary` / C0201
**Message emitted:**
Consider iterating the dictionary directly instead of calling .keys()
**Description:**
Emitted when you iterate a dict's keys through the `.keys()` method, or when you use `.keys()` for a membership check 🍓 bestie you do not need it: just iterate the dictionary itself with `for key in dictionary`. And for membership checks, `if key in dictionary` is *faster*. Free speed, no cap.
**Problematic code:**
FRUITS = {"apple": 1, "pear": 5, "peach": 10}
for fruit in FRUITS.keys(): # [consider-iterating-dictionary] print(fruit)
**Correct code:**
FRUITS = {"apple": 1, "pear": 5, "peach": 10}
for fruit in FRUITS: print(fruit)
Created by the `refactoring` checker — <pink>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/recommendation_checker.py`</pink> 🎀
### `consider-using-any-or-all` / C0501
**Message emitted:**
for loop could be %s
**Description:**
A `for` loop that checks for a condition and returns a bool? That whole thing can be swapped out for `any` or `all` 🔥 it's giving "why are we writing seven lines when one will do".
**Problematic code:**
`all_even.py`:
def all_even(items): """Return True if the list contains all even numbers""" for item in items: # [consider-using-any-or-all] if not item % 2 == 0: return False return True
`any_even.py`:
def any_even(items): """Return True if the list contains any even numbers""" for item in items: # [consider-using-any-or-all] if item % 2 == 0: return True return False
**Correct code:**
`all_even.py`:
def all_even(items): """Return True if the list contains all even numbers""" return all(item % 2 == 0 for item in items)
`any_even.py`:
def any_even(items): """Return True if the list contains any even numbers""" return any(item % 2 == 0 for item in items)
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.for_any_all
> **Note:** this message is emitted by the optional `'consider-using-any-or-all'` checker (anchor: `#pylint-extensions-for-any-all`), which requires the `pylint.extensions.for_any_all` plugin to be loaded 🌈
Created by the `consider-using-any-or-all` checker — <mint>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/for_any_all.py`</mint> 🧚
### `consider-using-dict-items` / C0206
**Message emitted:**
Consider iterating with .items()
**Description:**
Emitted when you're iterating over a dict's keys and then grabbing the value by index lookup 🙈 you can get *both* the key and the value at once by iterating with the dictionary's `.items()` method instead. Two for the price of one, slay.
**Problematic code:**
ORCHESTRA = { "violin": "strings", "oboe": "woodwind", "tuba": "brass", "gong": "percussion", }
for instrument in ORCHESTRA: # [consider-using-dict-items] print(f"{instrument}: {ORCHESTRA[instrument]}")
**Correct code:**
ORCHESTRA = { "violin": "strings", "oboe": "woodwind", "tuba": "brass", "gong": "percussion", }
for instrument, section in ORCHESTRA.items(): print(f"{instrument}: {section}")
Created by the `refactoring` checker — <pink>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/recommendation_checker.py`</pink> 🎀
### `consider-using-enumerate` / C0200
**Message emitted:**
Consider using enumerate instead of iterating with range and len
**Description:**
Emitted when pylint catches code iterating with `range` and `len` together 😭 that whole pattern gets simpler with the `enumerate` builtin, and honestly it's iconic.
**Problematic code:**
seasons = ["Spring", "Summer", "Fall", "Winter"]
for i in range(len(seasons)): # [consider-using-enumerate] print(i, seasons[i])
**Correct code:**
seasons = ["Spring", "Summer", "Fall", "Winter"]
for i, season in enumerate(seasons): print(i, season)
Created by the `refactoring` checker — <pink>`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/recommendation_checker.py`</pink> 🎀
### `consider-using-f-string` / C0209
**Message emitted:**
Formatting a regular string which could be an f-string
**Description:**
Used when we detect a string being formatted with `format()` or `%` that could potentially be an f-string. f-strings are the preferred glow-up 💅 this requires Python 3.6 and `py-version >= 3.6`, so check your version bestie.
**Problematic code:**
from string import Template
menu = ("eggs", "spam", 42.4)
old_order = "%s and %s: %.2f ¤" % menu # [consider-using-f-string] beginner_order = menu[0] + " and " + menu[1] + ": " + str(menu[2]) + " ¤" joined_order = " and ".join(menu[:2])
+1: [consider-using-f-string]
format_order = "{} and {}: {:0.2f} ¤".format(menu[0], menu[1], menu[2])
+1: [consider-using-f-string]
named_format_order = "{eggs} and {spam}: {price:0.2f} ¤".format( eggs=menu[0], spam=menu[1], price=menu[2] ) template_order = Template("$eggs and $spam: $price ¤").substitute( eggs=menu[0], spam=menu[1], price=menu[2] )
**Correct code:**
menu = ("eggs", "spam", 42.4)
f_string_order = f"{menu[0]} and {menu[1]}: {menu[2]:0.2f} ¤"
**Additional details:**
Formatted string literals (f-strings) give you a concise, consistent syntax that can replace most use cases for the `%` formatting operator, `str.format()` and `string.Template` 👑
F-strings also perform better than the alternatives — this tweet is the receipts for a simple example: <cyan>`https://twitter.com/raymondh/status/1205969258800275456`</cyan> 🔥
### `dict-init-mutate` / C3401
**Message emitted:**
Declare all known key/values when initializing the dictionary.
**Description:** bestie, you can literally build a dict in *one* statement with dictionary literal syntax 💅 doing it in pieces is giving "i've never heard of a dict literal" energy.
**Problematic code:**
fruit_prices = {} # [dict-init-mutate] fruit_prices["apple"] = 1 fruit_prices["banana"] = 10
**Correct code:**
fruit_prices = {"apple": 1, "banana": 10}
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.dict_init_mutate,
> **Note:** this message is emitted by the optional <pink>'dict-init-mutate'</pink> checker, which requires the `pylint.extensions.dict_init_mutate` plugin to be loaded. no plugin, no message, that's the vibe check ✨
Created by the <cyan>dict-init-mutate</cyan> checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/dict_init_mutate.py
---
### `disallowed-name` / C0104
**Message emitted:**
Disallowed name "%s"
**Description:** fired when the name matches `bad-names` or `bad-names-rgxs-` (unauthorized names 🚫). like, you're not allowed to sit with us.
**Problematic code:**
def foo(): # [disallowed-name] print("apples")
**Correct code:**
def print_fruit(): print("apples")
**Configuration file:**
[MAIN] bad-names=foo,bar,baz
Created by the <cyan>basic</cyan> checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/name_checker/checker.py
---
### `docstring-first-line-empty` / C0199
**Message emitted:**
First line empty in %s docstring
**Description:** used when a blank line is found at the beginning of a docstring 🫠 no cap, the first line is prime real estate, don't waste it on nothing.
**Problematic code:**
def foo(): # [docstring-first-line-empty] """ Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book """
**Correct code:**
def foo(): """Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book """
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.docstyle
> **Note:** emitted by the optional <pink>'docstyle'</pink> checker, which requires the `pylint.extensions.docstyle` plugin to be loaded 🌸
Created by the <cyan>docstyle</cyan> checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/docstyle.py
---
### `import-outside-toplevel` / C0415
**Message emitted:**
Import outside toplevel (%s)
**Description:** used whenever an `import` statement shows up anywhere that isn't the module toplevel. girl. move that import to the top of the file 😭 it's literally free real estate up there.
**Problematic code:**
def print_python_version(): import sys # [import-outside-toplevel]
print(sys.version_info)
**Correct code:**
import sys
def print_python_version(): print(sys.version_info)
Created by the <cyan>imports</cyan> checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py
---
### `import-private-name` / C2701
**Message emitted:**
Imported private %s (%s)
**Description:** fired when a private module or object prefixed with `_` gets imported 🙈 PEP8 guidance on Naming Conventions states that public attributes with leading underscores should be considered private — so like, you're not supposed to be in there, iykyk.
**Problematic code:**
from argparse import _AttributeHolder, _SubParsersAction # [import-private-name]
attr_holder = _AttributeHolder()
def add_sub_parser(sub_parsers: _SubParsersAction): sub_parsers.add_parser("my_subparser") # ...
**Correct code:**
"""Private import can be used as type annotations."""
from argparse import _SubParsersAction
def add_sub_parser(sub_parsers: _SubParsersAction): sub_parsers.add_parser("my_subparser") # ...
**Configuration file:**
[main] load-plugins = pylint.extensions.private_import
**Additional details:** using private imports exposes you to unexpected breaking changes for *any* version bump of your dependencies — even in patch versions 💀 yeah. even the tiny ones. prepare yourself.
> **Note:** this one comes from the optional <pink>'import-private-name'</pink> checker, which requires the `pylint.extensions.private_import` plugin to be loaded 🦋
Created by the <cyan>import-private-name</cyan> checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/private_import.py
---
### `invalid-characters-in-docstring` / C0403
**Message emitted:**
Invalid characters %r in a docstring
**Description:** used when a word in a docstring cannot be checked by `enchant` 😵💫
**Additional details:** this is a message linked to an internal problem in `enchant`. there's genuinely nothing to change in your code — but maybe in pylint's configuration, or in the way you installed the `'enchant'` system library. not your fault bestie, it's an enchant thing 🫶
Created by the <cyan>spelling</cyan> checker — https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/spelling.py
---
### `invalid-name` / C0103
**Message emitted:**
%s name "%s" doesn't conform to %s
**Description:** used when the name doesn't conform to the naming rules associated with its type (constant, variable, class... 👀). the rules exist and honestly they *slap* once you get used to them.
**Problematic code:**
class cat: # [invalid-name] def Meow(self, NUMBER_OF_MEOW): # [invalid-name, invalid-name] print("Meow" * NUMBER_OF_MEOW) return NUMBER_OF_MEOW
Cat = cat().Meow(42) # [invalid-name]
**Correct code:**
class Cat: def meow(self, number_of_meow): print("Meow" * number_of_meow) return number_of_meow
CAT = Cat().meow(42)
**Additional details:**
Pylint recognizes a number of different name types internally. With a few exceptions, the type of the name is governed by <pink>where the assignment to a name is found</pink> — not by the type of object assigned. iykyk 💅
- **`module`** — Module and package names, same as the file names.
- **`const`** — Module-level constants: any name defined at module level that is not bound to a class object nor reassigned.
- **`class`** — Names in `class` statements, as well as names bound to class objects at module level.
- **`function`** — Functions, toplevel or nested in functions or methods.
- **`method`** — Methods, functions defined in class bodies. Includes static and class methods.
- **`attr`** — Attributes created on class instances inside methods.
- **`argument`** — Arguments to any function type, including lambdas.
- **`variable`** — Local variables in function scopes or module-level names that are assigned multiple times.
- **`class-attribute`** — Attributes defined in class bodies.
- **`class-const`** — Enum constants and class variables annotated with `Final`.
- **`inlinevar`** — Loop variables in list comprehensions and generator expressions.
- **`typevar`** — Type variable declared with `TypeVar`.
- **`paramspec`** — Parameter specification variable declared with `ParamSpec`.
- **`typevartuple`** — Type variable tuple declared with `TypeVarTuple`.
- **`typealias`** — Type alias declared with `TypeAlias` or assignments of `Union`.
## Default behavior 💖
By default, Pylint will enforce PEP8 (`https://peps.python.org/pep-0008`)<pink>-suggested names</pink>. periodt ✨
## Predefined Naming Styles ✨
Pylint provides a set of predefined naming styles. Those predefined naming styles may be used to adjust Pylint configuration to the coding style used in the linted project. 🫶
Following predefined naming styles are available:
- `snake_case`
- `camelCase`
- `PascalCase`
- `UPPER_CASE`
- `any` — fake style which does not enforce any limitations
The following options are exposed:
- **`--module-naming-style=<style>`**
- **`--const-naming-style=<style>`**
- **`--class-naming-style=<style>`**
- **`--function-naming-style=<style>`**
- **`--method-naming-style=<style>`**
- **`--attr-naming-style=<style>`**
- **`--argument-naming-style=<style>`**
- **`--variable-naming-style=<style>`**
- **`--class-attribute-naming-style=<style>`**
- **`--class-const-naming-style=<style>`**
- **`--inlinevar-naming-style=<style>`**
## Predefined Naming Patterns 🎀
Pylint provides predefined naming patterns for some names. These patterns are often based on a Naming Style, but there is <hotpink>no option</hotpink> to choose one of the styles mentioned above. The pattern can be overwritten with the options discussed below. 💅
okay so for the predefined naming patterns, here's the whole name-type tea 💅
- **`typevar`** — good names: `T`, `_CallableT`, `_T_co`, `AnyStr`, `DeviceTypeT`, `IPAddressT`. Bad names: `DICT_T`, `CALLABLE_T`, `ENUM_T`, `DeviceType`, `_StrType`.
- **`paramspec`** — good names: `P`, `_P`. Bad names: `CALLABLE_P`.
- **`typevartuple`** — good names: `Ts`, `_Ts`. Bad names: `TUPLE_TS`.
- **`typealias`** — good names: `GoodName`, `_GoodName`, `IPAddressType`, `GoodName2`, and other PascalCase variants that don't start with `T` or `Type`. This is to distinguish them from typevars. Note that `TopName` is allowed but `TTopName` isn't. Bad names: `BadNameT`, `badName`, `TBadName`, `TypeBadName`, `_1BadName`.
Before pylint 3.0, most predefined patterns also enforced a minimum length of three characters. If this behavior is desired in versions 3.0 and following, it can be had by providing custom regular expressions as described next. (Or, if the disallowed-name check is sufficient instead of invalid-name, providing the single option `bad-names-rgxs="^..?$"` will suffice to fail 1-2 character names.
---
## ✨ Custom regular expressions ✨
If predefined naming styles are too limited, checker behavior may be further customized. For each name type, a separate regular expression matching valid names of this type can be defined. If any of custom regular expressions are defined, it overrides `*-naming-style` option value.
Regular expressions for the names are anchored at the beginning, any anchor for the end must be supplied explicitly. Any name not matching the regular expression will lead to an instance of `invalid-name`.
- **`--module-rgx=<regex>`** — the custom regex slot for this name type.
- **`--const-rgx=<regex>`** — the custom regex slot for this name type.
- **`--class-rgx=<regex>`** — the custom regex slot for this name type.
- **`--function-rgx=<regex>`** — the custom regex slot for this name type.
- **`--method-rgx=<regex>`** — the custom regex slot for this name type.
- **`--attr-rgx=<regex>`** — the custom regex slot for this name type.
- **`--argument-rgx=<regex>`** — the custom regex slot for this name type.
- **`--variable-rgx=<regex>`** — the custom regex slot for this name type.
- **`--class-attribute-rgx=<regex>`** — the custom regex slot for this name type.
- **`--class-const-rgx=<regex>`** — the custom regex slot for this name type.
- **`--inlinevar-rgx=<regex>`** — the custom regex slot for this name type.
- **`--typevar-rgx=<regex>`** — the custom regex slot for this name type.
- **`--paramspec-rgx=<regex>`** — the custom regex slot for this name type.
- **`--typevartuple-rgx=<regex>`** — the custom regex slot for this name type.
- **`--typealias-rgx=<regex>`** — the custom regex slot for this name type.
---
## 🌈 Multiple naming styles for custom regular expressions 🌈
Large code bases that have been worked on for multiple years often exhibit an evolution in style as well. In some cases, modules can be in the same package, but still have different naming style based on the stratum they belong to. However, intra-module consistency should still be required, to make changes inside a single file easier. For this case, Pylint supports regular expression with several named capturing group.
Rather than emitting name warnings immediately, Pylint will determine the prevalent naming style inside each module and enforce it on all names.
Consider the following (simplified) example:
pylint --function-rgx='(?:(?P<snake>[a-z_]+)|(?P<camel>[a-z]+([A-Z][a-z])))$' sample.py
The regular expression defines two naming styles, `snake` for snake-case names, and `camel` for camel-case names.
In `sample.py`, the function name on line 1 and 7 will mark the module and enforce the match of named group `snake` for the remaining names in the module:
def valid_snake_case(arg): ...
def InvalidCamelCase(arg): ...
def more_valid_snake_case(arg): ...
Because of this, the name on line 4 will trigger an `invalid-name` warning, even though the name matches the given regex.
Matches named `exempt` or `ignore` can be used for non-tainting names, to prevent built-in or interface-dictated names to trigger certain naming styles.
- **`--name-group=<name1:name2:...,...>`** — Default value: empty. Format: comma-separated groups of colon-separated names. This option can be used to combine name styles. For example, `function:method` enforces that functions and methods use the same style, and a style triggered by either name type carries over to the other. This requires that the regular expression for the combined name types use the same group names.
---
## 🎀 Name Hints 🎀
- **`--include-naming-hint=y|n`** — Default: off. Include a hint (regular expression used) for the correct name format with every `invalid-name` warning.
Created by the basic checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/name_checker/checker.py`.
---
## 🔥 line-too-long / C0301 🔥
Message emitted:
Line too long (%s/%s)
Description:
Used when a line is longer than a given number of characters.
Problematic code:
+1: [line-too-long]
FRUIT = ["apricot", "blackcurrant", "cantaloupe", "dragon fruit", "elderberry", "fig", "grapefruit", ]
Correct code:
FRUIT = [ "apricot", "blackcurrant", "cantaloupe", "dragon fruit", "elderberry", "fig", "grapefruit", ]
Configuration file:
[MAIN] max-line-length=100
Additional details:
Pragma controls such as `# pylint: disable=all` are not counted toward line length for the purposes of this message.
If you attempt to disable this message via `# pylint: disable=line-too-long` in a module with no code, you may receive a message for `useless-suppression`. This is a false positive of `useless-suppression` we can't easily fix.
See `https://github.com/pylint-dev/pylint/issues/3368` for more information.
Created by the format checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py`.
---
## 🍓 misplaced-comparison-constant / C2201 🍓
Message emitted:
Comparison should be %s
Description:
Used when the constant is placed on the left side of a comparison. It is usually clearer in intent to place it in the right hand side of the comparison.
Problematic code:
def compare_apples(apples=20): for i in range(10): if 5 <= i: # [misplaced-comparison-constant] pass if 1 == i: # [misplaced-comparison-constant] pass if 20 < len(apples): # [misplaced-comparison-constant] pass
Correct code:
def compare_apples(apples=20): for i in range(10): if i >= 5: pass if i == 1: pass if len(apples) > 20: pass
Configuration file:
[MAIN] load-plugins=pylint.extensions.comparison_placement
> Note: This message is emitted by the optional `comparison-placement` `<#pylint-extensions-comparison-placement>` checker, which requires the `pylint.extensions.comparison_placement` plugin to be loaded.
Created by the comparison-placement checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/comparison_placement.py`.
---
## 🦋 missing-class-docstring / C0115 🦋
Message emitted:
Missing class docstring
Description:
Used when a class has no docstring. Even an empty class must have a docstring.
Problematic code:
class Person: # [missing-class-docstring] def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name
Correct code:
class Person: """Class representing a person"""
def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/docstring_checker.py` — slay 💅
### missing-final-newline / C0304 ✨
<pink>**Message emitted:**</pink> `Final newline missing`
<pink>**Description:**</pink> This one fires when the last line in a file is missing a newline. Like, the file just ends with no final lil’ line terminator. 😭
<pink>**Problematic code**</pink> 🫠
`crlf.py:`
print("Hello") # CRLF (\r\n) print("world") # End-of-file (EOF)
[missing-final-newline]
`lf.py:`
print("Hello") # LF (\n) print("world") # End-of-file (EOF)
[missing-final-newline]
<pink>**Correct code**</pink> 💖
`crlf.py:`
print("Hello") # CRLF (\r\n) print("world") # CRLF (\r\n)
End-of-file (EOF)
`lf.py:`
print("Hello") # LF (\n) print("world") # LF (\n)
End-of-file (EOF)
<pink>**Additional details:**</pink>
The POSIX standard defines a line as:
> "A sequence of zero or more non- <newline> characters plus a terminating <newline> character."
<pink>**Related links:**</pink>
- POSIX Standard — `https://pubs.opengroup.org/onlinepubs/9699919799/`
- POSIX Standard Chapter 3.206 Line — `https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206`
Created by the `format` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py` — periodt. 💅
### missing-function-docstring / C0116 🎀
<pink>**Message emitted:**</pink> `Missing function or method docstring`
<pink>**Description:**</pink> Used when a function or method has no docstring. Some special methods like `__init__` do not require a docstring. So not every single one needs the lil’ explanation text, iykyk. 🦋
<pink>**Problematic code**</pink> 😭
import sys
def print_python_version(): # [missing-function-docstring] print(sys.version)
<pink>**Correct code**</pink> ✨
import sys
def print_python_version(): """Function printing python version.""" print(sys.version)
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/docstring_checker.py` — no cap. 💅
### missing-module-docstring / C0114 🌸
<pink>**Message emitted:**</pink> `Missing module docstring`
<pink>**Description:**</pink> Used when a module has no docstring. Empty modules do not require a docstring. So if the module is literally just vibes and no content, it gets a pass. 🫶
<pink>**Problematic code**</pink> 😭
import sys # [missing-module-docstring]
def print_python_version(): print(sys.version)
<pink>**Correct code**</pink> 💖
"""Module providing a function printing python version."""
import sys
def print_python_version(): print(sys.version)
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/docstring_checker.py` — iconic. 💅
### mixed-line-endings / C0327 🦋
<pink>**Message emitted:**</pink> `Mixed line endings LF and CRLF`
<pink>**Description:**</pink> Used when there are mixed (LF and CRLF) newline signs in a file. It’s giving chaos energy in the line endings. 💀
<pink>**Problematic code**</pink> 🙈
print("Hello") # CRLF (\r\n) print("World") # LF (\n) # [mixed-line-endings]
<pink>**Correct code**</pink> ✨
`full_crlf.py:`
print("Hello") # CRLF (\r\n) print("World") # CRLF (\r\n)
`full_lf.py:`
print("Hello") # LF (\n) print("World") # LF (\n)
<pink>**Related links:**</pink>
- History of CRLF and LF — `https://stackoverflow.com/a/6521730/2519059`
- Dealing with line endings in Git — `https://stackoverflow.com/a/10855862/2519059`
- A Collection of Useful `.gitattributes` Templates — `https://github.com/alexkaratarakis/gitattributes`
Created by the `format` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py` — slay. 💅
### multiple-imports / C0410 🎀
<pink>**Message emitted:**</pink> `Multiple imports on one line (%s)`
<pink>**Description:**</pink> Used when an import statement importing multiple modules is detected. One import per line, bestie — it’s cleaner and the vibes are immaculate. 🌈
<pink>**Problematic code**</pink> 😭
import os, sys # [multiple-imports]
<pink>**Correct code**</pink> 💖
import os import sys
Created by the `imports` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py` — periodt. 💅
### multiple-statements / C0321 ⭐
<pink>**Message emitted:**</pink> `More than one statement on a single line`
<pink>**Description:**</pink> Used when more than on statement are found on the same line. Yes, that’s “more than on” in the original — we’re keeping the facts, not fixing the typo. 😭
<pink>**Problematic code**</pink> 🫠
fruits = ["apple", "orange", "mango"]
if "apple" in fruits: pass # [multiple-statements] else: print("no apples!")
<pink>**Correct code**</pink> ✨
fruits = ["apple", "orange", "mango"]
if "apple" in fruits: pass else: print("no apples!")
Created by the `format` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py` — no cap. 💅
### non-ascii-module-import / C2403 🦋
<pink>**Message emitted:**</pink> `%s name "%s" contains a non-ASCII character, use an ASCII-only alias for import.`
<pink>**Description:**</pink> Used when the name contains at least one non-ASCII unicode character. See `https://peps.python.org/pep-0672/#confusing-features` for a background why this could be bad. If your programming guideline defines that you are programming in English, then there should be no need for non ASCII characters in Python Names. If not you can simply disable this check. 💅
<pink>**Problematic code**</pink> 😭
from os.path import join as łos # [non-ascii-module-import]
foo = łos("a", "b")
<pink>**Correct code**</pink> 💖
from os.path import join as os_join
foo = os_join("a", "b")
Created by the `nonascii-checker` — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/non_ascii_names.py` — slay. 💅
### non-ascii-name / C2401 🌸
<pink>**Message emitted:**</pink> `%s name "%s" contains a non-ASCII character, consider renaming it.`
<pink>**Description:**</pink> Used when the name contains at least one non-ASCII unicode character. See `https://peps.python.org/pep-0672/#confusing-features` for a background why this could be bad. If your programming guideline defines that you are programming in English, then there should be no need for non ASCII characters in Python Names. If not you can simply disable this check. 🫶
<pink>**Problematic code**</pink> 🙈
ápple_count = 4444 # [non-ascii-name]
<pink>**Correct code**</pink> ✨
apple_count = 4444
Created by the `nonascii-checker` — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/non_ascii_names.py` — periodt. 💅
### single-string-used-for-slots / C0205 🎀
<pink>**Message emitted:**</pink> `Class __slots__ should be a non-string iterable`
<pink>**Description:**</pink> Used when a class `__slots__` is a simple string, rather than an iterable. Bestie, it wants a non-string iterable, not just one string. 💀
<pink>**Problematic code**</pink> 😭
class Fruit: # [single-string-used-for-slots] __slots__ = "name"
def __init__(self, name): self.name = name
<pink>**Correct code**</pink> 💖
class Fruit: __slots__ = ("name",)
def __init__(self, name): self.name = name
Created by the `classes` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py` — iconic. 💅
### singleton-comparison / C0121 ⭐
<pink>**Message emitted:**</pink> `Comparison %s should be %s`
<pink>**Description:**</pink> Used when an expression is compared to singleton values like `True`, `False` or `None`. It’s giving “extra comparison energy” when you don’t need it. 🔥
<pink>**Problematic code**</pink> 🫠
game_won = True if game_won == True: # [singleton-comparison] print("Game won !")
<pink>**Correct code**</pink> ✨
game_won = True if game_won: print("Game won !")
<pink>**Related links:**</pink>
- PEP 285 – Adding a bool type — `https://peps.python.org/pep-0285/`
Created by the `basic` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/comparison_checker.py` — no cap. 💅
### superfluous-parens / C0325 🦋
<pink>**Message emitted:**</pink> `Unnecessary parens after %r keyword`
<pink>**Description:**</pink> Used when a single item in parentheses follows an `if`, `for`, or other keyword. Extra parentheses are not the main character here, bestie. 😭
<pink>**Problematic code**</pink> 🙈
`example_1.py:`
x = input() y = input() if (x == y): # [superfluous-parens] pass
`example_2.py:`
i = 0 exclude = [] if (i - 0) in exclude: # [superfluous-parens] pass
<pink>**Correct code**</pink> 💖
`example_1.py:`
x = input() y = input() if x == y: pass
`example_2.py:`
i = 0 exclude = [] if i - 0 in exclude: pass
Created by the `format` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py` — slay. 💅
### too-many-lines / C0302 🌈
<pink>**Message emitted:**</pink> `Too many lines in module (%s/%s)`
<pink>**Description:**</pink> Used when a module has too many lines, reducing its readability. Too much in one file = not the vibe. 😭
<pink>**Problematic code**</pink> 🫠
def is_palindrome(string): # [too-many-lines] left_pos = 0 right_pos = len(string) - 1 while right_pos >= left_pos: if not string[left_pos] == string[right_pos]: return False left_pos += 1 right_pos -= 1 return True
def main(): print(is_palindrome("aza")) print(is_palindrome("racecar")) print(is_palindrome("trigger")) print(is_palindrome("ogre"))
<pink>**Correct code**</pink> ✨
`__init__.py:`
__all__ = ["is_palindrome", "main"]
from is_palindrome import is_palindrome from main import main
`is_palindrome.py:`
def is_palindrome(string): return string == string[::-1]
`main.py:`
from is_palindrome import is_palindrome
def main(): for string in ["aza", "racecar", "trigger", "ogre"]: print(is_palindrome(string))
<pink>**Configuration file:**</pink>
[main] max-module-lines=15
<pink>**Additional details:**</pink>
When a module has too many lines it can make it difficult to read and understand. There might be performance issue while editing the file because the IDE must parse more code. You need more expertise to navigate the file properly (go to a particular line when debugging, or search for a specific code construct, instead of navigating by clicking and scrolling). This measure is a proxy for higher cyclomatic complexity that you might not be calculating if you're not using `load-plugins=pylint.extensions.mccabe,`. Cyclomatic complexity is slower to compute, but also a more fine grained measure than raw SLOC. In particular, you can't make the code less readable by making a very complex one liner if you're using cyclomatic complexity.
The example simplify the code, but it's not always possible. Most of the time bursting the file by creating a package with the same API is the only solution. Anticipating and creating the file from the get go will permit to have the same end result with a better version control history. 💅
Created by the `format` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py` — periodt. 💅
### trailing-newlines / C0305 🎀
<pink>**Message emitted:**</pink> `Trailing newlines`
<pink>**Description:**</pink> Used when there are trailing blank lines in a file. Like, the file has extra blank-line crumbs at the end. 🙈
<pink>**Problematic code**</pink> 😭:
print("apple")
The file ends with 2 lines that are empty # +1: [trailing-newlines]
**Correct code:**
print("apple")
Created by the `format` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py`).
### **`trailing-whitespace` / `C0303`** 🧹
**Message emitted:** `Trailing whitespace`
**Description:** Used when there is <pink>whitespace between the end of a line and the newline</pink>. bestie, it’s literally invisible clutter at the end of your line.
**Problematic code:**
print("Hello") # [trailing-whitespace]
^^^ trailing whitespaces
**Correct code:**
print("Hello")
Created by the `format` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py`).
### **`typevar-double-variance` / `C0131`** 🦋
**Message emitted:** `TypeVar cannot be both covariant and contravariant`
**Description:** Emitted when both the `"covariant"` and `"contravariant"` keyword arguments are set to `"True"` in a `TypeVar`. that’s giving “pick a lane” energy, fr fr.
**Problematic code:**
from typing import TypeVar
T = TypeVar("T", covariant=True, contravariant=True) # [typevar-double-variance]
**Correct code:**
from typing import TypeVar
T_co = TypeVar("T_co", covariant=True) T_contra = TypeVar("T_contra", contravariant=True)
Created by the `basic` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/name_checker/checker.py`).
### **`typevar-name-incorrect-variance` / `C0105`** 🎀
**Message emitted:** `Type variable name does not reflect variance%s`
**Description:** Emitted when a `TypeVar` name doesn’t reflect its type variance. According to <purple>PEP8</purple>, it is recommended to add suffixes `'_co'` and `'_contra'` to the variables used to declare covariant or contravariant behaviour respectively. Invariant (default) variables do not require a suffix. The message is also emitted when invariant variables do have a suffix.
**Problematic code:**
from typing import TypeVar
T_co = TypeVar("T_co") # [typevar-name-incorrect-variance]
**Correct code:**
from typing import TypeVar
T = TypeVar("T")
**Additional details:** When naming type vars, only use a `_co` suffix when indicating covariance or `_contra` when indicating contravariance.
Created by the `basic` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/name_checker/checker.py`).
### **`typevar-name-mismatch` / `C0132`** 🫶
**Message emitted:** `TypeVar name "%s" does not match assigned variable name "%s"`
**Description:** Emitted when a `TypeVar` is assigned to a variable that does not match its name argument. no cap, the name on the box has to match the name on the label.
**Problematic code:**
from typing import TypeVar
X = TypeVar("T") # [typevar-name-mismatch]
**Correct code:**
from typing import TypeVar
T = TypeVar("T")
Created by the `basic` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/name_checker/checker.py`).
### **`unexpected-line-ending-format` / `C0328`** 🌈
**Message emitted:** `Unexpected line ending format. There is '%s' while it should be '%s'.`
**Description:** Used when there is different newline than expected. lowkey a line-ending vibe check.
**Problematic code:**
print("I'm drinking tea!") # CRLF (\r\n) # [unexpected-line-ending-format] print("I'm drinking water!") # CRLF (\r\n) # [unexpected-line-ending-format]
**Correct code:**
print("I'm drinking tea!") # LF (\n) print("I'm drinking water!") # LF (\n)
**Configuration file:**
[FORMAT] expected-line-ending-format=LF
**Related links:**
- History of CRLF and LF — `https://stackoverflow.com/a/6521730/2519059`
- Dealing with line endings in Git — `https://stackoverflow.com/a/10855862/2519059`
- A Collection of Useful .gitattributes Templates — `https://github.com/alexkaratarakis/gitattributes`
Created by the `format` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/format.py`).
### **`ungrouped-imports` / `C0412`** 🍓
**Message emitted:** `Imports from package %s are not grouped`
**Description:** Used when imports are not grouped by packages. keep your little import family together, bestie.
**Problematic code:**
import logging import os import sys import logging.config # [ungrouped-imports] from logging.handlers import WatchedFileHandler
**Correct code:**
import logging import logging.config import os import sys from logging.handlers import FileHandler
Created by the `imports` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py`).
### **`unnecessary-direct-lambda-call` / `C3002`** 🔥
**Message emitted:** `Lambda expression called directly. Execute the expression inline instead.`
**Description:** Used when a lambda expression is directly called rather than executing its contents inline. it’s giving extra steps for no reason.
**Problematic code:**
y = (lambda x: x**2 + 2 * x + 1)(a) # [unnecessary-direct-lambda-call]
**Correct code:**
y = a**2 + 2 * a + 1
Created by the `lambda-expressions` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/lambda_expressions.py`).
### **`unnecessary-dunder-call` / `C2801`** 💅
**Message emitted:** `Unnecessarily calls dunder method %s. %s.`
**Description:** Used when a dunder method is manually called instead of using the corresponding function/method/operator. you don’t need to summon the dunder yourself, bestie.
**Problematic code:**
three = (3.0).__str__() # [unnecessary-dunder-call] twelve = "1".__add__("2") # [unnecessary-dunder-call]
def is_bigger_than_two(x): return x.__gt__(2) # [unnecessary-dunder-call]
**Correct code:**
three = str(3.0) twelve = "1" + "2"
def is_bigger_than_two(x): return x > 2
**Related links:**
- Define dunder methods but don't call them directly — `https://www.pythonmorsels.com/avoid-dunder-methods/`
Created by the `unnecessary-dunder-call` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/dunder_methods.py`).
### **`unnecessary-lambda-assignment` / `C3001`** 🧚
**Message emitted:** `Lambda expression assigned to a variable. Define a function using the "def" keyword instead.`
**Description:** Used when a lambda expression is assigned to variable rather than defining a standard function with the `"def"` keyword. if it has a name, give it a whole `def`, periodt.
**Problematic code:**
foo = lambda x: x**2 + 2 * x + 1 # [unnecessary-lambda-assignment]
**Correct code:**
def foo(x): return x**2 + 2 * x + 1
Created by the `lambda-expressions` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/lambda_expressions.py`).
### **`unnecessary-negation` / `C0117`** 🙈
**Message emitted:** `Consider changing "%s" to "%s"`
**Description:** Used when a boolean expression contains an unneeded negation, e.g. when two negation operators cancel each other out. double negative? in this economy?
**Problematic code:**
`double_not.py`:
if not not input(): # [unnecessary-negation] pass
`equivalent_comparator_exists.py`:
a = 3 b = 10 if not a > b: # [unnecessary-negation] pass
**Correct code:**
`double_not.py`:
if input(): pass
`equivalent_comparator_exists.py`:
a = 3 b = 10 if a <= b: pass
Created by the `refactoring` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/not_checker.py`).
### **`use-implicit-booleaness-not-comparison` / `C1803`** 💖
**Message emitted:** `"%s" can be simplified to "%s", if it is strictly a sequence, as an empty %s is falsey`
**Description:** Empty sequences are considered false in a boolean context. Following this check blindly in weakly typed code base can create hard to debug issues. If the value can be something else that is falsey but not a sequence (for example `None`, an empty string, or `0`) the code will not be equivalent.
**Problematic code:**
z = []
if z != []: # [use-implicit-booleaness-not-comparison] print("z is not an empty sequence")
**Correct code:**
z = []
if z: print("z is not an empty sequence")
Created by the `refactoring` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/implicit_booleaness_checker.py`).
### **`use-implicit-booleaness-not-comparison-to-string` / `C1804`** 🌸
**Message emitted:** `"%s" can be simplified to "%s", if it is strictly a string, as an empty string is falsey`
**Description:** Empty string are considered false in a boolean context. Following this check blindly in weakly typed code base can create hard to debug issues. If the value can be something else that is falsey but not a string (for example `None`, an empty sequence, or `0`) the code will not be equivalent.
> **Caution:** This message is disabled by default. To enable it, add `use-implicit-booleaness-not-comparison-to-string` to the `enable` option.
**Problematic code:**
def important_string_manipulation(x: str, y: str) -> None: if x == "": # [use-implicit-booleaness-not-comparison-to-string] print("x is an empty string")
if y != "": # [use-implicit-booleaness-not-comparison-to-string] print("y is not an empty string")
**Correct code:**
def important_string_manipulation(x: str, y: str) -> None: if not x: print("x is an empty string")
if y: print("y is not an empty string")
**Configuration file:**
[main] enable=use-implicit-booleaness-not-comparison-to-string
Created by the `refactoring` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/implicit_booleaness_checker.py`).
### **`use-implicit-booleaness-not-comparison-to-zero` / `C1805`** ⭐
**Message emitted:** `"%s" can be simplified to "%s", if it is strictly an int, as 0 is falsey`
**Description:** 0 is considered false in a boolean context. Following this check blindly in weakly typed code base can create hard to debug issues. If the value can be something else that is falsey but not an int (for example `None`, an empty string, or an empty sequence) the code will not be equivalent.
> **Caution:** This message is disabled by default. To enable it, add `use-implicit-booleaness-not-comparison-to-zero` to the `enable` option.
**Problematic code:**
def important_math(x: int, y: int) -> None: if x == 0: # [use-implicit-booleaness-not-comparison-to-zero] print("x is equal to zero")
if y != 0: # [use-implicit-booleaness-not-comparison-to-zero] print("y is not equal to zero")
**Correct code:**
def important_math(x: int, y: int) -> None: if not x: print("x is equal to zero")
if y: print("y is not equal to zero")
**Configuration file:**
[main] enable=use-implicit-booleaness-not-comparison-to-zero
Created by the refactoring checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/implicit_booleaness_checker.py>).
### **`use-implicit-booleaness-not-len` / C1802** 🌸
**Message emitted:** Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty
**Description:** Okay so empty sequences are already falsey in a boolean context — literally. You can either remove the call to `len` (`if not x`) or compare the length against a scalar (`if len(x) > 1`).
**Problematic code:**
fruits = ["orange", "apple"]
if len(fruits): # [use-implicit-booleaness-not-len] print(fruits)
**Correct code:**
fruits = ["orange", "apple"]
if fruits: print(fruits)
Created by the refactoring checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/implicit_booleaness_checker.py>).
### **`use-maxsplit-arg` / C0207** 🎀
**Message emitted:** `Use %s instead`
**Description:** Emitted when accessing only the first or last element of `str.split()`. The first and last element can be accessed by using `str.split(sep, maxsplit=1)[0]` or `str.rsplit(sep, maxsplit=1)[-1]` instead.
**Problematic code:**
url = "www.example.com" suffix = url.split(".")[-1] # [use-maxsplit-arg]
**Correct code:**
url = "www.example.com" suffix = url.rsplit(".", maxsplit=1)[-1]
**Additional details:** Be aware that the performance improvement from not splitting the string so many times will only be realized in cases presenting more instances of the splitting character than the minimal example here. Lowkey the perf glow-up only shows up when your string has way more split characters than this tiny example, bestie.
Created by the refactoring checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/recommendation_checker.py>).
### **`use-sequence-for-iteration` / C0208** 🦋
**Message emitted:** Use a sequence type when iterating over values
**Description:** When iterating over values, sequence types (e.g., `lists`, `tuples`, `ranges`) are more efficient than `sets`.
**Problematic code:**
for food in {"apples", "lemons", "water"}: # [use-sequence-for-iteration] print(f"I like {food}.")
**Correct code:**
`list.py:`
for food in ["apples", "lemons", "water"]: print(f"I like {food}.")
`tuple.py:`
for food in ("apples", "lemons", "water"): print(f"I like {food}.")
**Additional details:** <https://gist.github.com/hofrob/8b1c1e205a0d4c66a680b1fe4bfeba11> — this example script shows a significant increase in performance when using a list, tuple or range over a set in python version 3.11.1.
Created by the refactoring checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/recommendation_checker.py>).
### **`useless-import-alias` / C0414** 💅
**Message emitted:** Import alias does not rename original package
**Description:** Used when an import alias is same as original package, e.g., using `import numpy as numpy` instead of `import numpy as np`.
**Problematic code:**
import pandas as pandas # [useless-import-alias]
**Correct code:**
import pandas as pd
**Additional details:**
#### Known issue 🫶
If you prefer to use "from-as" to explicitly reexport in API (`from fruit import orange as orange`) instead of using `__all__` this message will be a false positive.
Use `--allow-reexport-from-package` to allow explicit reexports by alias in package `__init__` files.
**Related links:**
- `--allow-reexport-from-package` <#imports-options>
- PEP 8, Import Guideline <https://peps.python.org/pep-0008/#imports>
- Pylint block-disable <#block-disables>
- `mypy --no-implicit-reexport` <https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-no-implicit-reexport>
Created by the imports checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py>).
### **`wrong-import-order` / C0411** ✨
**Message emitted:** `%s should be placed before %s`
**Description:** Used when PEP8 import order is not respected (standard imports first, then third-party libraries, then local imports).
**Problematic code:**
import os from . import utils import pylint # [wrong-import-order] import sys # [wrong-import-order]
**Correct code:**
import os import sys
import pylint
from . import utils
Created by the imports checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py>).
### **`wrong-import-position` / C0413** 🌈
**Message emitted:** `Import "%s" should be placed at the top of the module`
**Description:** Used when code and imports are mixed.
**Problematic code:**
import os
home = os.environ["HOME"]
import sys # [wrong-import-position]
print(f"Home directory is {home}", file=sys.stderr)
**Correct code:**
import os import sys
home = os.environ["HOME"] print(f"Home directory is {home}", file=sys.stderr)
Created by the imports checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py>).
### **`wrong-spelling-in-comment` / C0401** 🍓
**Message emitted:** `Wrong spelling of a word '%s' in a comment: %s %s Did you mean: '%s'?`
**Description:** Used when a word in comment is not spelled correctly.
**Problematic code:**
There's a mistkae in this string # [wrong-spelling-in-comment]
**Correct code:**
There's no mistake in this string
**Configuration file:**
[main]
This might not run in your env if you don't have the en_US dict installed.
spelling-dict=en_US
Created by the spelling checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/spelling.py>).
### **`wrong-spelling-in-docstring` / C0402** 🧚
**Message emitted:** `Wrong spelling of a word '%s' in a docstring: %s %s Did you mean: '%s'?`
**Description:** Used when a word in docstring is not spelled correctly.
**Problematic code:**
"""There's a mistkae in this string""" # [wrong-spelling-in-docstring]
**Correct code:**
"""There's no mistake in this string"""
**Configuration file:**
[main]
This might not run in your env if you don't have the en_US dict installed.
spelling-dict=en_US
Created by the spelling checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/spelling.py>).
**All renamed messages in the convention category:**
- **`blacklisted-name` / C0102** — `'blacklisted-name'` has been renamed. The new message can be found at:
- **`compare-to-empty-string` / C1901** — `'compare-to-empty-string'` has been renamed. The new message can be found at:
- **`compare-to-zero` / C2001** — `'compare-to-zero'` has been renamed. The new message can be found at:
- **`len-as-condition` / C1801** — `'len-as-condition'` has been renamed. The new message can be found at:
- **`missing-docstring` / C0111** — `'missing-docstring'` has been renamed. The new message can be found at:
- **`old-misplaced-comparison-constant` / C0122** — `'old-misplaced-comparison-constant'` has been renamed. The new message can be found at:
- **`old-non-ascii-name` / C0144** — `'old-non-ascii-name'` has been renamed. The new message can be found at:
- **`unneeded-not` / C0113** — `'unneeded-not'` has been renamed. The new message can be found at:
## 🔧 Refactor 🔧
All messages in the refactor category:
### **`chained-comparison` / R1716** 💖
**Message emitted:** Simplify chained comparison between the operands
**Description:** This message is emitted when pylint encounters boolean operation like "a < b and b < c", suggesting instead to refactor it to "a < b < c"
**Problematic code:**
a = int(input()) b = int(input()) c = int(input()) if a < b and b < c: # [chained-comparison] pass
**Correct code:**
a = int(input()) b = int(input()) c = int(input()) if a < b < c: pass
Created by the refactoring checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py>).
### **`comparison-of-constants` / R0133** ⭐
**Message emitted:** `Comparison between constants: "%s %s %s" has a constant value`
**Description:** When two literals are compared with each other the result is a constant. Using the constant directly is both easier to read and more performant. Initializing 'True' and 'False' this way is not required since Python 2.3.
**Problematic code:**
def is_the_answer() -> bool: return 42 == 42 # [comparison-of-constants]
**Correct code:**
def is_the_answer(meaning_of_life: int) -> bool: return meaning_of_life == 42
Created by the basic checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/comparison_checker.py>).
### **`comparison-with-itself` / R0124** 🔥
**Message emitted:** `Redundant comparison - %s`
**Description:** Used when something is compared against itself.
**Problematic code:**
def is_an_orange(fruit): an_orange = "orange" return fruit == fruit # [comparison-with-itself]
**Correct code:**
def is_an_orange(fruit): an_orange = "orange" return an_orange == fruit
Created by the basic checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/comparison_checker.py>).
### **`condition-evals-to-constant` / R1727** 👑
**Message emitted:** `Boolean condition '%s' will always evaluate to '%s'`
**Description:** Emitted when a boolean condition can be simplified to a constant value.
**Problematic code:**
def is_a_fruit(fruit): return bool(fruit in {"apple", "orange"} or True) # [condition-evals-to-constant]
**Correct code:**
def is_a_fruit(fruit): return fruit in {"apple", "orange"}
Created by the refactoring checker (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py>).
### **`confusing-consecutive-elif` / R5601** 💀
**Message emitted:** Consecutive elif with differing indentation level, consider creating a function to separate the inner elif
**Description:** Used when an elif statement follows right after an indented block which itself ends with if or elif. It may not be obvious if the elif statement was willingly or mistakenly unindented. Extracting the indented if statement into a separate function might avoid confusion and prevent errors.
**Problematic code:**
def myfunc(shall_continue: bool, shall_exit: bool): if shall_continue: if input("Are you sure?") == "y": print("Moving on.") elif shall_exit: # [confusing-consecutive-elif] print("Exiting.")
**Correct code:**
Option 1 is the “just add an explicit `else`” route, and Option 2 is the “extract a function” route — bestie, they’re both right here:
Option 1: add explicit 'else'
def myfunc(shall_continue: bool, shall_exit: bool): if shall_continue: if input("Are you sure?") == "y": print("Moving on.") else: pass elif shall_exit: print("Exiting.")
Option 2: extract function
def user_confirmation(): if input("Are you sure?") == "y": print("Moving on.")
def myfunc2(shall_continue: bool, shall_exit: bool): if shall_continue: user_confirmation() elif shall_exit: print("Exiting.")
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.confusing_elif
**Additional details:** Creating a function for the nested conditional, or adding an explicit `else` in the indented `if` statement, even if it only contains a `pass` statement, can help clarify the code. 🫶
> <bold>Note:</bold> This message is emitted by the optional `confusing_elif` checker (`<# pylint-extensions-confusing-elif>`), which requires the `pylint.extensions.confusing_elif` plugin to be loaded.
Created by the `confusing_elif` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/confusing_elif.py`).
---
### ✨ consider-alternative-union-syntax / R6003 ✨
**Message emitted:** `Consider using alternative union syntax instead of '%s'%s`
**Description:** Emitted when `typing.Union` or `typing.Optional` is used instead of the shorthand union syntax. For example, `Union[int, float]` instead of `int | float`. Using the shorthand for unions aligns with Python typing recommendations, removes the need for imports, and avoids confusion in function signatures.
**Problematic code:**
from typing import Optional, Union
def forecast( temp: Union[int, float], # [consider-alternative-union-syntax] unit: Optional[str], # [consider-alternative-union-syntax] ) -> None: print(f'Temperature: {temp}{unit or ""}')
**Correct code:**
def forecast(temp: int | float, unit: str | None) -> None: print(f'Temperature: {temp}{unit or ""}')
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.typing
**Additional details:** Using the shorthand syntax for union types is recommended over the `typing` module (`https://docs.python.org/3/library/typing.html#typing.Union`). This is consistent with the broader recommendation to prefer built-in types over imports (for example, using `list` instead of the now-deprecated `typing.List`).
`typing.Optional` can also cause confusion in annotated function arguments, since an argument annotated as `Optional` is still a required argument when a default value is not set. Explicitly annotating such arguments with `type | None` makes the intention clear. 💅
> <bold>Note:</bold> This message is emitted by the optional `typing` checker (`<# pylint-extensions-typing>`), which requires the `pylint.extensions.typing` plugin to be loaded.
Created by the `typing` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/typing.py`).
---
### ✨ consider-math-not-float / R6106 ✨
**Message emitted:** `Consider %smath.%s instead of %s`
**Description:** Using `math.inf` or `math.nan` permits to benefit from typing and it is up to 4 times faster than a `float` call (after the initial import of `math`). This check also catches typos in `float` calls as a side effect.
**Problematic code:**
swag = float("inf") # [consider-math-not-float]
**Correct code:**
import math
swag = math.inf
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.code_style
**Additional details:** This is an extension check because the typing advantage could be fixed.
Regarding performance, `float("nan")` and `float("inf")` are slower than their counterpart `math.inf` and `math.nan` by a factor of 4 after the initial import of `math`.
import math import timeit
time_math_inf = timeit.timeit('math.nan', globals=globals(), number=10**8) print(f'math.nan: {time_math_inf:.2f} seconds')
import timeit time_inf_str = timeit.timeit('float("nan")', number=10**8) print(f'float("nan"): {time_inf_str:.2f} seconds')
**Result:**
math.nan: 1.24 seconds float("nan"): 5.15 seconds
But if we take the initial import into account it’s worse. 😭
import timeit
time_math_inf = timeit.timeit('import math;math.nan', globals=globals(), number=10**8) print(f'math.nan: {time_math_inf:.2f} seconds')
import timeit time_inf_str = timeit.timeit('float("nan")', number=10**8) print(f'float("nan"): {time_inf_str:.2f} seconds')
**Result:**
math.nan: 9.08 seconds float("nan"): 5.33 seconds
So the decision depends on how and how often you need to use it and what matter to you. 🫶
> <bold>Note:</bold> This message is emitted by the optional `code_style` checker (`<# pylint-extensions-code-style>`), which requires the `pylint.extensions.code_style` plugin to be loaded.
Created by the `code_style` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/code_style.py`).
---
### ✨ consider-merging-isinstance / R1701 ✨
**Message emitted:** `Consider merging these isinstance calls to isinstance(%s, (%s))`
**Description:** Used when multiple consecutive `isinstance` calls can be merged into one.
**Problematic code:**
from typing import Any
def is_number(value: Any) -> bool: # +1: [consider-merging-isinstance] return isinstance(value, int) or isinstance(value, float)
**Correct code:**
from typing import Any
def is_number(value: Any) -> bool: return isinstance(value, (int, float))
Created by the `refactoring` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`).
---
### ✨ consider-refactoring-into-while-condition / R3501 ✨
**Message emitted:** `Consider using 'while %s' instead of 'while %s:' an 'if', and a 'break'`
**Description:** Emitted when `while True:` loop is used and the first statement is a break condition. The `if / break` construct can be removed if the check is inverted and moved to the `while` statement.
**Problematic code:**
fruit_basket = ["apple", "orange", "banana", "cherry", "guava"]
while True: # [consider-refactoring-into-while-condition] if len(fruit_basket) == 0: break fruit = fruit_basket.pop() print(f"We removed {fruit} from the basket")
**Correct code:**
fruit_basket = ["apple", "orange", "banana", "cherry", "guava"]
while len(fruit_basket) != 0: fruit = fruit_basket.pop() print(f"We removed {fruit} from the basket")
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.consider_refactoring_into_while_condition
> <bold>Note:</bold> This message is emitted by the optional `'consider_refactoring_into_while'` (`<# pylint-extensions-consider-refactoring-into-while-condition>`) checker, which requires the `pylint.extensions.consider_refactoring_into_while_condition` plugin to be loaded.
Created by the `consider_refactoring_into_while` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/consider_refactoring_into_while_condition.py`).
---
### ✨ consider-swap-variables / R1712 ✨
**Message emitted:** `Consider using tuple unpacking for swapping variables`
**Description:** You do not have to use a temporary variable in order to swap variables. Using `"tuple unpacking"` to directly swap variables makes the intention more clear.
**Problematic code:**
a = 1 b = 2
temp = a # [consider-swap-variables] a = b b = temp
**Correct code:**
a = 1 b = 2
a, b = b, a
Created by the `refactoring` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`).
---
### ✨ consider-using-alias / R6002 ✨
**Message emitted:** `'%s' will be deprecated with PY39, consider using '%s' instead%s`
**Description:** Only emitted if `'runtime-typing=no'` and a deprecated typing alias is used in a type annotation context in Python 3.7 or 3.8.
**Problematic code:**
import typing
cats: typing.Dict[str, int] # [consider-using-alias]
**Correct code:**
import typing
cats: typing.cast(dict[str, int], "string")
**Configuration file:**
[main] load-plugins = pylint.extensions.typing py-version = 3.7 runtime-typing=no
> <bold>Note:</bold> This message is emitted by the optional `typing` checker (`<# pylint-extensions-typing>`), which requires the `pylint.extensions.typing` plugin to be loaded.
Created by the `typing` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/typing.py`).
---
### ✨ consider-using-assignment-expr / R6103 ✨
**Message emitted:** `Use '%s' instead`
**Description:** Emitted when an `if` assignment is directly followed by an `if` statement and both can be combined by using an assignment expression `:=`. Requires Python 3.8 and `py-version >= 3.8`.
**Problematic code:**
apples = 2
if apples: # [consider-using-assignment-expr] print("God apples!")
**Correct code:**
if apples := 2: print("God apples!")
**Configuration file:**
[MAIN] py-version=3.8 load-plugins=pylint.extensions.code_style
> <bold>Note:</bold> This message is emitted by the optional `code_style` checker (`<# pylint-extensions-code-style>`), which requires the `pylint.extensions.code_style` plugin to be loaded.
Created by the `code_style` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/code_style.py`).
---
### ✨ consider-using-augmented-assign / R6104 ✨
**Message emitted:** `Use '%s' to do an augmented assign directly`
**Description:** Emitted when an assignment is referring to the object that it is assigning to. This can be changed to be an augmented assign. Disabled by default!
> <bold>Caution:</bold> This message is disabled by default. To enable it, add `consider-using-augmented-assign` to the `enable` option.
**Problematic code:**
x = 1 x = x + 1 # [consider-using-augmented-assign]
**Correct code:**
x = 1 x += 1
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.code_style enable=consider-using-augmented-assign
> <bold>Note:</bold> This message is emitted by the optional `code_style` checker (`<# pylint-extensions-code-style>`), which requires the `pylint.extensions.code_style` plugin to be loaded.
Created by the `code_style` checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/code_style.py`).
### 💅 consider-using-dict-comprehension / R1717 ✨
**Message emitted:** `Consider using a dictionary comprehension`
**Description:** okay so bestie, pylint spotted a dictionary being made with the `dict()` callable plus a transient list. There's literally nothing syntactically wrong with that code, but it's lowkey hard to read and can be simplified into a dict comprehension. It's also faster, since you don't need to create another transient list. no cap.
**Problematic code:**
NUMBERS = [1, 2, 3]
+1: [consider-using-dict-comprehension]
DOUBLED_NUMBERS = dict([(number, number * 2) for number in NUMBERS])
**Correct code:**
NUMBERS = [1, 2, 3]
DOUBLED_NUMBERS = {number: number * 2 for number in NUMBERS}
**Additional details:** `pyupgrade` (`https://github.com/asottile/pyupgrade`) or `ruff` (`https://docs.astral.sh/ruff/`) can fix this issue automatically.
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 🦋 consider-using-from-import / R0402 🌸
**Message emitted:** `Use 'from %s import %s' instead`
**Description:** this one fires when a submodule of a package is imported and aliased with the same name. Like, instead of `import concurrent.futures as futures`, use `from concurrent import futures`. it's giving cleaner import energy, honestly.
**Problematic code:**
import os.path as path # [consider-using-from-import]
**Correct code:**
from os import path
Created by the `imports` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py`.
---
### 🎀 consider-using-generator / R1728 💖
**Message emitted:** `Consider using a generator instead '%s(%s)'`
**Description:** if your container can be large, using a generator will bring better performance. highkey a glow-up for big data vibes.
**Problematic code:**
list([0 for y in list(range(10))]) # [consider-using-generator] tuple([0 for y in list(range(10))]) # [consider-using-generator] sum([y2 for y in list(range(10))]) # [consider-using-generator] max([y2 for y in list(range(10))]) # [consider-using-generator] min([y**2 for y in list(range(10))]) # [consider-using-generator]
**Correct code:**
list(0 for y in list(range(10))) tuple(0 for y in list(range(10))) sum(y2 for y in list(range(10))) max(y2 for y in list(range(10))) min(y**2 for y in list(range(10)))
**Additional details:** removing `[]` inside calls that can use containers or generators should be considered for performance reasons, since a generator will have an upfront cost to pay. The performance will be better if you are working with long lists or sets.
For `max`, `min` and `sum`, using a generator is also recommended by `pep289`.
**Related links:**
- PEP 289 — `https://peps.python.org/pep-0289/`
- Benchmark and discussion for `any`/`all`/`list`/`tuple` — `https://github.com/pylint-dev/pylint/pull/3309#discussion_r576683109`
- Benchmark and discussion for `sum`/`max`/`min` — `https://github.com/pylint-dev/pylint/pull/6595#issuecomment-1125704244`
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 🌈 consider-using-get / R1715 🫶
**Message emitted:** `Consider using dict.get for getting values from a dict if a key is present or a default if not`
**Description:** using the builtin `dict.get` to get a value from a dictionary if a key is present, or a default if not, is simpler and considered more idiomatic. sometimes it's a bit slower, though — bestie, it's a tradeoff.
**Problematic code:**
knights = {"Gallahad": "the pure", "Robin": "the brave"}
if "Gallahad" in knights: # [consider-using-get] DESCRIPTION = knights["Gallahad"] else: DESCRIPTION = ""
**Correct code:**
knights = {"Gallahad": "the pure", "Robin": "the brave"}
description = knights.get("Gallahad", "")
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### ⭐ consider-using-in / R1714 💅
**Message emitted:** `Consider merging these comparisons with 'in' by using '%s %sin (%s)'. Use a set instead if elements are hashable.`
**Description:** to check if a variable is equal to one of many values, combine the values into a set or tuple and check if the variable is contained `"in"` it, instead of checking for equality against each of the values. it's faster and less verbose. periodt.
**Problematic code:**
def fruit_is_round(fruit): # +1: [consider-using-in] return fruit == "apple" or fruit == "orange" or fruit == "melon"
**Correct code:**
def fruit_is_round(fruit): return fruit in {"apple", "orange", "melon"}
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 🧚 consider-using-join / R1713 🍓
**Message emitted:** `Consider using str.join(sequence) for concatenating strings from an iterable`
**Description:** using `str.join(sequence)` is faster, uses less memory, and increases readability compared to for-loop iteration. it's giving optimized string energy.
**Problematic code:**
def fruits_to_string(fruits): formatted_fruit = "" for fruit in fruits: formatted_fruit += fruit # [consider-using-join] return formatted_fruit
print(fruits_to_string(["apple", "pear", "peach"]))
**Correct code:**
print("".join(["apple", "pear", "peach"]))
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 👑 consider-using-max-builtin / R1731 🔥
**Message emitted:** `Consider using '%s' instead of unnecessary if block`
**Description:** using the `max` builtin instead of a conditional improves readability and conciseness. iconic behavior, honestly.
**Problematic code:**
def get_max(value1, value2): if value1 < value2: # [consider-using-max-builtin] value1 = value2 return value1
print(get_max(1, 2))
**Correct code:**
print(max(1, 2))
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 💜 consider-using-min-builtin / R1730 🌸
**Message emitted:** `Consider using '%s' instead of unnecessary if block`
**Description:** using the `min` builtin instead of a conditional improves readability and conciseness. main character energy for real.
**Problematic code:**
def get_min(value1, value2): if value1 > value2: # [consider-using-min-builtin] value1 = value2 return value1
print(get_min(1, 2))
**Correct code:**
print(min(1, 2))
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 🦋 consider-using-namedtuple-or-dataclass / R6101 🎀
**Message emitted:** `Consider using namedtuple or dataclass for dictionary values`
**Description:** emitted when dictionary values can be replaced by namedtuples or dataclass instances. it's basically saying your dict values could be serving more structure, bestie.
**Problematic code:**
FELIDAES = { # [consider-using-namedtuple-or-dataclass] "The queen's cymric, fragile furry friend": { "tail_length_cm": 1, "paws": 4, "eyes": 2, "Elizabethan collar": 1, }, "Rackat the red, terror of the sea": { "tail_length_cm": 13, "paws": 3, "eyes": 1, "Red Hat": 1, }, }
**Correct code:**
from typing import NamedTuple
class FelidaeCharacteristics(NamedTuple): tail_length_cm: int paws: int eyes: int hat: str | None
FELIDAES = { "The queen's cymric, fragile furry friend": FelidaeCharacteristics( tail_length_cm=1, paws=4, eyes=2, hat="Elizabethan collar" ), "Rackat the red, terror of the sea": FelidaeCharacteristics( tail_length_cm=21, paws=3, eyes=1, hat="Red Hat" ), }
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.code_style
**Note:** this message is emitted by the optional `code_style` checker (see `#pylint-extensions-code-style`), which requires the `pylint.extensions.code_style` plugin to be loaded.
Created by the `code_style` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/code_style.py`.
---
### ✨ consider-using-set-comprehension / R1718 💖
**Message emitted:** `Consider using a set comprehension`
**Description:** although there is nothing syntactically wrong with this code, it is hard to read and can be simplified to a set comprehension. also it is faster since you don't need to create another transient list. slay.
**Problematic code:**
NUMBERS = [1, 2, 2, 3, 4, 4]
+1: [consider-using-set-comprehension]
UNIQUE_EVEN_NUMBERS = set([number for number in NUMBERS if number % 2 == 0])
**Correct code:**
NUMBERS = [1, 2, 2, 3, 4, 4]
UNIQUE_EVEN_NUMBERS = {number for number in NUMBERS if number % 2 == 0}
**Additional details:** `pyupgrade` (`https://github.com/asottile/pyupgrade`) or `ruff` (`https://docs.astral.sh/ruff/`) can fix this issue automatically.
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 🌟 consider-using-sys-exit / R1722 💅
**Message emitted:** `Consider using 'sys.exit' instead`
**Description:** contrary to `exit()` or `quit()`, `sys.exit` does not rely on the `site` module being available (as the `sys` module is always available). it's giving reliable exit energy.
**Problematic code:**
if __name__ == "__main__": user = input("Enter user name: ") print(f"Hello, {user}") exit(0) # [consider-using-sys-exit]
**Correct code:**
import sys
if __name__ == "__main__": user = input("Enter user name: ") print(f"Hello, {user}") sys.exit(0)
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 🌸 consider-using-ternary / R1706 🦋
**Message emitted:** `Consider using ternary (%s)`
**Description:** used when one of known pre-python 2.5 ternary syntax is used. bestie, this is ancient vibes.
**Problematic code:**
x, y = 1, 2 maximum = x >= y and x or y # [consider-using-ternary]
**Correct code:**
x, y = 1, 2 maximum = x if x >= y else y
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`.
---
### 🎀 consider-using-tuple / R6102 💜
**Message emitted:**
### `consider-using-tuple` 🍎
**Consider using an in-place tuple instead of list**
**Description:**
Ok so bestie, this one is *only* for style consistency! It gets emitted when you've got an in-place defined `list` that could totally be swapped for a `tuple`. And before you come for me — no, there is **no performance benefit** from doing this, because CPython already optimises that. It's purely a vibe thing ✨
**Problematic code:**
for i in [1, 2, 3]: # [consider-using-tuple] print(i)
**Correct code:**
for i in (1, 2, 3): print(i)
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.code_style
> **Note:** this message is emitted by the optional `code_style` checker, which requires the `pylint.extensions.code_style` plugin to be loaded. No plugin, no message — that's the deal 🫶
Created by the `code_style` checker → <cyan>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/code_style.py</cyan>
---
### `consider-using-with` / R1732 🧚
**Message emitted:**
**Consider using 'with' for resource-allocating operations**
**Description:**
This one fires when a resource-allocating assignment or call could be replaced by a `with` block. The whole point? Using `with` makes *sure* the resources get released — even if an exception gets thrown. Safety slay 💅
**Problematic code:**
`close.py`:
file = open("apple.txt", "r", encoding="utf8") # [consider-using-with] contents = file.read() file.close()
`not_even_close.py`:
contents = open("apple.txt", "r", encoding="utf8").read() # [consider-using-with]
**Correct code:**
with open("apple.txt", "r", encoding="utf8") as file: contents = file.read()
**Additional details:**
So if you call `write()` *without* the `with` keyword, or you call `close()`, there's a real risk that the arguments of `write()` don't get completely written to disk — even if your program exits all successfully 😭 Yeah. Care.
This message applies to callables of Python's stdlib which can be replaced by a `with` statement. And it gets **suppressed** in these cases:
- the call is located inside a context manager
- the call result is returned from the enclosing function
- the call result is used in a `with` statement itself
**Related links:**
- Python doc: Reading and writing files → <cyan>https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files</cyan>
- PEP 343 → <cyan>https://peps.python.org/pep-0343/</cyan>
- Context managers in Python by John Lekberg → <cyan>https://johnlekberg.com/blog/2020-10-11-ctx-manage.html</cyan>
- Rationale → <cyan>https://stackoverflow.com/a/73181877/2519059</cyan>
Created by the `refactoring` checker → <purple>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py</purple>
---
### `cyclic-import` / R0401 🌀
**Message emitted:**
**Cyclic import (%s)**
**Description:**
Used when a cyclic import between two or more modules is detected. It's giving "who imported who first" but make it a paradox 🙈
**Problematic code:**
`__init__.py`:
`bad.py`:
def count_to_one(): return 1
def count_to_three(): from .bad2 import count_to_two
return count_to_two() + 1
`bad2.py`:
from .bad import count_to_one # [cyclic-import]
def count_to_two(): return count_to_one() + 1
**Correct code:**
def count_to_one(): return 1
def count_to_two(): return count_to_one() + 1
def count_to_three(): return count_to_two() + 1
**Additional details:**
Ok real talk — that good code is *just an example*. There are various strategies to resolving cyclic imports, and the best choice relies heavily on the context of the code and the affected modules. So don't come at me like it's the only way, iykyk 🫶
Created by the `imports` checker → <blue>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/imports.py</blue>
---
### `duplicate-code` / R0801 👯
**Message emitted:**
**Similar lines in %s files %s**
**Description:**
Indicates that a set of similar lines has been detected among multiple file. This usually means that the code should be refactored to avoid this duplication. Twin behavior, but make it a code smell 💀
**Problematic code:**
`__init__.py`:
`apple.py`:
class Apple: def __init__(self): self.remaining_bites = 3
def take_bite(self): if self.remaining_bites > 0: print("You take a bite of the apple.") self.remaining_bites -= 1 else: print("The apple is already eaten up!")
def eaten_by_animal(self, animal): self.remaining_bites = 0 print("The apple has been eaten by an animal.")
`orange.py`:
class Orange: # [duplicate-code] def __init__(self): self.remaining_bites = 3
def take_bite(self): if self.remaining_bites > 0: print("You take a bite of the apple.") self.remaining_bites -= 1 else: print("The orange is already eaten up!")
def eaten_by_animal(self, animal): if animal == "cat": raise ValueError("A cat would never do that !") self.remaining_bites = 0 print("The orange has been eaten by an animal.")
**Correct code:**
`__init__.py`:
`apple.py`:
from fruit import Fruit
class Apple(Fruit): ...
`fruit.py`:
class Fruit: def __init__(self): self.remaining_bites = 3
def take_bite(self): if self.remaining_bites > 0: print(f"You take a bite of the {self.__class__.__name__.lower()}.") self.remaining_bites -= 1 else: print(f"The {self.__class__.__name__.lower()} is already eaten up!")
def eaten_by_animal(self, animal): self.remaining_bites = 0 print(f"The {self.__class__.__name__.lower()} has been eaten by an animal.")
`orange.py`:
from fruit import Fruit
class Orange(Fruit): def eaten_by_animal(self, animal): if animal == "cat": raise ValueError("A cat would never do that !") super().eaten_by_animal(animal)
**Additional details:**
So here's why dupes are a whole problem, bestie: if you need to make a change to the logic or functionality of the duplicated code, you will need to identify **all** the places that need to be changed — which can be time-consuming and error-prone 😭 And if there are multiple copies of the same code, then you will also need to test each copy to ensure that the functionality is correct.
Duplicate code can also be confusing for someone who is trying to understand the logic and flow of the code if they come across multiple identical or nearly identical blocks of code. The reader can then skim and think something is identical when it actually isn't — which is *particularly* true during review 👀
Created by the `similarities` checker → <mint>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/symilar.py</mint>
---
### `else-if-used` / R5501 🪜
**Message emitted:**
**Consider using "elif" instead of "else" then "if" to remove one indentation level**
**Description:**
Used when an `else` statement is immediately followed by an `if` statement and does not contain statements that would be unrelated to it. Basically: you're nesting for no reason, babe, flatten it ✨
**Problematic code:**
if input(): pass else: if len(input()) >= 10: # [else-if-used] pass else: pass
**Correct code:**
if input(): pass elif len(input()) >= 10: pass else: pass
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.check_elif
> **Note:** this message is emitted by the optional `else_if_used` checker, which requires the `pylint.extensions.check_elif` plugin to be loaded 🫶
Created by the `else_if_used` checker → <lavender>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/check_elif.py</lavender>
---
### `empty-comment` / R2044 🕳️
**Message emitted:**
**Line with empty comment**
**Description:**
Used when a `#` symbol appears on a line not followed by an actual comment. Like, why is the hashtag there? It's giving placeholder energy and nobody's buying 🙈
**Problematic code:**
+1:[empty-comment]
#
+1:[empty-comment]
x = 0 #
**Correct code:**
comment
x = 0 # comment
**Configuration file:**
[main] load-plugins=pylint.extensions.empty_comment
> **Note:** this message is emitted by the optional `empty-comment` checker, which requires the `pylint.extensions.empty_comment` plugin to be loaded 💖
Created by the `empty-comment` checker → <peach>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/empty_comment.py</peach>
---
### `inconsistent-return-statements` / R1710 🎭
**Message emitted:**
**Either all return statements in a function should return an expression, or none of them should.**
**Description:**
According to PEP8, if any `return` statement returns an expression, any `return` statements where no value is returned should explicitly state this as `return None`, and an explicit `return` statement should be present at the end of the function (if reachable). Pick a lane, bestie — all in or all out 💅
**Problematic code:**
def get_the_answer(value: str) -> str | None: # [inconsistent-return-statements] if value: return value
**Correct code:**
def get_the_answer(value: str) -> str | None: if value: return value return None
Created by the `refactoring` checker → <purple>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py</purple>
---
### `literal-comparison` / R0123 ⚖️
**Message emitted:**
**In '%s', use '%s' when comparing constant literals not '%s' ('%s')**
**Description:**
Used when comparing an object to a literal — which is usually what you do *not* want to do, since you can compare to a different literal than what was expected altogether 😭 Like, the comparison might "work" and still be about something completely else. Not the flex you think it is.
**Problematic code:**
def is_an_orange(fruit): return fruit is "orange" # [literal-comparison]
**Correct code:**
def is_an_orange(fruit): return fruit == "orange"
**Related links:**
- Comparison operations in Python → <cyan>https://docs.python.org/3/library/stdtypes.html#comparisons</cyan>
Created by the `basic` checker → <blue>https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/base/comparison_checker.py</blue>
---
### `magic-value-comparison` / R2004 🔮
**Message emitted:**
**Consider using a named constant or an enum instead of '%s'.**
**Description:**
Using named constants instead of magic values helps improve readability and maintainability of your code — so yeah, try to avoid them in comparisons. Random numbers appearing out of nowhere is *not* the main character energy we're going for ✨
**Problematic code:**
import random
measurement = random.randint(0, 200) above_threshold = False i = 0 while i < 5: # [magic-value-comparison] above_threshold = measurement > 100 # [magic-value-comparison] if above_threshold: break measurement = random.randint(0, 200)
(the glow-up version of this one is right around the corner, don't you worry 💅)
<pink>**Correct code**</pink> (yep, this is the glow-up for that last one):
import random
MAX_NUM_OF_ITERATIONS = 5 THRESHOLD_VAL = 100 MIN_MEASUREMENT_VAL = 0 MAX_MEASUREMENT_VAL = 200
measurement = random.randint(MIN_MEASUREMENT_VAL, MAX_MEASUREMENT_VAL) above_threshold = False i = 0 while i < MAX_NUM_OF_ITERATIONS: above_threshold = measurement > THRESHOLD_VAL if above_threshold: break measurement = random.randint(MIN_MEASUREMENT_VAL, MAX_MEASUREMENT_VAL)
**Configuration file:**
[main] load-plugins=pylint.extensions.magic_value
> <purple>Note:</purple> this message is emitted by the optional <cyan>'magic-value'</cyan> checker, which requires the `pylint.extensions.magic_value` plugin to be loaded. No plugin, no message, bestie 💅
Created by the <hotpink>magic-value</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/magic_value.py`).
---
### 💀 match-class-bind-self / R1905
**Message emitted:**
`Use '%s() as %s' instead`
**Description:**
Match class patterns are *so* much faster if the name binding happens for the whole pattern and any lookup for `__match_args__` can be avoided. Speed, slay ✨
**Problematic code:**
class Book: __match_args__ = ("title", "year")
def __init__(self, title, year): self.title = title self.year = year
def func(item: Book): match item: case Book(title=str(title)): # [match-class-bind-self] ... case Book(year=int(year)): # [match-class-bind-self] ...
**Correct code:**
class Book: __match_args__ = ("title", "year")
def __init__(self, title, year): self.title = title self.year = year
def func(item: Book): match item: case Book(title=str() as title): ... case Book(year=int() as year): ...
**Related links:**
- Python documentation — `https://docs.python.org/3/reference/compound_stmts.html#class-patterns`
Created by the <hotpink>match_statements</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/match_statements_checker.py`).
---
### 🦋 match-class-positional-attributes / R1906
**Message emitted:**
`Use keyword attributes instead of positional ones (%s)`
**Description:**
Keyword attributes are more explicit and slightly faster since CPython can skip the `__match_args__` lookup. Like, less work = more speed, periodt 🏃♀️💨
**Problematic code:**
class Book: __match_args__ = ("title", "year")
def __init__(self, title, year): self.title = title self.year = year
def func(item: Book): match item: case Book("abc", 2000): # [match-class-positional-attributes] ...
**Correct code:**
class Book: __match_args__ = ("title", "year")
def __init__(self, title, year): self.title = title self.year = year
def func(item: Book): match item: case Book(title="abc", year=2000): ...
**Related links:**
- Python documentation — `https://docs.python.org/3/reference/compound_stmts.html#class-patterns`
Created by the <hotpink>match_statements</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/match_statements_checker.py`).
---
### 🎀 no-classmethod-decorator / R0202
**Message emitted:**
`Consider using a decorator instead of calling classmethod`
**Description:**
Used when a class method is defined without using the decorator syntax. Girl, just use the `@` — it's giving elegance 💅
**Problematic code:**
class Fruit: COLORS = []
def __init__(self, color): self.color = color
def pick_colors(cls, *args): """classmethod to pick fruit colors""" cls.COLORS = args
pick_colors = classmethod(pick_colors) # [no-classmethod-decorator]
**Correct code:**
class Fruit: COLORS = []
def __init__(self, color): self.color = color
@classmethod def pick_colors(cls, *args): """classmethod to pick fruit colors""" cls.COLORS = args
Created by the <hotpink>classes</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`).
---
### 🌸 no-else-break / R1723
**Message emitted:**
`Unnecessary "%s" after "break", %s`
**Description:**
Used in order to highlight an unnecessary block of code following an if containing a `break` statement. As such, it will warn when it encounters an `else` following a chain of `if`s, all of them containing a `break` statement. Basically the `else` is doing absolutely nothing, main character energy wasted 😭
**Problematic code:**
def next_seven_elements(iterator): for i, item in enumerate(iterator): if i == 7: # [no-else-break] break else: yield item
**Correct code:**
def next_seven_elements(iterator): for i, item in enumerate(iterator): if i == 7: break yield item
Created by the <hotpink>refactoring</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`).
---
### 🍓 no-else-continue / R1724
**Message emitted:**
`Unnecessary "%s" after "continue", %s`
**Description:**
Used in order to highlight an unnecessary block of code following an if containing a `continue` statement. As such, it will warn when it encounters an `else` following a chain of `if`s, all of them containing a `continue` statement. Same vibe as the last one, different keyword 💀
**Problematic code:**
def even_number_under(n: int): for i in range(n): if i % 2 == 1: # [no-else-continue] continue else: yield i
**Correct code:**
def even_number_under(n: int): for i in range(n): if i % 2 == 1: continue yield i
Created by the <hotpink>refactoring</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`).
---
### 🔥 no-else-raise / R1720
**Message emitted:**
`Unnecessary "%s" after "raise", %s`
**Description:**
Used in order to highlight an unnecessary block of code following an if, or a `try`/`except` containing a `raise` statement. As such, it will warn when it encounters an `else` following a chain of `if`s, all of them containing a `raise` statement. The `else` is extra. We don't do extra 🙈
**Problematic code:**
def integer_sum(a: int, b: int) -> int: if not (isinstance(a, int) and isinstance(b, int)): # [no-else-raise] raise ValueError("Function supports only integer parameters.") else: return a + b
**Correct code:**
def integer_sum(a: int, b: int) -> int: if not (isinstance(a, int) and isinstance(b, int)): raise ValueError("Function supports only integer parameters.") return a + b
Created by the <hotpink>refactoring</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`).
---
### ⭐ no-else-return / R1705
**Message emitted:**
`Unnecessary "%s" after "return", %s`
**Description:**
Used in order to highlight an unnecessary block of code following an if, or a `try`/`except` containing a `return` statement. As such, it will warn when it encounters an `else` following a chain of `if`s, all of them containing a `return` statement. The classic. The icon. The one you've definitely seen before 👑
**Problematic code:**
def compare_numbers(a: int, b: int) -> int: if a == b: # [no-else-return] return 0 elif a < b: return -1 else: return 1
**Correct code:**
def compare_numbers(a: int, b: int) -> int: if a == b: return 0 if a < b: return -1 return 1
Created by the <hotpink>refactoring</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`).
---
### 🧚 no-self-use / R6301
**Message emitted:**
`Method could be a function`
**Description:**
Used when a method doesn't use its bound instance, and so could be written as a function. Like bestie… you're not even using `self`, why are you in this class 😭
**Problematic code:**
class Person: def greeting(self): # [no-self-use] print("Greetings pythonista!")
**Correct code:**
`function.py`:
def greeting(): print("Greetings pythonista!")
`staticmethod.py`:
class Person: @staticmethod def greeting(): print("Greetings pythonista!")
`use_self.py`:
class Person: name: str = "Amelia"
def greeting(self): print(f"Greetings {self.name} the pythonista!")
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.no_self_use,
(yeah the trailing comma is literally there in the original, leave it alone 💅)
**Additional details:**
If a function is not using any class attribute it can be a `@staticmethod`, or a function outside the class. Both are iconic choices, pick your fave 🫶
> <purple>Note:</purple> this message is emitted by the optional <cyan>'no_self_use'</cyan> checker, which requires the `pylint.extensions.no_self_use` plugin to be loaded.
Created by the <hotpink>no_self_use</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/no_self_use.py`).
---
### 🎀 no-staticmethod-decorator / R0203
**Message emitted:**
`Consider using a decorator instead of calling staticmethod`
**Description:**
Used when a static method is defined without using the decorator syntax. Twin of the classmethod one — same energy, different decorator ✨
**Problematic code:**
class Worm: def bore(self): pass
bore = staticmethod(bore) # [no-staticmethod-decorator]
**Correct code:**
class Worm: @staticmethod def bore(self): pass
Created by the <hotpink>classes</hotpink> checker (`https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`).
---
### 💜 prefer-typing-namedtuple / R6105
**Message emitted:**
`Prefer 'typing.NamedTuple' over 'collections.namedtuple'`
**Description:**
`'typing.NamedTuple'` uses the well-known `'class'` keyword with type-hints for readability (it's also faster as it avoids an internal `exec` call). Disabled by default! Like, it's not even on unless you turn it on, iykyk 🙈
> <red>Caution:</red> this message is disabled by default. To enable it, add `prefer-typing-namedtuple` to the `enable` option.
**Problematic code:**
from collections import namedtuple
Philosophy = namedtuple( # [prefer-typing-namedtuple] "Philosophy", ("goodness", "truth", "beauty") )
**Correct code:**
from typing import NamedTuple
class Philosophy(NamedTuple): goodness: str truth: bool beauty: float
**Configuration file:**
[MAIN] load-plugins = pylint.extensions.code_style
**Related links:**
- `typing.NamedTuple` — `https://docs.python.org/3/library/typing.html#typing.NamedTuple`
> **Note:** this one is emitted by the optional `code_style` checker (anchor: `#pylint-extensions-code-style`) — it literally requires the `pylint.extensions.code_style` plugin to be loaded, no cap. 💅
Created by the `code_style` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/code_style.py`
### property-with-parameters / R0206
**Message emitted:** `Cannot have defined parameters for properties`
**Description:** this fires when a property also has parameters, which are useless because properties cannot be called with extra arguments. It’s giving “why are those params even here” energy. 🙈
Problematic code:
class Worm: @property def bore(self, depth): # [property-with-parameters] pass
Correct code:
class Worm: @property def bore(self): """Property accessed with '.bore'.""" pass
def bore_with_depth(depth): """Function called with .bore_with_depth(depth).""" pass
Created by the `classes` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/classes/class_checker.py`
### redefined-argument-from-local / R1704
**Message emitted:** `Redefining argument with the local name %r`
**Description:** used when a local name is redefining an argument, which might suggest a potential error. This is taken into account only for a handful of name binding operations, such as `for` iteration, `with` statement assignment and exception handler assignment. 👀
Problematic code:
def show(host_id=10.11): # +1: [redefined-argument-from-local] for host_id, host in [[12.13, "Venus"], [14.15, "Mars"]]: print(host_id, host)
Correct code:
def show(host_id=10.11): for inner_host_id, host in [[12.13, "Venus"], [14.15, "Mars"]]: print(host_id, inner_host_id, host)
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`
### redefined-variable-type / R0204
**Message emitted:** `Redefinition of %s type from %s to %s`
**Description:** used when the type of a variable changes inside a method or a function. Bestie, pick a lane. 😭
Problematic code:
x = 1 x = "2" # [redefined-variable-type]
Correct code:
x = 1 x = 2
Configuration file:
[MAIN] load-plugins=pylint.extensions.redefined_variable_type,
> **Note:** this message is emitted by the optional `multiple_types` checker (anchor: `#pylint-extensions-redefined-variable-type`), which requires the `pylint.extensions.redefined_variable_type` plugin to be loaded. 🫶
Created by the `multiple_types` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/redefined_variable_type.py`
### redundant-typehint-argument / R6006
**Message emitted:**
Type %s is used more than once in union type annotation. Remove redundant typehints.
**Description:** duplicated type arguments will be skipped by the `mypy` tool, so they should be removed to avoid confusion. It’s giving “why are you repeating yourself in the type hint” vibes. 💀
Problematic code:
from typing import Union
sweet_count: Union[int, str, int] = 42 # [redundant-typehint-argument]
Correct code:
from typing import Union
sweet_count: Union[str, int] = 42
Configuration file:
[main] load-plugins=pylint.extensions.typing
> **Note:** this message is emitted by the optional `typing` checker (anchor: `#pylint-extensions-typing`), which requires the `pylint.extensions.typing` plugin to be loaded. ✨
Created by the `typing` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/typing.py`
### simplifiable-condition / R1726
**Message emitted:** `Boolean condition "%s" may be simplified to "%s"`
**Description:** emitted when a boolean condition is able to be simplified. Lowkey a glow-up opportunity for your condition. 🌸
Problematic code:
def has_apples(apples) -> bool: return bool(apples or False) # [simplifiable-condition]
Correct code:
def has_apples(apples) -> bool: return bool(apples)
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`
### simplifiable-if-expression / R1719
**Message emitted:** `The if expression can be replaced with %s`
**Description:** used when an if expression can be replaced with `bool(test)` or simply `test` if the boolean cast is implicit. So iconic when code gets shorter and clearer. 👑
Problematic code:
FLYING_THINGS = ["bird", "plane", "superman", "this example"]
def is_flying_thing(an_object): return True if an_object in FLYING_THINGS else False # [simplifiable-if-expression]
def is_not_flying_thing(an_object): return False if an_object in FLYING_THINGS else True # [simplifiable-if-expression]
Correct code:
FLYING_THINGS = ["bird", "plane", "superman", "this example"]
def is_flying_thing(an_object): return an_object in FLYING_THINGS
def is_not_flying_thing(an_object): return an_object not in FLYING_THINGS
**Related links:**
- Simplifying an `'if'` statement with `bool()` — `https://stackoverflow.com/questions/49546992/`
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`
### simplifiable-if-statement / R1703
**Message emitted:** `The if statement can be replaced with %s`
**Description:** used when an if statement can be replaced with `bool(test)`. It’s the same vibe as the previous one but for statements, bestie. 🦋
Problematic code:
FLYING_THINGS = ["bird", "plane", "superman", "this example"]
def is_flying_animal(an_object): # +1: [simplifiable-if-statement] if isinstance(an_object, Animal) and an_object in FLYING_THINGS: is_flying = True else: is_flying = False return is_flying
Correct code:
FLYING_THINGS = ["bird", "plane", "superman", "this example"]
def is_flying_animal(an_object): is_flying = isinstance(an_object, Animal) and an_object.name in FLYING_THINGS return is_flying
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`
### simplify-boolean-expression / R1709
**Message emitted:** `Boolean expression may be simplified to %s`
**Description:** emitted when redundant pre-python 2.5 ternary syntax is used. Ma’am, this is a Wendy’s, not 2004. 💀
Problematic code:
def has_oranges(oranges, apples=None) -> bool: return apples and False or oranges # [simplify-boolean-expression]
Correct code:
def has_oranges(oranges, apples=None) -> bool: return oranges
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`
### stop-iteration-return / R1708
**Message emitted:** `Do not raise StopIteration in generator, use return statement instead`
**Description:** according to PEP479, the raise of `StopIteration` to end the loop of a generator may lead to hard to find bugs. This PEP specifies that raising `StopIteration` has to be replaced by a simple `return` statement. Fr fr, this one matters. 🚨
Problematic code:
`fruit_generator.py:`
def fruit_generator(): for fruit in ["apple", "banana"]: yield fruit raise StopIteration # [stop-iteration-return]
`two_fruit_generator.py:`
def two_fruits_generator(fruits): for fruit in fruits: yield fruit, next(fruits) # [stop-iteration-return]
`two_good_fruit_generator.py:`
def two_good_fruits_generator(fruits): for fruit in fruits: if not fruit.is_tasty(): continue while True: next_fruit = next(fruits) # [stop-iteration-return] if next_fruit.is_tasty(): yield fruit, next_fruit break
Correct code:
`fruit_generator.py:`
def fruit_generator(): """The example is simple enough you don't need an explicit return.""" for fruit in ["apple", "banana"]: yield fruit
`two_fruit_generator.py:`
def two_fruits_generator(fruits): """Catching the StopIteration.""" for fruit in fruits: try: yield fruit, next(fruits) except StopIteration: print("Sorry there is only one fruit left.") yield fruit, None
`two_good_fruit_generator.py:`
def two_good_fruits_generator(fruits): """A return can be used to end the iterator early, but not a StopIteration.""" for fruit in fruits: if not fruit.is_tasty(): continue while True: next_fruit = next(fruits, None) if next_fruit is None: print("Sorry there is only one fruit left.") yield fruit, None # We reached the end of the 'fruits' generator but raising a # StopIteration instead of returning would create a RuntimeError return if next_fruit.is_tasty(): yield fruit, next_fruit break
**Additional details:** it’s possible to give a default value to `next` or catch the `StopIteration`, or return directly. A `StopIteration` cannot be propagated from a generator. Periodt. 💅
**Related links:**
- PEP 479 — `https://peps.python.org/pep-0479/`
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`
### super-with-arguments / R1725
**Message emitted:** `Consider using Python 3 style super() without arguments`
**Description:** emitted when calling the `super()` builtin with the current class and instance. On Python 3 these arguments are the default and they can be omitted. It’s giving “we don’t do that anymore” energy. ✨
Problematic code:
class Fruit: pass
class Orange(Fruit): def __init__(self): super(Orange, self).__init__() # [super-with-arguments]
Correct code:
class Fruit: pass
class Orange(Fruit): def __init__(self): super().__init__()
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py`
### too-complex / R1260
**Message emitted:** `%s is too complex. The McCabe rating is %d`
**Description:**
so like — this one fires when a method or function is too complex based on McCabe Complexity Cyclomatic. yes bestie, the math kind of complex 💅
**Problematic code:**
def fifty_percent_off(whole): return (float(whole)) * 50 / 100
def calculate_sum_and_display_price_of_fruits(*fruits): # [too-complex] # McCabe rating is 13 here (by default 10) shopping_list = []
if "apple" in fruits: v = fifty_percent_off(1.1) shopping_list.append(v) if "pear" in fruits: shopping_list.append(0.8) if "banana" in fruits: shopping_list.append(1.2) if "mango" in fruits: shopping_list.append(3.5) if "peach" in fruits: shopping_list.append(0.5) if "melon" in fruits: shopping_list.append(4.9) if "orange" in fruits: shopping_list.append(2.0) if "strawberry" in fruits: shopping_list.append(2.5) if "mandarin" in fruits: shopping_list.append(2.3) if "plum" in fruits: shopping_list.append(0.5) if "watermelon" in fruits: v = fifty_percent_off(6.4) shopping_list.append(v)
combine = zip(fruits, shopping_list)
for i in combine: print(f"{i[0]} ${i[1]:.2f}")
total = sum(shopping_list) print(f"Total price is ${total:.2f}")
fruits_to_buy = ["apple", "orange", "watermelon"] calculate_sum_and_display_price_of_fruits(*fruits_to_buy)
**Correct code:**
FRUIT_PRICES = { "apple": 1.1, "pear": 0.8, "banana": 1.2, "mango": 3.5, "peach": 0.5, "melon": 4.9, "orange": 2.0, "strawberry": 2.5, "mandarin": 2.3, "plum": 0.5, "watermelon": 6.4, } DISCOUNTED_FRUITS = ["apple", "watermelon"]
def fifty_percent_off(whole): return (float(whole)) * 50 / 100
def get_price(fruit): full_price = FRUIT_PRICES.get(fruit) if fruit in DISCOUNTED_FRUITS: return fifty_percent_off(full_price) else: return full_price
def display_fruit_and_price(fruits): for fruit in fruits: print(f"{fruit} ${get_price(fruit) :.2f}")
def get_total(fruits): return sum(get_price(f) for f in fruits)
fruits_to_buy = ["apple", "orange", "watermelon"] display_fruit_and_price(fruits_to_buy) print(f"Total price is ${get_total(fruits_to_buy):.2f}")
**Configuration file:**
[main]
load-plugins=pylint.extensions.mccabe
> ok important 🫶 this message is emitted by the optional `'design'` checker (iykyk, the `pylint-extensions-mccabe` one), and it **requires the `pylint.extensions.mccabe` plugin to be loaded.** no plugin, no message, periodt.
Created by the `design` checker: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/mccabe.py`
---
### 🐛 too-few-public-methods / R0903
**Message emitted:**
Too few public methods (%s/%s)
**Description:**
This one goes off when a class has too few public methods, so like… be sure it's really worth it, bestie. is it even a class or should it be a function 💀
**Problematic code:**
class Worm: # [too-few-public-methods] def __init__(self, name: str, fruit_of_residence: Fruit): self.name = name self.fruit_of_residence = fruit_of_residence
def bore(self): print(f"{self.name} is boring into {self.fruit_of_residence}")
**Correct code:**
`dataclass_and_function.py`:
import dataclasses
@dataclasses.dataclass class Worm: name: str fruit_of_residence: Fruit
def bore(worm: Worm): print(f"{worm.name} is boring into {worm.fruit_of_residence}")
`function.py`:
def bore(fruit: Fruit, worm_name: str): print(f"{worm_name} is boring into {fruit}")
`larger_api.py`:
class Worm: def __init__(self, name: str, fruit_of_residence: Fruit): self.name = name self.fruit_of_residence = fruit_of_residence
def bore(self): print(f"{self.name} is boring into {self.fruit_of_residence}")
def wiggle(self): print(f"{self.name} wiggle around wormily.")
Created by the `design` checker: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py`
---
### 🌳 too-many-ancestors / R0901
**Message emitted:**
Too many ancestors (%s/%s)
**Description:**
Fires when a class has too many parent classes — try to reduce this to get a simpler (and so easier to use) class. like girl, your family tree is not a personality ✨
**Problematic code:**
class Animal: ... class BeakyAnimal(Animal): ... class FurryAnimal(Animal): ... class Swimmer(Animal): ... class EggLayer(Animal): ... class VenomousAnimal(Animal): ... class ProtectedSpecie(Animal): ... class BeaverTailedAnimal(Animal): ... class Vertebrate(Animal): ...
max of 7 by default, can be configured
each edge of a diamond inheritance counts
class Playtypus( # [too-many-ancestors] BeakyAnimal, FurryAnimal, Swimmer, EggLayer, VenomousAnimal, ProtectedSpecie, BeaverTailedAnimal, Vertebrate, ): pass
**Correct code:**
class Animal: beaver_tailed: bool can_swim: bool has_beak: bool has_fur: bool has_vertebrae: bool lays_egg: bool protected_specie: bool venomous: bool
class Invertebrate(Animal): has_vertebrae = False
class Vertebrate(Animal): has_vertebrae = True
class Mammal(Vertebrate): has_beak = False has_fur = True lays_egg = False venomous = False
class Playtypus(Mammal): beaver_tailed = True can_swim = True has_beak = True lays_egg = True protected_specie = True venomous = True
Created by the `design` checker: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py`
---
### 🎀 too-many-arguments / R0913
**Message emitted:**
Too many arguments (%s/%s)
**Description:**
Used when a function or method takes too many arguments. bestie if your signature needs a scroll bar that's a vibe check fail 😭
**Problematic code:**
def three_d_chess_move( # [too-many-arguments] x_white, y_white, z_white, piece_white, x_black, y_black, z_black, piece_black, x_blue, y_blue, z_blue, piece_blue, current_player, ): pass
**Correct code:**
from dataclasses import dataclass
@dataclass class ThreeDChessPiece: x: int y: int z: int type: str
def three_d_chess_move( white: ThreeDChessPiece, black: ThreeDChessPiece, blue: ThreeDChessPiece, current_player, ): pass
Created by the `design` checker: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py`
---
### 🧮 too-many-boolean-expressions / R0916
**Message emitted:**
Too many boolean expressions in if statement (%s/%s)
**Description:**
Used when an `if` statement contains too many boolean expressions. the `and`-spam is real 🙈
**Problematic code:**
def can_be_divided_by_two_and_are_not_zero(x, y, z): # Maximum number of boolean expressions in an if statement (by default 5) # +1: [too-many-boolean-expressions] if (x and y and z) and (x % 2 == 0 and y % 2 == 0 and z % 2 == 0): pass
**Correct code:**
def can_be_divided_by_two_and_are_not_zero(x, y, z): if all(i and i % 2 == 0 for i in [x, y, z]): pass
Created by the `design` checker: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py`
---
### 🌿 too-many-branches / R0912
**Message emitted:**
Too many branches (%s/%s)
**Description:**
Used when a function or method has too many branches, making it hard to follow. nobody can track that many plot twists, bestie 💀
**Problematic code:**
def num_to_word(x): # [too-many-branches] if x == 0: return "zero" elif x == 1: return "one" elif x == 2: return "two" elif x == 3: return "three" elif x == 4: return "four" elif x == 5: return "five" elif x == 6: return "six" elif x == 7: return "seven" elif x == 8: return "eight" elif x == 9: return "nine" else: return None
**Correct code:**
def num_to_word(x): return { 0: "zero", 1: "one", 2: "two", 3: "three", 4: "four", 5: "five", 6: "six", 7: "seven", 8: "eight", 9: "nine", }.get(x)
**Configuration file:**
[main] max-branches=10
Created by the `design` checker: `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py`
---
### 🍎 too-many-instance-attributes / R0902
**Message emitted:**
Too many instance attributes (%s/%s)
**Description:**
Used when class has too many instance attributes — try to reduce this to get a simpler (and so easier to use) class. the `self.` collection is not a cute little haul, bestie 🎀
**Problematic code:**
class Fruit: # [too-many-instance-attributes] def __init__(self): # max of 7 attributes by default, can be configured self.worm_name = "Jimmy" self.worm_type = "Codling Moths" self.worm_color = "light brown" self.fruit_name = "Little Apple" self.fruit_color = "Bright red" self.fruit_vitamins = ["A", "B1"] self.fruit_antioxidants = None self.secondary_worm_name = "Kim" self.secondary_worm_type = "Apple maggot" self.secondary_worm_color = "Whitish"
**Correct code:**
import dataclasses
@dataclasses.dataclass class Worm: name: str type: str color: str
class Fruit: def __init__(self): self.name = "Little Apple" self.color = "Bright red" self.vitamins = ["A", "B1"] self.antioxidants = None self.worms = [ Worm(name="Jimmy", type="Codling Moths", color="light brown"), Worm(name="Kim", type="Apple maggot", color="Whitish"), ]
Created by the design checker — <cyan>`pylint/checkers/design_analysis.py`</cyan> 💅 (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py>)
---
### 🍬 `too-many-locals` / `R0914`
**Message emitted:**
Too many local variables (%s/%s)
**Description:**
Fires when a function or method is hoarding way too many local variables, bestie. Like, *so* many locals. 🫣
**Problematic code:**
from childhood import Child, Sweet
def handle_sweets(infos): # [too-many-locals] # Create children children = [Child(info) for info in infos] number_of_sweets = 87 sweets = [Sweet() * number_of_sweets] number_of_sweet_per_child = 5 money = 45.0 sweets_given = 0 time_to_eat_sweet = 54 price_of_sweet = 0.42 # distribute sweet for child in children: sweets_given += number_of_sweet_per_child child.give(sweets[number_of_sweet_per_child:]) # calculate prices cost_of_children = sweets_given * price_of_sweet # Calculate remaining money remaining_money = money - cost_of_children # Calculate time it took time_it_took_assuming_parallel_eating = ( time_to_eat_sweet * number_of_sweet_per_child ) print( f"{children} ate {cost_of_children}¤ of sweets in {time_it_took_assuming_parallel_eating}, " f"you still have {remaining_money}" )
**Correct code:**
from typing import NamedTuple
from childhood import Child, Sweet
class SweetDistrubutionCharacteristics(NamedTuple): number_of_sweets: int number_of_sweet_per_child: int number_of_children: int
@property def sweets_given(self): return self.number_of_sweet_per_child * self.number_of_children
def handle_sweets(infos): children = [Child(info) for info in infos] characteristics = SweetDistrubutionCharacteristics(87, 5, len(children)) _allocate_sweets_to_children(children, characteristics) financial_impact = _assess_financial_impact(characteristics) print(f"{children} ate {financial_impact}")
def _allocate_sweets_to_children( children, characteristics: SweetDistrubutionCharacteristics ) -> None: sweets = [Sweet() * characteristics.number_of_sweets] for child in children: child.give(sweets[characteristics.number_of_sweet_per_child :])
def _assess_financial_impact(characteristics: SweetDistrubutionCharacteristics) -> str: time_to_eat_sweet = 54 money = 45.0 price_of_sweet = 0.42 cost_of_children = characteristics.sweets_given * price_of_sweet remaining_money = money - cost_of_children time_it_took_assuming_parallel_eating = ( time_to_eat_sweet * characteristics.number_of_sweet_per_child ) return ( f"{cost_of_children}¤ of sweets in " f"{time_it_took_assuming_parallel_eating}, you still have {remaining_money}" )
**Configuration file:**
[design] max-locals = 11
**Additional details:**
Okay so when you've got a *million* locals, that's the universe gently telling you your function is doing too much 😭 and that maybe you should've made some classes to group those attributes together. Like, could some of these operations live in their own separate functions? And fr fr, ask yourself: are all these variables actually *closely related*? Be honest. 👀
Created by the design checker — <cyan>`pylint/checkers/design_analysis.py`</cyan> (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py>)
---
### 🪆 `too-many-nested-blocks` / `R1702`
**Message emitted:**
Too many nested blocks (%s/%s)
**Description:**
Fires when a function or method has too many nested blocks going on — and like, it makes the code less understandable and less maintainable. It's giving "inception but make it a codebase." 💀
**Problematic code:**
def correct_fruits(fruits): if len(fruits) > 1: # [too-many-nested-blocks] if "apple" in fruits: if "orange" in fruits: count = fruits["orange"] if count % 2: if "kiwi" in fruits: if count == 2: return True return False
**Correct code:**
def correct_fruits(fruits): if len(fruits) > 1 and "apple" in fruits and "orange" in fruits: count = fruits["orange"] if count % 2 and "kiwi" in fruits and count == 2: return True return False
Created by the refactoring checker — <cyan>`pylint/checkers/refactoring/refactoring_checker.py`</cyan> (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py>)
---
### 🎯 `too-many-positional-arguments` / `R0917`
**Message emitted:**
Too many positional arguments (%s/%s)
**Description:**
Fires when a function has too many positional arguments. Yes, there *is* such a thing as too many. 💅
**Problematic code:**
# +1: [too-many-positional-arguments] def calculate_drag_force(velocity, area, density, drag_coefficient): """Each argument is positional-or-keyword unless a / or * is present.""" return 0.5 * drag_coefficient * density * area * velocity**2
drag_force = calculate_drag_force(30, 2.5, 1.225, 0.47)
**Correct code:**
def calculate_drag_force(, velocity, area, density, drag_coefficient): """This function is 'Keyword only' for all args due to the ''.""" return 0.5 * drag_coefficient * density * area * velocity**2
# This is now impossible to do and will raise a TypeError: # drag_force = calculate_drag_force(30, 2.5, 1.225, 0.47) # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # TypeError: calculate_drag_force() takes 0 positional arguments but 4 were given
# And this is the only way to call 'calculate_drag_force' drag_force = calculate_drag_force( velocity=30, area=2.5, density=1.225, drag_coefficient=0.47 )
**Configuration file:**
[DESIGN] max-positional-arguments=3
**Additional details:**
Good function signatures don't have many positional parameters — that's just the tea ☕. For almost *all* interfaces, comprehensibility starts suffering beyond a handful of arguments.
But ok, positional arguments genuinely slap in cases where the use cases are self-evident — like unittest's `assertEqual(first, second, "assert msg")` or `zip(fruits, vegetables)`. Iconic, no notes. ✨
And there are a few exceptions where four or more positional parameters make total sense — for example `rgba(1.0, 0.5, 0.3, 1.0)`, because that uses a very well-known and well-established convention, and forcing keywords all the time would just be a waste of time. Periodt.
**Related links:**
- Special parameters in python — <https://docs.python.org/3/tutorial/controlflow.html#special-parameters>
Created by the design checker — <cyan>`pylint/checkers/design_analysis.py`</cyan> (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py>)
---
### 🛸 `too-many-public-methods` / `R0904`
**Message emitted:**
Too many public methods (%s/%s)
**Description:**
Fires when a class has too many public methods. Try reducing this so you get a simpler (and therefore easier to use) class — your future self will thank you 🫶
**Problematic code:**
class SpaceInvaders: # [too-many-public-methods] def __init__(self): pass
def fire_laser_beam(self): pass
def deploy_shield(self): pass
def launch_missile(self): pass
def activate_super_laser(self): pass
def summon_mothership(self): pass
def destroy_planet(self): pass
def teleport(self): pass
def invoke_aliens(self): pass
def invade_earth(self): pass
def takeover_galaxy(self): pass
**Correct code:**
class LaserBeam: def __init__(self): pass
def fire(self): pass
def activate_super(self): pass
def destroy_planet(self): pass
class Shield: def deploy(self): pass
class Missile: def launch(self): pass
class SpaceInvaders: def __init__(self): self.laser = LaserBeam() self.shield = Shield() self.missile = Missile()
def summon_mothership(self): pass
def destroy_planet(self): pass
def teleport(self): pass
def invoke_aliens(self): pass
def invade_earth(self): pass
def takeover_galaxy(self): pass
**Configuration file:**
[main] max-public-methods=7
**Additional details:**
Too many public methods is basically a vibe check fail — it's an indication that you might not be respecting the Single-responsibility principle (the S of SOLID). 🧚
A class should have only one reason to change, but in that example the spaceship has at least 4 people who could ask for changes to it (laser manager, shield manager, missile manager, teleportation officer...). Like, pick a lane bestie 😭
**Related links:**
- Single-responsibility principle — <https://en.wikipedia.org/wiki/Single-responsibility_principle>
Created by the design checker — <cyan>`pylint/checkers/design_analysis.py`</cyan> (<https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py>)
---
### 🔁 `too-many-return-statements` / `R0911`
**Message emitted:**
Too many return statements (%s/%s)
**Description:**
Fires when a function or method has too many return statements, making it hard to follow. It's giving "where does this even end" energy 💀
**Problematic code:**
def to_string(x): # [too-many-return-statements] # max of 6 by default, can be configured if x == 1: return "This is one." if x == 2: return "This is two." if x == 3: return "This is three." if x == 4: return "This is four." if x == 5: return "This is five." if x == 6: return "This is six." if x == 7: return "This is seven."
**Correct code:**
NUMBERS_TO_STRINGS = { 1: "one", 2: "two", 3: "three", 4: "four", 5: "five", 6: "six", 7: "seven", }
def to_string(x): return f"This is {NUMBERS_TO_STRINGS.get(x)}."
Created by the `design` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py` ✨
---
### `too-many-statements` / `R0915`
Message emitted:
Too many statements (%s/%s)
Description:
Fires when a function or method is doing *way* too much — too many statements, bestie. The move is to split it into smaller functions / methods. Your future self will literally thank you 💖
Problematic code:
import random
def distribute_candies( # [too-many-statements] children: list[Child], candies_per_child: int ): # This function is a masterpiece of code that embodies the epitome of efficiency # it's also an essential part of a high-priority project with extremely tight deadlines # and there is absolutely no time to refactor it to make it more concise. # The lead developer on the project, who has decades of experience, # has personally reviewed this implementation and deemed it good enough as it is. # The person writing this code has a demanding job and multiple responsibilities, # and simply does not have the luxury of spending time making this code more readable. total_candies = len(children) * candies_per_child eaten_candies = 0 # Counting candies given to each child for child in children: # If a child eat more than 1 candies they're going to eat all # the candies for sure eaten_for_child = random.choices([0, 1, candies_per_child]) print( f"Child {child} gets {candies_per_child} candies and eat {eaten_for_child}" ) remaining_candies_for_children = child.eat_candies(eaten_for_child) if remaining_candies_for_children == 0: print(f"All the candies have been devoured by {child.name}!") else: print( f"{child.name} still have {remaining_candies_for_children} candies left." ) eaten_candies += eaten_for_child return eaten_candies, total_candies
Correct code:
import random
def distribute_candies(children: list[Child], candies_per_child: int): total_candies = len(children) * candies_per_child eaten_candies = 0 for child in children: eaten_candies += _distribute_candies_to_child(candies_per_child, child) return eaten_candies, total_candies
def _distribute_candies_to_child(candies_per_child: int, child: Child): # If a child eat more than 1 candies they're going to eat all # the candies for sure eaten_for_child = random.choices([0, 1, candies_per_child]) print(f"Child {child} gets {candies_per_child} candies and eat {eaten_for_child}") remaining_candies_for_children = child.eat_candies(eaten_for_child) if remaining_candies_for_children == 0: print(f"All the candies have been devoured by {child.name}!") else: print(f"{child.name} still have {remaining_candies_for_children} candies left.") return eaten_for_child
Configuration file:
[DESIGN] max-statements=7
Created by the `design` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/design_analysis.py` 🎀
---
### `trailing-comma-tuple` / `R1707`
Message emitted:
Disallow trailing comma tuple
Description:
Okay so this is a classic Python plot twist 😭 In Python, a tuple is actually created by the **comma** symbol, not by the parentheses. Which means one misplaced trailing comma and boom — you've accidentally made a tuple, and that's exactly the kind of sneaky bug that haunts you at 3am. Always use parentheses explicitly when you're creating a tuple. No cap.
Problematic code:
COMPASS = "north", "south", "east", "west", # [trailing-comma-tuple]
Correct code:
COMPASS = ("north", "south", "east", "west")
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py` 🦋
---
### `unnecessary-comprehension` / `R1721`
Message emitted:
Unnecessary use of a comprehension, use %s instead.
Description:
If you're writing an identity comprehension, like, *why* bestie 💅 Just use the list, dict or set constructor instead — it's faster and simpler. It's giving "doing the most for no reason."
Problematic code:
NUMBERS = [1, 1, 2, 2, 3, 3]
UNIQUE_NUMBERS = {number for number in NUMBERS} # [unnecessary-comprehension]
Correct code:
NUMBERS = [1, 1, 2, 2, 3, 3]
UNIQUE_NUMBERS = set(NUMBERS)
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py` ✨
---
### `unnecessary-default-type-args` / `R6007`
Message emitted:
Type %s has unnecessary default type args. Change it to %s.
Description:
Emitted when types have default type args that can just be omitted. It's mainly for `typing.Generator` and `typing.AsyncGenerator` 🌸
Problematic code:
from collections.abc import AsyncGenerator, Generator
a1: AsyncGenerator[int, None] # [unnecessary-default-type-args] b1: Generator[int, None, None] # [unnecessary-default-type-args]
Correct code:
from collections.abc import AsyncGenerator, Generator
a1: AsyncGenerator[int] b1: Generator[int]
Configuration file:
[main] load-plugins=pylint.extensions.typing
Additional details:
Right now, this check only works for `Generator` and `AsyncGenerator` 👑
Starting with Python 3.13, the `SendType` and `ReturnType` default to `None`. So it's no longer necessary to specify them. The `collections.abc` variants don't validate the number of type arguments — therefore the defaults for these can be used in earlier versions as well.
Related links:
- Python documentation for AsyncGenerator — `https://docs.python.org/3.13/library/typing.html#typing.AsyncGenerator`
- Python documentation for Generator — `https://docs.python.org/3.13/library/typing.html#typing.Generator`
> Note: This message is emitted by the optional `typing` (`#pylint-extensions-typing`) checker, which requires the `pylint.extensions.typing` plugin to be loaded. Don't forget to load it bestie, or this check literally won't show up 🙈
Created by the `typing` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/extensions/typing.py` 🧚
---
### `unnecessary-dict-index-lookup` / `R1733`
Message emitted:
Unnecessary dictionary index lookup, use '%s' instead
Description:
Emitted when you're iterating over dictionary items (key-item pairs) and then grabbing the value by index lookup. Bestie, the value is *right there* — access it directly 😭
Problematic code:
FRUITS = {"apple": 1, "orange": 10, "berry": 22}
for fruit_name, fruit_count in FRUITS.items(): print(FRUITS[fruit_name]) # [unnecessary-dict-index-lookup]
Correct code:
FRUITS = {"apple": 1, "orange": 10, "berry": 22}
for fruit_name, fruit_count in FRUITS.items(): print(fruit_count)
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py` 🍓
---
### `unnecessary-list-index-lookup` / `R1736`
Message emitted:
Unnecessary list index lookup, use '%s' instead
Description:
Emitted when you're iterating over an enumeration and then accessing the value by index lookup. Same vibes as the dict one — the value can be accessed directly instead 💖
Problematic code:
letters = ["a", "b", "c"]
for index, letter in enumerate(letters): print(letters[index]) # [unnecessary-list-index-lookup]
Correct code:
letters = ["a", "b", "c"]
for index, letter in enumerate(letters): print(letter)
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py` 🌈
---
### `use-a-generator` / `R1729`
Message emitted:
Use a generator instead '%s(%s)'
Description:
A comprehension inside `any`, `all`, `max`, `min` or `sum` is unnecessary. A generator would be sufficient *and* faster — it's giving efficiency queen 👑
Problematic code:
from random import randint
all([randint(-5, 5) > 0 for _ in range(10)]) # [use-a-generator] any([randint(-5, 5) > 0 for _ in range(10)]) # [use-a-generator]
Correct code:
from random import randint
all(randint(-5, 5) > 0 for _ in range(10)) any(randint(-5, 5) > 0 for _ in range(10))
Additional details:
By using a generator you can cut the execution tree and exit directly at the first element that is `False` for `all` or `True` for `any`, instead of calculating all the elements. Except in the worst possible case where you still need to evaluate everything (all values are `True` for `all`, or all values are false for `any`), performance will be better 🔥
Related links:
- PEP 289 – Generator Expressions — `https://peps.python.org/pep-0289/`
- Benchmark and discussion during initial implementation — `https://github.com/pylint-dev/pylint/pull/3309#discussion_r576683109`
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py` ⭐
---
### `use-dict-literal` / `R1735`
Message emitted:
Consider using '%s' instead of a call to 'dict'.
Description:
Emitted when you use `dict()` to create a dictionary instead of a literal `{ ... }`. The literal is faster because it avoids an extra function call, and we love a free glow-up 💅
Problematic code:
`empty_dict.py`:
empty_dict = dict() # [use-dict-literal]
`init_dict_from_another.py`:
original_dict = {"name": "Sunny", "age": 10, "favorite_color": "yellow"} copied_dict = dict(**original_dict) # [use-dict-literal]
`init_with_keyword.py`:
response_dict = dict(answer="No") # [use-dict-literal]
Correct code:
`empty_dict.py`:
empty_dict = {}
`init_dict_from_another.py`:
original_dict = {"name": "Sunny", "age": 10, "favorite_color": "yellow"}
shallow copy a dict
copied_dict = {**original_dict}
`init_with_literal.py`:
response_dict = {"answer": "No"}
Additional details:
`https://gist.github.com/hofrob/ad143aaa84c096f42489c2520a3875f9`
This example script shows an 18% increase in performance when using a literal over the constructor in python version 3.10.6 👀
Related links:
- Performance Analysis of Python’s `dict()` vs dict literal — `https://madebyme.today/blog/python-dict-vs-curly-brackets/`
Created by the `refactoring` checker — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/refactoring/refactoring_checker.py` 💜
---
### `use-list-literal` / `R1734`
Message emitted:
Consider using [] instead of list()
Description:
Emitted when you use `list()` to create an empty list instead of the literal `[]`. The literal is faster since it skips an extra function call — iconic behavior, honestly ✨
Problematic code:
empty_list = list() # [use-list-literal]
empty_list = []
Created by the `refactoring` checker (`pylint/checkers/refactoring/refactoring_checker.py`). ✨
---
### 💅 `use-set-for-membership` / `R6201`
**Message emitted:** `Consider using set for membership test`
**Description:** membership tests are *so* much more efficient when you run them against a lookup-optimized datatype like `sets`. Like, don't make your code do extra cardio for no reason, bestie.
**Problematic code:**
def fruit_is_dangerous_for_cat(fruit: str) -> bool: """This list is only a silly example, don't make decision regarding your cat diet based on it.""" return fruit in ["cherry", "grapes"] # [use-set-for-membership]
**Correct code:**
def fruit_is_dangerous_for_cat(fruit: str) -> bool: """This list is only a silly example, don't make decision regarding your cat diet based on it.""" return fruit in {"cherry", "grapes"}
**Configuration file:**
[MAIN] load-plugins=pylint.extensions.set_membership
> 🫶 **Note:** this message comes from the *optional* `set_membership` checker, which means you literally have to load the `pylint.extensions.set_membership` plugin first or it simply will not show up. Periodt.
Created by the `set_membership` checker (`pylint/extensions/set_membership.py`). 🍓
---
### 🌈 `use-yield-from` / `R1737`
**Message emitted:** `Use 'yield from' directly instead of yielding each element one by one`
**Description:** yielding straight from the iterator is faster and, ngl, arguably cleaner code than looping over each element and yielding them one by one. It's giving "why are you doing this manually."
**Problematic code:**
def bad_yield_from(generator): for item in generator: # [use-yield-from] yield item
**Correct code:**
def good_yield_from(generator): yield from generator
**Additional details:**
`yield from` is basically removing the middleman — that middleman being *your* `for` loop — between the function caller and the generator they asked for. This means the caller can talk to the generator **directly** (think `send()`). That whole conversation is just not possible when you're manually yielding each element one at a time in a loop. 💀
PEP 380 talks about the possibility of adding optimizations specific to `yield from`. As of the time of writing, it looks like those have not been implemented. Even without those optimizations though, the snippet below shows `yield from` is marginally faster:
$ python3 -m timeit "def yield_from(): yield from range(100)" "for _ in yield_from(): pass" 100000 loops, best of 5: 2.44 usec per loop $ python3 -m timeit "def yield_loop():" " for item in range(100): yield item" "for _ in yield_loop(): pass" 100000 loops, best of 5: 2.49 usec per loop
**Related links:**
- PEP 380 — `https://peps.python.org/pep-0380/`
Created by the `refactoring` checker (`pylint/checkers/refactoring/refactoring_checker.py`). 🦋
---
### 👑 `useless-object-inheritance` / `R0205`
**Message emitted:** `Class %r inherits from object, can be safely removed from bases in python3`
**Description:** fires when a class inherits from `object`, which under python3 is implicit — so it can be safely yeeted from the bases list.
**Problematic code:**
class Banana(object): # [useless-object-inheritance] ...
**Correct code:**
class Banana: ...
Created by the `classes` checker (`pylint/checkers/classes/class_checker.py`). 🎀
---
### ⭐ `useless-return` / `R1711`
**Message emitted:** `Useless return at end of function or method`
**Description:** emitted when a single `"return"` or `"return None"` statement shows up at the end of a function or method definition. You can safely remove it, because Python will just implicitly return `None` anyway. It's literally doing nothing, bestie.
**Problematic code:**
import sys
def print_python_version(): # [useless-return] print(sys.version) return None
**Correct code:**
import sys
def print_python_version(): print(sys.version)
Created by the `refactoring` checker (`pylint/checkers/refactoring/refactoring_checker.py`). 💜
---
**All renamed messages in the refactor category:**
### `old-no-self-use` / `R0201`
`'old-no-self-use'` has been renamed. The new message can be found at:
### `old-simplifiable-if-statement` / `R0102`
`'old-simplifiable-if-statement'` has been renamed. The new message can be found at:
### `old-too-many-nested-blocks` / `R0101`
`'old-too-many-nested-blocks'` has been renamed. The new message can be found at:
> 🙈 okay so the manpage literally just trails off there — "can be found at:" and then nothing. That's what it says, I'm not making it up. Iykyk.
---
## 💬 Information
All messages in the information category:
### 🌸 `bad-inline-option` / `I0010`
**Message emitted:** `Unable to consider inline option %r`
**Description:** used when an inline option is either badly formatted or flat-out can't be used inside modules. Like it just doesn't belong there.
> ⚠️ **Caution:** this message is **disabled by default**. To enable it, add `bad-inline-option` to the `enable` option.
> ⚠️ **Caution:** by default, this message will **not** fail the execution (pylint will return `0`). To make pylint fail for this message, use the `--fail-on=I0010` option, or `--fail-on=I` to fail on all enabled informational messages.
**Problematic code:**
2:[bad-inline-option]
pylint: disable line-too-long
**Correct code:**
pylint: disable=line-too-long
Created by the `main` checker (`pylint/lint/pylinter.py`). ✨
---
### 🦋 `c-extension-no-member` / `I1101`
**Message emitted:** `%s %r has no %r member%s, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.`
**Description:** used when a variable is accessed for a non-existent member of a C extension. Because the source isn't available, static analysis is impossible — but it *can* be done by introspecting living objects at run-time. Spooky but valid.
> ⚠️ **Caution:** by default, this message will **not** fail the execution (pylint will return `0`). To make pylint fail for this message, use the `--fail-on=I1101` option, or `--fail-on=I` to fail on all enabled informational messages.
**Additional details:**
`c-extension-no-member` is basically the informational variant of `no-member`, and it exists to encourage allowing introspection of C extensions the way it's described on the `no-member` page (`https://pylint.readthedocs.io/en/latest/user_guide/messages/error/no-member.html`).
Created by the `typecheck` checker (`pylint/checkers/typecheck.py`). 🧚
---
### 🎀 `deprecated-pragma` / `I0022`
**Message emitted:** `Pragma "%s" is deprecated, use "%s" instead`
**Description:** some inline pylint options got renamed or reworked, and only the most recent form should be used. **NOTE:** `skip-all` is only available with pylint >= `0.26`. Yes, that old.
> ⚠️ **Caution:** this message is **disabled by default**. To enable it, add `deprecated-pragma` to the `enable` option.
> ⚠️ **Caution:** by default, this message will **not** fail the execution (pylint will return `0`). To make pylint fail for this message, use the `--fail-on=I0022` option, or `--fail-on=I` to fail on all enabled informational messages.
**Problematic code:**
pylint: disable-msg=eval-used # [deprecated-pragma]
**Correct code:**
pylint: disable = eval-used
Created by the `main` checker (`pylint/lint/pylinter.py`). 🍓
---
### 💀 `file-ignored` / `I0013`
**Message emitted:** `Ignoring entire file`
**Description:** used to inform you that the file will not be checked. At all. Not even a little.
> ⚠️ **Caution:** this message is **disabled by default**. To enable it, add `file-ignored` to the `enable` option.
> ⚠️ **Caution:** by default, this message will **not** fail the execution (pylint will return `0`). To make pylint fail for this message, use the `--fail-on=I0013` option, or `--fail-on=I` to fail on all enabled informational messages.
**Problematic code:**
pylint: skip-file
-1: [file-ignored]
**Correct code:**
*(nothing, bestie — it's empty in the original too)* 😭
**Additional details:**
There are no checks at all for a file if it starts with `# pylint: skip-file`. Like, zero. The whole vibe check is skipped.
Created by the `main` checker (`pylint/lint/pylinter.py`). 🫶
---
### 🌈 `locally-disabled` / `I0011`
**Message emitted:** `Locally disabling %s (%s)`
**Description:** used when an inline option disables a message, or a whole messages category.
> ⚠️ **Caution:** this message is **disabled by default**. To enable it, add `locally-disabled` to the `enable` option.
> ⚠️ **Caution:** by default, this message will **not** fail the execution (pylint will return `0`). To make pylint fail for this message, use the `--fail-on=I0011` option, or `--fail-on=I` to fail on all enabled informational messages.
**Problematic code:**
def wizard_spells(spell_book): # pylint: disable=maybe-no-member # [locally-disabled] for spell in spell_book: print(f"Abracadabra! {spell}.")
spell_list = ["Levitation", "Invisibility", "Fireball", "Teleportation"] wizard_spells(spell_list)
**Correct code:**
def wizard_spells(spell_book): for spell in spell_book: print(f"Abracadabra! {spell}.")
spell_list = ["Levitation", "Invisibility", "Fireball", "Teleportation"] wizard_spells(spell_list)
Created by the `main` checker (`pylint/lint/pylinter.py`). 🔥
---
### ⭐ `raw-checker-failed` / `I0001`
**Message emitted:** `Unable to run raw checkers on built-in module %s`
**Description:** used to inform you that a built-in module hasn't been checked using the raw checkers.
> ⚠️ **Caution:** this message is **disabled by default**. To enable it, add `raw-checker-failed` to the `enable` option.
> ⚠️ **Caution:** by default, this message will **not** fail the execution (pylint will return `0`). To make pylint fail for this message, use the `--fail-on=I0001` option, or `--fail-on=I` to fail on all enabled informational messages.
**Additional details:**
This is warning you that a builtin module was impossible to analyse (an ast node isn't pure python). There's nothing to change in *your* code — this is a warning about astroid and pylint's limitations, not about you. Don't take it personally. 💅
Created by the `main` checker (`pylint/lint/pylinter.py`). 🌸
---
### 🦋 `suppressed-message` / `I0020`
**Message emitted:** `Suppressed %s (from line %d)`
**Description:** a message was triggered on a line, but got suppressed explicitly by a `disable=` comment in the file. Heads up: this message is **not** generated for messages that are ignored due to configuration settings.
> ⚠️ **Caution:** this message is **disabled by default**. To enable it, add `suppressed-message` to the `enable` option.
> <yellow>⚠️</yellow> **Caution:** bestie, by default this message will **not** fail the execution (pylint will return `0`). To make pylint fail for this message use the `--fail-on=I0020` option or `--fail-on=I` to fail on all enabled informational messages.
**Problematic code:**
This is a contrived example, to show how suppressed-message works.
First we enable all messages
pylint: enable=all
Here we disable two messages so we get two warnings
pylint: disable=locally-disabled, useless-suppression # [suppressed-message, suppressed-message]
Here we disable a message, so we get a warning for suppressed-message again.
"A" # pylint: disable=pointless-statement # [suppressed-message, suppressed-message]
**Correct code:**
"""Instead of a single string somewhere in the file, write a module docstring!"""
**Additional details:**
`suppressed-message` is simply a way to see messages that would be raised *without* the disable in your codebase. It should not be activated most of the time. See also `useless-suppression` if you want to see the message that are disabled for no reasons.
Created by the main checker 🫶 — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py`
---
## 🦋 `use-symbolic-message-instead` / `I0023`
**Message emitted:**
%s
**Description:**
Used when a message is enabled or disabled by id. <pink>iykyk</pink> — use the name, not the number, okay?
> **Caution:** this message is disabled by default. To enable it, add `use-symbolic-message-instead` to the enable option. ✨
> **Caution:** by default, this message will not fail the execution (pylint will return `0`). To make pylint fail for this message use the `--fail-on=I0023` option or `--fail-on=I` to fail on all enabled informational messages.
**Problematic code:**
fruit_name = "plum"
pylint: disable-next=W0621
def eat(fruit_name: str): # [use-symbolic-message-instead] ...
**Correct code:**
fruit_name = "plum"
pylint: disable-next=redefined-outer-name
def eat(fruit_name: str): ...
Created by the miscellaneous checker 🧚 — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/checkers/misc.py`
---
## 💀 `useless-suppression` / `I0021`
**Message emitted:**
Useless suppression of %s
**Description:**
Reported when a message is explicitly disabled for a line or a block of code, but never triggered. <lavender>like bringing an umbrella and the sky is literally clear</lavender> 😭
> **Caution:** this message is disabled by default. To enable it, add `useless-suppression` to the enable option.
> **Caution:** by default, this message will not fail the execution (pylint will return `0`). To make pylint fail for this message use the `--fail-on=I0021` option or `--fail-on=I` to fail on all enabled informational messages.
**Problematic code:**
fruit_counter = 0
pylint: disable-next=redefined-outer-name
def eat(fruit_name: str): # [useless-suppression] ...
**Correct code:**
fruit_counter = 0
def eat(fruit_name: str): ...
Created by the main checker 🫶 — `https://github.com/pylint-dev/pylint/blob/main/build/lib/pylint/lint/pylinter.py`
---
### 🔁 All renamed messages in the information category:
## `deprecated-disable-all` / `I0014`
`'deprecated-disable-all'` has been renamed. The new message can be found at: <cyan>(that's it, that's the whole entry — the rename notice points elsewhere)</cyan>
---
# 🎀 Messages control 🎀
In order to control messages, pylint accepts the following values:
- a symbolic message: `no-member`, `undefined-variable` etc.
- a numerical ID: `E1101`, `E1102` etc.
- The name of the group of checks. You can grab those with `pylint --list-groups`. For example, you can disable / enable all the checks related to type checking, with `typecheck` or all the checks related to variables with `variables` 👑
- Corresponding category of the checks:
- `C` convention related checks
- `R` refactoring related checks
- `W` various warnings
- `E` errors, for probable bugs in the code
- `F` fatal, if an error occurred which prevented pylint from doing further processing.
- All the checks with `all` ✨
---
## 🚧 Block disables 🚧
This describes how the pragma controls operate at a code level.
The pragma controls can disable / enable:
- All the violations on a single line:
a, b = ... # pylint: disable=unbalanced-tuple-unpacking
- All the violations on the following line:
pylint: disable-next=unbalanced-tuple-unpacking
a, b = ...
- All the violations in a single scope:
def test(): # Disable all the no-member violations in this function # pylint: disable=no-member ...
- All the violations in a block. For instance, each separate branch of an `if` statement is considered a separate block, as in the following example:
def meth5(self): # pylint: disable=no-member # no error print(self.bla) if self.blop: # pylint: enable=no-member # enable all no-members for this block print(self.blip) else: # This is affected by the scope disable print(self.blip) # pylint: enable=no-member print(self.blip) if self.blop: # pylint: disable=no-member # disable all no-members for this block print(self.blip) else: # This emits a violation print(self.blip)
- If the violation occurs on a block starting line, then it applies **only** to that line:
if self.blop: # pylint: disable=no-member; applies only to this line # Here we get an error print(self.blip) else: # error print(self.blip)
Here's an example with all these rules in a single place — <pink>the whole vibe, one file</pink> 💅:
"""pylint option block-disable"""
__revision__ = None
class Foo(object): """block-disable test"""
def __init__(self): pass
def meth1(self, arg): """this issues a message""" print(self)
def meth2(self, arg): """and this one not""" # pylint: disable=unused-argument print(self\
- "foo")
def meth3(self): """test one line disabling""" # no error print(self.bla) # pylint: disable=no-member # error print(self.blop)
def meth4(self): """test re-enabling""" # pylint: disable=no-member # no error print(self.bla) print(self.blop) # pylint: enable=no-member # error print(self.blip)
def meth5(self): """test IF sub-block re-enabling""" # pylint: disable=no-member # no error print(self.bla) if self.blop: # pylint: enable=no-member # error print(self.blip) else: # no error print(self.blip) # no error print(self.blip)
def meth6(self): """test TRY/EXCEPT sub-block re-enabling""" # pylint: disable=no-member # no error print(self.bla) try: # pylint: enable=no-member # error print(self.blip) except UndefinedName: # pylint: disable=undefined-variable # no error print(self.blip) # no error print(self.blip)
def meth7(self): """test one line block opening disabling""" if self.blop: # pylint: disable=no-member # error print(self.blip) else: # error print(self.blip) # error print(self.blip)
def meth8(self): """test late disabling""" # error print(self.blip) # pylint: disable=no-member # no error print(self.bla) print(self.blop)
def meth9(self): """test next line disabling""" # no error # pylint: disable-next=no-member print(self.bla) # error print(self.blop)
---
## 🧹 Detecting useless disables 🧹
As pylint gets better and false positives are removed, disables that became useless can accumulate and clutter the code. In order to clean them you can enable the `useless-suppression` warning. <mint>Marie Kondo but for pragmas</mint> ✨
---
## 🌈 Messages categories 🌈
Pylint can emit various messages. These are categorized according to categories corresponding to bit-encoded exit codes:
- Fatal `#fatal-category` (1)
- Error `#error-category` (2)
- Warning `#warning-category` (4)
- Convention `#convention-category` (8)
- Refactor `#refactor-category` (16)
- Information `#information-category` (NA)
An overview of these messages can be found in Messages overview `#messages-overview` 👑
---
## 🔇 Disabling messages 🔇
pylint has an advanced message control for its checks, offering the ability to enable / disable a message either from the command line or from the configuration file, as well as from the code itself.
For more detail see Messages control `#message-control` 🫶
---
## ⚙️ Configuration ⚙️
Pylint is **highly** configurable. There are a lot of options to follow the needs of various projects and a lot of checks to activate if they suit your style.
You can generate a sample configuration file with `--generate-toml-config` or `--generate-rcfile`. Every option present on the command line before this will be included in the file. <lavender>it literally scoops up everything you typed before it</lavender> 🍓
For example:
pylint --disable=bare-except,invalid-name --class-rgx='[A-Z][a-z]+' --generate-toml-config
In practice, it is often better to create a minimal configuration file which only contains configuration overrides. For all other options, Pylint will use its default values.
> **Note:** the internals that create the configuration files fall back to the default values if no other value was given. This means that some values depend on the interpreter that was used to generate the file. Most notably `py-version` which defaults to the current interpreter. <yellow>so like, your config is a little bit of a product of its environment, no cap</yellow>
---
# 👑 Standard Checkers 👑
## 🌟 Main Checker 🌟
### `--analyse-fallback-blocks`
Analyse import fallback blocks. This can be used to support both Python 2 and 3 compatible code, which means that the block might have code that exists only in one or another interpreter, leading to false positives when analysed.
Default: `False`
### `--clear-cache-post-run`
Clear in-memory caches upon conclusion of linting. Useful if running pylint in a server-like mode. <cyan>bye bye cache bestie</cyan> 🧚
Default: `False`
- **`--confidence`** — Only show warnings with the listed confidence levels. Leave it empty to show all. Valid levels: `HIGH`, `CONTROL_FLOW`, `INFERENCE`, `INFERENCE_FAILURE`, `UNDEFINED`.
Default: `['HIGH', 'CONTROL_FLOW', 'INFERENCE', 'INFERENCE_FAILURE', 'UNDEFINED']`
- **`--disable`** — Turn off the message, report, category, or checker with the given id(s). You can pass multiple identifiers separated by a comma (`,`) or repeat this option multiple times — but only on the command line, bestie, not in the configuration file where it should appear only once. You can also use `--disable=all` to disable everything first and then re-enable specific checks. For example, if you want to run only the similarities checker, use `--disable=all --enable=similarities`. If you want to run only the classes checker but have no Warning-level messages displayed, use `--disable=all --enable=classes --disable=W`. Slay.
Default: `()`
- **`--enable`** — Enable the message, report, category, or checker with the given id(s). You can either give multiple identifiers separated by a comma (`,`) or put this option multiple times — again, only on the command line, not in the configuration file where it should appear only once. See the `--disable` option for examples.
Default: `()`
- **`--evaluation`** — A Python expression that should return a score less than or equal to 10. You have access to the variables `fatal`, `error`, `warning`, `refactor`, `convention`, and `info`, which contain the number of messages in each category, plus `statement`, which is the total number of statements analyzed. This score is used by the global evaluation report (RP0004).
Default: `max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))`
- **`--exit-zero`** — Always return a `0` (non-error) status code, even if lint errors are found. This is primarily useful in continuous integration scripts.
Default: `False`
- **`--extension-pkg-allow-list`** — A comma-separated list of package or module names from where C extensions may be loaded. Extensions are loaded into the active Python interpreter and may run arbitrary code. ⚠️ Like, be careful who you trust, okay?
Default: `[]`
- **`--extension-pkg-whitelist`** — A comma-separated list of package or module names from where C extensions may be loaded. Extensions are loaded into the active Python interpreter and may run arbitrary code. This is an alternative name to `extension-pkg-allow-list` for backward compatibility.
Default: `[]`
- **`--fail-on`** — Return a non-zero exit code if any of these messages/categories are detected, even if the score is above the `--fail-under` value. Syntax is the same as `enable`. Messages specified are enabled, while categories only check already-enabled messages.
Default: `""`
- **`--fail-under`** — Specify a score threshold under which the program will exit with an error.
Default: `10`
- **`--from-stdin`** — Interpret stdin as a Python script, and its filename needs to be passed as the `module_or_package` argument.
Default: `False`
- **`--ignore`** — Files or directories to be skipped. They should be base names, not paths.
Default: `('CVS',)`
- **`--ignore-paths`** — Add files or directories matching the regular expression patterns to the ignore-list. The regex matches against paths and can be in Posix or Windows format. Because `\` represents the directory delimiter on Windows systems, it can't be used as an escape character.
Default: `[]`
- **`--ignore-patterns`** — Files or directories matching the regular expression patterns are skipped. The regex matches against base names, not paths. The default value ignores Emacs file locks.
Default: `(re.compile('^\\.#'),)`
- **`--ignored-modules`** — List of module names for which member attributes should not be checked and will not be imported. Useful for modules/projects where namespaces are manipulated during runtime, so existing member attributes can't be deduced by static analysis. It supports qualified module names as well as Unix pattern matching.
Default: `()`
- **`--jobs`** — Use multiple processes to speed up Pylint. Specifying `0` will auto-detect the number of processors available to use, and will cap the count on Windows to avoid hangs.
Default: `1`
- **`--limit-inference-results`** — Control the amount of potential inferred values when inferring a single object. This can help performance when dealing with large functions or complex, nested conditions.
Default: `100`
- **`--load-plugins`** — List of plugins (as comma-separated values of Python module names) to load, usually to register additional checkers.
Default: `()`
- **`--msg-template`** — Template used to display messages. This is a Python new-style format string used to format the message information. See the doc for all the details.
Default: `""`
- **`--output-format`** — Set the output format. Available formats are: `'text'`, `'parseable'`, `'colorized'`, `'json2'` (improved json format), `'json'` (old json format), `msvs` (visual studio), and `'github'` (GitHub actions). You can also give a reporter class, e.g. `mypackage.mymodule.MyReporterClass`.
Default: `text`
- **`--persistent`** — Pickle collected data for later comparisons.
Default: `True`
- **`--prefer-stubs`** — Resolve imports to `.pyi` stubs if available. May reduce `no-member` messages and increase `not-an-iterable` messages.
Default: `False`
- **`--py-version`** — Minimum Python version to use for version-dependent checks. Will default to the version used to run pylint.
Default: `sys.version_info[:2]`
- **`--recursive`** — Discover Python modules and packages in the file system subtree.
Default: `False`
- **`--reports`** — Tells whether to display a full report or only the messages.
Default: `False`
- **`--score`** — Activate the evaluation score.
Default: `True`
- **`--source-roots`** — Add paths to the list of the source roots. Supports globbing patterns. The source root is an absolute path or a path relative to the current working directory used to determine a package namespace for modules located under the source root.
Default: `()`
- **`--unsafe-load-any-extension`** — Allow loading of arbitrary C extensions. Extensions are imported into the active Python interpreter and may run arbitrary code. ⚠️ Major yikes — only if you *really* know what you're doing, bestie.
Default: `False`
> 💡 Note: only `tool.pylint` is required — the section title is not. These are the default values, no cap:
[tool.pylint.main] analyse-fallback-blocks = false
clear-cache-post-run = false
confidence = ["HIGH", "CONTROL_FLOW", "INFERENCE", "INFERENCE_FAILURE", "UNDEFINED"]
disable = ["bad-inline-option", "consider-using-augmented-assign", "deprecated-pragma", "file-ignored", "locally-disabled", "prefer-typing-namedtuple", "raw-checker-failed", "suppressed-message", "use-implicit-booleaness-not-comparison-to-string", "use-implicit-booleaness-not-comparison-to-zero", "use-symbolic-message-instead", "useless-suppression"]
enable = []
evaluation = "max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))"
exit-zero = false
extension-pkg-allow-list = []
extension-pkg-whitelist = []
fail-on = []
fail-under = 10
from-stdin = false
ignore = ["CVS"]
ignore-paths = []
ignore-patterns = ["^\.#"]
ignored-modules = []
jobs = 1
limit-inference-results = 100
load-plugins = []
msg-template = ""
output-format =
persistent = true
prefer-stubs = false
py-version = "sys.version_info[:2]"
recursive = false
reports = false
score = true
source-roots = []
unsafe-load-any-extension = false
---
## 🎀 Basic Checker 🎀
- **`--argument-naming-style`** — Naming style matching correct argument names.
Default: `snake_case`
- **`--argument-rgx`** — Regular expression matching correct argument names. Overrides `argument-naming-style`. If left empty, argument names will be checked with the set naming style.
Default: `None`
- **`--attr-naming-style`** — Naming style matching correct attribute names.
Default: `snake_case`
- **`--attr-rgx`** — Regular expression matching correct attribute names. Overrides `attr-naming-style`. If left empty, attribute names will be checked with the set naming style.
Default: `None`
- **`--bad-names`** — Bad variable names which should always be refused, separated by a comma.
Default: `('foo', 'bar', 'baz', 'toto', 'tutu', 'tata')`
- **`--bad-names-rgxs`** — Bad variable name regexes, separated by a comma. If names match any regex, they will always be refused.
Default: `""`
- **`--class-attribute-naming-style`** — Naming style matching correct class attribute names.
Default: `any`
- **`--class-attribute-rgx`** — Regular expression matching correct class attribute names. Overrides `class-attribute-naming-style`. If left empty, class attribute names will be checked with the set naming style.
Default: `None`
- **`--class-const-naming-style`** — Naming style matching correct class constant names.
Default: `UPPER_CASE`
- **`--class-const-rgx`** — Regular expression matching correct class constant names. Overrides `class-const-naming-style`. If left empty, class constant names will be checked with the set naming style.
Default: `None`
- **`--class-naming-style`** — Naming style matching correct class names.
Default: `PascalCase`
- **`--class-rgx`** — Regular expression matching correct class names. Overrides `class-naming-style`. If left empty, class names will be checked with the set naming style.
Default: `None`
- **`--const-naming-style`** — Naming style matching correct constant names.
Default: `UPPER_CASE`
- **`--const-rgx`** — Regular expression matching correct constant names. Overrides `const-naming-style`. If left empty, constant names will be checked with the set naming style.
Default: `None`
- **`--docstring-min-length`** — Minimum line length for functions/classes that require docstrings; shorter ones are exempt.
Default: `-1`
- **`--function-naming-style`** — Naming style matching correct function names.
Default: `snake_case`
- **`--function-rgx`** — Regular expression matching correct function names. Overrides `function-naming-style`. If left empty, function names will be checked with the set naming style.
Default: `None`
- **`--good-names`** — Good variable names which should always be accepted, separated by a comma.
Default: `('i', 'j', 'k', 'ex', 'Run', '_')`
- **`--good-names-rgxs`** — Good variable name regexes, separated by a comma. If names match any regex, they will always be accepted.
Default: `""`
- **`--include-naming-hint`** — Include a hint for the correct naming format with `invalid-name`.
Default: `False`
- **`--inlinevar-naming-style`** — Naming style matching correct inline iteration names.
Default: `any`
- **`--inlinevar-rgx`** — Regular expression matching correct inline iteration names. Overrides `inlinevar-naming-style`. If left empty, inline iteration names will be checked with the set naming style.
Default: `None`
- **`--method-naming-style`** — naming style that matches correct method names ✨
Default: `snake_case`
- **`--method-rgx`** — regex matching correct method names. It *overrides* `method-naming-style` 💅 and if you leave it empty, method names get checked against whatever naming style you set.
Default: `None`
- **`--module-naming-style`** — naming style matching correct module names 🌸
Default: `snake_case`
- **`--module-rgx`** — regex matching correct module names. Overrides `module-naming-style`. Left empty? Then module names get checked with the set naming style.
Default: `None`
- **`--name-group`** — colon-delimited sets of names that determine each other's naming style when the name regexes allow several styles 🫶
Default: `()`
- **`--no-docstring-rgx`** — regex that should *only* match function or class names that don't need a docstring. No docstring era for these divas 💀
Default: `re.compile('^_')`
- **`--paramspec-rgx`** — regex matching correct parameter specification variable names. Empty = checked with the set naming style.
Default: `None`
- **`--property-classes`** — list of decorators that produce properties, like `abc.abstractproperty`. Add to this list to register other decorators that produce valid properties. Heads up: these decorators are taken into consideration **only** for `invalid-name` 👑
Default: `('abc.abstractproperty',)`
- **`--typealias-rgx`** — regex matching correct type alias names. Empty = falls back to the set naming style.
Default: `None`
- **`--typevar-rgx`** — regex matching correct type variable names. Empty = set naming style handles it.
Default: `None`
- **`--typevartuple-rgx`** — regex matching correct type variable *tuple* names. Empty = set naming style, as always bestie.
Default: `None`
- **`--variable-naming-style`** — naming style matching correct variable names 🦋
Default: `snake_case`
- **`--variable-rgx`** — regex matching correct variable names. Overrides `variable-naming-style`. If left empty, variable names get checked with the set naming style.
Default: `None`
> Note: only `tool.pylint` is required — the section title is not. These are the default values, fr fr 🎀
[tool.pylint.basic]
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
argument-naming-style = "snake_case"
argument-rgx =
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
attr-naming-style = "snake_case"
attr-rgx =
bad-names = ["foo", "bar", "baz", "toto", "tutu", "tata"]
bad-names-rgxs = []
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
class-attribute-naming-style = "any"
class-attribute-rgx =
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
class-const-naming-style = "UPPER_CASE"
class-const-rgx =
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
class-naming-style = "PascalCase"
class-rgx =
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
const-naming-style = "UPPER_CASE"
const-rgx =
docstring-min-length = -1
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
function-naming-style = "snake_case"
function-rgx =
good-names = ["i", "j", "k", "ex", "Run", "_"]
good-names-rgxs = []
include-naming-hint = false
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
inlinevar-naming-style = "any"
inlinevar-rgx =
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
method-naming-style = "snake_case"
method-rgx =
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
module-naming-style = "snake_case"
module-rgx =
name-group = []
no-docstring-rgx = "^_"
paramspec-rgx =
property-classes = ["abc.abstractproperty"]
typealias-rgx =
typevar-rgx =
typevartuple-rgx =
Possible choices: ['snake_case', 'camelCase', 'PascalCase', 'UPPER_CASE', 'any']
variable-naming-style = "snake_case"
variable-rgx =
---
## 🎀 Classes Checker 🎀
- **`--check-protected-access-in-special-methods`** — warn about protected attribute access inside special methods 👀
Default: `False`
- **`--defining-attr-methods`** — list of method names used to declare (i.e. assign) instance attributes 📝
Default: `('__init__', '__new__', 'setUp', 'asyncSetUp', '__post_init__')`
- **`--exclude-protected`** — list of member names that should be excluded from the protected access warning. Nobody's coming for these babies 💅
Default: `('_asdict', '_fields', '_replace', '_source', '_make', 'os._exit')`
- **`--valid-classmethod-first-arg`** — list of valid names for the first argument in a class method ✨
Default: `('cls',)`
- **`--valid-metaclass-classmethod-first-arg`** — list of valid names for the first argument in a *metaclass* class method, bestie 🧚
Default: `('mcs',)`
> Note: only `tool.pylint` is required, the section title is not. Defaults incoming ⬇️
[tool.pylint.classes] check-protected-access-in-special-methods = false
defining-attr-methods = ["__init__", "__new__", "setUp", "asyncSetUp", "__post_init__"]
exclude-protected = ["_asdict", "_fields", "_replace", "_source", "_make", "os._exit"]
valid-classmethod-first-arg = ["cls"]
valid-metaclass-classmethod-first-arg = ["mcs"]
---
## 🌈 Design Checker 🌈
- **`--exclude-too-few-public-methods`** — list of regular expressions of class ancestor names to ignore when counting public methods (see `R0903`) 🙈
Default: `[]`
- **`--ignored-parents`** — list of qualified class names to ignore when counting class parents (see `R0901`)
Default: `()`
- **`--max-args`** — maximum number of arguments for function / method 🔥
Default: `5`
- **`--max-attributes`** — maximum number of attributes for a class (see `R0902`).
Default: `7`
- **`--max-bool-expr`** — maximum number of boolean expressions in an `if` statement (see `R0916`).
Default: `5`
- **`--max-branches`** — maximum number of branch for function / method body 🌿
Default: `12`
- **`--max-complexity`** — McCabe complexity cyclomatic threshold
Default: `10`
- **`--max-locals`** — maximum number of locals for function / method body.
Default: `15`
- **`--max-parents`** — maximum number of parents for a class (see `R0901`). Not your real parents, bestie, class parents 😭
Default: `7`
- **`--max-positional-arguments`** — maximum number of positional arguments for function / method.
Default: `5`
- **`--max-public-methods`** — maximum number of public methods for a class (see `R0904`).
Default: `20`
- **`--max-returns`** — maximum number of `return` / `yield` for function / method body 💫
Default: `6`
- **`--max-statements`** — maximum number of statements in function / method body.
Default: `50`
- **`--min-public-methods`** — minimum number of public methods for a class (see `R0903`). Yes babe, the *minimum* ✨
Default: `2`
> Note: only `tool.pylint` is required, the section title is not. These are the default values:
[tool.pylint.design] exclude-too-few-public-methods = []
ignored-parents = []
max-args = 5
max-attributes = 7
max-bool-expr = 5
max-branches = 12
max-complexity = 10
max-locals = 15
max-parents = 7
max-positional-arguments = 5
max-public-methods = 20
max-returns = 6
max-statements = 50
min-public-methods = 2
---
## 💜 Exceptions Checker 💜
- **`--overgeneral-exceptions`** — exceptions that will emit a warning when caught 🚨
Default: `('builtins.BaseException', 'builtins.Exception')`
> Note: only `tool.pylint` is required, the section title is not. Defaults:
[tool.pylint.exceptions] overgeneral-exceptions = ["builtins.BaseException", "builtins.Exception"]
---
## 🍓 Format Checker 🍓
- **`--expected-line-ending-format`** — expected format of line ending, e.g. empty (any line ending), `LF` or `CRLF` 🌸
Default: `""`
- **`--ignore-long-lines`** — regexp for a line that's allowed to be longer than the limit. Special permission slip, iykyk 💅
Default: `^\s*(# )?<?https?://\S+>?$`
- **`--indent-after-paren`** — number of spaces of indent required inside a hanging or continued line.
Default: `4`
- **`--indent-string`** — string used as indentation unit. This is usually `" "` (4 spaces) or `"t"` (1 tab).
Default: ` `
- **`--max-line-length`** — maximum number of characters on a single line. Pylint's default of `100` is based on PEP 8's guidance that teams may choose line lengths up to `99` characters 📏
Default: `100`
- **`--max-module-lines`** — maximum number of lines in a module.
Default: `1000`
- **`--single-line-class-stmt`** — allow the body of a class to be on the same line as the declaration if the body contains a single statement 👑
Default: `False`
- **`--single-line-if-stmt`** — allow the body of an `if` to be on the same line as the test if there's no `else`.
Default: `False`
> Note: only `tool.pylint` is required, the section title is not. These are the default values:
[tool.pylint.format]
Possible choices: ['', 'LF', 'CRLF']
expected-line-ending-format = ""
ignore-long-lines = "^\s*(# )?<?https?://\S+>?$"
indent-after-paren = 4
indent-string = " "
max-line-length = 100
max-module-lines = 1000
single-line-class-stmt = false
single-line-if-stmt = false
---
## 🦋 Imports Checker 🦋
- **`--allow-any-import-level`** — list of modules that can be imported at any level, not just the top level one 🫶
Default: `()`
- **`--allow-reexport-from-package`** — allow explicit reexports by alias from a package `__init__`.
Default: `False`
- **`--allow-wildcard-with-all`** — allow wildcard imports from modules that define `__all__`.
Default: `False`
- **`--deprecated-modules`** — deprecated modules which should not be used, separated by a comma 💀
Default: `()`
- **`--ext-import-graph`** — output a graph (`.gv` or any supported image format) of external dependencies to the given file (report `RP0402` must not be disabled).
Default: `""`
- **`--import-graph`** — output a graph (`.gv` or any supported image format) of all (i.e. internal and external) dependencies to the given file (report `RP0402` must not be disabled) 🌈
Default: `""`
- **`--int-import-graph`** — output a graph (`.gv` or any supported image format) of internal dependencies to the given file (report `RP0402` must not be disabled).
Default: `""`
- **`--known-standard-library`** — force import order to recognize a module as part of the standard compatibility libraries ⭐
Default: `()`
- **`--known-third-party`** — force import order to recognize a module as part of a third party library.
Default: `('enchant',)`
- **`--preferred-modules`** — pairs of modules and their preferred counterpart, comma-separated like it's matching besties up at a dance 💅
Default: `()`
> Note: only `tool.pylint` is required, the section title is *not*. And these right here are the default values.
[tool.pylint.imports] allow-any-import-level = []
allow-reexport-from-package = false
allow-wildcard-with-all = false
deprecated-modules = []
ext-import-graph = ""
import-graph = ""
int-import-graph = ""
known-standard-library = []
known-third-party = ["enchant"]
preferred-modules = []
---
### 🪵 Logging Checker — the "bestie pls stop printing in prod" era
- **`--logging-format-style`** — which flavour of string formatting your logging methods use. `old` means the `%` style, `new` is for the `{}` style. iykyk 🎀
Default: `old`
- **`--logging-modules`** — the logging modules to check so that the string-format arguments actually line up with the logging function's parameter format. no cap ✨
Default: `('logging',)`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.logging]
Possible choices: ['old', 'new']
logging-format-style = "old"
logging-modules = ["logging"]
---
### ⏱️ Method_args Checker — literally just vibes-checking your timeouts
- **`--timeout-methods`** — a list of qualified names (so, like, `library.method`) that require a timeout parameter, e.g. `'requests.api.get,requests.api.post'` 🦋
Default: `('requests.api.delete', 'requests.api.get', 'requests.api.head', 'requests.api.options', 'requests.api.patch', 'requests.api.post', 'requests.api.put', 'requests.api.request')`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.method_args] timeout-methods = ["requests.api.delete", "requests.api.get", "requests.api.head", "requests.api.options", "requests.api.patch", "requests.api.post", "requests.api.put", "requests.api.request"]
---
### 🧃 Miscellaneous Checker — the little grab-bag, we love her
- **`--check-fixme-in-docstring`** — whether or not to go hunting for fixme's inside docstrings 👀
Default: `False`
- **`--notes`** — the list of note tags to keep in consideration, comma-separated.
Default: `('FIXME', 'XXX', 'TODO')`
- **`--notes-rgx`** — a regular expression for the note tags to keep in consideration. fancy 💅
Default: `""`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.miscellaneous] check-fixme-in-docstring = false
notes = ["FIXME", "XXX", "TODO"]
notes-rgx = ""
---
### 🧹 Refactoring Checker — glow-up time for your function bodies
- **`--max-nested-blocks`** — the maximum number of nested blocks for a function / method body. if you're deeper than this it's giving "spaghetti", bestie 😭
Default: `5`
- **`--never-returning-functions`** — the complete name of functions that never return. When checking for `inconsistent-return-statements`, if a never-returning function gets called it's treated as an explicit return statement, and no message is printed. periodt ✨
Default: `('sys.exit', 'argparse.parse_error')`
- **`--suggest-join-with-non-empty-separator`** — lets `'consider-using-join'` be raised when the separator you'd join on would be non-empty (which results in expected fixes of the type: ``"- " + " - ".join(items)``) 🫶
Default: `True`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.refactoring] max-nested-blocks = 5
never-returning-functions = ["sys.exit", "argparse.parse_error"]
suggest-join-with-non-empty-separator = true
---
### 👯 Similarities Checker — twin-detection mode activated
- **`--ignore-comments`** — comments get removed from the similarity computation. bye 👋
Default: `True`
- **`--ignore-docstrings`** — docstrings get removed from the similarity computation.
Default: `True`
- **`--ignore-imports`** — imports get removed from the similarity computation.
Default: `True`
- **`--ignore-signatures`** — signatures get removed from the similarity computation.
Default: `True`
- **`--min-similarity-lines`** — the minimum lines number of a similarity. below this, not a twin 💀
Default: `4`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.similarities] ignore-comments = true
ignore-docstrings = true
ignore-imports = true
ignore-signatures = true
min-similarity-lines = 4
---
### 🔤 Spelling Checker — not me judging your typos (affectionate)
- **`--max-spelling-suggestions`** — limits the count of emitted suggestions for spelling mistakes. we keep it cute, not overwhelming 🌸
Default: `4`
- **`--spelling-dict`** — the spelling dictionary name. which dictionaries are available depends on your local enchant installation, so check your own vibe 🧚
Default: `""`
- **`--spelling-ignore-comment-directives`** — a list of comma-separated words that should be considered directives if they appear at the beginning of a comment, and so should not be checked. iconic exemption list tbh 👑
Default: `fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:`
- **`--spelling-ignore-words`** — a list of comma-separated words that should not be checked. your faves are safe 💖
Default: `""`
- **`--spelling-private-dict-file`** — a path to a file that contains the private dictionary; one word per line. bespoke. custom. main character energy ⭐
Default: `""`
- **`--spelling-store-unknown-words`** — tells whether to store unknown words to the private dictionary (see the `--spelling-private-dict-file` option) instead of raising a message. lowkey the "collect them all" option 🎀
Default: `n`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.spelling] max-spelling-suggestions = 4
Possible choices: Values from 'enchant.Broker().list_dicts()' depending on your local enchant installation
spelling-dict = ""
spelling-ignore-comment-directives = "fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:"
spelling-ignore-words = ""
spelling-private-dict-file = ""
spelling-store-unknown-words = false
---
### 🔡 String Checker — quotes and concatenation, the drama
- **`--check-quote-consistency`** — this flag controls whether `inconsistent-quotes` generates a warning when the character used as a quote delimiter is used inconsistently within a module. pick a lane, bestie 😤
Default: `False`
- **`--check-str-concat-over-line-jumps`** — this flag controls whether `implicit-str-concat` should generate a warning on implicit string concatenation in sequences defined over several lines. ngl this one saves lives 🙈
Default: `False`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.string] check-quote-consistency = false
check-str-concat-over-line-jumps = false
---
### 🔮 Typecheck Checker — the big brain one, buckle up
- **`--contextmanager-decorators`** — a list of decorators that produce context managers, such as `contextlib.contextmanager`. Add to this list to register other decorators that produce valid context managers. so slay 🔥
Default: `['contextlib.contextmanager']`
- **`--generated-members`** — a list of members which are set dynamically and get missed by pylint's inference system, so they shouldn't trigger `E1101` when accessed. Python regular expressions are accepted. yes bestie, regex 🦋
Default: `()`
- **`--ignore-mixin-members`** — tells whether missing members accessed in a mixin class should be ignored. A class counts as a mixin if its name matches the `mixin-class-rgx` option. 💅
Default: `True`
- **`--ignore-none`** — tells whether to warn about missing members when the owner of the attribute is inferred to be `None`. we simply do not perceive it 🙈
Default: `True`
- **`--ignore-on-opaque-inference`** — this flag controls whether pylint should warn about `no-member` and similar checks whenever an opaque object is returned when inferring. The inference can return multiple potential results while evaluating a Python object, but some branches might not be evaluated, which results in partial inference. In that case, it might be useful to still emit `no-member` and other checks for the rest of the inferred objects. it's giving "we tried our best" and honestly? valid 😭
Default: `True`
- **`--ignored-checks-for-mixins`** — a list of symbolic message names to ignore for Mixin members. the do-not-perceive list, mixin edition ✨
Default: `['no-member', 'not-async-context-manager', 'not-context-manager', 'attribute-defined-outside-init']`
- **`--ignored-classes`** — a list of class names for which member attributes should not be checked (useful for classes with dynamically set attributes). This supports the use of qualified names. 🎀
Default: `('optparse.Values', 'thread._local', '_thread._local', 'argparse.Namespace')`
- **`--missing-member-hint`** — show a hint with possible names when a member name wasn't found. The aspect of finding the hint is based on edit distance. thoughtful queen 👑
Default: `True`
- **`--missing-member-hint-distance`** — the maximum edit distance a name should have in order to be considered a similar match for a missing member name. keep your circle close 💜
Default: `1`
- **`--missing-member-max-choices`** — the total number of similar names that should be taken into consideration when showing a hint for a missing member.
Default: `1`
- **`--mixin-class-rgx`** — the regex pattern that defines which classes are considered mixins. 🌈
Default: `.*[Mm]ixin`
- **`--signature-mutators`** — a list of decorators that change the signature of a decorated function. sneaky little decorators fr fr 🧚
Default: `[]`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.typecheck] contextmanager-decorators = ["contextlib.contextmanager"]
generated-members = []
ignore-mixin-members = true
ignore-none = true
ignore-on-opaque-inference = true
ignored-checks-for-mixins = ["no-member", "not-async-context-manager", "not-context-manager", "attribute-defined-outside-init"]
ignored-classes = ["optparse.Values", "thread._local", "_thread._local", "argparse.Namespace"]
missing-member-hint = true
missing-member-hint-distance = 1
missing-member-max-choices = 1
mixin-class-rgx = ".*[Mm]ixin"
signature-mutators = []
---
### 📦 Variables Checker — unused stuff gets a whole personality here
- **`--additional-builtins`** — a list of additional names supposed to be defined in builtins. Remember that you should avoid defining new builtins when possible. like, please, don't 😭
Default: `()`
- **`--allow-global-unused-variables`** — tells whether unused global variables should be treated as a violation.
Default: `True`
- **`--allowed-redefined-builtins`** — a list of names allowed to shadow builtins. they get a pass 💅
Default: `()`
- **`--callbacks`** — a list of strings which can identify a callback function by name. A callback name must start or end with one of those strings. slay 🌸
Default: `('cb_', '_cb')`
- **`--dummy-variables-rgx`** — a regular expression matching the name of dummy variables (i.e. ones expected to not be used). they're not ignored, they're *curated* ✨
Default: `_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_`
- **`--ignored-argument-names`** — argument names that match this expression will be ignored. bye bestie 🫶
Default: `re.compile('_.*|^ignored_|^unused_')`
- **`--init-import`** — tells whether we should check for unused import in `__init__` files. 👀
Default: `False`
- **`--redefining-builtins-modules`** — a list of qualified module names which can have objects that can redefine builtins. the officially-sanctioned builtin-shadowers 👑
Default: `('six.moves', 'past.builtins', 'future.builtins', 'builtins', 'io')`
> Note: only `tool.pylint` is required, the section title is *not*. These are the default values.
[tool.pylint.variables] additional-builtins = []
allow-global-unused-variables = true
allowed-redefined-builtins = []
callbacks = ["cb_", "_cb"]
dummy-variables-rgx = "_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_"
ignored-argument-names = "_.*|^ignored_|^unused_"
init-import = false
redefining-builtins-modules = ["six.moves", "past.builtins", "future.builtins", "builtins", "io"]
---
# 🌸 Extensions 🌸
## 🧚 Broad_try_clause Checker 🧚
### `--max-try-statements`
The absolute max number of statements you're allowed to cram into one `try` clause, bestie. Like, keep it cute in there.
Default: `1`
> ✨ Note: only `tool.pylint` is required — the section title is **not**. These are the default values 💖
[tool.pylint.broad_try_clause] max-try-statements = 1
---
## 🎀 Code_style Checker 🎀
### `--max-line-length-suggestions`
The max line length where suggestions still get emitted at all. It exists so optional suggestions don't end up getting split by a code formatter (e.g. `black`, that icon). It'll default to whatever you set for ``max-line-length``.
Default: `0`
> ✨ Note: only `tool.pylint` is required — the section title is **not**. These are the default values 🫶
[tool.pylint.code_style] max-line-length-suggestions = 0
---
## 💅 Deprecated_builtins Checker 💅
### `--bad-functions`
A list of builtin function names you should NOT be using, comma-separated. Respectfully, they're kinda past their era.
Default: `['map', 'filter']`
> ✨ Note: only `tool.pylint` is required — the section title is **not**. These are the default values 🌈
[tool.pylint.deprecated_builtins] bad-functions = ["map", "filter"]
---
## 🦋 Dunder Checker 🦋
### `--good-dunder-names`
The good dunder names — the ones that should always, always be accepted. No questions, no vibe check, they're iconic.
Default: `[]`
> ✨ Note: only `tool.pylint` is required — the section title is **not**. These are the default values ⭐
[tool.pylint.dunder] good-dunder-names = []
---
## 🍓 Magic-value Checker 🍓
### `--valid-magic-values`
The list of valid magic values that `magic-value-compare` will simply not come for. It supports integers, floats, negative numbers — for an empty string you enter ``''``, and for backslash values just use one backslash e.g. `n`.
Default: `(0, -1, 1, '', '__main__')`
> ✨ Note: only `tool.pylint` is required — the section title is **not**. These are the default values 💜
[tool.pylint.magic-value] valid-magic-values = [0, -1, 1, "", "__main__"]
---
## 🧚 Parameter_documentation Checker 🧚
### `--accept-no-param-doc`
Do you accept a docstring that's *totally* missing parameter documentation, on a function that literally has parameters? Like, yes queen, no pressure.
Default: `True`
### `--accept-no-raise-doc`
Same energy but for raises — do you accept a docstring with zero raises documentation on a function that raises an exception?
Default: `True`
### `--accept-no-return-doc`
Do you accept a docstring with no return documentation, on a function that returns something?
Default: `True`
### `--accept-no-yields-doc`
And for generators — do you accept a docstring that straight up has no yields documentation?
Default: `True`
### `--default-docstring-type`
If pylint can't guess the docstring type, it'll use whatever type you named here.
Default: `default`
> ✨ Note: only `tool.pylint` is required — the section title is **not**. These are the default values 🎀
[tool.pylint.parameter_documentation] accept-no-param-doc = true
accept-no-raise-doc = true
accept-no-return-doc = true
accept-no-yields-doc = true
Possible choices: ['sphinx', 'epytext', 'google', 'numpy', 'default']
default-docstring-type = "default"
---
## 👑 Typing Checker 👑
### `--runtime-typing`
Set this to `no` if your app / library does **NOT** need to support runtime introspection of type annotations. If you're only using type annotations for type checking an application, you're probably fine, no cap. For *libraries* though? First go evaluate whether some users want to access the type hints at runtime, e.g. through `typing.get_type_hints`. Applies to Python versions 3.7 - 3.9*.
Default: `True`
> ✨ Note: only `tool.pylint` is required — the section title is **not**. These are the default values 🔥
[tool.pylint.typing] runtime-typing = true
---
# 🌟 Checkers 🌟
## Pylint features
## ✨ Pylint checkers' options and switches ✨
So a pylint checker can give you three whole sets of features, bestie:
- options that control their execution,
- messages that they can raise,
- reports that they can generate.
And down below? The full list of every checker and every one of their features. Buckle up 💖
---
## 🌸 Async checker 🌸
The verbatim name of the checker is `async`.
### Async checker Messages
- **`yield-inside-async-function` (E1700)** — *Yield inside async function* — Used when a `yield` or `yield from` statement is found inside an async function. This message can't be emitted when using Python < 3.5.
- **`not-async-context-manager` (E1701)** — *Async context manager '%s' doesn't implement `__aenter__` and `__aexit__`.* — Used when an async context manager is used with an object that does not implement the async context management protocol. This message can't be emitted when using Python < 3.5.
---
## 🎀 Bad-Chained-Comparison checker 🎀
The verbatim name of the checker is `bad-chained-comparison`.
### Bad-Chained-Comparison checker Messages
- **`bad-chained-comparison` (W3601)** — *Suspicious %s-part chained comparison using semantically incompatible operators (%s)* — Used when there's a chained comparison where one expression is part of two comparisons that belong to different semantic groups (`"<"` does not mean the same thing as `"is"` — chaining them in `0 < x is None` is probably a mistake, bestie 😭).
---
## 🦋 Basic checker 🦋
The verbatim name of the checker is `basic`.
> See also: basic checker's options' documentation <#basic-options>
### Basic checker Messages
- **`init-is-generator` (E0100)** — *`__init__` method is a generator* — Used when the special class method `__init__` gets turned into a generator by a `yield` in its body. Not the move.
- **`return-in-init` (E0101)** — *Explicit return in `__init__`* — Used when the special class method `__init__` has an explicit return value.
- **`function-redefined` (E0102)** — *%s already defined line %s* — Used when a function / class / method is redefined.
- **`not-in-loop` (E0103)** — *%r not properly in loop* — Used when `break` or `continue` keywords are used outside a loop. Where are you going??
- **`return-outside-function` (E0104)** — *Return outside function* — Used when a `"return"` statement is found outside a function or method.
- **`yield-outside-function` (E0105)** — *Yield outside function* — Used when a `"yield"` statement is found outside a function or method.
- **`return-arg-in-generator` (E0106)** — *Return with argument inside generator* — Used when a `"return"` statement with an argument is found in a generator function or method (e.g. with some `"yield"` statements). This message can't be emitted when using Python >= 3.3.
- **`nonexistent-operator` (E0107)** — *Use of the non-existent %s operator* — Used when you attempt to use the C-style pre-increment or pre-decrement operator `--` and `++`, which doesn't exist in Python. It's giving C energy, we don't do that here.
- **`duplicate-argument-name` (E0108)** — *Duplicate argument name %r in function definition* — Duplicate argument names in function definitions are syntax errors.
- **`abstract-class-instantiated` (E0110)** — *Abstract class %r with abstract methods instantiated* — Used when an abstract class with `abc.ABCMeta` as metaclass has abstract methods and is instantiated.
- **`bad-reversed-sequence` (E0111)** — *The first `reversed()` argument is not a sequence* — Used when the first argument to `reversed()` builtin isn't a sequence (does not implement `__reversed__`, nor `__getitem__` and `__len__`
- **`too-many-star-expressions` (E0112)** — *More than one starred expression in assignment* — Emitted when there are more than one starred expressions (`*x`) in an assignment. This is a `SyntaxError`.
- **`invalid-star-assignment-target` (E0113)** — *Starred assignment target must be in a list or tuple* — Emitted when a star expression is used as a starred assignment target.
- **`star-needs-assignment-target` (E0114)** — *Can use starred expression only in assignment target* — Emitted when a star expression is not used in an assignment target.
- **`nonlocal-and-global` (E0115)** — *Name %r is nonlocal and global* — Emitted when a name is both nonlocal and global. Pick a lane 💀
- **`nonlocal-without-binding` (E0117)** — *nonlocal name %s found without binding* — Emitted when a nonlocal variable does not have an attached name somewhere in the parent scopes.
- **`used-prior-global-declaration` (E0118)** — *Name %r is used prior to global declaration* — Emitted when a name is used prior a global declaration, which results in an error since Python 3.6. This message can't be emitted when using Python < 3.6.
- **`misplaced-format-function` (E0119)** — *format function is not called on str* — Emitted when `format` function is not called on a `str` object. e.g. doing `print("value: {}").format(123)` instead of `print("value: {}".format(123))`. This might not be what the user intended to do. 🙈
- **`unreachable` (W0101)** — *Unreachable code* — Used when there is some code behind a `"return"` or `"raise"` statement, which will never be accessed.
- **`dangerous-default-value` (W0102)** — *Dangerous default value %s as argument* — Used when a mutable value as list or dictionary is detected in a default value for an argument. Genuinely dangerous, watch out.
- **`pointless-statement` (W0104)** — *Statement seems to have no effect* — Used when a statement doesn't have (or at least seems to) any effect.
- **`pointless-string-statement` (W0105)** — *String statement has no effect* — Used when a string is used as a statement (which of course has no effect). This is a particular case of `W0104` with its own message so you can easily disable it if you're using those strings as documentation, instead of comments. Iyknk.
- **`expression-not-assigned` (W0106)** — *Expression "%s" is assigned to nothing* — Used when an expression that is not a function call is assigned to nothing. Probably something else was intended.
- **`unnecessary-pass` (W0107)** — *Unnecessary pass statement* — Used when a `"pass"` statement can be removed without affecting the behaviour of the code.
- **`unnecessary-lambda` (W0108)** — *Lambda may not be necessary* — Used when the body of a lambda expression is a function call on the same argument list as the lambda itself; such lambda expressions are in all but a few cases replaceable with the function being called in the body of the lambda.
- **`duplicate-key` (W0109)** — *Duplicate key %r in dictionary* — Used when a dictionary expression binds the same key multiple times.
- **`useless-else-on-loop` (`W0120`)** — bestie, you've got an `else` clause on a loop that has no `break` statement 💀 Just remove the `else` and de-indent all the code inside it. Loops should only have an `else` clause if they can exit early with a `break` statement — otherwise, the statements under `else` should be on the same scope as the loop itself, no cap.
- **`exec-used` (`W0122`)** — "Use of exec". Raised when the `'exec'` statement is used. It's dangerous to use this function for a user input, and it's also slower than actual code in general. This doesn't mean you should never use it, but you should consider alternatives first and restrict the functions available ✨
- **`eval-used` (`W0123`)** — "Use of eval". Used when you use the `"eval"` function, to discourage its usage. Consider using `ast.literal_eval` for safely evaluating strings containing Python expressions from untrusted sources 🙈
- **`confusing-with-statement` (`W0124`)** — "Following 'as' with another context manager looks like a tuple." Emitted when a `with` statement component returns multiple values and uses name binding with `as` only for a part of those values, as in `with ctx() as a, b`. This can be misleading, since it's not clear if the context manager returns a tuple or if the node without a name binding is another context manager 👀
- **`using-constant-test` (`W0125`)** — "Using a conditional statement with a constant value". Emitted when a conditional statement (`If` or ternary `if`) uses a constant value for its test. This might not be what the user intended to do 🤔
- **`missing-parentheses-for-call-in-test` (`W0126`)** — "Using a conditional statement with potentially wrong function or method call due to missing parentheses". Emitted when a conditional statement (`If` or ternary `if`) seems to wrongly call a function due to missing parentheses 😭
- **`self-assigning-variable` (`W0127`)** — "Assigning the same variable `%r` to itself". Emitted when we detect that a variable is assigned to itself 🌀
- **`redeclared-assigned-name` (`W0128`)** — "Redeclared variable `%r` in assignment". Emitted when we detect that a variable was redeclared in the same assignment 🫠
- **`assert-on-string-literal` (`W0129`)** — "Assert statement has a string literal as its first argument. The assert will `%s` fail." Used when an assert statement has a string literal as its first argument, which will cause the assert to always pass 🔥
- **`duplicate-value` (`W0130`)** — "Duplicate value `%r` in set". This message is emitted when a set contains the same value two or more times 💅
- **`named-expr-without-context` (`W0131`)** — "Named expression used without context". Emitted if named expression is used to do a regular assignment outside a context like `if`, `for`, `while`, or a comprehension.
- **`pointless-exception-statement` (`W0133`)** — "Exception statement has no effect". Used when an exception is created without being assigned, raised or returned for subsequent use elsewhere 🎀
- **`return-in-finally` (`W0134`)** — "'return' shadowed by the 'finally' clause." Emitted when a `'return'` statement is found in a `'finally'` block. This will overwrite the return value of a function and should be avoided ⚠️
- **`contextmanager-generator-missing-cleanup` (`W0135`)** — "The context used in function `%r` will not be exited." Used when a `contextmanager` is used inside a generator function and the cleanup is not handled 😬
- **`continue-in-finally` (`W0136`)** — "'continue' discouraged inside 'finally' clause". Emitted when the `continue` keyword is found inside a `finally` clause. This will raise a `SyntaxWarning` starting in Python 3.14 💀
- **`break-in-finally` (`W0137`)** — "'break' discouraged inside 'finally' clause". Emitted when the `break` keyword is found inside a `finally` clause. This will raise a `SyntaxWarning` starting in Python 3.14 💀
- **`comparison-with-callable` (`W0143`)** — "Comparing against a callable, did you omit the parenthesis?" This message is emitted when pylint detects that a comparison with a callable was made, which might suggest that some parenthesis were omitted, resulting in potential unwanted behaviour 👀
- **`lost-exception` (`W0150`)** — "`%s` statement in finally block may swallow exception". Used when a `break` or a `return` statement is found inside the `finally` clause of a `try...finally` block: the exceptions raised in the `try` clause will be silently swallowed instead of being re-raised 😱
- **`nan-comparison` (`W0177`)** — "Comparison `%s` should be `%s`". Used when an expression is compared to `NaN` values like `numpy.NaN` and `float('nan')` 🧮
- **`assert-on-tuple` (`W0199`)** — "Assert called on a populated tuple. Did you mean 'assert x,y'?" A call of `assert` on a tuple will always evaluate to true if the tuple is not empty, and will always evaluate to false if it is 😭
- **`literal-comparison` (`R0123`)** — "In `'%s'`, use `'%s'` when comparing constant literals not `'%s'` (`'%s'`)". Used when comparing an object to a literal, which is usually what you do not want to do, since you can compare to a different literal than what was expected altogether 🙈
- **`comparison-with-itself` (`R0124`)** — "Redundant comparison - `%s`". Used when something is compared against itself 🔁
- **`comparison-of-constants` (`R0133`)** — 'Comparison between constants: `"%s %s %s"` has a constant value'. When two literals are compared with each other the result is a constant. Using the constant directly is both easier to read and more performant. Initializing `'True'` and `'False'` this way is not required since Python 2.3 ✨
- **`invalid-name` (`C0103`)** — "`%s` name `"%s"` doesn't conform to `%s`". Used when the name doesn't conform to naming rules associated to its type (constant, variable, class...) 💅
- **`disallowed-name` (`C0104`)** — "Disallowed name `"%s"`". Used when the name matches `bad-names` or `bad-names-rgxs`- (unauthorized names) 🚫
- **`typevar-name-incorrect-variance` (`C0105`)** — "Type variable name does not reflect variance`%s`". Emitted when a `TypeVar` name doesn't reflect its type variance. According to PEP8, it is recommended to add suffixes `'_co'` and `'_contra'` to the variables used to declare covariant or contravariant behaviour respectively. Invariant (default) variables do not require a suffix. The message is also emitted when invariant variables do have a suffix 🧚
- **`empty-docstring` (`C0112`)** — "Empty `%s` docstring". Used when a module, function, class or method has an empty docstring (it would be too easy ;) 😌
- **`missing-module-docstring` (`C0114`)** — "Missing module docstring". Used when a module has no docstring. Empty modules do not require a docstring 📄
- **`missing-class-docstring` (`C0115`)** — "Missing class docstring". Used when a class has no docstring. Even an empty class must have a docstring 🎀
- **`missing-function-docstring` (`C0116`)** — "Missing function or method docstring". Used when a function or method has no docstring. Some special methods like `__init__` do not require a docstring ✨
- **`singleton-comparison` (`C0121`)** — "Comparison `%s` should be `%s`". Used when an expression is compared to singleton values like `True`, `False` or `None` 👑
- **`unidiomatic-typecheck` (`C0123`)** — "Use `isinstance()` rather than `type()` for a typecheck." The idiomatic way to perform an explicit typecheck in Python is to use `isinstance(x, Y)` rather than `type(x) == Y`, `type(x) is Y`. Though there are unusual situations where these give different results 🤓
- **`typevar-double-variance` (`C0131`)** — "TypeVar cannot be both covariant and contravariant". Emitted when both the `"covariant"` and `"contravariant"` keyword arguments are set to `"True"` in a `TypeVar` 💀
- **`typevar-name-mismatch` (`C0132`)** — "TypeVar name `"%s"` does not match assigned variable name `"%s"`". Emitted when a `TypeVar` is assigned to a variable that does not match its name argument 🙃
---
## 🧾 Basic checker Reports
- **`RP0101`** — Statistics by type 📊
---
## 🦋 Classes checker
Verbatim name of the checker is `classes` 💜
See also `classes` checker's options' documentation (the `#classes-options` bit of the docs, iykyk).
### 🌈 Classes checker Messages
- **`method-hidden` (`E0202`)** — "An attribute defined in `%s` line `%s` hides this method". Used when a class defines a method which is hidden by an instance attribute from an ancestor class or set by some client code 🙈
- **`access-member-before-definition` (`E0203`)** — "Access to member `%r` before its definition line `%s`". Used when an instance member is accessed before it's actually assigned 😬
- **`no-method-argument` (`E0211`)** — "Method `%r` has no argument". Used when a method which should have the bound instance as first argument has no argument defined 💀
- **`no-self-argument` (`E0213`)** — "Method `%r` should have `"self"` as first argument". Used when a method has an attribute different the `"self"` as first argument. This is considered as an error since this is a so common convention that you shouldn't break it! 🔥
- **`invalid-slots-object` (`E0236`)** — "Invalid object `%r` in `__slots__`, must contain only non empty strings". Used when an invalid (non-string) object occurs in `__slots__` 🚫
- **`assigning-non-slot` (`E0237`)** — "Assigning to attribute `%r` not defined in class slots". Used when assigning to an attribute not defined in the class slots 😭
- **`invalid-slots` (`E0238`)** — "Invalid `__slots__` object". Used when an invalid `__slots__` is found in class. Only a string, an iterable or a sequence is permitted ⭐
- **`inherit-non-class` (`E0239`)** — "Inheriting `%r`, which is not a class." Used when a class inherits from something which is not a class 🫠
- **`inconsistent-mro` (`E0240`)** — "Inconsistent method resolution order for class `%r`". Used when a class has an inconsistent method resolution order 🌀
- **`duplicate-bases` (`E0241`)** — "Duplicate bases for class `%r`". Duplicate use of base classes in derived classes raise `TypeError`s 💀
- **`class-variable-slots-conflict` (`E0242`)** — "Value `%r` in slots conflicts with class variable". Used when a value in `__slots__` conflicts with a class variable, property or method 👀
- **`invalid-class-object` (`E0243`)** — "Invalid assignment to `'__class__'`. Should be a class definition but got a `'%s'`". Used when an invalid object is assigned to a `__class__` property. Only a class is permitted 🎀
- **`invalid-enum-extension` (`E0244`)** — "Extending inherited Enum class `"%s"`". Used when a class tries to extend an inherited Enum class. Doing so will raise a `TypeError` at runtime 😤
- **`declare-non-slot` (`E0245`)** — message: `No such name %r in __slots__`. Fires when you've got a type annotation on a class but that name isn't in `__slots__`, and `__slots__` doesn't have a `__dict__` entry either. 🫠
- **`non-iterator-returned` (`E0301`)** — message: `__iter__ returns non-iterator`. Your `__iter__` returned something that isn't an iterable, meaning it has no `__next__` method. Sad. 😭
- **`unexpected-special-method-signature` (`E0302`)** — message: `The special method %r expects %s param(s), %d %s given`. You defined a special method with the wrong number of params — too few or too many and it might just not work at all, bestie. 💀
- **`invalid-length-returned` (`E0303`)** — message: `__len__ does not return non-negative integer`. Your `__len__` returned something that is *not* a non-negative integer. Rude.
- **`invalid-bool-returned` (`E0304`)** — message: `__bool__ does not return bool`. Your `__bool__` returned something that is not a `bool`. 🚩
- **`invalid-index-returned` (`E0305`)** — message: `__index__ does not return int`. Your `__index__` returned something that's not an integer.
- **`invalid-repr-returned` (`E0306`)** — message: `__repr__ does not return str`. Your `__repr__` returned something that isn't a string. Not the flex you thought it was. 💅
- **`invalid-str-returned` (`E0307`)** — message: `__str__ does not return str`. Your `__str__` returned something that isn't a string.
- **`invalid-bytes-returned` (`E0308`)** — message: `__bytes__ does not return bytes`. Your `__bytes__` returned something that isn't `bytes`.
- **`invalid-hash-returned` (`E0309`)** — message: `__hash__ does not return int`. Your `__hash__` returned something that isn't an integer.
- **`invalid-length-hint-returned` (`E0310`)** — message: `__length_hint__ does not return non-negative integer`. Your `__length_hint__` returned something that isn't a non-negative integer. 🍓
- **`invalid-format-returned` (`E0311`)** — message: `__format__ does not return str`. Your `__format__` returned something that isn't a string.
- **`invalid-getnewargs-returned` (`E0312`)** — message: `__getnewargs__ does not return a tuple`. Your `__getnewargs__` returned something that isn't a tuple.
- **`invalid-getnewargs-ex-returned` (`E0313`)** — message: `__getnewargs_ex__ does not return a tuple containing (tuple, dict)`. Your `__getnewargs_ex__` returned something that isn't of the form `tuple(tuple, dict)`. 🎀
- **`attribute-defined-outside-init` (`W0201`)** — message: `Attribute %r defined outside __init__`. An instance attribute got defined somewhere that isn't the `__init__` method. Put it back. 🙈
- **`bad-staticmethod-argument` (`W0211`)** — message: `Static method with %r as first argument`. A static method's first argument is either `"self"` or one of the values you set in the `valid-classmethod-first-arg` option or the `valid-metaclass-classmethod-first-arg` option — no thank you. 💅
- **`protected-access` (`W0212`)** — message: `Access to a protected member %s of a client class`. You touched a protected member (that's a class member whose name starts with an underscore, iykyk) from outside the class or a descendant of the class where it's defined. Boundaries, bestie. 🚧
- **`implicit-flag-alias` (`W0213`)** — message: `Flag member %(overlap)s shares bit positions with %(sources)s`. Multiple integer values inside an `enum.IntFlag` class are sharing a common bit position. Overlap is not the vibe. 🔥
- **`arguments-differ` (`W0221`)** — message: `%s %s %r method`. A method has a different number of arguments than the implemented interface or the overridden method. Extra arguments that have default values are ignored, so don't stress about those. ✨
- **`signature-differs` (`W0222`)** — message: `Signature differs from %s %r method`. A method's signature is different from the one in the implemented interface or in the overridden method.
- **`abstract-method` (`W0223`)** — message: `Method %r is abstract in class %r but is not overridden in child class %r`. An abstract method (one that raises `NotImplementedError`) never got overridden in a concrete class. Somebody has to do the work. 😤
- **`super-init-not-called` (`W0231`)** — message: `__init__ method from base class %r is not called`. An ancestor class has an `__init__` method and your derived class just… didn't call it. Rude to your ancestors, honestly.
- **`non-parent-init-called` (`W0233`)** — message: `__init__ method from a non direct base class %r is called`. You called `__init__` on a class that isn't in the direct ancestors of the class being analysed.
- **`invalid-overridden-method` (`W0236`)** — message: `Method %r was expected to be %r, found it instead as %r`. A method was overridden in a way that doesn't match its base class, which could legit cause bugs at runtime. Not a drill. 🔥
- **`arguments-renamed` (`W0237`)** — message: `%s %s %r method`. A method parameter has a different name than it does in the implemented interface or in the overridden method. Same energy, different name — pylint noticed. 👀
- **`unused-private-member` (`W0238`)** — message: ``Unused private member `%s.%s` ``. A private member of a class is defined but never used. It's just sitting there. 😭
- **`overridden-final-method` (`W0239`)** — message: `Method %r overrides a method decorated with typing.final which is defined in class %r`. You overrode a method decorated with `typing.final`. It said final. It meant final. 💀
- **`subclassed-final-class` (`W0240`)** — message: `Class %r is a subclass of a class decorated with typing.final: %r`. You subclassed a class decorated with `typing.final`. Bold of you. 🙈
- **`redefined-slots-in-subclass` (`W0244`)** — message: `Redefined slots %r in subclass`. A slot got re-defined in a subclass. 🎀
- **`super-without-brackets` (`W0245`)** — message: `Super call without brackets`. You wrote a `super` call without the brackets, so it's not an actual call and it does *not* work as expected. Brackets. Please. 🫶
- **`useless-parent-delegation` (`W0246`)** — message: `Useless parent or super() delegation in method %r`. Whenever pylint can detect that an overridden method is useless because it just relies on parent or `super()` delegation to do the same thing as another method from the MRO. Why are you even here then. 💅
- **`no-classmethod-decorator` (`R0202`)** — message: `Consider using a decorator instead of calling classmethod`. A class method was defined without using the decorator syntax. Consider the decorator, bestie. ✨
- **`no-staticmethod-decorator` (`R0203`)** — message: `Consider using a decorator instead of calling staticmethod`. A static method was defined without using the decorator syntax. Same note. 🎀
- **`useless-object-inheritance` (`R0205`)** — message: `Class %r inherits from object, can be safely removed from bases in python3`. A class inherits from `object`, which under `python3` is implicit, hence it can be safely removed from bases. Slay, delete it. 🦋
- **`property-with-parameters` (`R0206`)** — message: `Cannot have defined parameters for properties`. A property also has parameters, which are useless given that properties can't be called with additional arguments. Ngl, that's not a thing.
- **`bad-classmethod-argument` (`C0202`)** — message: `Class method %s should have %s as first argument`. A class method's first argument is named differently than the value in the `valid-classmethod-first-arg` option (which defaults to `"cls"`) — naming it `cls` is recommended so you can easily tell them apart from regular instance methods. 💖
- **`bad-mcs-method-argument` (`C0203`)** — message: `Metaclass method %s should have %s as first argument`. A metaclass method's first argument is named differently than the value in the `valid-classmethod-first-arg` option (defaults to `"cls"`) — recommended so they're easy to differentiate from regular instance methods. 🌸
- **`bad-mcs-classmethod-argument` (`C0204`)** — message: `Metaclass class method %s should have %s as first argument`. A metaclass class method's first argument is named differently than the value in the `valid-metaclass-classmethod-first-arg` option (defaults to `"mcs"`) — recommended so you can differentiate them from regular instance methods. 👑
- **`single-string-used-for-slots` (`C0205`)** — message: `Class __slots__ should be a non-string iterable`. Your class's `__slots__` is a simple string instead of an iterable. Iterable, bestie. ⭐
- **`method-check-failed` (`F0202`)** — message: `Unable to check methods signature (%s / %s)`. Pylint was unable to check methods signature compatibility for an unexpected reason. Please report this kind if you don't make sense of it. 🫠
---
## 🎀 Dataclass checker 🎀
Verbatim name of the checker is `dataclass`. 🦋
### Dataclass checker Messages
- **`invalid-field-call` (`E3701`)** — message: `Invalid usage of field(), %s`. The `dataclasses.field()` specifier should only be used as the value of an assignment within a dataclass, or within the `make_dataclass()` function. Nowhere else. No cap. 💅
---
## 🌈 Design checker 🌈
Verbatim name of the checker is `design`.
See also design checker's options' documentation `<#design-options>` — the link is right there, go click it, iykyk. 🫶
### Design checker Messages
- **`too-many-ancestors` (`R0901`)** — message: `Too many ancestors (%s/%s)`. Your class has too many parent classes — try to reduce this to get a simpler (and so easier to use) class. Family tree is giving forest. 🌳
- **`too-many-instance-attributes` (`R0902`)** — message: `Too many instance attributes (%s/%s)`. Your class has too many instance attributes — reduce, simplify, slay. ✨
- **`too-few-public-methods` (`R0903`)** — message: `Too few public methods (%s/%s)`. Your class has too few public methods, so just be sure it's really worth it. 💀
- **`too-many-public-methods` (`R0904`)** — message: `Too many public methods (%s/%s)`. Too many public methods — try to reduce this to get a simpler (and so easier to use) class. 🎀
- **`too-many-return-statements` (`R0911`)** — message: `Too many return statements (%s/%s)`. A function or method has too many return statements, which makes it hard to follow. Where are you even going. 😭
- **`too-many-branches` (`R0912`)** — message: `Too many branches (%s/%s)`. A function or method has too many branches, making it hard to follow. 🌿
- **`too-many-arguments` (`R0913`)** — message: `Too many arguments (%s/%s)`. A function or method takes too many arguments. That's a lot of arguments for someone who isn't even in the group chat. 🙈
- **`too-many-locals` (`R0914`)** — message: `Too many local variables (%s/%s)`. A function or method has too many local variables. 🍓
- **`too-many-statements` (`R0915`)** — message: `Too many statements (%s/%s)`. A function or method has too many statements — you should then split it in smaller functions / methods. Break it up, bestie. 🧚
- **`too-many-boolean-expressions` (`R0916`)** — message: `Too many boolean expressions in if statement (%s/%s)`. An `if` statement contains too many boolean expressions. The `if` is doing the absolute most. 🔥
### `too-many-positional-arguments` (`R0917`)
Too many positional arguments (`%s/%s`) — no cap, if your function has too many positional arguments, pylint is gonna call it out. Bestie, that's a vibe check your signature just failed. 😭
---
## 🎀 Exceptions checker 🎀
In case you were wondering what its government name is — the verbatim name of the checker is `exceptions`. ✨
See also the exceptions checker's options documentation: `#exceptions-options`.
## 💜 Exceptions checker Messages 💜
### `bad-except-order` (`E0701`)
Bad except clauses order (`%s`) — used when your `except` clauses aren't in the correct order (going from the more specific to the more generic). And ngl this one actually matters: if you don't fix the order, some exceptions may not be caught by the most specific handler. Like, the general one is literally out here stealing the specific one's moment. 💀
### `raising-bad-type` (`E0702`)
Raising `%s` while only classes or instances are allowed — used when something that's neither a class nor an instance gets raised (i.e. a `TypeError` will be raised). You tried to raise a whole vibe and Python said "not today". 🙈
### `misplaced-bare-raise` (`E0704`)
The raise statement is not inside an `except` clause — used when a bare `raise` is not used inside an `except` clause. This generates an error, since there are no active exceptions to be reraised. The one exception (pun intended, iykyk) is a bare `raise` inside a `finally` clause, which might work, as long as an exception is raised inside the `try` block — but it is nevertheless a code smell that must not be relied upon. So bestie, please don't build your whole app on it. 🫶
### `bad-exception-cause` (`E0705`)
Exception cause set to something which is not an exception, nor `None` — used when using the syntax `"raise ... from ..."`, where the exception cause is not an exception, nor `None`. The `from` part has one job and it's not doing it. 😭
### `raising-non-exception` (`E0710`)
Raising a class which doesn't inherit from `BaseException` — used when a class which doesn't inherit from `BaseException` is raised. Ma'am, that's not an exception, that's just a class having main character energy. 👑
### `notimplemented-raised` (`E0711`)
`NotImplemented` raised - should raise `NotImplementedError` — used when `NotImplemented` is raised instead of `NotImplementedError`. They are two different girlies, do not confuse them. 💅
### `catching-non-exception` (`E0712`)
Catching an exception which doesn't inherit from `Exception`: `%s` — used when a class which doesn't inherit from `Exception` is used as an exception in an `except` clause. Catch hands, not non-exceptions. 🔥
### `bare-except` (`W0702`)
No exception type(s) specified — a bare `except:` clause will catch `SystemExit` and `KeyboardInterrupt` exceptions, making it harder to interrupt a program with Control-C, and can disguise other problems. If you want to catch all exceptions that signal program errors, use `except Exception:` (bare `except` is equivalent to `except BaseException:`). So like — bare `except` is *not* the slay you think it is. 🚩
### `duplicate-except` (`W0705`)
Catching previously caught exception type `%s` — used when an `except` catches a type that was already caught by a previous handler. It already got caught, bestie. It's in the group chat twice. 🫠
### `try-except-raise` (`W0706`)
The except handler raises immediately — used when an except handler uses `raise` as its first or only operator. This is useless because it raises back the exception immediately. Remove the `raise` operator or the entire `try-except-raise` block! Literally what was the point. 😭
### `raise-missing-from` (`W0707`)
Consider explicitly re-raising using `%s'%s from %s'` — Python's exception chaining shows the traceback of the current exception, but also of the original exception. When you raise a new exception after another exception was caught it's likely that the second exception is a friendly re-wrapping of the first exception. In such cases `raise from` provides a better link between the two tracebacks in the final error. It's giving "cite your sources". ✨
### `binary-op-exception` (`W0711`)
Exception to catch is the result of a binary `"%s"` operation — used when the exception to catch is of the form `"except A or B:"`. If intending to catch multiple, rewrite as `"except (A, B):"`. The `or` is a lie and pylint clocked it. 💅
### `raising-format-tuple` (`W0715`)
Exception arguments suggest string formatting might be intended — used when passing multiple arguments to an exception constructor, the first of them a string literal containing what appears to be placeholders intended for formatting. Like, you put `%s` in there and pylint is like "girl, did you mean to f-string that?" 🔍
### `wrong-exception-operation` (`W0716`)
Invalid exception operation. `%s` — used when an operation is done against an exception, but the operation is not valid for the exception in question. Usually emitted when having binary operations between exceptions in except handlers. Exceptions are not math, bestie. 🧮
### `broad-exception-caught` (`W0718`)
Catching too general exception `%s` — if you use a naked `except Exception:` clause, you might end up catching exceptions other than the ones you expect to catch. This can hide bugs or make it harder to debug programs when unrelated errors are hidden. Highkey the worst kind of plot twist. 😭
### `broad-exception-raised` (`W0719`)
Raising too general exception: `%s` — raising exceptions that are too generic force you to catch exceptions generically too. It will force you to use a naked `except Exception:` clause. You might then end up catching exceptions other than the ones you expect to catch. This can hide bugs or make it harder to debug programs when unrelated errors are hidden. Be specific, it's giving thoughtful. 🦋
---
## 🌸 Format checker 🌸
Verbatim name of the checker is `format`. ✨
See also the format checker's options documentation: `#format-options`.
## 🎀 Format checker Messages 🎀
### `unnecessary-semicolon` (`W0301`)
Unnecessary semicolon — used when a statement is ended by a semi-colon (`";"`), which isn't necessary (that's python, not C ;). The `;` is a guest who wasn't invited. 🙈
### `bad-indentation` (`W0311`)
Bad indentation. Found `%s` `%s`, expected `%s` — used when an unexpected number of indentation's tabulations or spaces has been found. Spaces vs tabs discourse, but in your actual file. 😭
### `line-too-long` (`C0301`)
Line too long (`%s/%s`) — used when a line is longer than a given number of characters. She's doing too much, and not in a good way. 💀
### `too-many-lines` (`C0302`)
Too many lines in module (`%s/%s`) — used when a module has too many lines, reducing its readability. It's giving "the file became a whole saga". 📚
### `trailing-whitespace` (`C0303`)
Trailing whitespace — used when there is whitespace between the end of a line and the newline. Invisible, uninvited, iconic in the worst way. 👻
### `missing-final-newline` (`C0304`)
Final newline missing — used when the last line in a file is missing a newline. Give the file a proper ending, periodt. 🎬
### `trailing-newlines` (`C0305`)
Trailing newlines — used when there are trailing blank lines in a file. Extra blank lines at the end = the file is stalling. ✋
### `multiple-statements` (`C0321`)
More than one statement on a single line — used when more than one statement is found on the same line. One thing at a time, bestie. 🫶
### `superfluous-parens` (`C0325`)
Unnecessary parens after `%r` keyword — used when a single item in parentheses follows an `if`, `for`, or other keyword. Extra parens, no thoughts, just vibes. 💅
### `mixed-line-endings` (`C0327`)
Mixed line endings LF and CRLF — used when there are mixed (LF and CRLF) newline signs in a file. Two aesthetics, one file, absolute chaos. 😵💫
### `unexpected-line-ending-format` (`C0328`)
Unexpected line ending format. There is `'%s'` while it should be `'%s'`. — used when there is different newline than expected. Not the newline we ordered. 😤
---
## 🦋 Imports checker 🦋
Verbatim name of the checker is `imports`. ✨
See also the imports checker's options documentation: `#imports-options`.
## 💜 Imports checker Messages 💜
### `import-error` (`E0401`)
Unable to import `%s` — used when pylint has been unable to import a module. The module ghosted you. 💔
### `relative-beyond-top-level` (`E0402`)
Attempted relative import beyond top-level package — used when a relative import tries to access too many levels in the current package. Bestie, you went too far up the family tree. 🌳
### `wildcard-import` (`W0401`)
Wildcard import `%s` — used when `from module import *` is detected. `*` said "I'll bring everyone" and now you don't know who's at the party. 🎉
### `reimported` (`W0404`)
Reimport `%r` (imported line `%s`) — used when a module is imported more than once. It's already here, girl, it's already in the group chat. 😭
### `import-self` (`W0406`)
Module import itself — used when a module is importing itself. The snake is eating its own tail and honestly? Not the serve you think. 🐍
### `preferred-module` (`W0407`)
Prefer importing `%r` instead of `%r` — used when a module imported has a preferred replacement module. Pylint has opinions and it's sharing them. 💅
### `misplaced-future` (`W0410`)
`__future__` import is not the first non docstring statement — Python 2.5 and greater require `__future__` import to be the first non docstring statement in the module. It's the opening act, not the surprise guest. 🎤
### `shadowed-import` (`W0416`)
Shadowed `%r` (imported line `%s`) — used when a module is aliased with a name that shadows another import. Name collisions are so not it. 🫠
### `deprecated-module` (`W4901`)
Deprecated module `%r` — a module marked as deprecated is imported. She's retiring, bestie, let her go. 👋
### `cyclic-import` (`R0401`)
Cyclic import (`%s`) — used when a cyclic import between two or more modules is detected. They're both obsessed with each other and nothing ever loads. 💞
### `consider-using-from-import` (`R0402`)
Use `'from %s import %s'` instead — emitted when a submodule of a package is imported and aliased with the same name, e.g., instead of `import concurrent.futures as futures` use `from concurrent import futures`. Glow-up available, take it. ✨
### `multiple-imports` (`C0410`)
Multiple imports on one line (`%s`) — used when an import statement importing multiple modules is detected. One per line, like one snack per hand. 🍓
### `wrong-import-order` (`C0411`)
`%s` should be placed before `%s` — used when PEP8 import order is not respected (standard imports first, then third-party libraries, then local imports). Order matters, it's a lineup not a pile. 🔥
### `ungrouped-imports` (`C0412`)
Imports from package `%s` are not grouped — used when imports are not grouped by packages. Sit with your people. 🫶
### `wrong-import-position` (`C0413`)
Import `"%s"` should be placed at the top of the module — used when code and imports are mixed. Imports go up top, that's the whole assignment. 👑
### `useless-import-alias` (`C0414`)
Import alias does not rename original package — used when an import alias is same as original package, e.g., using `import numpy as numpy` instead of `import numpy as np`. That alias did absolutely nothing, it's giving participation trophy. 💀
### `import-outside-toplevel` (`C0415`)
Import outside toplevel (`%s`) — used when an import statement is used anywhere other than the module toplevel. Move this import to the top of the file. She's out here importing in the middle of nowhere. 😭
## 🌈 Imports checker Reports 🌈
- `RP0401` — External dependencies
- `RP0402` — Modules dependencies graph
---
## 🧚 Lambda-Expressions checker 🧚
Verbatim name of the checker is `lambda-expressions`. ⭐
## 💜 Lambda-Expressions checker Messages 💜
### `unnecessary-lambda-assignment` (`C3001`)
Lambda expression assigned to a variable. Define a function using the `"def"` keyword instead. — used when a lambda expression is assigned to a variable rather than defining a standard function with the `"def"` keyword. Named functions are right there, bestie. 💅
### `unnecessary-direct-lambda-call` (`C3002`)
Lambda expression called directly. Execute the expression inline instead. — used when a lambda expression is directly called rather than executing its contents inline. Why wrap it just to unwrap it. 🙈
---
## 🍓 Logging checker 🍓
Verbatim name of the checker is `logging`. ✨
See also the logging checker's options documentation: `#logging-options`.
## 🎀 Logging checker Messages 🎀
### `logging-unsupported-format` (`E1200`)
Unsupported logging format character `%r` (`%#02x`) at index `%d` — used when an unsupported format character is used in a logging statement format string. That format char is not on the list, babe. 💀
- **`logging-format-truncated (E1201)`** — bestie, your logging format string just… stops? In the middle of a conversion specifier? Yeah. This one fires when a logging statement's format string terminates *before* the end of a conversion specifier. It's giving "sentence that ends in the middle of a" 💀
- **`logging-too-many-args (E1205)`** — too many arguments for the logging format string. You showed up with extra guests and there's no seat for them 🪑
- **`logging-too-few-args (E1206)`** — not enough arguments for the logging format string. The format string asked for more, you delivered less 😭
- **`logging-not-lazy (W1201)`** — "Use %s formatting in logging functions." Fires when a logging statement has a call form of `logging.<logging method>(format_string % (format_args...))`. So pls use another type of string formatting instead, okay? You *can* use % formatting but leave the interpolation to the logging function by passing the parameters as arguments. If `logging-fstring-interpolation` is disabled then you can use fstring formatting. If `logging-format-interpolation` is disabled then you can use `str.format`. Choose your fighter 🥊
- **`logging-format-interpolation (W1202)`** — "Use %s formatting in logging functions." Same energy, different shape: fires when a logging statement has a call form of `logging.<logging method>(format_string.format(format_args...))`. Use another type of string formatting instead. You can use % formatting but leave the interpolation to the logging function by passing the parameters as arguments. If `logging-fstring-interpolation` is disabled then you can use fstring formatting. If `logging-not-lazy` is disabled then you can use % formatting as normal. iykyk 💅
- **`logging-fstring-interpolation (W1203)`** — "Use %s formatting in logging functions." Fires when a logging statement has a call form of `logging.<logging method>(f"...")`. Use another type of string formatting instead. You can use % formatting but leave the interpolation to the logging function by passing the parameters as arguments. If `logging-format-interpolation` is disabled then you can use `str.format`. If `logging-not-lazy` is disabled then you can use % formatting as normal. The fstring was cute but this is not its moment 🎀
---
## 🧚 Match Statements checker
Verbatim name of the checker is `match_statements`.
## ✨ Match Statements checker Messages ✨
- **`bare-name-capture-pattern (E1901)`** — "The name capture `case %s` makes the remaining patterns unreachable. Use a dotted name (for example an enum) to fix this." Emitted when a name capture pattern is used in a match statement and there are case statements below it. The cases underneath are literally screaming "hello?? we're right here??" and nobody hears them 😭
- **`invalid-match-args-definition (E1902)`** — "`__match_args__` must be a tuple of strings." Emitted if `__match_args__` isn't a tuple of strings required for match. That's it. That's the requirement. Don't argue with the tuple 🫶
- **`too-many-positional-sub-patterns (E1903)`** — "%s expects %d positional sub-patterns (given %d)". Emitted when the number of allowed positional sub-patterns exceeds the number of allowed sub-patterns specified in `__match_args__`. Too many sub-patterns bestie, count again ✨
- **`multiple-class-sub-patterns (E1904)`** — "Multiple sub-patterns for attribute %s". Emitted when there's more than one sub-pattern for a specific attribute in a class pattern. One sub-pattern per attribute, we're not doing duplicates here 💅
- **`match-class-bind-self (R1905)`** — "Use '%s() as %s' instead". Match class patterns are faster if the name binding happens for the whole pattern and any lookup for `__match_args__` can be avoided. Speed. It's giving optimization 👑
- **`match-class-positional-attributes (R1906)`** — "Use keyword attributes instead of positional ones (%s)". Keyword attributes are more explicit and slightly faster since CPython can skip the `__match_args__` lookup. More explicit AND faster? Obsessed 🌸
---
## 🦋 Method Args checker
Verbatim name of the checker is `method_args`.
> See also method_args checker's options' documentation — `#method-args-options` 🔍
## ✨ Method Args checker Messages ✨
- **`positional-only-arguments-expected (E3102)`** — "`%s()` got some positional-only arguments passed as keyword arguments: %s". Emitted when positional-only arguments have been passed as keyword arguments. Fix: remove the keywords for the affected arguments in the function call. They said positional-only and they *meant* positional-only, no cap 🙈
- **`missing-timeout (W3101)`** — "Missing timeout argument for method '%s' can cause your program to hang indefinitely." Used when a method needs a `'timeout'` parameter in order to avoid waiting for a long time. If no timeout is specified explicitly, the default value is used. For example, for `'requests'` the program will never time out (i.e. hang indefinitely). Your program will simply sit there. Forever. Staring at the wall 🫠
---
## ⭐ Metrics checker
Verbatim name of the checker is `metrics`.
## ✨ Metrics checker Reports ✨
- **`RP0701`** Raw metrics
---
## 💜 Miscellaneous checker
Verbatim name of the checker is `miscellaneous`.
> See also miscellaneous checker's options' documentation — `#miscellaneous-options` 🔍
## ✨ Miscellaneous checker Messages ✨
- **`fixme (W0511)`** — Used when a warning note as `FIXME` or `XXX` is detected. We love a little note-to-self that we will absolutely never come back to 🎀
- **`use-symbolic-message-instead (I0023)`** — Used when a message is enabled or disabled by id. Names, bestie. Use the names ✨
---
## 🌈 Modified Iteration checker
Verbatim name of the checker is `modified_iteration`.
## ✨ Modified Iteration checker Messages ✨
- **`modified-iterating-dict (E4702)`** — "Iterated dict '%s' is being modified inside for loop body, iterate through a copy of it instead." Emitted when items are added or removed to a dict being iterated through. Doing so raises a `RuntimeError`. Not a vibe check you want to fail, fr fr 💀
- **`modified-iterating-set (E4703)`** — "Iterated set '%s' is being modified inside for loop body, iterate through a copy of it instead." Emitted when items are added or removed to a set being iterated through. Doing so raises a `RuntimeError`. Same crime, different data structure 🚨
- **`modified-iterating-list (W4701)`** — "Iterated list '%s' is being modified inside for loop body, consider iterating through a copy of it instead." Emitted when items are added or removed to a list being iterated through. Doing so can result in unexpected behaviour, that is why it is preferred to use a copy of the list. "Unexpected behaviour" is menace-speak. Just copy the list bestie 😭
---
## 🧸 Nested Min Max checker
Verbatim name of the checker is `nested_min_max`.
## ✨ Nested Min Max checker Messages ✨
- **`nested-min-max (W3301)`** — "Do not use nested call of '%s'; it's possible to do '%s' instead". Nested calls `min(1, min(2, 3))` can be rewritten as `min(1, 2, 3)`. Flatten it. Slay 🦋
---
## 🎀 Newstyle checker
Verbatim name of the checker is `newstyle`.
## ✨ Newstyle checker Messages ✨
- **`bad-super-call (E1003)`** — "Bad first argument %r given to super()". Used when another argument than the current class is given as first argument of the `super` builtin. That is not your class, babe 💅
---
## 🍓 Nonascii-Checker checker
Verbatim name of the checker is `nonascii-checker`.
## ✨ Nonascii-Checker checker Messages ✨
- **`non-ascii-file-name (W2402)`** — "%s name \"%s\" contains a non-ASCII character." Under python 3.5, PEP 3131 allows non-ascii identifiers, but not non-ascii file names. Since Python 3.5, even though Python supports UTF-8 files, some editors or tools don't. So like… it's not Python's fault, it's everyone else's 🙈
- **`non-ascii-name (C2401)`** — "%s name \"%s\" contains a non-ASCII character, consider renaming it." Used when the name contains at least one non-ASCII unicode character. See `https://peps.python.org/pep-0672/#confusing-features` for a background why this could be bad. If your programming guideline defines that you are programming in English, then there should be no need for non ASCII characters in Python Names. If not you can simply disable this check. Your call, bestie 🫶
- **`non-ascii-module-import (C2403)`** — "%s name \"%s\" contains a non-ASCII character, use an ASCII-only alias for import." Used when the name contains at least one non-ASCII unicode character. See `https://peps.python.org/pep-0672/#confusing-features` for a background why this could be bad. If your programming guideline defines that you are programming in English, then there should be no need for non ASCII characters in Python Names. If not you can simply disable this check. Alias era ✨
---
## 👑 Refactoring checker
Verbatim name of the checker is `refactoring`.
> See also refactoring checker's options' documentation — `#refactoring-options` 🔍
## ✨ Refactoring checker Messages ✨
- **`consider-merging-isinstance (R1701)`** — "Consider merging these isinstance calls to isinstance(%s, (%s))". Used when multiple consecutive `isinstance` calls can be merged into one. Two calls walked so one call could run 🏃♀️
- **`too-many-nested-blocks (R1702)`** — "Too many nested blocks (%s/%s)". Used when a function or a method has too many nested blocks. This makes the code less understandable and maintainable. It's giving inception. We're three levels deep and nobody knows the way out 😵💫
- **`simplifiable-if-statement (R1703)`** — "The if statement can be replaced with %s". Used when an if statement can be replaced with `'bool(test)'`. Simplify her 💅
- **`redefined-argument-from-local (R1704)`** — "Redefining argument with the local name %r". Used when a local name is redefining an argument, which might suggest a potential error. This is taken in account only for a handful of name binding operations, such as for iteration, `with` statement assignment and exception handler assignment. Same name, different meaning — that's a plot twist nobody asked for 🎭
- **`no-else-return (R1705)`** — "Unnecessary \"%s\" after \"return\", %s". Used in order to highlight an unnecessary block of code following an if, or a try/except containing a return statement. As such, it will warn when it encounters an else following a chain of ifs, all of them containing a return statement. You already returned. The else is just standing there. Unemployed 💀
- **`consider-using-ternary (R1706)`** — "Consider using ternary (%s)". Used when one of known pre-python 2.5 ternary syntax is used. That syntax is vintage in the "why is this still in the closet" way, not the cute way 🌸
- **`trailing-comma-tuple (R1707)`** — "Disallow trailing comma tuple". In Python, a tuple is actually created by the comma symbol, not by the parentheses. Unfortunately, one can actually create a tuple by misplacing a trailing comma, which can lead to potential weird bugs in your code. You should always use parentheses explicitly for creating a tuple. The comma is the main character here and that's the problem 👑
- **`stop-iteration-return (R1708)`** — "Do not raise StopIteration in generator, use return statement instead". According to PEP479, the raise of `StopIteration` to end the loop of a generator may lead to hard to find bugs. This PEP specify that raise `StopIteration` has to be replaced by a simple return statement. Just return. It's not that deep 🫶
- **`simplify-boolean-expression (R1709)`** — "Boolean expression may be simplified to %s". Emitted when redundant pre-python 2.5 ternary syntax is used. Simplify. Simplify. Simplify. 🔥
### 🌸 `inconsistent-return-statements` (R1710)
Okay so this one is a vibe check on your `return` statements: either **all** the return statements in a function return an expression, or **none** of them do. No mixing, bestie, it's giving chaos. Per PEP8, if any return statement returns an expression, then any return statement where no value comes back has to say it out loud as `return None`, and there should be an explicit return statement at the end of the function (if it's reachable). 💅
### 🎀 `useless-return` (R1711)
**Useless return at end of function or method.** Emitted when a single `"return"` or `"return None"` statement shows up at the end of a function or method definition. You can safely delete it because Python will implicitly return `None` anyway — it's just sitting there doing cardio for nothing 😭
### ✨ `consider-swap-variables` (R1712)
**Consider using tuple unpacking for swapping variables.** You literally do not have to bring a temporary variable into this to swap two variables. Using `"tuple unpacking"` to swap them directly makes your intention so much clearer. Iconic behavior only. 🫶
### 💖 `consider-using-join` (R1713)
**Consider using `str.join(sequence)` for concatenating strings from an iterable.** Compared to for-loop iteration, `str.join(sequence)` is faster, uses less memory, and is easier to read. It's the main character of string concatenation, ngl.
### 🌈 `consider-using-in` (R1714)
**Consider merging these comparisons with `'in'` by using `'%s %sin (%s)'`.** Use a set instead if the elements are hashable. So if you're checking whether a variable equals one of many values, combine those values into a set or tuple and check whether the variable is contained `"in"` it instead of checking equality against each one individually. Faster *and* less verbose — we love a two-for-one. 🔥
### 🦋 `consider-using-get` (R1715)
**Consider using `dict.get` for getting values from a dict if a key is present or a default if not.** Using the builtin `dict.get` for grabbing a value from a dictionary when the key is there, or a default when it isn't, is simpler and considered more idiomatic — although sometimes it's a bit slower. Trade-offs, bestie, we live with them 💀
### ⭐ `chained-comparison` (R1716)
**Simplify chained comparison between the operands.** This message drops when pylint runs into a boolean operation like `a < b and b < c`, and it's suggesting you refactor it to `a < b < c` instead. Shorter, cuter, slays. ✨
### 🍓 `consider-using-dict-comprehension` (R1717)
**Consider using a dictionary comprehension.** Emitted when pylint detects a dictionary being created with the `dict()` callable plus a transient list. There's nothing syntactically wrong with that code, but it's hard to read and can be simplified into a dict comprehension. Bonus: it's faster since you don't need to create that extra transient list. 💅
### 🧚 `consider-using-set-comprehension` (R1718)
**Consider using a set comprehension.** Again — nothing syntactically wrong here, it's just hard to read and can be simplified into a set comprehension. Also faster, because you skip creating another transient list. Same energy as its dict sibling, honestly. 🦋
### 👑 `simplifiable-if-expression` (R1719)
**The if expression can be replaced with `%s`.** Used when an if expression can be replaced with `'bool(test)'`, or just `'test'` if the boolean cast is already implicit. Less is more, periodt.
### 🌸 `no-else-raise` (R1720)
**Unnecessary `"%s"` after `"raise"`, `%s`.** Used to highlight an unnecessary block of code following an `if`, or a `try`/`except` containing a `raise` statement. So it warns when it runs into an `else` after a chain of `if`s, all of which contain a `raise` statement. That `else` is doing nothing, bestie, let it go 😭
### 💜 `unnecessary-comprehension` (R1721)
**Unnecessary use of a comprehension, use `%s` instead.** Instead of using an identity comprehension, consider using the list, dict, or set constructor. It's faster and simpler — no cap.
### 🎀 `consider-using-sys-exit` (R1722)
**Consider using `'sys.exit'` instead.** Contrary to `'exit()'` or `'quit()'`, `'sys.exit'` does not rely on the site module being available (since the `'sys'` module is always available). Reliability is the vibe. ✨
### 🦋 `no-else-break` (R1723)
**Unnecessary `"%s"` after `"break"`, `%s`.** Used to highlight an unnecessary block of code following an `if` that contains a `break` statement. It warns when it encounters an `else` after a chain of `if`s, all of them containing a `break` statement. Yeah, that `else` is just hanging around. 💀
### 💖 `no-else-continue` (R1724)
**Unnecessary `"%s"` after `"continue"`, `%s`.** Same deal — it highlights an unnecessary block of code following an `if` that contains a `continue` statement. Warns when it hits an `else` following a chain of `if`s, all of them containing a `continue` statement. Trio complete 🫶
### 🌈 `super-with-arguments` (R1725)
**Consider using Python 3 style `super()` without arguments.** Emitted when you call the `super()` builtin with the current class and instance. On Python 3 those arguments are the default and they can totally be omitted. ✨
### ⭐ `simplifiable-condition` (R1726)
**Boolean condition `"%s"` may be simplified to `"%s"`.** Emitted when a boolean condition is able to be simplified. That's it, that's the message, and honestly it's doing the most by doing the least.
### 🍓 `condition-evals-to-constant` (R1727)
**Boolean condition `'%s'` will always evaluate to `'%s'`.** Emitted when a boolean condition can be simplified to a constant value. It's giving "this check is lying to you" 🙈
### 🧚 `consider-using-generator` (R1728)
**Consider using a generator instead `'%s(%s)'`.** If your container can be large, using a generator will bring better performance. Big container = big yikes, so generate on demand, bestie. 🔥
### 👑 `use-a-generator` (R1729)
**Use a generator instead `'%s(%s)'`.** A comprehension inside of `'any'`, `'all'`, `'max'`, `'min'`, or `'sum'` is unnecessary. A generator would be sufficient and faster. Free upgrade, no notes. 💅
### 🌸 `consider-using-min-builtin` (R1730)
**Consider using `'%s'` instead of unnecessary if block.** Using the `min` builtin instead of a conditional improves readability and conciseness. It ate, honestly.
### 💜 `consider-using-max-builtin` (R1731)
**Consider using `'%s'` instead of unnecessary if block.** Using the `max` builtin instead of a conditional improves readability and conciseness. Twin of the one above, equally slay. ✨
### 🎀 `consider-using-with` (R1732)
**Consider using `'with'` for resource-allocating operations.** Emitted if a resource-allocating assignment or call may be replaced by a `'with'` block. By using `'with'`, the release of the allocated resources is ensured **even in the case of an exception**. Safety first, no cap 🫶
### 🦋 `unnecessary-dict-index-lookup` (R1733)
**Unnecessary dictionary index lookup, use `'%s'` instead.** Emitted when you're iterating over the dictionary items (that's the key-item pairs) and you access the value by index lookup. You can access that value directly instead — stop making it work so hard 😭
### 💖 `use-list-literal` (R1734)
**Consider using `[]` instead of `list()`.** Emitted when you use `list()` to create an empty list instead of the literal `[]`. The literal is faster because it avoids an additional function call. Micro-optimization but make it cute. ✨
### 🌈 `use-dict-literal` (R1735)
**Consider using `'%s'` instead of a call to `'dict'`.** Emitted when you use `dict()` to create a dictionary instead of a literal `'{ ... }'`. The literal is faster since it avoids that extra function call. Same icon energy as the list one.
### ⭐ `unnecessary-list-index-lookup` (R1736)
**Unnecessary list index lookup, use `'%s'` instead.** Emitted when you iterate over an enumeration and access the value by index lookup. The value can be accessed directly instead. Bestie, it's right there. 💀
### 🍓 `use-yield-from` (R1737)
**Use `'yield from'` directly instead of yielding each element one by one.** Yielding directly from the iterator is faster and arguably cleaner code than yielding each element one by one inside the loop. Glow-up for your generator 🔥
### 🧚 `unnecessary-negation` (C0117)
**Consider changing `"%s"` to `"%s"`.** Used when a boolean expression contains an unneeded negation — for example when two negation operators cancel each other out. Double negative = no thanks 🙈
### 👑 `consider-using-enumerate` (C0200)
**Consider using `enumerate` instead of iterating with `range` and `len`.** Emitted when code that iterates with `range` and `len` is encountered. Such code can be simplified using the `enumerate` builtin. It's literally built for this 💅
### 🌸 `consider-iterating-dictionary` (C0201)
**Consider iterating the dictionary directly instead of calling `.keys()`.** Emitted when the keys of a dictionary are iterated through the `.keys()` method, or when `.keys()` is used for a membership check. It's enough to iterate through the dictionary itself — `for key in dictionary`. And for membership checks, `if key in dictionary` is faster. Fewer characters, more speed. ✨
### 💜 `consider-using-dict-items` (C0206)
**Consider iterating with `.items()`.** Emitted when you iterate over the keys of a dictionary and access the value by index lookup. Both the key *and* the value can be accessed by iterating using the `.items()` method of the dictionary instead. Two birds, one method, slay 🫶
### 🎀 `use-maxsplit-arg` (C0207)
**Use `%s` instead.** Emitted when you're accessing only the first or last element of `str.split()`. You can grab the first and last element by using `str.split(sep, maxsplit=1)[0]` or `str.rsplit(sep, maxsplit=1)[-1]` instead. Peak efficiency, honestly.
### 🦋 `use-sequence-for-iteration` (C0208)
**Use a sequence type when iterating over values.** When iterating over values, sequence types (e.g., lists, tuples, ranges) are more efficient than sets. Sets are for vibes and membership checks, not this ✨
### 💖 `consider-using-f-string` (C0209)
**Formatting a regular string which could be an f-string.** Used when pylint detects a string being formatted with `format()` or `%` that could potentially be an f-string. The use of f-strings is preferred. **Requires Python 3.6 and `py-version` >= 3.6.** f-strings ate and left no crumbs 🔥
### 🌈 `use-implicit-booleaness-not-len` (C1802)
**Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty.** Empty sequences are considered false in a boolean context. So you can either remove the call to `'len'` (`if not x`) or compare the length against a scalar (`if len(x) > 1`). Pick a lane, bestie 💅
### ⭐ `use-implicit-booleaness-not-comparison` (C1803)
**`"%s"` can be simplified to `"%s"`, if it is strictly a sequence, as an empty `%s` is falsey.** Empty sequences are considered false in a boolean context. But — and this is the important part — following this check blindly in a weakly typed codebase can create hard to debug issues. If the value can be something else that's falsey but **not** a sequence (for example `None`, an empty string, or `0`), the code will not be equivalent. So, careful 🫶
### 🍓 `use-implicit-booleaness-not-comparison-to-string` (C1804)
**`"%s"` can be simplified to `"%s"`, if it is strictly a string, as an empty string is falsey.** Empty strings are considered false in a boolean context. Same warning as before though: following this check blindly in a weakly typed codebase can create hard to debug issues. If the value can be something else that's falsey but not a string (for example `None`, an empty sequence, or `0`), the code will not be equivalent. Ngl this one's saving you from a 3am debugging session 😭
- **`use-implicit-booleaness-not-comparison-to-zero` (C1805)** — like, `"%s"` can totes be simplified to `"%s"` *if* it's strictly an `int`, because `0` is falsey — `0` counts as false in a boolean context, periodt. But bestie, listen: following this check blindly in a weakly typed code base can create *hard to debug* issues. If the value could be something else that's falsey but not an `int` (think `None`, an empty string, or an empty sequence) then the code will **not** be equivalent. 🙈 So vibe-check your types first.
## 🦋 Similarities checker
Verbatim name of the checker is `similarities`.
See also similarities checker's options' documentation ` <#similarities-options>`
### 💬 Similarities checker Messages
- **`duplicate-code` (R0801)** — "Similar lines in `%s` files" — this one fires when a set of similar lines has been detected among multiple files. Basically it's giving copy-paste energy, and this usually means the code should be refactored to avoid this duplication. ✨
### 📊 Similarities checker Reports
- `RP0801` Duplication
## 🎀 Spelling checker
Verbatim name of the checker is `spelling`.
See also spelling checker's options' documentation ` <#spelling-options>`
### 💬 Spelling checker Messages
- **`wrong-spelling-in-comment` (C0401)** — "Wrong spelling of a word '%s' in a comment:" — used when a word in a comment is not spelled correctly. (Not me getting called out by a linter for my comment spelling 💀)
- **`wrong-spelling-in-docstring` (C0402)** — "Wrong spelling of a word '%s' in a docstring:" — used when a word in a docstring is not spelled correctly.
- **`invalid-characters-in-docstring` (C0403)** — "Invalid characters `%r` in a docstring" — used when a word in a docstring cannot be checked by `enchant`.
## 🌸 Stdlib checker
Verbatim name of the checker is `stdlib`.
### 💬 Stdlib checker Messages
- **`invalid-envvar-value` (E1507)** — "`%s` does not support `%s` type argument" — env manipulation functions support only string type arguments. See <https://docs.python.org/3/library/os.html#os.getenv>. 🫶
- **`singledispatch-method` (E1519)** — `singledispatch` decorator should not be used with methods, use `singledispatchmethod` instead. `singledispatch` should decorate *functions* and not class/instance methods — use `singledispatchmethod` for those cases, iykyk.
- **`singledispatchmethod-function` (E1520)** — `singledispatchmethod` decorator should not be used with functions, use `singledispatch` instead. `singledispatchmethod` should decorate *class/instance methods* and not functions — use `singledispatch` for those cases.
- **`bad-open-mode` (W1501)** — "`"%s"` is not a valid mode for `open`." Python supports: `r`, `w`, `a[, x]` modes with `b`, `+`, and `U` (only with `r`) options. See <https://docs.python.org/3/library/functions.html#open>.
- **`boolean-datetime` (W1502)** — "Using `datetime.time` in a boolean context." Using `datetime.time` in a boolean context can hide subtle bugs when the time they represent matches midnight UTC. This behaviour was fixed in Python 3.5. See <https://bugs.python.org/issue13936> for reference. This message can't be emitted when using Python >= 3.5.
- **`redundant-unittest-assert` (W1503)** — "Redundant use of `%s` with constant value `%r`" — the first argument of `assertTrue` and `assertFalse` is a condition. If a constant is passed as parameter, that condition will always be true, so in this case a warning should be emitted. 👀
- **`bad-thread-instantiation` (W1506)** — "`threading.Thread` needs the target function" — the warning is emitted when a `threading.Thread` class is instantiated without the target function being passed as a kwarg or as a second argument. By default, the first parameter is the `group` param, **not** the `target` param. Careful bb.
- **`shallow-copy-environ` (W1507)** — "Using `copy.copy(os.environ)`. Use `os.environ.copy()` instead." `os.environ` is not a `dict` object but proxy object, so shallow copy has still effects on original object. See <https://bugs.python.org/issue15373> for reference.
- **`invalid-envvar-default` (W1508)** — "`%s` default type is `%s`. Expected `str` or `None`." Env manipulation functions return `None` or `str` values. Supplying anything different as a default may cause bugs. See <https://docs.python.org/3/library/os.html#os.getenv>.
- **`subprocess-popen-preexec-fn` (W1509)** — "Using `preexec_fn` keyword which may be unsafe in the presence of threads" — the `preexec_fn` parameter is not safe to use in the presence of threads in your application. The child process could **deadlock** before `exec` is called. 😭 If you *must* use it, keep it trivial! Minimize the number of libraries you call into. See <https://docs.python.org/3/library/subprocess.html#popen-constructor>.
- **`subprocess-run-check` (W1510)** — "'`subprocess.run`' used without explicitly defining the value for '`check`'." The `check` keyword is set to `False` by default. It means the process launched by `subprocess.run` can exit with a non-zero exit code and fail silently. It's better to set it explicitly to make clear what the error-handling behavior is.
- **`unspecified-encoding` (W1514)** — "Using `open` without explicitly specifying an encoding" — it is better to specify an encoding when opening documents. Using the system default implicitly can create problems on other operating systems. See <https://peps.python.org/pep-0597/>. This message can't be emitted when using Python >= 3.15.
- **`forgotten-debug-statement` (W1515)** — "Leaving functions creating breakpoints in production code is not recommended" — calls to `breakpoint()`, `sys.breakpointhook()` and `pdb.set_trace()` should be removed from code that is not actively being debugged. Ma'am, please. 🙈
- **`method-cache-max-size-none` (W1518)** — "'`lru_cache(maxsize=None)`' or '`cache`' will keep all method args alive indefinitely, including '`self`'" — by decorating a method with `lru_cache` or `cache` the `self` argument will be linked to the function and therefore never garbage collected. Unless your instance will never need to be garbage collected (singleton) it is recommended to refactor code to avoid this pattern or add a `maxsize` to the cache. The default value for `maxsize` is `128`. 🔥
- **`deprecated-method` (W4902)** — "Using deprecated method `%s()`" — the method is marked as deprecated and will be removed in the future.
- **`deprecated-argument` (W4903)** — "Using deprecated argument `%s` of method `%s()`" — the argument is marked as deprecated and will be removed in the future.
- **`deprecated-class` (W4904)** — "Using deprecated class `%s` of module `%s`" — the class is marked as deprecated and will be removed in the future.
- **`deprecated-decorator` (W4905)** — "Using deprecated decorator `%s()`" — the decorator is marked as deprecated and will be removed in the future.
- **`deprecated-attribute` (W4906)** — "Using deprecated attribute `%r`" — the attribute is marked as deprecated and will be removed in the future.
## 🧚 String checker
Verbatim name of the checker is `string`.
See also string checker's options' documentation ` <#string-options>`
### 💬 String checker Messages
- **`bad-format-character` (E1300)** — "Unsupported format character `%r` (`%#02x`) at index `%d`" — used when an unsupported format character is used in a format string.
- **`truncated-format-string` (E1301)** — "Format string ends in middle of conversion specifier" — used when a format string terminates before the end of a conversion specifier. It's giving cliffhanger 💀
- **`mixed-format-string` (E1302)** — "Mixing named and unnamed conversion specifiers in format string" — used when a format string contains both named (e.g. `'%(foo)d'`) and unnamed (e.g. `'%d'`) conversion specifiers. This is also used when a named conversion specifier contains `*` for the minimum field width and/or precision.
- **`format-needs-mapping` (E1303)** — "Expected mapping for format string, not `%s`" — used when a format string that uses named conversion specifiers is used with an argument that is not a mapping.
- **`missing-format-string-key` (E1304)** — "Missing key `%r` in format string dictionary" — used when a format string that uses named conversion specifiers is used with a dictionary that doesn't contain all the keys required by the format string.
- **`too-many-format-args` (E1305)** — "Too many arguments for format string" — used when a format string that uses unnamed conversion specifiers is given too many arguments.
- **`too-few-format-args` (E1306)** — "Not enough arguments for format string" — used when a format string that uses unnamed conversion specifiers is given too few arguments.
- **`bad-string-format-type` (E1307)** — "Argument `%r` does not match format type `%r`" — used when a type required by format string is not suitable for actual argument type.
- **`bad-str-strip-call` (E1310)** — "Suspicious argument in `%s.%s` call" — the argument to a `str.{l,r,}strip` call contains a duplicate character,
- **`bad-format-string-key` (W1300)** — "Format string dictionary key should be a string, not `%s`" — used when a format string that uses named conversion specifiers is used with a dictionary whose keys are not all strings.
- **`unused-format-string-key` (W1301)** — "Unused key `%r` in format string dictionary" — used when a format string that uses named conversion specifiers is used with a dictionary that contains keys not required by the format string.
- **`bad-format-string` (W1302)** — "Invalid format string" — used when a PEP 3101 format string is invalid.
- **`missing-format-argument-key` (W1303)** — "Missing keyword argument `%r` for format string" — used when a PEP 3101 format string that uses named fields doesn't receive one or more required keywords.
- **`unused-format-string-argument` (W1304)** — "Unused format argument `%r`" — used when a PEP 3101 format string that uses named fields is used with an argument that is not required by the format string.
- **`format-combined-specification` (W1305)** — "Format string contains both automatic field numbering and manual field specification" — used when a PEP 3101 format string contains both automatic field numbering (e.g. `'{}'`) and manual field specification (e.g. `'{0}'`).
- **`missing-format-attribute` (W1306)** — "Missing format attribute `%r` in format specifier `%r`" — used when a PEP 3101 format string uses an attribute specifier (`{0.length}`), but the argument passed for formatting doesn't have that attribute. 🦋
okay so we’re still deep in the message list, and bestie, these string-formatting warnings are <pink>everywhere</pink> — let’s keep it moving:
- **`invalid-format-index (W1307)`** — the message is `Using invalid lookup key %r in format specifier %r`. It fires when a PEP 3101 format string uses a lookup specifier like `{a[1]}`, but the argument passed for formatting doesn’t contain or doesn’t have that key as an attribute.
- **`duplicate-string-formatting-argument (W1308)`** — message: `Duplicate string formatting argument %r, consider passing as named argument`. It pops off when string formatting repeats an argument instead of using named string arguments.
- **`f-string-without-interpolation (W1309)`** — message: `Using an f-string that does not have any interpolated variables`. Fires when an f-string has zero interpolation variables, which means it could just be a normal string — or it’s a bug in the code.
- **`format-string-without-interpolation (W1310)`** — message: `Using formatting for a string that does not have any interpolated variables`. Fires when a string has no interpolation variables, so it can be either a normal string without formatting or a bug in the code.
- **`anomalous-backslash-in-string (W1401)`** — message: `Anomalous backslash in string: '%s'. String constant might be missing an r prefix.` It fires when a backslash is in a literal string but not as an escape.
- **`anomalous-unicode-escape-in-string (W1402)`** — message: `Anomalous Unicode escape in byte string: '%s'. String constant might be missing an r or u prefix.` Fires when an escape like `u` is encountered in a byte string where it has no effect.
- **`implicit-str-concat (W1404)`** — message: `Implicit string concatenation found in %s`. String literals are implicitly concatenated in a literal iterable definition — maybe a comma is missing?
- **`inconsistent-quotes (W1405)`** — message: `Quote delimiter %s is inconsistent with the rest of the file`. Quote delimiters are not used consistently throughout a module, with allowances made for avoiding unnecessary escaping.
- **`redundant-u-string-prefix (W1406)`** — message: `The u prefix for strings is no longer necessary in Python >=3.0`. Fires when a string has a `u` prefix. Those prefixes were necessary in Python 2 to indicate a string was Unicode, but since Python 3.0 strings are Unicode by default.
---
# 🧵 Threading checker 🧵
Verbatim name of the checker is `threading`.
## 🧵 Threading checker Messages 🧵
- **`useless-with-lock (W2101)`** — message: `'%s()' directly created in 'with' has no effect`. Fires when a new lock instance is created by using a `with` statement, which has no effect. Instead, an existing instance should be used to acquire the lock.
---
# 🦋 Typecheck checker 🦋
Verbatim name of the checker is `typecheck`.
See also typecheck checker’s options’ documentation `<#typecheck-options>`.
## 🦋 Typecheck checker Messages 🦋
- **`no-member (E1101)`** — message: `%s %r has no %r member%s`. Fires when a variable is accessed for a nonexistent member.
- **`not-callable (E1102)`** — message: `%s is not callable`. Fires when an object being called has been inferred to be a non-callable object.
- **`assignment-from-no-return (E1111)`** — message: `Assigning result of a function call, where the function has no return`. Fires when an assignment is done on a function call but the inferred function doesn’t return anything.
- **`no-value-for-parameter (E1120)`** — message: `No value for argument %s in %s call`. Fires when a function call passes too few arguments.
- **`too-many-function-args (E1121)`** — message: `Too many positional arguments for %s call`. Fires when a function call passes too many positional arguments.
- **`unexpected-keyword-arg (E1123)`** — message: `Unexpected keyword argument %r in %s call`. Fires when a function call passes a keyword argument that doesn’t correspond to one of the function’s parameter names.
- **`redundant-keyword-arg (E1124)`** — message: `Argument %r passed by position and keyword in %s call`. Fires when a function call would result in assigning multiple values to a function parameter — one value from a positional argument, one from a keyword argument.
- **`missing-kwoa (E1125)`** — message: `Missing mandatory keyword argument %r in %s call`. Fires when a function call does not pass a mandatory keyword-only argument.
- **`invalid-sequence-index (E1126)`** — message: `Sequence index is not an int, slice, or instance with __index__`. Fires when a sequence type is indexed with an invalid type. Valid types are ints, slices, and objects with an `__index__` method.
- **`invalid-slice-index (E1127)`** — message: `Slice index is not an int, None, or instance with __index__`. Fires when a slice index is not an integer, `None`, or an object with an `__index__` method.
- **`assignment-from-none (E1128)`** — message: `Assigning result of a function call, where the function returns None`. Fires when an assignment is done on a function call but the inferred function returns nothing but `None`.
- **`not-context-manager (E1129)`** — message: `Context manager '%s' doesn't implement __enter__ and __exit__.`. Fires when an instance in a `with` statement doesn’t implement the context manager protocol (`__enter__`/`__exit__`).
- **`invalid-unary-operand-type (E1130)`** — emitted when a unary operand is used on an object which does not support this type of operation.
- **`unsupported-binary-operation (E1131)`** — emitted when a binary arithmetic operation between two operands is not supported.
- **`repeated-keyword (E1132)`** — message: `Got multiple values for keyword argument %r in function call`. Emitted when a function call got multiple values for a keyword.
- **`not-an-iterable (E1133)`** — message: `Non-iterable value %s is used in an iterating context`. Fires when a non-iterable value is used in a place where an iterable is expected.
- **`not-a-mapping (E1134)`** — message: `Non-mapping value %s is used in a mapping context`. Fires when a non-mapping value is used in a place where a mapping is expected.
- **`unsupported-membership-test (E1135)`** — message: `Value '%s' doesn't support membership test`. Emitted when an instance in a membership test expression doesn’t implement the membership protocol (`__contains__`/`__iter__`/`__getitem__`).
- **`unsubscriptable-object (E1136)`** — message: `Value '%s' is unsubscriptable`. Emitted when a subscripted value doesn’t support subscription (i.e. doesn’t define `__getitem__` method or `__class_getitem__` for a class).
- **`unsupported-assignment-operation (E1137)`** — message: `%r does not support item assignment`. Emitted when an object does not support item assignment (i.e. doesn’t define `__setitem__` method).
- **`unsupported-delete-operation (E1138)`** — message: `%r does not support item deletion`. Emitted when an object does not support item deletion (i.e. doesn’t define `__delitem__` method).
- **`invalid-metaclass (E1139)`** — message: `Invalid metaclass %r used`. Emitted whenever we can detect that a class is using, as a metaclass, something which might be invalid for using as a metaclass.
- **`dict-iter-missing-items (E1141)`** — message: `Unpacking a dictionary in iteration without calling .items()`. Emitted when trying to iterate through a `dict` without calling `.items()`.
- **`await-outside-async (E1142)`** — message: `'await' should be used within an async function`. Emitted when `await` is used outside an async function.
- **`unhashable-member (E1143)`** — message: `'%s' is unhashable and can't be used as a %s in a %s`. Emitted when a dict key or set member is not hashable (i.e. doesn’t define `__hash__` method).
- **`invalid-slice-step (E1144)`** — message: `Slice step cannot be 0`. Fires when a slice step is `0` and the object doesn’t implement a custom `__getitem__` method.
- **`async-context-manager-with-regular-with (E1145)`** — message: `Context manager '%s' is async and should be used with 'async with'.`. Fires when an async context manager is used with a regular `'with'` statement instead of `'async with'`.
- **`keyword-arg-before-vararg (W1113)`** — message: `Keyword argument before variable positional arguments list in the definition of %s function`. When defining a keyword argument before variable positional arguments, one can end up having multiple values passed for the aforementioned parameter in case the method is called with keyword arguments.
- **`arguments-out-of-order (W1114)`** — message: `Positional arguments appear to be out of order`. Emitted when the caller’s argument names fully match the parameter names in the function signature but do not have the same order.
- **`non-str-assignment-to-dunder-name (W1115)`** — message: `Non-string value assigned to __name__`. Emitted when a non-string value is assigned to `__name__`.
- **`isinstance-second-argument-not-valid-type (W1116)`** — message: `Second argument of isinstance is not a type`. Emitted when the second argument of an `isinstance` call is not a type.
- **`kwarg-superseded-by-positional-arg (W1117)`** — message: `%r will be included in %r since a positional-only parameter with this name already exists`. Emitted when a function is called with a keyword argument that has the same name as a positional-only parameter and the function contains a keyword variadic parameter dict.
- **`c-extension-no-member (I1101)`** — message: `%s %r has no %r member%s, but source is unavailable. Consider adding this module to extension-pkg-allow-list if you want to perform analysis based on run-time introspection of living objects.`. Fires when a variable is accessed for a non-existent member of a C extension. Due to unavailability of source, static analysis is impossible, but it may be performed by introspecting living objects in run-time.
---
# 🌈 Unicode Checker checker 🌈
Verbatim name of the checker is `unicode_checker`.
## 🌈 Unicode Checker checker Messages 🌈
- **`invalid-unicode-codec (E2501)`** — message: `UTF-16 and UTF-32 aren't backward compatible. Use UTF-8 instead`. For compatibility, use UTF-8 instead of UTF-16/UTF-32. See also `<https://bugs.python.org/issue1503789>` for a history of this issue. And `<https://softwareengineering.stackexchange.com/questions/102205/>` for some possible problems when using UTF-16, for instance.
#### **`bidirectional-unicode` (`E2502`)** 💀
ok so this one is genuinely scary — it flags control characters that let someone write code that **executes differently than it displays**. bidirectional unicode chars are typically invisible characters you need to render right-to-left (RTL) scripts (i.e. Chinese, Japanese, Arabic, Hebrew, ...) correctly. so like... can you trust this code? are you *sure* it displayed correctly in all editors? if you didn't write it, or your language isn't RTL, **remove the special characters**, because they could be used to trick you into executing code that does something else than what it looks like. more info: `https://en.wikipedia.org/wiki/Bidirectional_text` and `https://trojansource.codes/` 🫶
#### **`invalid-character-backspace` (`E2510`)** 🎀
invalid unescaped character backspace, use `"b"` instead. it moves the cursor back, so the character after it will overwrite the character before. sneaky little guy.
#### **`invalid-character-carriage-return` (`E2511`)** 🎀
invalid unescaped character carriage-return, use `"r"` instead. moves the cursor to the start of line, and subsequent characters overwrite the start of the line. classic misdirection.
#### **`invalid-character-sub` (`E2512`)** 🎀
invalid unescaped character sub, use `"x1A"` instead. that's Ctrl+Z, the "end of text" on Windows. some programs (such as `type`) ignore the rest of the file after it — so everything below just... vanishes. 😭
#### **`invalid-character-esc` (`E2513`)** 🎀
invalid unescaped character esc, use `"x1B"` instead. commonly initiates escape codes which allow arbitrary control of the terminal. <red>yes, arbitrary control of your terminal, bestie.</red>
#### **`invalid-character-nul` (`E2514`)** 🎀
invalid unescaped character nul, use `"0"` instead. mostly end of input for python.
#### **`invalid-character-zero-width-space` (`E2515`)** 🎀
invalid unescaped character zero-width-space, use `"u200B"` instead. invisible space character could hide real code execution. ngl that's giving jump scare 🙈
#### **`bad-file-encoding` (`C2503`)** 🎀
PEP8 recommends UTF-8 as encoding for Python files, PEP8 recommends UTF-8 default encoding for Python files. see `https://peps.python.org/pep-0008/#source-file-encoding`
---
### Unnecessary-Dunder-Call checker 🦋
verbatim name of the checker is `unnecessary-dunder-call`.
#### Unnecessary-Dunder-Call checker Messages
- **`unnecessary-dunder-call` (`C2801`)** — Unnecessarily calls dunder method `%s`. `%s`. used when a dunder method is manually called instead of using the corresponding function/method/operator.
---
### Unnecessary Ellipsis checker 🦋
verbatim name of the checker is `unnecessary_ellipsis`.
#### Unnecessary Ellipsis checker Messages
- **`unnecessary-ellipsis` (`W2301`)** — Unnecessary ellipsis constant. used when the ellipsis constant is encountered and can be avoided. a line of code consisting of an ellipsis is unnecessary if there is a docstring on the preceding line or if there is a statement in the same scope. like bestie, we see it, it's not doing anything ✨
---
### Unsupported Version checker 🦋
verbatim name of the checker is `unsupported_version`.
#### Unsupported Version checker Messages
- **`using-f-string-in-unsupported-version` (`W2601`)** — F-strings are not supported by all versions included in the `py-version` setting. used when the `py-version` set by the user is lower than 3.6 and pylint encounters an f-string.
- **`using-final-decorator-in-unsupported-version` (`W2602`)** — `typing.final` is not supported by all versions included in the `py-version` setting. used when the `py-version` set by the user is lower than 3.8 and pylint encounters a `typing.final` decorator.
- **`using-exception-groups-in-unsupported-version` (`W2603`)** — Exception groups are not supported by all versions included in the `py-version` setting. used when the `py-version` set by the user is lower than 3.11 and pylint encounters `except*` or `ExceptionGroup`.
- **`using-generic-type-syntax-in-unsupported-version` (`W2604`)** — Generic type syntax (PEP 695) is not supported by all versions included in the `py-version` setting. used when the `py-version` set by the user is lower than 3.12 and pylint encounters generic type syntax.
- **`using-assignment-expression-in-unsupported-version` (`W2605`)** — Assignment expression is not supported by all versions included in the `py-version` setting. used when the `py-version` set by the user is lower than 3.8 and pylint encounters an assignment expression (walrus) operator.
- **`using-positional-only-args-in-unsupported-version` (`W2606`)** — Positional-only arguments are not supported by all versions included in the `py-version` setting. used when the `py-version` set by the user is lower than 3.8 and pylint encounters positional-only arguments.
---
### Variables checker 🦋
verbatim name of the checker is `variables`.
see also the variables checker's options' documentation at `#variables-options`.
#### Variables checker Messages
- **`used-before-assignment` (`E0601`)** — Using variable `%r` before assignment. emitted when a local variable is accessed before its assignment took place. assignments in `try` blocks are assumed not to have occurred when evaluating associated `except`/`finally` blocks. assignments in `except` blocks are assumed not to have occurred when evaluating statements outside the block, except when the associated `try` block contains a `return` statement. <pink>the nuance!!</pink>
- **`undefined-variable` (`E0602`)** — Undefined variable `%r`. used when an undefined variable is accessed.
- **`undefined-all-variable` (`E0603`)** — Undefined variable name `%r` in `__all__`. used when an undefined variable name is referenced in `__all__`.
- **`invalid-all-object` (`E0604`)** — Invalid object `%r` in `__all__`, must contain only strings. used when an invalid (non-string) object occurs in `__all__`.
- **`invalid-all-format` (`E0605`)** — Invalid format for `__all__`, must be tuple or list. used when `__all__` has an invalid format.
- **`possibly-used-before-assignment` (`E0606`)** — Possibly using variable `%r` before assignment. emitted when a local variable is accessed before its assignment took place in **both** branches of an `if`/`else` switch.
- **`no-name-in-module` (`E0611`)** — No name `%r` in module `%r`. used when a name cannot be found in a module.
- **`unpacking-non-sequence` (`E0633`)** — Attempting to unpack a non-sequence`%s`. used when something which is not a sequence is used in an unpack assignment.
- **`potential-index-error` (`E0643`)** — Invalid index for iterable length. emitted when an index used on an iterable goes beyond the length of that iterable.
- **`global-variable-undefined` (`W0601`)** — Global variable `%r` undefined at the module level. used when a variable is defined through the `"global"` statement but the variable is not defined in the module scope.
- **`global-variable-not-assigned` (`W0602`)** — Using global for `%r` but no assignment is done. when a variable defined in the global scope is modified in an inner scope, the `'global'` keyword is required in the inner scope **only** if there is an assignment operation done in the inner scope.
- **`global-statement` (`W0603`)** — Using the global statement. used when you use the `"global"` statement to update a global variable. Pylint discourages its usage. that doesn't mean you cannot use it! 💅
- **`global-at-module-level` (`W0604`)** — Using the global statement at the module level. used when you use the `"global"` statement at the module level since it has no effect. pointless era, honestly.
- **`unused-import` (`W0611`)** — Unused `%s`. used when an imported module or variable is not used.
- **`unused-variable` (`W0612`)** — Unused variable `%r`. used when a variable is defined but not used.
- **`unused-argument` (`W0613`)** — Unused argument `%r`. used when a function or method argument is not used.
- **`unused-wildcard-import` (`W0614`)** — Unused import(s) `%s` from wildcard import of `%s`. used when an imported module or variable is not used from a `'from X import *'` style import.
- **`redefined-outer-name` (`W0621`)** — Redefining name `%r` from outer scope (line `%s`). used when a variable's name hides a name defined in an outer scope or except handler.
- **`redefined-builtin` (`W0622`)** — Redefining built-in `%r`. used when a variable or function override a built-in.
- **`undefined-loop-variable` (`W0631`)** — Using possibly undefined loop variable `%r`. used when a loop variable (i.e. defined by a `for` loop or a list comprehension or a generator expression) is used outside the loop.
- **`unbalanced-tuple-unpacking` (`W0632`)** — Possible unbalanced tuple unpacking with sequence `%s`: left side has `%d` label`%s`, right side has `%d` value`%s`. used when there is an unbalanced tuple unpacking in assignment.
- **`cell-var-from-loop` (`W0640`)** — Cell variable `%s` defined in loop. a variable used in a closure is defined in a loop. this will result in all closures using the same value for the closed-over variable. <yellow>the classic bug fr fr</yellow>
- **`possibly-unused-variable` (`W0641`)** — Possibly unused variable `%r`. used when a variable is defined but might not be used. the possibility comes from the fact that `locals()` might be used, which could consume or not the said variable.
- **`self-cls-assignment` (`W0642`)** — Invalid assignment to `%s` in method. invalid assignment to `self` or `cls` in instance or class method respectively.
- **`unbalanced-dict-unpacking` (`W0644`)** — Possible unbalanced dict unpacking with `%s`: left side has `%d` label`%s`, right side has `%d` value`%s`. used when there is an unbalanced dict unpacking in assignment or `for` loop.
---
### Optional checkers 🌈
Pylint provides the following optional plugins:
- Deprecated Builtins checker
- Broad Try Clause checker
- Else If Used checker
- Code Style checker
- Comparison-Placement checker
- Confusing Elif checker
- Consider Refactoring Into While checker
- Consider Ternary Expression checker
- Dict-Init-Mutate checker
- Parameter Documentation checker
- Docstyle checker
- Dunder checker
- Empty-Comment checker
- Eq-Without-Hash checker
- Consider-Using-Any-Or-All checker
- Magic-Value checker
- Design checker
- No Self Use checker
- Overlap-Except checker
- Import-Private-Name checker
- Redefined-Loop-Name checker
- Multiple Types checker
- Set Membership checker
- Typing checker
- While Used checker
you can activate any or all of these extensions by adding a `load-plugins` line to the `MAIN` section of your `.pylintrc`, for example:
load-plugins=pylint.extensions.docparams,pylint.extensions.docstyle
<purple>mix and match, bestie — it's a whole build-your-own-linter situation 🫶</purple>
### 🎀 Broad Try Clause checker
This one's a plugin, bestie — it lives in `pylint.extensions.broad_try_clause`. The verbatim name of the checker is `broad_try_clause`.
See also the `broad_try_clause` checker's options' documentation `<#broad-try-clause-options>`
### 💅 Broad Try Clause checker Messages
- **`too-many-try-statements (W0717)`** — Try clause contains too many statements. Like, that `try` block is doing the absolute most and somebody needs to tell it to chill.
---
### ✨ Code Style checker
Shoutout to `pylint.extensions.code_style` for providing this one. The verbatim name of the checker is `code_style`.
### 🦋 Code Style checker Documentation
These are the checkers that make your code more *consistent*, okay? And listen — they don't necessarily give you a performance benefit, and honestly they're often times just opinionated. Vibes-based linting, if you will. 💅
See also the `code_style` checker's options' documentation `<#code-style-options>`
### 🌸 Code Style checker Messages
- **`consider-using-namedtuple-or-dataclass (R6101)`** — Consider using namedtuple or dataclass for dictionary values. Emitted when dictionary values can be replaced by namedtuples or dataclass instances. It's giving "you could be so much more organized rn."
- **`consider-using-tuple (R6102)`** — Consider using an in-place tuple instead of list. Only for style consistency! Emitted where an in-place defined list can be replaced by a tuple. And ngl, due to optimizations by CPython, there is **no performance benefit** from it — it's purely a style thing, so don't come for me about speed.
- **`consider-using-assignment-expr (R6103)`** — Use `'%s'` instead. Emitted when an if assignment is directly followed by an if statement and both can be combined by using an assignment expression `:=`. Requires Python 3.8 and `py-version` >= 3.8.
- **`consider-using-augmented-assign (R6104)`** — Use `'%s'` to do an augmented assign directly. Emitted when an assignment is referring to the object that it is assigning to. This can be changed to be an augmented assign. **Disabled by default!** 🙈
- **`prefer-typing-namedtuple (R6105)`** — Prefer `'typing.NamedTuple'` over `'collections.namedtuple'`. `'typing.NamedTuple'` uses the well-known `'class'` keyword with type-hints for readability (it's also faster as it avoids an internal `exec` call). **Disabled by default!** 😭
- **`consider-math-not-float (R6106)`** — Consider `%smath.%s` instead of `%s`. Using `math.inf` or `math.nan` permits to benefit from typing and it is up to 4 times faster than a `float` call (after the initial import of `math`). This check also catches typos in `float` calls as a side effect. Slay, honestly.
---
### 🔥 Comparison-Placement checker
Provided by `pylint.extensions.comparison_placement`, no cap. The verbatim name of the checker is `comparison-placement`.
### ⭐ Comparison-Placement checker Messages
- **`misplaced-comparison-constant (C2201)`** — Comparison should be `%s`. Used when the constant is placed on the left side of a comparison. It is usually clearer in intent to place it in the right hand side of the comparison. Like babe, put the constant where it belongs.
---
### 🧚 Confusing Elif checker
This beauty comes from `pylint.extensions.confusing_elif`. The verbatim name of the checker is `confusing_elif`.
### 💜 Confusing Elif checker Messages
- **`confusing-consecutive-elif (R5601)`** — Consecutive elif with differing indentation level, consider creating a function to separate the inner elif. Used when an `elif` statement follows right after an indented block which itself ends with `if` or `elif`. It may not be obvious if the `elif` statement was willingly or mistakenly unindented. Extracting the indented `if` statement into a separate function might avoid confusion and prevent errors. Fr fr, this one's looking out for you.
---
### 🌈 Consider-Using-Any-Or-All checker
Provided by `pylint.extensions.for_any_all`. The verbatim name of the checker is `consider-using-any-or-all`.
### 🍓 Consider-Using-Any-Or-All checker Messages
- **`consider-using-any-or-all (C0501)`** — `for` loop could be `%s`. A `for` loop that checks for a condition and return a bool can be replaced with `any` or `all`. Iconic shortcut behavior.
---
### 👑 Consider Refactoring Into While checker
Brought to you by `pylint.extensions.consider_refactoring_into_while_condition`. (Long name, I know. She's worth it.) The verbatim name of the checker is `consider_refactoring_into_while`.
### 💖 Consider Refactoring Into While checker Messages
- **`consider-refactoring-into-while-condition (R3501)`** — Consider using `'while %s'` instead of `'while %s:'` an `'if'`, and a `'break'`. Emitted when `while True:` loop is used and the first statement is a break condition. The `if` / `break` construct can be removed if the check is inverted and moved to the `while` statement. Genuinely such a glow-up when you do this.
---
### 🦋 Consider Ternary Expression checker
Provided by `pylint.extensions.consider_ternary_expression`. The verbatim name of the checker is `consider_ternary_expression`.
### ✨ Consider Ternary Expression checker Messages
- **`consider-ternary-expression (W0160)`** — Consider rewriting as a ternary expression. Multiple assign statements spread across `if`/`else` blocks can be rewritten with a single assignment and ternary expression. One line. Main character energy.
---
### 🫶 Deprecated Builtins checker
Provided by `pylint.extensions.bad_builtin`. The verbatim name of the checker is `deprecated_builtins`.
### 🎀 Deprecated Builtins checker Documentation
So this used to be the `bad-builtin` core checker, but it was moved to an extension instead — glow-up era. It can be used for finding prohibited used builtins, such as `map` or `filter`, for which other alternatives exists.
If you want to control for what builtins the checker should warn about, you can use the `bad-functions` option:
$ pylint a.py --load-plugins=pylint.extensions.bad_builtin --bad-functions=apply,reduce ...
See also the `deprecated_builtins` checker's options' documentation `<#deprecated-builtins-options>`
### 🌸 Deprecated Builtins checker Messages
- **`bad-builtin (W0141)`** — Used builtin function `%s`. Used when a disallowed builtin function is used (see the `bad-function` option). Usual disallowed functions are the ones like `map`, or `filter`, where Python offers now some cleaner alternative like list comprehension.
---
### 🌟 Design checker
Provided by `pylint.extensions.mccabe`. The verbatim name of the checker is `design`.
### 💅 Design checker Documentation
You can now use this plugin for finding complexity issues in your code base. It's giving "your function needs to go to therapy."
Activate it through `pylint --load-plugins=pylint.extensions.mccabe`. It introduces a new warning, `too-complex`, which is emitted when a code block has a complexity higher than a preestablished value, which can be controlled through the `max-complexity` option, such as in this example:
$ cat a.py def f10(): """McCabe rating: 11""" myint = 2 if myint == 5: return myint elif myint == 6: return myint elif myint == 7: return myint elif myint == 8: return myint elif myint == 9: return myint elif myint == 10: if myint == 8: while True: return True elif myint == 8: with myint: return 8 else: if myint == 2: return myint return myint return myint $ pylint a.py --load-plugins=pylint.extensions.mccabe R:1: 'f10' is too complex. The McCabe rating is 11 (too-complex) $ pylint a.py --load-plugins=pylint.extensions.mccabe --max-complexity=50 $
See also the `design` checker's options' documentation `<#design-options>`
### 🔥 Design checker Messages
- **`too-complex (R1260)`** — `%s` is too complex. The McCabe rating is `%d`. Used when a method or function is too complex based on McCabe Complexity Cyclomatic.
---
### 🍓 Dict-Init-Mutate checker
Provided by `pylint.extensions.dict_init_mutate`. The verbatim name of the checker is `dict-init-mutate`.
### 💜 Dict-Init-Mutate checker Messages
- **`dict-init-mutate (C3401)`** — Declare all known key/values when initializing the dictionary. Dictionaries can be initialized with a single statement using dictionary literal syntax. Do it in one go, bestie, it's so much cleaner.
---
### 🧚 Docstyle checker
Provided by `pylint.extensions.docstyle`. The verbatim name of the checker is `docstyle`.
### ⭐ Docstyle checker Messages
- **`bad-docstring-quotes (C0198)`** — Bad docstring quotes in `%s`, expected `"""`, given `%s`. Used when a docstring does not have triple double quotes.
- **`docstring-first-line-empty (C0199)`** — First line empty in `%s` docstring. Used when a blank line is found at the beginning of a docstring. Like, say something, don't leave us on read. 😭
---
### 🦋 Dunder checker
Provided by `pylint.extensions.dunder`. The verbatim name of the checker is `dunder`.
See also the `dunder` checker's options' documentation `<#dunder-options>`
### 🌈 Dunder checker Messages
- **`bad-dunder-name (W3201)`** — Bad or misspelled dunder method name `%s`. Used when a dunder method is misspelled or defined with a name not within the predefined list of dunder names. `__innit__` girlies, this one's for you. 💀
---
### 👑 Else If Used checker
Provided by `pylint.extensions.check_elif`. The verbatim name of the checker is `else_if_used`.
### 🎀 Else If Used checker Messages
- **`else-if-used (R5501)`** — Consider using `"elif"` instead of `"else"` then `"if"` to remove one indentation level. Used when an `else` statement is immediately followed by an `if` statement and does not contain statements that would be unrelated to it. One less indent = one less thing to cry about.
---
### 💖 Empty-Comment checker
Provided by `pylint.extensions.empty_comment`. The verbatim name of the checker is `empty-comment`.
### 🌸 Empty-Comment checker Messages
- **`empty-comment (R2044)`** — Line with empty comment. Used when a `#` symbol appears on a line not followed by an actual comment. A lone `#` is just… nothing. Empty. Void. Not the vibe.
---
### ✨ Eq-Without-Hash checker
Provided by `pylint.extensions.eq_without_hash`. The verbatim name of the checker is `eq-without-hash`.
### 🍓 Eq-Without-Hash checker Messages
- **`eq-without-hash (W1641)`** — Implementing `__eq__` without also implementing `__hash__`. Used when a class implements `__eq__` but not `__hash__`. Objects get `None` as their default `__hash__` implementation if they also implement `__eq__`. And then they're unhashable, which is a whole situation. 🫶
---
### 🧚 Import-Private-Name checker
Provided by `pylint.extensions.private_import`. The verbatim name of the checker is `import-private-name`.
### 💜 Import-Private-Name checker Messages
- **`import-private-name (C2701)`** — Imported private `%s` (`%s`). Used when a private module or object prefixed with `_` is imported. PEP8 guidance on Naming Conventions states that public attributes with leading underscores should be considered private. Like, respect the underscore, it's there for a reason.
---
### 🌟 Magic-Value checker
Provided by `pylint.extensions.magic_value`. The verbatim name of the checker is `magic-value`.
See also the `magic-value` checker's options' documentation `<#magic-value-options>`
### 🎀 Magic-Value checker Messages 🎀
- **`magic-value-comparison`** (`R2004`) — `Consider using a named constant or an enum instead of '%s'.` — okay so like, using named constants instead of magic values is the whole glow-up here: it makes your code way more readable and maintainable, so pls try to avoid raw magic values in comparisons, mwah 💖
---
### 🦋 Multiple Types checker 🦋
This checker is provided by `pylint.extensions.redefined_variable_type`. The verbatim name of the checker is `multiple_types`.
### Multiple Types checker Messages
- **`redefined-variable-type`** (`R0204`) — `Redefinition of %s type from %s to %s` — fires when the type of a variable changes inside a method or a function. Like, pick a lane bestie 😭
---
### 🌸 No Self Use checker 🌸
This checker is provided by `pylint.extensions.no_self_use`. The verbatim name of the checker is `no_self_use`.
### No Self Use checker Messages
- **`no-self-use`** (`R6301`) — `Method could be a function` — used when a method doesn't use its bound instance, which means it could literally just be a function. main character method with zero self-energy fr 💅
---
### 🎀 Overlap-Except checker 🎀
This checker is provided by `pylint.extensions.overlapping_exceptions`. The verbatim name of the checker is `overlap-except`.
### Overlap-Except checker Messages
- **`overlapping-except`** (`W0714`) — `Overlapping exceptions (%s)` — used when exceptions in a handler overlap or are, like, literally identical. redundant much 🙈
---
### ✨ Parameter Documentation checker ✨
This checker is provided by `pylint.extensions.docparams`. The verbatim name of the checker is `parameter_documentation`.
### Parameter Documentation checker Documentation
Okay so if you systematically document the parameters of your functions, methods and constructors — and their types — in your code, this optional component might be super useful for you. Sphinx style, Google style, and Numpy style are all supported. (For some examples, see `https://pypi.org/project/sphinxcontrib-napoleon/` .)
You can activate this checker by adding the line:
load-plugins=pylint.extensions.docparams
to the `MAIN` section of your `.pylintrc`.
This checker verifies that all function, method, and constructor docstrings include documentation of the
- parameters and their types
- return value and its type
- exceptions raised
and it can handle docstrings in
- Sphinx style (`param`, `type`, `return`, `rtype`, `raise` / `except`):
def function_foo(x, y, z): '''function foo ...
:param x: bla x :type x: int
:param y: bla y :type y: float
:param int z: bla z
:return: sum :rtype: float
:raises OSError: bla ''' return x + y + z
- or the Google style (`Args:`, `Returns:`, `Raises:`):
def function_foo(x, y, z): '''function foo ...
Args: x (int): bla x y (float): bla y
z (int): bla z
Returns: float: sum
Raises: OSError: bla ''' return x + y + z
- or the Numpy style (`Parameters`, `Returns`, `Raises`):
def function_foo(x, y, z): '''function foo ...
Parameters ---------- x: int bla x y: float bla y
z: int bla z
Returns ------- float sum
Raises ------ OSError bla ''' return x + y + z
You'll be notified of missing parameter documentation, but also of naming inconsistencies between the signature and the documentation — which, ngl, happens all the time when parameters get renamed automatically in the code but not in the documentation 😭
> Note: by default, docstrings of private and magic methods are **not** checked. To change this behaviour (for example, to also check `__init__`) add `no-docstring-rgx=^(?!__init__$)_` to the `BASIC` section of your `.pylintrc`.
Constructor parameters can be documented in either the class docstring or the `__init__` docstring, but not both:
class ClassFoo(object): '''Sphinx style docstring foo
:param float x: bla x
:param y: bla y :type y: int ''' def __init__(self, x, y): pass
class ClassBar(object): def __init__(self, x, y): '''Google style docstring bar
Args: x (float): bla x y (int): bla y ''' pass
In some cases, having to document all parameters is a nuisance — like, say many of your functions or methods just follow a common interface. To remove this burden, the checker accepts missing parameter documentation if one of the following phrases is found in the docstring:
- `For the other parameters, see`
- `For the parameters, see`
(with arbitrary whitespace between the words). Please add a link to the docstring defining the interface, e.g. a superclass method, after "see":
def callback(x, y, z): '''Sphinx style docstring for callback ...
:param x: bla x :type x: int
For the other parameters, see :class:MyFrameworkUsingAndDefiningCallback ''' return x + y + z
def callback(x, y, z): '''Google style docstring for callback ...
Args: x (int): bla x
For the other parameters, see :class:MyFrameworkUsingAndDefiningCallback ''' return x + y + z
Naming inconsistencies in existing parameter and their type documentations are still detected, bestie. No escaping that vibe check 👑
See also `parameter_documentation` checker's options' documentation <#parameter-documentation-options>
### Parameter Documentation checker Messages
- **`multiple-constructor-doc`** (`W9005`) — `"%s" has constructor parameters documented in class and __init__` — please remove parameter declarations in the class or constructor. Pick one spot, not both 🫶
- **`missing-raises-doc`** (`W9006`) — `"%s" not documented as being raised` — please document exceptions for all raised exception types.
- **`redundant-returns-doc`** (`W9008`) — `Redundant returns documentation` — please remove the `return`/`rtype` documentation from this method.
- **`redundant-yields-doc`** (`W9010`) — `Redundant yields documentation` — please remove the yields documentation from this method.
- **`missing-return-doc`** (`W9011`) — `Missing return documentation` — please add documentation about what this method returns.
- **`missing-return-type-doc`** (`W9012`) — `Missing return type documentation` — please document the type returned by this method.
- **`missing-yield-doc`** (`W9013`) — `Missing yield documentation` — please add documentation about what this generator yields.
- **`missing-yield-type-doc`** (`W9014`) — `Missing yield type documentation` — please document the type yielded by this method.
- **`missing-param-doc`** (`W9015`) — `"%s" missing in parameter documentation` — please add parameter declarations for all parameters.
- **`missing-type-doc`** (`W9016`) — `"%s" missing in parameter type documentation` — please add parameter type declarations for all parameters.
- **`differing-param-doc`** (`W9017`) — `"%s" differing in parameter documentation` — please check parameter names in declarations.
- **`differing-type-doc`** (`W9018`) — `"%s" differing in parameter type documentation` — please check parameter names in type declarations.
- **`useless-param-doc`** (`W9019`) — `"%s" useless ignored parameter documentation` — please remove the ignored parameter documentation.
- **`useless-type-doc`** (`W9020`) — `"%s" useless ignored parameter type documentation` — please remove the ignored parameter type documentation.
- **`missing-any-param-doc`** (`W9021`) — `Missing any documentation in "%s"` — please add parameter and/or type documentation.
---
### 🧚 Redefined-Loop-Name checker 🧚
This checker is provided by `pylint.extensions.redefined_loop_name`. The verbatim name of the checker is `redefined-loop-name`.
### Redefined-Loop-Name checker Messages
- **`redefined-loop-name`** (`W2901`) — `Redefining %r from loop (line %s)` — used when a loop variable is overwritten in the loop body. girl what was the loop variable even for then 😭
---
### 🍓 Set Membership checker 🍓
This checker is provided by `pylint.extensions.set_membership`. The verbatim name of the checker is `set_membership`.
### Set Membership checker Messages
- **`use-set-for-membership`** (`R6201`) — `Consider using set for membership test` — membership tests are more efficient when performed on a lookup-optimized datatype like sets. so like, lowkey free speed ✨
---
### 💜 Typing checker 💜
This checker is provided by `pylint.extensions.typing`. The verbatim name of the checker is `typing`.
### Typing checker Documentation
Finds issues specifically related to type annotations 🔥
See also `typing` checker's options' documentation <#typing-options>
### Typing checker Messages
- **`broken-noreturn`** (`E6004`) — `'NoReturn' inside compound types is broken in 3.7.0 / 3.7.1` — `typing.NoReturn` inside compound types is broken in Python 3.7.0 and 3.7.1. If not dependent on runtime introspection, use string annotation instead. E.g. `Callable[..., 'NoReturn']`. `https://bugs.python.org/issue34921`
- **`broken-collections-callable`** (`E6005`) — `'collections.abc.Callable' inside Optional and Union is broken in 3.9.0 / 3.9.1 (use 'typing.Callable' instead)` — `collections.abc.Callable` inside `Optional` and `Union` is broken in Python 3.9.0 and 3.9.1. Use `typing.Callable` for these cases instead. `https://bugs.python.org/issue42965`
- **`deprecated-typing-alias`** (`W6001`) — `'%s' is deprecated, use '%s' instead` — emitted when a deprecated typing alias is used. move on bestie, it's over 💀
- **`consider-using-alias`** (`R6002`) — `'%s' will be deprecated with PY39, consider using '%s' instead%s` — only emitted if `runtime-typing=no` and a deprecated typing alias is used in a type annotation context in Python 3.7 or 3.8.
- **`consider-alternative-union-syntax`** (`R6003`) — `Consider using alternative union syntax instead of '%s'%s` — emitted when `typing.Union` or `typing.Optional` is used instead of the shorthand union syntax. For example, `Union[int, float]` instead of `int | float`. Using the shorthand for unions aligns with Python typing recommendations, removes the need for imports, and avoids confusion in function signatures. iconic behavior honestly 👑
- **`redundant-typehint-argument`** (`R6006`) — ``Type `%s` is used more than once in union type annotation. Remove redundant typehints.`` — duplicated type arguments will be skipped by the mypy tool, therefore they should be removed to avoid confusion. no cap 🫶
- **`unnecessary-default-type-args` (R6007)** — Type `%s` has unnecessary default type args. Change it to `%s`. This one fires off when your types have default type args that could literally just be omitted, bestie. Mostly showing up for `typing.Generator` and `typing.AsyncGenerator` 💅
## While Used checker
This checker comes from `pylint.extensions.while_used`, and the verbatim name of the checker is `while_used`. Periodt.
### While Used checker Messages
- **`while-used` (W0149)** — Used `while` loop. Okay so unbounded `while` loops can often be rewritten as bounded `for` loops instead 🦋 Exceptions can totally be made for cases such as event loops, listeners, etc. iykyk
---
# ✨ API ✨
You can call Pylint, symilar and pyreverse from another Python program thanks to their APIs:
from pylint import run_pylint, run_pyreverse, run_symilar
run_pylint("--disable=C", "myfile.py") run_pyreverse(...) run_symilar(...)
## Pylint
### As you would launch the command line
You can use the `run_pylint` function, which is the same function called by the command line (using `sys.argv`). And yes, you can supply arguments yourself:
from pylint import run_pylint
run_pylint(argv=["--disable=line-too-long", "myfile.py"])
### Recover the result in a stream
You can also use `pylint.lint.Run` directly if you want to do something that can't be done using only pylint's command line options. Here's the basic example:
from pylint.lint import Run
Run(argv=["--disable=line-too-long", "myfile.py"])
With `Run` it is possible to invoke pylint programmatically with a reporter initialized with a custom stream:
from io import StringIO
from pylint.lint import Run from pylint.reporters.text import TextReporter
pylint_output = StringIO() # Custom open stream reporter = TextReporter(pylint_output) Run(["test_file.py"], reporter=reporter, exit=False) print(pylint_output.getvalue()) # Retrieve and print the text report
The reporter can accept any stream object as as parameter. In this example, the stream outputs to a file:
from pylint.lint import Run from pylint.reporters.text import TextReporter
with open("report.out", "w") as f: reporter = TextReporter(f) Run(["test_file.py"], reporter=reporter, exit=False)
This would be useful to capture pylint output in an open stream which can be passed onto another program. So handy fr fr 🫶
If your program expects that the files being linted might be edited between runs, you will need to clear pylint's inference cache:
from pylint.lint import pylinter pylinter.MANAGER.clear_cache()
---
# 🌸 How To Guides 🌸
## How to Write a Checker
You can find some simple examples in the distribution (`custom.py` <https://github.com/pylint-dev/pylint/blob/main/examples/custom.py> , `custom_raw.py` <https://github.com/pylint-dev/pylint/blob/main/examples/custom_raw.py> and `deprecation_checker.py` <https://github.com/pylint-dev/pylint/blob/main/examples/deprecation_checker.py>). Iconic little starter pack honestly 🎀
There are three kinds of checkers:
- Raw checkers, which analyse each module as a raw file stream.
- Token checkers, which analyse a file using the list of tokens that represent the source code in the file.
- AST checkers, which work on an AST representation of the module.
The AST representation is provided by the astroid library. astroid adds additional information and methods over `ast` in the standard library, to make tree navigation and code introspection easier. Obsessed with her, ngl 💜
### Writing an AST Checker
Let's implement a checker to make sure that all return nodes in a function return a unique constant. Firstly we will need to fill in some required boilerplate:
import astroid from astroid import nodes from typing import TYPE_CHECKING, Optional
from pylint.checkers import BaseChecker
if TYPE_CHECKING: from pylint.lint import PyLinter
class UniqueReturnChecker(BaseChecker):
name = "unique-returns" msgs = { "W0001": ( "Returns a non-unique constant.", "non-unique-returns", "All constants returned in a function should be unique.", ), } options = ( ( "ignore-ints", { "default": False, "type": "yn", "metavar": "<y or n>", "help": "Allow returning non-unique integers", }, ), )
So far we have defined the following required components of our checker:
- A name. The name is used to generate a special configuration section for the checker, when options have been provided.
- A message dictionary. Each checker is being used for finding problems in your code, the problems being displayed to the user through messages. The message dictionary should specify what messages the checker is going to emit. See Defining a Message for the details about defining a new message.
We have also defined an optional component of the checker. The options list defines any user configurable options. It has the following format:
options = ( ("option-symbol", {"argparse-like-kwarg": "value"}), )
- The option-symbol is a unique name for the option. This is used on the command line and in config files. The hyphen is replaced by an underscore when used in the checker, similarly to how you would use `argparse.Namespace`:
if not self.linter.config.ignore_ints: ...
Next we'll track when we enter and leave a function.
def __init__(self, linter: Optional["PyLinter"] = None) -> None: super().__init__(linter) self._function_stack = []
def visit_functiondef(self, node: nodes.FunctionDef) -> None: self._function_stack.append([])
def leave_functiondef(self, node: nodes.FunctionDef) -> None: self._function_stack.pop()
In the constructor we initialise a stack to keep a list of return nodes for each function. An AST checker is a visitor, and should implement `visit_<lowered class name>` or `leave_<lowered class name>` methods for the nodes it's interested in. In this case we have implemented `visit_functiondef` and `leave_functiondef` to add a new list of return nodes for this function, and to remove the list of return nodes when we leave the function. Literally so clean ✨
Finally we'll implement the check. We will define a `visit_return` function, which is called with an `.astroid.nodes.Return` node.
We'll need to be able to figure out what attributes an `.astroid.nodes.Return` node has available. We can use `astroid.extract_node` for this:
node = astroid.extract_node("return 5") node
<Return l.1 at 0x7efe62196390>
help(node) node.value
<Const.int l.1 at 0x7efe62196ef0>
We could also construct a more complete example:
node_a, node_b = astroid.extract_node("""
... def test(): ... if True: ... return 5 #@ ... return 5 #@ ... """)
node_a.value
<Const.int l.4 at 0x7efe621a74e0>
node_a.value.value
5
node_a.value.value == node_b.value.value
True
For `astroid.extract_node`, you can use `#@` at the end of a line to choose which statements will be extracted into nodes. Cute little marker, no cap 🎀
For more information on `astroid.extract_node`, see the astroid documentation <https://pylint.readthedocs.io/projects/astroid/en/latest/>.
Now we know how to use the astroid node, we can implement our check.
def visit_return(self, node: nodes.Return) -> None: if not isinstance(node.value, nodes.Const): return for other_return in self._function_stack[-1]: if node.value.value == other_return.value.value and not ( self.linter.config.ignore_ints and node.value.pytype() == int ): self.add_message("non-unique-returns", node=node)
self._function_stack[-1].append(node)
Once we have established that the source code has failed our check, we use `~.BaseChecker.add_message` to emit our failure message.
Finally, we need to register the checker with pylint. Add the `register` function to the top level of the file.
def register(linter: "PyLinter") -> None: """This required method auto registers the checker during initialization. :param linter: The linter to register the checker to. """ linter.register_checker(UniqueReturnChecker(linter))
We are now ready to debug and test our checker! 🫶
### Debugging a Checker
It is very simple to get to a point where we can use `pdb`. We'll need a small test case. Put the following into a Python file:
def test(): if True: return 5 return 5
def test2(): if True: return 1 return 5
After inserting pdb into our checker and installing it, we can run pylint with only our checker:
$ pylint --load-plugins=my_plugin --disable=all --enable=non-unique-returns test.py (Pdb)
Now we can debug our checker! 💀✨
> **Note:**
> `my_plugin` refers to a module called `my_plugin.py`. The preferred way of making this plugin available to pylint is by installing it as a package. This can be done either from a packaging index like PyPI or by installing it from a local source such as with `pip install`.
>
> Alternatively, the plugin module can be made available to pylint by putting this module's parent directory in your `PYTHONPATH` environment variable.
>
> If your pylint config has an `init-hook` that modifies `sys.path` to include the module's parent directory, this will also work, but only if either:
>
> - the `init-hook` and the `load-plugins` list are both defined in a configuration file, or...
> - the `init-hook` is passed as a command-line argument and the `load-plugins` list is in the configuration file
>
> So, you cannot load a custom plugin by modifying `sys.path` if you supply the `init-hook` in a configuration file, but pass the module name in via `--load-plugins` on the command line. This is because pylint loads plugins specified on command line before loading any configuration from other sources. Lowkey important, don't skip this one 🙈
### Defining a Message
Pylint message is defined using the following format:
msgs = { "E0401": ( # message id "Unable to import %s", # template of displayed message "import-error", # message symbol "Used when pylint has been unable to import a module.", # Message description { # Additional parameters: # message control support for the old names of the messages: "old_names": [("F0401", "old-import-error")] "minversion": (3, 5), # No check under this version "maxversion": (3, 7), # No check above this version }, ),
okay so that's the whole shape of it 💖 — the message gets formatted using the `args` parameter you pass to `add_message`, like in `self.add_message("import-error", args=module_we_cant_import, node=importnode)`. The value sitting in `module_we_cant_import` — say it's `patglib` — gets interpolated in, and the final result you actually see is: `Unable to import patglib`. ✨
---
- **the message-id** — it's gotta be a 4-digit number with a message category glued on the front. The categories are `C`, `W`, `E`, `F`, `R`, and they stand for Convention, Warning, Error, Fatal and Refactoring (iconic lineup honestly 👑). Those 4 digits should *not* collide with any existing checkers, and the first 2 digits should stay consistent across the checker (except for shared messages, they get a pass). Also — use `51`-`99` as the first 2 digits for custom checkers, that range is literally reserved for you and it's safe. 🎀
- **the displayed-message** — this is the one that shows up to the user once the message is emitted. It's giving main character energy for your error. 💅
- **the message-symbol** — it's just an alias of the message id, and you can use it literally anywhere you'd use the message id. Two names, one icon. 🦋
- **the message-help** — this is what pops up when you call `pylint --help-msg`. 🫶
Optionally the message can carry some extra optional options, and they're cute:
- **the `old_names` option** — lets you change the message id or symbol of a message *without* breaking the message control your users have on the old messages. You specify it as a list of tuples `(message-id, old-message-symbol)`, e.g. `{"old_names": [("F0401", "old-import-error")]}`. The symbol / msgid association has to be unique, so if you're changing the message id, the symbol needs to change too — and you can generally just slap the `old-` prefix on it. no cap. 🔥
- **the `minversion` or `maxversion` options** — these specify the minimum or maximum version of python that's relevant for this message. Value goes in as a tuple, major version number first, minor version number second, e.g. `{"minversion": (3, 5)}`. 🍓
- **the `shared` option** — this one enables sharing a message between multiple checkers. Like, normally the message *cannot* be shared between multiple checkers (that's just the rules). To allow a message to be shared between multiple checkers, `shared` has to be set to `True`. periodt. ⭐
---
### ⚡ Parallelize a Checker
`BaseChecker` has two methods — `get_map_data` and `reduce_map_data` — and these are the ones that let you parallelize the checks when you're using the `-j` option. 👑
So if a checker actually needs to reduce data, it should define `get_map_data` as returning something different than `None`, and then let its `reduce_map_data` handle a list of the types that `get_map_data` returned. That's the whole vibe check. 🌈
You can see a whole example by looking at `pylint/checkers/similar.py`. go look bestie, it's right there. 💜
---
### 🧪 Testing a Checker
Pylint is *so* well suited to test driven development, like genuinely obsessed. 😭 The flow is: implement the template of the checker, produce all of your test cases, check that they fail, then implement the checker, then check that all of your test cases work. Slay, that's the loop. ✨
Pylint provides a `pylint.testutils.CheckerTestCase` to make test cases very simple — thank you pylint, king behaviour. We can use the example code that we used for debugging as our test cases:
import astroid import my_plugin import pylint.testutils
class TestUniqueReturnChecker(pylint.testutils.CheckerTestCase): CHECKER_CLASS = my_plugin.UniqueReturnChecker
def test_finds_non_unique_ints(self): func_node, return_node_a, return_node_b = astroid.extract_node(""" def test(): #@ if True: return 5 #@ return 5 #@ """)
self.checker.visit_functiondef(func_node) self.checker.visit_return(return_node_a) with self.assertAddsMessages( pylint.testutils.MessageTest( msg_id="non-unique-returns", node=return_node_b, ), ): self.checker.visit_return(return_node_b)
def test_ignores_unique_ints(self): func_node, return_node_a, return_node_b = astroid.extract_node(""" def test(): #@ if True: return 1 #@ return 5 #@ """)
with self.assertNoMessages(): self.checker.visit_functiondef(func_node) self.checker.visit_return(return_node_a) self.checker.visit_return(return_node_b)
Once again we're using `astroid.extract_node` to construct our test cases — it's our bestie at this point, fr fr. `pylint.testutils.CheckerTestCase` has already created the linter and the checker for us, so all we do is simulate a traversal of the AST tree using the nodes we're interested in. cute, efficient, no notes. 🎀
---
## 🧩 How To Write a Pylint Plugin
Pylint supports writing **two** types of extensions, and they're different girlies. 🦋
First, there's the concept of **checkers** — these are the ones you use for finding problems in your code. Second, there's the concept of a **transform plugin** — that's the way through which the inference and the capabilities of Pylint can be enhanced and tailored to a particular module, library or framework. 👑
In general, a plugin is a module that should have a function `register`, which takes an instance of `pylint.lint.PyLinter` as input.
A plugin can *optionally* define a function `load_configuration`, which also takes an instance of `pylint.lint.PyLinter` as input. That function gets called after Pylint loads configuration from the configuration file and the command line interface — and it should load additional plugin specific configuration into Pylint. 💅
So a basic hello-world plugin can be implemented as:
Inside hello_plugin.py
from typing import TYPE_CHECKING
import astroid
if TYPE_CHECKING: from pylint.lint import PyLinter
def register(linter: "PyLinter") -> None: """This required method auto registers the checker during initialization.
:param linter: The linter to register the checker to. """ print('Hello world')
We can run this plugin by placing this module in the `PYTHONPATH` and invoking pylint as:
$ pylint -E --load-plugins hello_plugin foo.py Hello world
We can extend the hello-world plugin to ignore some specific names using the `load_configuration` function:
Inside hello_plugin.py
from typing import TYPE_CHECKING
import astroid
if TYPE_CHECKING: from pylint.lint import PyLinter
def register(linter: "PyLinter") -> None: """This required method auto registers the checker during initialization.
:param linter: The linter to register the checker to. """ print('Hello world')
def load_configuration(linter):
name_checker = get_checker(linter, NameChecker)
We consider as good names of variables Hello and World
name_checker.config.good_names += ('Hello', 'World')
We ignore bin directory
linter.config.black_list += ('bin',)
Depending on whether we need a transform plugin or a checker, this might not be enough. 🙈 For the former, it's enough to just declare the module as a plugin. But in the case of the latter, we need to register our checker with the linter object, by calling the following inside the `register` function:
linter.register_checker(OurChecker(linter))
For more information on writing a checker, see the *How to Write a Checker* section (aka `#write-a-checker`). 🌸
---
## 🦋 Transform plugins
### 💭 Why write a plugin?
Pylint is a static analysis tool, and Python is a dynamically typed language. So there are gonna be cases where Pylint literally cannot analyze files properly — and ngl, this problem can happen in statically typed languages too, if reflection or dynamic evaluation is used. 💀
Plugins are the way to tell Pylint how to handle those cases, since only the user would know what needs to be done. They're usually operating on the AST level, modifying or changing it in a way that eases its understanding by Pylint. 🫶
### 🌸 Example
Let's run Pylint on a module from the Python source — `warnings.py` (`https://hg.python.org/cpython/file/2.7/Lib/warnings.py`) — and see what happens:
amitdev$ pylint -E Lib/warnings.py E:297,36: Instance of 'WarningMessage' has no 'message' member (no-member) E:298,36: Instance of 'WarningMessage' has no 'filename' member (no-member) E:298,51: Instance of 'WarningMessage' has no 'lineno' member (no-member) E:298,64: Instance of 'WarningMessage' has no 'line' member (no-member)
Did we catch a genuine error? 👀 Let's open the code and look at the `WarningMessage` class:
class WarningMessage(object):
"""Holds the result of a single showwarning() call."""
_WARNING_DETAILS = ("message", "category", "filename", "lineno", "file", "line")
def __init__(self, message, category, filename, lineno, file=None, line=None): local_values = locals() for attr in self._WARNING_DETAILS: setattr(self, attr, local_values[attr]) self._category_name = category.__name__ if category else None
def __str__(self): ...
Ah. The fields (`message`, `category` etc) are not defined statically on the class — instead they're added using `setattr`. Pylint would have a tough time figuring that out. poor thing 😭
### 🚪 Enter Plugin
We can write a transform plugin to tell Pylint how to analyze this properly. ✨
One way to fix our example with a plugin would be to transform the `WarningMessage` class by setting the attributes so that Pylint can see them. This is done by registering a transform function — and you can transform *any* node in the parsed AST, like Module, Class, Function etc. In our case we need to transform a class. It can be done so:
from typing import TYPE_CHECKING
import astroid
if TYPE_CHECKING: from pylint.lint import PyLinter
def register(linter: "PyLinter") -> None: """This required method auto registers the checker during initialization.
:param linter: The linter to register the checker to. """ pass
def transform(cls): if cls.name == 'WarningMessage': import warnings for f in warnings.WarningMessage._WARNING_DETAILS: cls.locals[f] = [astroid.ClassDef(f, None)]
astroid.MANAGER.register_transform(astroid.ClassDef, transform)
Okay so let’s go through the plugin together, bestie. First we need to register a class transform — that’s done with the `register_transform` function in `MANAGER`. It takes the node type and the function as parameters. We need to change a class, so we use `astroid.ClassDef`. We also pass a transform function that does the actual transformation.
The transform function is simple too. If the class is `WarningMessage`, then we add the attributes to its locals — we are not bothered about the type of attributes, so setting them as `class` will do. But we could set them to any type we want. That’s it.
> **Note:** We don’t need to do anything in the register function of the plugin since we are not modifying anything in the linter itself.
Let’s run Pylint with this plugin and see:
amitdev$ pylint -E --load-plugins warning_plugin Lib/warnings.py amitdev$
All the false positives associated with `WarningMessage` are now gone. This is just an example — any code transformation can be done by plugins.
See `astroid/brain` — `https://github.com/pylint-dev/astroid/tree/main/astroid/brain` — for real life examples of transform plugins.
# 💅 Technical Reference 💅
## 🧠 Startup and the Linter Class 🧠
The two main classes in `pylint.lint` are `.pylint.lint.Run` and `.pylint.lint.PyLinter`.
The `.pylint.lint.Run` object is responsible for starting up pylint. It does some basic checking of the given command line options to find the initial hook to run, find the config file to use, and find which plugins have been specified. It can then create the main `.pylint.lint.PyLinter` instance and initialise it with the config file and plugins that were discovered when preprocessing the command line options. Finally the `.pylint.lint.Run` object launches any child linters for parallel jobs, and starts the linting process.
The `.pylint.lint.PyLinter` is responsible for coordinating the linting process. It parses the configuration and provides it for the checkers and other plugins, it handles the messages emitted by the checkers, it handles the output reporting, and it launches the checkers.
## 🧩 Checkers 🧩
All of the default pylint checkers exist in `pylint.checkers`. This is where most of pylint’s brains exist. Most checkers are AST based and so use `astroid`. `pylint.checkers.utils` provides a large number of utility methods for dealing with `astroid`.
## 🌱 Contributing to pylint 🌱
The contributor guide will help you if you want to contribute to pylint itself.
# 💖 Contributing 💖
## 🔎 Finding something to do 🔎
Want to contribute to pylint? There’s a lot of things you can do. Here’s a list of links you can check depending on what you want to do:
- **Asking a question** on discord — `https://discord.com/invite/qYxpadCgkx` — or on github — `https://github.com/pylint-dev/pylint/issues/new/choose`
- **Opening an issue** — `https://github.com/pylint-dev/pylint/issues/new?assignees=&labels=Needs+triage+%3Ainbox_tray%3A&template=BUG-REPORT.yml`
- **Making the documentation better** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Documentation+%3Agreen_book%3A%22`
- **Making the error message better** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen%20is%3Aissue%20project%3Apylint-dev%2Fpylint%2F4`
- **Reproducing bugs and confirming that issues are valid** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Needs+reproduction+%3Amag%3A%22%2C%22Cannot+reproduce+%F0%9F%A4%B7%22`
- **Investigating or debugging complicated issues** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Needs+investigation+%F0%9F%94%AC%22`
- **Designing or specifying a solution** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Needs+design+proposal+%3Alock%3A%22%2C%22Needs+specification+%3Aclosed_lock_with_key%3A%22`
- **Giving your opinion on ongoing discussion** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Needs+decision+%3Alock%3A%22`
- **Fixing bugs and crashes** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Bug+%3Abeetle%3A%22%2C%22Crash+%F0%9F%92%A5%22`
- **Fixing false positives** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22False+Positive+%F0%9F%A6%9F%22`
- **Creating new features or fixing false negatives** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22False+Negative+%F0%9F%A6%8B%22%2C%22Enhancement+%E2%9C%A8%22`
- **Reviewing pull requests** — `https://github.com/pylint-dev/pylint/pulls?q=is%3Aopen+is%3Apr+label%3A%22Needs+review+%F0%9F%94%8D%22`
If you are a pylint maintainer there’s also:
- **Triaging issues** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Needs+triage+%3Ainbox_tray%3A%22`
- **Labeling issues that do not have an actionable label yet** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+-label%3A%22Needs+astroid+Brain+%F0%9F%A7%A0%22+-label%3A%22Needs+astroid+update%22+-label%3A%22Needs+backport%22+-label%3A%22Needs+decision+%3Alock%3A%22+-label%3A%22Needs+investigation+%F0%9F%94%AC%22+-label%3A%22Needs+PR%22+-label%3A%22Needs+reproduction+%3Amag%3A%22+-label%3A%22Needs+review+%F0%9F%94%8D%22+-label%3A%22Needs+triage+%3Ainbox_tray%3A%22+-label%3A%22Waiting+on+author%22+-label%3A%22Work+in+progress%22+-label%3AMaintenance+sort%3Aupdated-desc+-label%3A%22Needs+specification+%3Aclosed_lock_with_key%3A%22+-label%3A%22Needs+design+proposal+%3Alock%3A%22`
- **Preparing the next patch release** — `https://github.com/pylint-dev/pylint/issues?q=is%3Aopen+is%3Aissue+label%3A%22Needs+backport%22`
- **Checking stale pull requests status** — `https://github.com/pylint-dev/pylint/pulls?q=is%3Aopen+is%3Apr+label%3A%22Work+in+progress%22%2C%22Needs+astroid+update%22%2C%22Waiting+on+author%22`
## 🚀 Creating a pull request 🚀
Got a change for Pylint? Below are a few steps you should take to make sure your patch gets accepted:
- You must use at least Python 3.8 for development of Pylint as it gives you access to the latest ast parser and some pre-commit hooks do not support python 3.7.
- Install the dev dependencies — see Contributor installation (`#contributor-install`).
- Use our test suite and write new tests — see Testing pylint (`#contributor-testing`).
- Document your change, if it is a non-trivial one:
- A maintainer might label the issue `skip-news` if the change does not need to be in the changelog.
- Otherwise, create a news fragment with `towncrier create <IssueNumber>.<type>` which will be included in the changelog. `<type>` can be one of the types defined in `./towncrier.toml`. If necessary you can write details or offer examples on how the new change is supposed to work.
- Generating the doc is done with `tox -e docs`
- Send a pull request from GitHub (see About pull requests `https://support.github.com/features/pull-requests` for more insight about this topic).
- Write comprehensive commit messages and/or a good description of what the PR does. Relate your change to an issue in the tracker if such an issue exists (see Closing issues via commit messages `https://github.blog/2013-01-22-closing-issues-via-commit-messages/` of the GitHub documentation for more information on this).
- Keep the change small, separate the consensual changes from the opinionated one.
- Don’t hesitate to open multiple PRs if the change requires it. If your review is so big it requires to actually plan and allocate time to review, it’s more likely that it’s going to go stale.
- Maintainers might have multiple 5 to 10 minutes review windows per day, Say while waiting for their teapot to boil, or for their partner to recover from their hilarious nerdy joke, but only one full hour review time per week, if at all.
- If you used multiple emails or multiple names when contributing, add your mails and preferred name in the `script/.contributors_aliases.json` file.
## ✨ Tips for Getting Started with Pylint Development ✨
- Read the Technical Reference (`#technical-reference`). It gives a short walk through of the pylint codebase and will help you identify where you will need to make changes for what you are trying to implement.
- `astroid.extract_node` is your friend. Most checkers are AST based, so you will likely need to interact with astroid. A short example of how to use `astroid.extract_node` is given here (`#astroid-extract-node`).
- When fixing a bug for a specific check, search the code for the warning message to find where the warning is raised, and therefore where the logic for that code exists.
- When adding a new checker class you can use the `get_unused_message_id_category.py` script in `./script` to get a message id that is not used by any of the other checkers.
## 📚 Building the documentation 📚
You can use the makefile in the doc directory to build the documentation:
$ make -C doc/ install-dependencies $ make -C doc/ html
We’re reusing generated files for speed, use `make clean` or `tox -e docs` when you want to start from scratch.
## 🎯 How to choose the target version ? 🎯
Choose depending on the kind of change you’re doing:
- In patch release (1.2.3), we only fix false positives and crashes.
- In minor releases (1.2.0), we add checks, remove checks, drop python interpreters past end of life, upgrade astroid minor/major versions and fix false negatives.
- In major releases (1.0.0) we change everything else (pylint options, json output, dev API...) while still trying to minimize disruption.
## 🧪 Testing pylint 🧪
Pylint is very well tested and has a high code coverage. New contributions are not accepted unless they include tests.
## 🛠️ Contributor installation 🛠️
### 🧵 Basic installation 🧵
Pylint is developed using the git distributed version control system.
You can clone Pylint using
git clone https://github.com/pylint-dev/pylint
Before you start testing your code, you need to install your source-code package locally. Suppose you just cloned pylint with the previous `git clone` command. To set up your environment for testing, open a terminal and run:
cd pylint python3 -m venv venv source venv/bin/activate pip install -r requirements_test_min.txt pip install -e .
This ensures your testing environment is similar to Pylint’s testing environment on GitHub.
Optionally (Because there’s an auto-fix if you open a merge request): We have pre-commit hooks which should take care of the autoformatting for you before each commit. To enable it, run `pre-commit install` in the pylint root directory.
### 🌱 Astroid installation
ok so if you're testing new changes in astroid, you also gotta clone astroid and do an editable install alongside pylint, like this:
Suppose you're in the pylint directory
git clone https://github.com/pylint-dev/astroid.git python3 -m pip install -e astroid/
and boom 🎉 you're now using the local astroid inside pylint, and you can control the version with git, for example:
cd astroid/ git switch my-astroid-dev-branch
---
### 🏃♀️ Launching tests
okay bestie this is where we actually run stuff — let's go 💅
### 🧪 pytest
we use pytest to run the tests, so you can also use it on its own (ngl we do recommend the tox command though):
pytest tests/ -k test_functional
you can use pytest directly, fr. if you wanna run tests on a specific portion of the code with pytest and your local python version:
python3 -m pytest
everything in `tests/message` with coverage for the relevant code (this one requires pytest-cov):
python3 -m pytest tests/message/ --cov=pylint.message coverage html
and if you only want the functional test `missing_kwoa_py3`:
python3 -m pytest "tests/test_functional.py::test_functional[missing_kwoa_py3]"
### 🧬 tox
you can also optionally install tox and run our tests using the tox package, like:
python -m tox python -m tox -epy313 # for Python 3.13 suite only python -m tox -epylint # for running Pylint over Pylint's codebase python -m tox -eformatting # for running formatting checks over Pylint's codebase
it's usually a *good idea* to run tox with `--recreate`. this flag tells tox to re-download all dependencies before running the tests 🙌 and that can be important when a new version of astroid or any of the other dependencies has been published:
python -m tox --recreate # The entire tox environment will be recreated python -m tox --recreate -e py310 # The python 3.10 tox environment will be recreated
to run only a specific test suite, use a pattern for the test filename (without the `.py` extension), like:
python -m tox -e py310 -- -k test_functional python -m tox -e py310 -- -k *func* python -m tox --recreate -e py310 -- -k test_functional # With recreation of the environment
### 🦋 Primer tests
pylint also uses what we call **primer tests** ✨ these are tests that run automatically in our Continuous Integration and check whether any changes in Pylint lead to crashes or fatal errors on the stdlib — and they also assess a pull request's impact on the linting of a selection of external repositories by posting the diff against pylint's current output as a comment. iconic behaviour tbh 👑
to run the primer test for the stdlib (the one that *only* checks for crashes and fatal errors), you can add `--primer-stdlib` to the pytest command. for example:
pytest -m primer_stdlib --primer-stdlib
to produce the output generated on Continuous Integration for the linting of external repositories, run these commands:
python tests/primer/__main__.py prepare --clone python tests/primer/__main__.py run --type=pr
to *fully* simulate the process on Continuous Integration, you should then checkout `main`, and then run these commands:
python tests/primer/__main__.py run --type=main python tests/primer/__main__.py compare
the list of repositories is created on the basis of three criteria: 1) projects need to use a diverse range of language features, 2) projects need to be well maintained and 3) projects should not have a codebase that is too repetitive. this guarantees a good balance between the speed of our CI and finding potential bugs 🎯
you can find the latest list of repositories and any relevant code for these tests in the `tests/primer` directory.
---
### ✍️ Writing tests
pylint uses three types of tests: unittests, functional tests and primer tests.
- **unittests** can be found in `pylint/tests`. unless you're working on pylint's internals you're probably not gonna have to write any 🙈
- **Global functional tests** can be found in the `pylint/tests/functional`. they're mainly used to test whether Pylint emits the correct messages.
- **Configuration's functional tests** can be found in the `pylint/tests/config/functional`. they're used to test Pylint's configuration loading.
- **Primer tests** — you can suggest a new external repository to check, but there's nothing to do most of the time 🫶
### 🔍 Unittest tests
most other tests live in the `/pylint/test` directory. these unittests can be used to test almost all functionality within Pylint. a good move before writing any new unittests is to look at some tests that test similar functionality — that often helps a *ton* when writing new ones.
if your new test needs any additional files, you can put those in the `/pylint/test/regrtest_data` directory. that's the directory we use to store any data needed for the unittests 🍓
### 🧚 Functional tests
these live under `/pylint/test/functional` and they're made up of multiple components. first, each Python file is considered to be a test case and it should be accompanied by a `.txt` file with the same name. the `.txt` file contains the pylint messages that are supposed to be emitted by that test file.
in your `.py` test file, every line where Pylint is supposed to emit a message has to be annotated with a comment following this pattern `# [message_symbol]`, like:
a, b, c = 1 # [unbalanced-tuple-unpacking]
if multiple messages are expected on the same line, then this syntax works:
a, b, c = 1.test # [unbalanced-tuple-unpacking, no-member]
you can also use `# +n: [` where *n* is an integer, to deal with special cases — e.g. where the regular syntax above makes the line too long:
A = 5
+1: [singleton-comparison]
B = A == None # The test will look for the singleton-comparison message in this line
if you need special control over Pylint's configuration, you can also create a `.rc` file, which can set sections of Pylint's configuration. the `.rc` file can also contain a section `[testoptions]` to pass options for the functional test runner. the following options are currently supported:
- `min_pyver`: minimal python version required to run the test
- `max_pyver`: python version from which the test won't be run. if the last supported version is 3.9 this setting should be set to 3.10.
- `min_pyver_end_position`: minimal python version required to check the `end_line` and `end_column` attributes of the message
- `requires`: packages required to be installed locally to run the test
- `except_implementations`: list of python implementations on which the test should not run
- `exclude_platforms`: list of operating systems on which the test should not run
#### 🌈 Different output for different Python versions
sometimes the linting result can change between Python releases 😭 in these cases errors can be marked as *conditional*. supported operators are `<`, `<=`, `>` and `>=`.
def some_func() -> X: # <3.14:[undefined-variable] ...
It can also be combined with offsets
+1:<3.14:[undefined-variable]
def some_other_func() -> X: ...
class X: ...
since the output messages are different, it's necessary to add two separate files for it. first `<test-file-name>.314.txt` — this will include the output messages for `>=3.14`, i.e. it should be empty here. second `<test-file-name>.txt` — this will be the default for all other Python versions.
> **Note:** this does only work if the code itself is parsable in all tested Python versions. for new syntax, use `min_pyver` / `max_pyver` instead 🫡
#### 📁 Functional test file locations
for existing checkers, new test cases should preferably be appended to the existing test file. for new checkers, a new file `new_checker_message.py` should be created (note the use of underscores!). this file should then be placed in the `test/functional/n` sub-directory.
some additional notes:
- if the checker is part of an extension the test should go in `test/functional/ext/extension_name`
- if the test is a regression test it should go in `test/r/regression` or `test/r/regression_02`. the file name should start with `regression_`.
- for some sub-directories, such as `test/functional/u`, there are additional sub-directories (`test/functional/u/use`). please check if your test file should be placed in any of these directories. it should be placed there if the sub-directory name matches the word before the first underscore of your test file name.
the folder structure is *enforced* when running the test suite, so you might be directed to put the file in a different sub-directory 💀
#### 🔁 Running and updating functional tests
during development it's sometimes helpful to run all functional tests in your current environment so you get faster feedback. run this from the Pylint root directory:
python tests/test_functional.py
you can use all the options you'd use for pytest, for example `-k "test_functional[len_checks]"`. and if you need to, the `.txt` file with expected messages can be regenerated based on the current output by appending `--update-functional-output` to the command line:
python tests/test_functional.py --update-functional-output -k "test_functional[len_checks]"
---
### ⚙️ Functional tests for configurations
to test the different ways you can configure Pylint, there's also a small functional test framework for configuration files 🎀 these tests can be found in the `/pylint/test/config` directory.
to create a new test, create a new file with an unused name in the directory of that type of configuration file. then add a `filename.result.json` file, with `filename` being the same name as your configuration file. this file should record what the configuration should be compared to the standard configuration.
for example, if the configuration should add a warning to the list of disabled messages and you changed the configuration for `job` to `10` instead of the default `1`, the `.json` file should include:
"functional_append": { "disable": [["a-message-to-be-added"],] } "jobs": 10,
similarly, if a message should be removed you can add the following to the `.json` file:
"functional_remove": { "disable": [["a-message-to-be-removed"],] }
...and if a configuration is incorrect and should legitimately lead to a crash or a warning being emitted, you can say so by adding a `.out` file. Naming goes like this:
`name_of_configuration_testfile.error_code.out`
So bestie, if your test is called `bad_configuration.toml` and it should exit with exit code `2`, your `.out` file must be named `bad_configuration.2.out`. The content of the `.out` file should follow a similar pattern as a normal Pylint output — and heads up, the module name should be `{abspath}` and the file name should be `{relpath}`.
## 🧚 Profiling and performance analysis
### 🔥 Performance analysis for Pylint
To analyse the performance of Pylint, the maintainers recommend the `cProfile` module from stdlib. Paired up with the `pstats` module, that's genuinely all the tools you need to profile a Pylint run and see which functions take how long to run. Obsessed, honestly.
The documentation for both modules lives at cProfile — `https://docs.python.org/3/library/profile.html`.
To profile a run of Pylint over itself you can use the following code and run it from the base directory. Note that `cProfile` will create a document called `stats` that is then read by `pstats`. The human-readable output will be stored by `pstats` in `./profiler_stats`. It will be sorted by cumulative time:
import cProfile import pstats import sys
sys.argv = ["pylint", "pylint"] cProfile.run("from pylint import __main__", "stats")
with open("profiler_stats", "w", encoding="utf-8") as file: stats = pstats.Stats("stats", stream=file) stats.sort_stats("cumtime") stats.print_stats()
You can also interact with the `stats` object by sorting or restricting the output. For example, to only print functions from the `pylint` module and sort by cumulative time, you could use:
import cProfile import pstats import sys
sys.argv = ["pylint", "pylint"] cProfile.run("from pylint import __main__", "stats")
with open("profiler_stats", "w", encoding="utf-8") as file: stats = pstats.Stats("stats", stream=file) stats.sort_stats("cumtime") stats.print_stats("pylint/pylint")
Lastly, to profile a run over your own module or code you can use:
import cProfile import pstats import sys
sys.argv = ["pylint", "your_dir/your_file"] cProfile.run("from pylint import __main__", "stats")
with open("profiler_stats", "w", encoding="utf-8") as file: stats = pstats.Stats("stats", stream=file) stats.sort_stats("cumtime") stats.print_stats()
The documentation of the `pstats` module discusses other possibilities to interact with the profiling output. 💅
### 🔍 Performance analysis of a specific checker
To analyse the performance of a specific checker within Pylint we can use the human-readable output created by `pstats`.
If you search in the `profiler_stats` file for the file name of the checker, you'll find all the functional calls from functions within that checker. Let's say we want to check the `visit_importfrom` method of the variables checker:
ncalls tottime percall cumtime percall filename:lineno(function) 622 0.006 0.000 8.039 0.013 /MY_PROGRAMMING_DIR/pylint/pylint/checkers/variables.py:1445(visit_importfrom)
The line above tells us that this method was called `622` times during the profile, and we were inside the function itself for `6` ms in total. The time per call is less than a millisecond (`0.006 / 622`) and thus is displayed as being `0`.
Often you're more interested in the cumulative time (per call). This refers to the time spent within the function **and** any of the functions it called — or the functions *they* called (etc., etc., it's giving recursion energy). In our example, the `visit_importfrom` method and all of its child-functions took a little over `8` seconds to execute, with an execution time of `0.013` ms per call.
You can also search the `profiler_stats` for an individual function you want to check. For example `_analyse_fallback_blocks`, a function called by `visit_importfrom` in the variables checker. This allows more detailed analysis of specific functions:
ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 /MY_PROGRAMMING_DIR/pylint/pylint/checkers/variables.py:1511(_analyse_fallback_blocks)
### 🎨 Parsing the profiler stats with other tools
Often you might want to create a visual representation of your profiling stats. A great tool for this is gprof2dot — `https://github.com/jrfonseca/gprof2dot`. This tool can create a `.dot` file from the profiling stats created by `cProfile` and `pstats`. You can then convert the `.dot` file to a `.png` file with one of the many converters found online.
You can read the gprof2dot documentation — `https://github.com/jrfonseca/gprof2dot` — for installation instructions for your specific environment.
Another option would be snakeviz — `https://jiffyclub.github.io/snakeviz/`. 🐍✨
## 🌈 OSS-Fuzz integration
### 🫶 Platform overview
OSS-Fuzz (`https://google.github.io/oss-fuzz/`) is Google's free fuzzing platform for open source software. It runs astroid's fuzz targets to help detect reliability issues that could affect astroid and Pylint.
Google provides public build logs (`https://oss-fuzz-build-logs.storage.googleapis.com/index.html#astroid`) and fuzzing stats (`https://introspector.oss-fuzz.com/project-profile?project=astroid`), but most of the details about bug reports and fuzzed testcases require approved access. 👑
### 🔑 Gaining access
The configuration files for the OSS-Fuzz integration can be found in the OSS-Fuzz repository — `https://github.com/google/oss-fuzz/tree/master/projects/astroid`. The `project.yaml` file controls who has access to bug reports and testcases. Ping the maintainers if you'd like to be added to the list — note that a Google account is required for access.
### 📈 Fuzzing progress
Once you have access to OSS-Fuzz, you can log in to `https://oss-fuzz.com/` with your Google account to see a dashboard of astroid's fuzzing progress.
### 🧪 Testcases
The dashboard contains a link to a testcases page (`https://oss-fuzz.com/testcases?project=astroid&open=yes`) that lists all testcases that currently trigger a bug in astroid.
Every testcase has a dedicated page with links to view and download a minimized testcase for reproducing the failure. Each testcase page also contains a stacktrace for the failure and stats about how often the failure is encountered while fuzzing. 😭 so useful.
### 💀 Reproducing a failure
You can download a minimized testcase and run it locally to debug a failure on your machine. For example, to reproduce a failure with the `fuzz_parse` fuzz target, you can run the following commands:
mkdir fuzzing-repro cd fuzzing-repro
Note: Atheris doesn't support Python 3.12+ yet:
https://github.com/google/atheris/issues/82
uv venv --python 3.11 source .venv/bin/activate
git clone https://github.com/pylint-dev/astroid.git cd astroid
uv pip install atheris==2.3.0 uv pip install --editable .
Save the minimized testcase as minimized.py in the astroid directory
cat << EOF > ./run_fuzz_parse.py
import astroid import atheris
with open('minimized.py', 'rb') as f: fdp = atheris.FuzzedDataProvider(f.read())
code = fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(0, 4096)) astroid.builder.parse(code) EOF
python ./run_fuzz_parse.py
If the failure does not reproduce locally, you can try reproducing the issue in an OSS-Fuzz container:
git clone https://github.com/google/oss-fuzz.git cd oss-fuzz
python infra/helper.py build_image astroid python infra/helper.py build_fuzzers astroid python infra/helper.py reproduce astroid fuzz_parse minimized.py
Some failures may only be reproducible in an OSS-Fuzz container because of differences in Python versions between the OSS-Fuzz platform and your local environment. Fr fr, don't blame yourself for that one. 🫶
### 🌸 Code coverage
The dashboard also links to code coverage data for individual fuzz targets and combined code coverage data for all targets — click on the `"TOTAL COVERAGE"` link for the combined data.
The combined coverage data is helpful for identifying coverage gaps, insufficient corpus data, and potential candidates for future fuzz targets.
### 🐛 Bug reports
Bug reports for new failures are automatically filed in the OSS-Fuzz bug tracker with an astroid label (`https://issues.oss-fuzz.com/issues?q=project:astroid%20status:open`). Make sure you are logged in to view all existing issues.
### 🛠️ Build maintenance
Google runs compiled fuzz targets on Google Compute Engine VMs. This architecture requires each project to provide a `Dockerfile` and `build.sh` script to download code, configure dependencies, compile fuzz targets, and package any corpus files.
astroid's build files and fuzz-target code can be found in the OSS-Fuzz repo — `https://github.com/google/oss-fuzz/blob/master/projects/astroid/`.
If dependencies change or if new fuzz targets are added, then you may need to modify the build files and build a new Docker image for OSS-Fuzz.
### 📦 Building an image
Run the following commands to build astroid's OSS-Fuzz image and fuzz targets:
git clone https://github.com/google/oss-fuzz.git cd oss-fuzz
python infra/helper.py build_image astroid python infra/helper.py build_fuzzers astroid
Any changes you make to the build files must be submitted as pull requests to the OSS-Fuzz repo. No sneaky direct pushes, bestie. 💅
### 🩹 Debugging build failures
You can debug build failures during the `build_fuzzers` stage by creating a container and manually running the compile command:
Create a container for building fuzz targets
python infra/helper.py shell astroid
Run this command inside the container to build the fuzz targets
compile
The `build.sh` script will be located at `/src/build.sh` inside the container.
### ⭐ Quick links
- OSS-Fuzz dashboard — `https://oss-fuzz.com/`
- OSS-Fuzz configuration files, build scripts, and fuzz targets for astroid — `https://github.com/google/oss-fuzz/tree/master/projects/astroid`
- All open OSS-Fuzz bugs for astroid — `https://issues.oss-fuzz.com/issues?q=project:astroid%20status:open`
- Google's OSS-Fuzz documentation — `https://google.github.io/oss-fuzz/`
---
## 🎀 Releasing a pylint version
So, you want to release the `X.Y.Z` version of pylint? Iconic. Let's go.
### 🌟 Releasing a major or minor version
Before releasing a major or minor version, check if there are any unreleased commits on the maintenance branch. If so, release a last patch release first. See ``Releasing a patch version``.
- Write the Summary -- Release highlights in `doc/whatsnew` and upgrade the release date.
- Install the release dependencies: `pip3 install -r requirements_test.txt`. This pins the exact astroid version used by the CI (see `requirements_test_min.txt`). Make sure this pinned version is the one installed before running `tbump`: a different astroid produces different functional test output, which then gets committed into the release and breaks the CI. 💀
- Bump the version and release by using `tbump X.Y.0 --no-push --no-tag`. (For example: `tbump 2.4.0 --no-push --no-tag`)
- Check the commit created with `git show`, amend the commit if required.
- Move the main branch up to a dev version with `tbump`:
tbump X.Y+1.0-dev0 --no-tag --no-push # You can interrupt after the first step git commit -am "Upgrade the version to x.y+1.0-dev0 following x.y.0 release"
For example:
tbump 2.5.0-dev0 --no-tag --no-push git commit -am "Upgrade the version to 2.5.0-dev0 following 2.4.0 release"
- `tbump` will have created a new *What's new in Pylint X.Y+1* document. Add it to `doc/whatsnew/3/index.rst`. Take a look at the examples from `doc/whatsnew`. Commit that with `git commit -a --amend`.
- Push to a release branch
- Open a merge request with the two commits (no one can push directly on main)
- After the merge, recover the merged commits on main and tag the first one (the version should be `X.Y.Z`) as `vX.Y.Z` (For example: `v2.4.0`)
- Push the tag.
- Release the version on GitHub with the same name as the tag and copy and paste the appropriate changelog in the description. This triggers the PyPI release. 🚀
- Create a `maintenance/X.Y.x` (For example: `maintenance/2.4.x` from the `v2.4.0` tag.)
- Upgrade the pattern for the protected branches in the settings under Branches / Branch protection rules. (For example: `maintenance/2.4*` instead of `maintenance/2.3*`.). There's a lot of configuration done in these settings, do **NOT** recreate it from scratch.
- Delete the `maintenance/X.Y-1.x` branch. (For example: `maintenance/2.3.x`)
- Select all the closed issues labelled `backport maintenance/X.Y-1.x` and label them `backported`, then rename the `backport maintenance/X.Y-1.x` label to `backport maintenance/X.Y.x` (for example rename `backport maintenance/2.3.x` to `backport maintenance/2.4.x`)
- Close the current milestone and create the new ones (For example: close `2.4.0`, create `2.4.1` and `2.6.0`)
- Hide and deactivate all the patch releases for the previous minor release on readthedocs `https://readthedocs.org/projects/pylint/versions/`, except the last one. (For example: hide `v2.4.0`, `v2.4.1`, `v2.4.2` and keep only `v2.4.3`)
## 🍒 Back-porting a fix from main to the maintenance branch
Whenever a PR on main should be released in a patch release on the current maintenance branch:
- Label the PR with `backport maintenance/X.Y-1.x`. (For example `backport maintenance/2.3.x`)
- Squash the PR before merging (alternatively rebase if there's a single commit)
- (If the automated cherry-pick has conflicts)
- Add a *Needs backport* label and do it manually.
- You might alternatively also:
- Cherry-pick the changes that create the conflict if it's not a new feature before doing the original PR cherry-pick manually.
- Decide to wait for the next minor to release the PR
- In any case upgrade the milestones in the original PR and newly cherry-picked PR to match reality.
- Release a patch version
## 🩹 Releasing a patch version
We release patch versions when a crash or a bug is fixed on the main branch and has been cherry-picked on the maintenance branch.
- Install the release dependencies: `pip3 install -r requirements_test.txt`. This pins the exact astroid version used by the CI (see `requirements_test_min.txt`). Make sure this pinned version is the one installed before running `tbump`: a different astroid produces different functional test output, which then gets committed into the release and breaks the CI. 😭 (yes, again — it really does matter)
- Bump the version and release by using `tbump X.Y-1.Z --no-push`. (For example: `tbump 2.3.5 --no-push`)
- Check the result visually with `git show`.
- Open a merge request of `release-X.Y-1.Z'` in `maintenance/X.Y.x` (For example: `release-2.3.5-branch` in `maintenance/2.3.x`) to run the CI tests for this branch.
- Create and push the tag.
- Release the version on GitHub with the same name as the tag and copy and paste the changelog from the ReadtheDoc generated documentation from the pull request pipeline in the description. This triggers the PyPI release.
- Merge the `maintenance/X.Y.x` branch on the main branch. The main branch should have the changelog for `X.Y-1.Z+1` (For example `v2.3.6`). This merge is required so `pre-commit autoupdate` works for pylint. 🎀
- Fix version conflicts properly, or bump the version to `X.Y.0-devZ` (For example: `2.4.0-dev6`) before pushing on the main branch
- Close the current milestone and create the new one (For example: close `2.3.5`, create `2.3.6`)
## 🎯 Milestone handling
We move issues that were not done to the next milestone and block releases only if there are any open issues labelled as blocker.
## 👑 Governance
### 🫶 How to become part of the project ?
### ✨ How to become a contributor ?
- Follow the code of conduct
- Search by yourself before asking for help
- Open an issue
- Investigate an issue and report your finding
- Open a merge request directly if you feel it's a consensual change
Reporting a bug is being a contributor already. fr fr, that counts 💖
### 🔍 How to become a triager ?
- Create a pylint plugin, then migrate it to the `'pylint-dev'` github organization.
Or:
- Contribute for more than 3 releases consistently.
- Do not be too opinionated, follow the code of conduct without requiring emotional works from the maintainers. It does not mean that disagreements are impossible, only that arguments should stay technical and reasonable so the conversation is civil and productive.
- Have a maintainer suggest that you become triager, without you asking
- Get unanimous approval or neutral agreement from current maintainers.
### 🛠️ How to become a maintainer ?
- Take ownership of a part of the code that is not maintained well at the moment or that you contributed personally (if we feel we can't merge something without your review, you're going to be able to merge those yourself soon).
Or:
- Contribute two big code merge requests over multiple releases (for example one checker in 2.13 and the following bug after release and one complicated bug fixes in 2.14). Otherwise contributing for more than 3 releases consistently with great technical and interpersonal skills.
- Triage for multiple months (close duplicate, clean up issues, answer questions...)
- Have an admin suggest that you become maintainer, without you asking
- Get unanimous approval or neutral agreement from current maintainers.
### 🌈 How to become an admin ?
- Contribute for several hundreds of commits over a long period of time with excellent interpersonal skills and code quality.
- Maintain pylint for multiple years (code review, triaging and maintenance tasks).
- At this point probably have another admin leave the project or become inactive for years.
- Have an admin suggest that you become an admin, without you asking.
- Get unanimous approval or neutral agreement from current admins.
### 🗳️ How are decisions made ?
Everyone is expected to follow the code of conduct. pylint is a do-ocracy / democracy. You're not allowed to behave poorly because you contributed a lot. But if you're not going to do the future maintenance work, your valid opinions might not be taken into account by those that will be affected by it.
### 📜 What are the fundamental tenets of pylint development?
General:
- We favor correctness over performance, because pylint is not used primarily for its performance. Performance is still important and needs to be taken into account from the get go.
- We then favor false negatives over false positives if correctness is impossible to achieve.
- We try to keep the configuration sane, but if there's a hard decision to take we add an option so that pylint is multiple sizes fit all (after configuration)
Where to add a new checker or message:
- Error messages (things that will result in an error if run) should be builtin checks, activated by default
- Messages that are opinionated, even slightly, should be opt-in (added as an extension <#plugins>)
- We don't shy away from opinionated checks (like the while checker), but there's such a thing as too opinionated, if something is too opinionated it should be an external pylint plugin <#plugins>.
### 💬 How are disagreements handled ?
When something is not consensual users, maintainers, and admins discuss until an agreement is reached.
Depending on the difficulty of the discussion and the importance of a fast resolution, a decision can be taken:
- Unanimously between discussion participants, contributors and maintainers (preferably)
- By asking discussion participants for their opinions with an emoji survey in the issue and then using the majority if no maintainers feel strongly about the issue.
- By majority of admins if no admins feel strongly about the issue.
- By asking all users for their opinions in a new issue that will be pinned for multiple months before taking the decision if two admins feel strongly on an opposite side of the issue. Once the result is obvious the majority decision is not up for discussion anymore.
---
## 🦋 Pyreverse
`pyreverse` is a powerful tool that creates UML diagrams from your Python code. It helps you visualize:
- Package dependencies and structure
- Class hierarchies and relationships
- Method and attribute organization
### 🎨 Output Formats
`pyreverse` supports multiple output formats:
- Native formats:
- `.dot`/`.gv` (Graphviz)
- `.puml`/`.plantuml` (PlantUML)
- `.mmd`/`.html` (MermaidJS)
- Additional formats (requires Graphviz installation):
- All Graphviz output formats `https://graphviz.org/docs/outputs/` (PNG, SVG, PDF, etc.)
- `pyreverse` first generates a temporary `.gv` file, which is then fed to Graphviz to generate the final image
### 🚀 Getting Started
Check out the Usage guide to learn about available options, or see Example Output for sample diagrams and common use cases.
### 🐚 Usage
`pyreverse` is run from the command line using the following syntax:
pyreverse [options] <packages>
where `<packages>` is one or more Python packages or modules to analyze. ✨
okay so these options are all sorted into these categories, keep up bestie:
- **Filtering and Scope** — controls which classes and relationships actually show up in your diagrams
- **Display Options** — makes it look cute: colors, labels, the whole aesthetic
- **Output Control** — picks the output format and sets where the files land
- **Project Configuration** — project settings like source roots and ignored files
---
## 🎯 Filtering and Scope
- **`--all-ancestors`** — shows *all* the ancestors of every class in *<projects>*. every last one. 💀
Default: `None`
- **`--all-associated`** — shows every class associated with the target classes, including the indirect associations (the friends-of-friends situation).
Default: `None`
- **`--class`** — makes a class diagram with all the classes related to *<class>*; this one uses the options `-ASmy` by default. iykyk ✨
Default: `None`
- **`--filter-mode`** — filters attributes and functions according to *<mode>*. the correct modes are: `'PUB_ONLY'` filters all non-public attributes [DEFAULT], equivalent to `PRIVATE`+`SPECIAL`; `'ALL'` no filter at all; `'SPECIAL'` filters Python special functions except the constructor; `'OTHER'` filters protected and private attributes.
Default: `PUB_ONLY`
- **`--max-depth`** — the maximum depth of packages/modules to include in the diagram, relative to the deepest specified package. a depth of `0` shows only the specified packages/modules, while `1` includes their immediate children, etc. when you specify nested packages, depth is calculated from the deepest package level. if you don't specify it, all packages/modules in the hierarchy get shown. 🌈
Default: `None`
- **`--show-ancestors`** — shows *<ancestor>* generations of ancestor classes that are **not** in *<projects>*.
Default: `None`
- **`--show-associated`** — shows *<association_level>* levels of associated classes that are **not** in *<projects>*.
Default: `None`
- **`--show-builtin`** — includes builtin objects in the representation of classes. the OGs 🫶
Default: `False`
- **`--show-stdlib`** — includes standard library objects in the representation of classes.
Default: `False`
---
## 💅 Display Options
- **`--color-palette`** — a comma separated list of colors used for the package depth coloring. a whole little rainbow, she's prepared:
Default: `('#77AADD', '#99DDFF', '#44BB99', '#BBCC33', '#AAAA00', '#EEDD88', '#EE8866', '#FFAABB', '#DDDDDD')`
- **`--colorized`** — uses colored output. classes/modules from the same package get the same color, so they're matching, so it slays. 🎀
Default: `False`
- **`--max-color-depth`** — uses separate colors up to a package depth of *<depth>*. anything deeper just reuses colors (recycling, we love sustainability).
Default: `2`
- **`--module-names`** — includes the module name in the representation of classes.
Default: `None`
- **`--no-standalone`** — only shows nodes that have connections. no lonely nodes allowed, this is a group chat not a solo podcast. 🙈
Default: `False`
- **`--only-classnames`** — does NOT show attributes and methods in the class boxes; heads up, this disables `-f` values.
Default: `False`
---
## 📤 Output Control
- **`--output`** — creates a `*.<format>` output file if the format is available. the available formats are: `.dot`, `.puml`, `.plantuml`, `.mmd`, `.html`. any other format will be tried via the `'dot'` command line tool, which requires a graphviz installation. in that case, these additional formats are available (see `Graphviz output formats <https://graphviz.org/docs/outputs/>`).
Default: `dot`
- **`--output-directory`** — sets the output directory path. where the magic lands. ✨
Default: `""`
---
## ⚙️ Project Configuration
- **`--ignore`** — files or directories to be skipped. ⚠️ these have to be **base names**, not paths.
Default: `('CVS',)`
- **`--project`** — sets the project name. this gets appended to the output file names later, so pick something iconic.
Default: `""`
- **`--source-roots`** — adds paths to the list of source roots. supports globbing patterns. the source root is an absolute path, or a path relative to the current working directory, used to determine a package namespace for modules located under the source root.
Default: `()`
- **`--verbose`** — makes pyreverse more verbose/talkative. mostly useful for debugging. she will NOT stop yapping 🔥
Default: `False`
---
## 🖼️ Example Output
example diagrams generated with the `.puml` output format are shown below.
### Package Diagram
[image: Package diagram generated by pyreverse] [image]
### Class Diagram
[image: Class diagram generated by pyreverse] [image]
### Creating Class Diagrams for Specific Classes
in soooo many cases, making a single diagram that shows every class in the project gives you a giant, unwieldy diagram. like, unreadable. 😭 while limiting the input path to a single package or module already helps *a lot* with narrowing the scope, the `-c` option gives you another way to build a class diagram focused on one single class and its collaborators. for example, running:
pyreverse -ASmy -c pylint.checkers.classes.ClassChecker pylint
will generate the full class and package diagrams for pylint, but will **additionally** generate a file `pylint.checkers.classes.ClassChecker.dot`:
[image: Package diagram generated by pyreverse] [image]
---
## 🧚 Symilar
the console script `symilar` finds copy-pasted blocks of text across a set of files. it gives you a command line interface to check only the `duplicate-code` message. no cap. 💜
it can be invoked with:
symilar [-d|--duplicates min_duplicated_lines] [-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] [--ignore-signatures] file1...
**all** files that shall be checked have to be passed in explicitly, e.g.:
symilar foo.py, bar.py, subpackage/spam.py, subpackage/eggs.py
`symilar` produces output like the following:
17 similar lines in 2 files ==tests/data/clientmodule_test.py:3 ==tests/data/suppliermodule_test.py:12 class Ancestor: """ Ancestor method """ cls_member = DoNothing()
def __init__(self, value): local_variable = 0 self.attr = 'this method shouldn't have a docstring' self.__value = value
def get_value(self): """ nice docstring ;-) """ return self.__value
def set_value(self, value): self.__value = value return 'this method shouldn't have a docstring' TOTAL lines=58 duplicates=17 percent=29.31
---
## 📜 4.x
this is the full list of change in pylint 4.x minors, by categories. buckle in. ⭐
### What's New in Pylint 4.0
`:Release:4.0 :Date: 2025-10-12`
### Summary -- Release highlights
- Pylint now supports Python 3.14. 🎉
- Pylint's inference engine (astroid) is now **much** more precise, understanding implicit booleanness and ternary expressions. (Thanks @zenlyj!)
consider this example:
class Result: errors: dict | None = None
result = Result() if result.errors: result.errors[field_key] # inference engine understands result.errors cannot be None # pylint no longer raises unsubscriptable-object
the required astroid version is now `4.0.0`. see the astroid changelog `https://pylint.readthedocs.io/projects/astroid/en/latest/changelog.html#what-s-new-in-astroid-4-0-0` for additional fixes, features, and performance improvements applicable to pylint.
- Handling of `invalid-name` at the module level was patchy. now, module-level constants that are reassigned are treated as variables and checked against `--variable-rgx` rather than `--const-rgx`. module-level lists, sets, and objects can pass against **either** regex. 👑
here, `LIMIT` is reassigned, so pylint only uses `--variable-rgx`:
LIMIT = 500 # [invalid-name] if sometimes: LIMIT = 1 # [invalid-name]
if this is undesired, refactor using exclusive assignment so that it is evident that this assignment happens only once:
if sometimes: LIMIT = 1 else: LIMIT = 500 # exclusive assignment: uses const regex, no warning
lists, sets, and objects still pass against either `const-rgx` or `variable-rgx` even if reassigned, but are no longer completely skipped:
MY_LIST = [] my_list = [] My_List = [] # [invalid-name]
remember to adjust the regexes `https://pylint.readthedocs.io/en/latest/user_guide/messages/convention/invalid-name.html` and allow lists `https://pylint.readthedocs.io/en/latest/user_guide/configuration/all-options.html#good-names` to your liking. 💖
### What's new in Pylint 4.0.7?
release date: `2026-08-09`
**False Positives Fixed**
- Fix a false positive for `invalid-name` when a module-level variable is assigned an instance of a `TypedDict` subclass. such a name is a value, not a type definition, so it is now checked against the constant or variable regex instead of `class-rgx`.
Closes #11231 (`#11231 <https://github.com/pylint-dev/pylint/issues/11231>`)
**Other Bug Fixes**
- Fix a crash in the `bad-open-mode` check when the `mode` argument of `open` is the `NotImplemented` constant (Python >= 3.14). 💀
Closes #11099 (`#11099 <https://github.com/pylint-dev/pylint/issues/11099>`)
- Fix a crash in the `not-context-manager` and `not-async-context-manager` checks when the context manager infers to a value without a name, such as the slice returned by `with slice(...)` / `async with slice(...)`.
Closes #11102 (`#11102 <https://github.com/pylint-dev/pylint/issues/11102>`)
- Fix a false positive for `nested-min-max` (W3301) when the inner min/max call carries a keyword argument such as `key=`. flattening the call dropped the keyword and changed the result, so nested calls whose inner call has keyword arguments are no longer flagged.
Closes #11130 (`#11130 <https://github.com/pylint-dev/pylint/issues/11130>`)
- Fix a false suggestion from `nested-min-max` (W3301): when rewriting a nested min/max into a splat call, arguments positioned after the splatted call were silently dropped, so the suggested code changed the result. not it. 😭
Closes #11134 (`#11134 <https://github.com/pylint-dev/pylint/issues/11134>`)
- Fix a false positive for `too-many-locals` (R0914): PEP 695 type parameters, i.e. the `T1` and `T2` in a generic `def f[T1, T2]` signature, were counted as local variables. they are type-system constructs, not runtime locals, and are now excluded from the local-variable count. slay. ✨
Closes #11136 (`#11136 <https://github.com/pylint-dev/pylint/issues/11136>`)
- Fix `literal-comparison` (R0123) emitting a corrupted suggestion for identifiers that contain `is` (e.g. `axis is 5` was rendered `ax== == 5`). the suggestion is now rebuilt from the operands and operator.
Closes #11146 (`#11146 <https://github.com/pylint-dev/pylint/issues/11146>`)
- Fix false positives in `bad-string-format-type` (E1307) for valid `%` formatting: `%i`/`%u` applied to a float (both truncate like `%d`) and `%a` applied to any non-int type (`%a` is type-agnostic like `%s`/`%r`).
Closes #11147 (`#11147 <https://github.com/pylint-dev/pylint/issues/11147>`)
- Fix a false positive for `useless-parent-delegation` / `W0246 <# useless-parent-delegation>` when an override changes the default value of a positional-only parameter.
- Closes `#11148` (`https://github.com/pylint-dev/pylint/issues/11148`) — that's the wrap-up for the fix from the last part, bestie 💅
- Fixed a crash in **`comparison-with-callable`** when you're comparing a lambda that's just vibing as a class attribute. It used to go 💀, now it doesn't. — Closes `#11175` (`https://github.com/pylint-dev/pylint/issues/11175`)
- **`too-many-lines`** was being lowkey unhinged: it could get reported at the line of a `# pylint: disable=too-many-lines` pragma that came from a *previously linted module* 😭. Pragma positions are now reset between modules, so the message gets reported at line 1 (or at the current module's own pragma) regardless of which files were linted before it. — Refs `#11191` (`https://github.com/pylint-dev/pylint/issues/11191`)
- No more crash when a call unpacks a dictionary whose keys aren't string constants, like `copy.copy(**{-1: 1})` 🫶 — Closes `#11222` (`https://github.com/pylint-dev/pylint/issues/11222`)
## ✨ What's new in Pylint 4.0.6? ✨
Release date: `2026-06-14`
### 🎀 False Positives Fixed 🎀
- <pink>**`implicit-str-concat`**</pink> is no longer emitted for an implicit concatenation of a raw string with a non-raw one (like `r"\d" "\n"`). Those literals literally *cannot* be merged into a single string, so the concatenation is intentional, not a forgotten comma. — Closes `#6663` (`https://github.com/pylint-dev/pylint/issues/6663`)
- Fixed a false positive for <hotpink>**`invalid-name`**</hotpink> (`C0103`) where the default `typevar-rgx` was rejecting TypeVar names that contain digits, such as `Ec2T`. Rude. — Closes `#8499` (`https://github.com/pylint-dev/pylint/issues/8499`)
- Fixed a false positive for <hotpink>**`too-many-arguments`**</hotpink> in (non-static) methods and classmethods. — Closes `#8675` (`https://github.com/pylint-dev/pylint/issues/8675`)
- Fixed a false positive for <hotpink>**`no-member`**</hotpink> when a value is inferred to several possible types and at least one of them defines a dynamic `__getattr__`. — Closes `#9833` (`https://github.com/pylint-dev/pylint/issues/9833`)
- Fixed a false positive for <hotpink>**`function-redefined`**</hotpink> (`E0102`) when you reuse names that match `dummy-variables-rgx` (such as `_`) — which is super common for `pytest-bdd` step definitions. This restores the behavior from before `pylint 4.0.0`; and as a consequence, the false negative that got fixed in `#9894` is reintroduced for functions whose name matches `dummy-variables-rgx`. Trade-offs, bestie 😭 — Closes `#10665` (`https://github.com/pylint-dev/pylint/issues/10665`)
- Fixed an <hotpink>**`undefined-variable`**</hotpink> false positive when a name used as a metaclass argument in a nested class is referenced again later in the module. — Closes `#10823` (`https://github.com/pylint-dev/pylint/issues/10823`)
- Fixed a false positive for <hotpink>**`unused-variable`**</hotpink> where global variables matching `dummy-variables-rgx` were still getting reported as unused when `allow-global-unused-variables` was disabled. — Closes `#10890` (`https://github.com/pylint-dev/pylint/issues/10890`)
- Fixed a false positive for <hotpink>**`bad-dunder-name`**</hotpink> when there's a user-defined `__suppress_context__` attribute on exception subclasses. — Closes `#10960` (`https://github.com/pylint-dev/pylint/issues/10960`)
- Fixed a <hotpink>**`used-before-assignment`**</hotpink> false positive for names bound by `from X import *` in every branch of an `if`/`elif`/`else` chain. — Refs `#10980` (`https://github.com/pylint-dev/pylint/issues/10980`)
- Now checks for the metaclass `__call__` signature when evaluating arguments of a class call 👑 — Closes `#11032` (`https://github.com/pylint-dev/pylint/issues/11032`)
- Fixed a false positive <hotpink>**`method-hidden`**</hotpink> when `cached_property` is imported directly with `from functools import cached_property`. — Refs `#11037` (`https://github.com/pylint-dev/pylint/issues/11037`)
### 🦋 Other Bug Fixes 🦋
- Fixed the suggestion from <cyan>**`unnecessary-comprehension`**</cyan> for a dict comprehension that iterates a dict directly, e.g. `{a: b for a, b in d}`. Iterating a dict yields its keys, so the suggestion is now `dict(d.keys())` instead of the incorrect `dict(d)`, which would just copy `d` 🙈 — Closes `#8256` (`https://github.com/pylint-dev/pylint/issues/8256`)
- Fixed a crash in <cyan>**`consider-using-enumerate`**</cyan> when the `for` loop target is an attribute (e.g. `for self.idx in range(len(x))`) rather than a simple variable name. — Closes `#10099` (`https://github.com/pylint-dev/pylint/issues/10099`)
- Fixed a crash when checking <cyan>**`attribute-defined-outside-init`**</cyan> on classes that inherit from a base class pylint can't fully analyze. — Closes `#10892` (`https://github.com/pylint-dev/pylint/issues/10892`)
- Fixed `add_message` silently overwriting an explicit `col_offset=0` (or any other zero-valued `line`/`end_lineno`/`end_col_offset`) with the AST node's value. The internal `_add_one_message` helper was using a falsey check (`if not col_offset:`) to detect an omitted argument, which incorrectly treated a legit `0` the same as `None`. It now uses an identity check against `None`. Zero is a number, babes 💀 — Refs `#11020` (`https://github.com/pylint-dev/pylint/issues/11020`)
- Fixed a crash in the name checker when a non-constant value is passed as the `covariant` or `contravariant` argument of a `TypeVar`. — Closes `#11022` (`https://github.com/pylint-dev/pylint/issues/11022`)
- Fixed a crash in the variable checker when a name resolves to a dataclass-synthesized `__init__`, which has no line number. — Closes `#11023` (`https://github.com/pylint-dev/pylint/issues/11023`)
- Fixed a crash in the variables checker when `NotImplemented` is used as the test of an `if` statement, which raised a `TypeError` in a boolean context on Python 3.14. — Closes `#11025` (`https://github.com/pylint-dev/pylint/issues/11025`)
- Avoided a crash from the implicit booleaness checker for `len()` calls without arguments. — Closes `#11028` (`https://github.com/pylint-dev/pylint/issues/11028`)
- Fixed a crash in the variables checker when a class declares a metaclass whose attribute-access chain doesn't bottom out at a name (e.g. `class C(metaclass=None._)`). Originally reported as `pylint-dev/astroid#3066`. — Refs `#11031` (`https://github.com/pylint-dev/pylint/issues/11031`)
- Fixed a crash in the name checker when a chained assignment of a `TypeAlias` value has a non-name target such as a `Subscript` (for example `a[0] = b = TypeAlias`). — Closes `#11056` (`https://github.com/pylint-dev/pylint/issues/11056`)
- Fixed a crash in the deprecated checker when `__import__` is called with a non-string constant argument (for example `__import__(1)`). — Closes `#11059` (`https://github.com/pylint-dev/pylint/issues/11059`)
- Avoided crashing when enum member inference fails while checking enum subclasses. — Closes `#11069` (`https://github.com/pylint-dev/pylint/issues/11069`)
- Prevented a crash in <cyan>**`unexpected-keyword-arg`**</cyan> analysis when `infer_call_result()` raises `InferenceError` while inspecting decorator return signatures. — Closes `#11070` (`https://github.com/pylint-dev/pylint/issues/11070`)
- Fixed a crash in the typecheck checker when a class uses a non-class object (for example a function) as its `metaclass=` argument. — Closes `#11071` (`https://github.com/pylint-dev/pylint/issues/11071`)
- Digits are now allowed in `ParamSpec` and `TypeVarTuple` names for the <cyan>**`invalid-name`**</cyan> check 🎉
The default `paramspec-rgx` and `typevartuple-rgx` patterns were rejecting names containing digits (e.g. `Ec2P`, `S3Ts`), emitting a false `invalid-name` (`C0103`). Digits are now allowed in the lowercase segments, consistent with the `typevar` and `typealias` patterns. — Closes `#11090` (`https://github.com/pylint-dev/pylint/issues/11090`)
### 🌈 Performance Improvements 🌈
- The <purple>**`duplicate-code`**</purple> checker no longer runs when its message (`R0801`) is disabled — even if `reports=yes` is set. Previously the checker's report (`RP0801`) would cause that expensive similarity computation to run regardless. Fast is a vibe ✨ — Closes `#3443` (`https://github.com/pylint-dev/pylint/issues/3443`)
---
## ⭐ What's new in Pylint 4.0.5? ⭐
Release date: `2026-02-20`
### 🎀 False Positives Fixed 🎀
- Fixed a <hotpink>**`possibly-used-before-assignment`**</hotpink> false positive when using `self.fail()` in tests. — Closes `#10743` (`https://github.com/pylint-dev/pylint/issues/10743`)
- Fixed a false positive for <hotpink>**`logging-unsupported-format`**</hotpink> when no arguments are provided to logging functions.
According to Python's logging documentation, no formatting is performed when no arguments are supplied, so strings like `logging.error("%test")` are valid 👍 — Closes `#10752` (`https://github.com/pylint-dev/pylint/issues/10752`)
- Fixed a false positive for <hotpink>**`invalid-name`**</hotpink> where a dataclass field typed with `Final` was evaluated against the `class_const` regex instead of the `class_attribute` regex. — Closes `#10790` (`https://github.com/pylint-dev/pylint/issues/10790`)
- Avoids emitting <hotpink>**`unspecified-encoding`**</hotpink> (`W1514`) when `py-version` is `3.15+`. — Refs `#10791` (`https://github.com/pylint-dev/pylint/issues/10791`)
### 🦋 Other Bug Fixes 🦋
- Fixed `--known_third_party` config being ignored 🫠 — Closes `#10801` (`https://github.com/pylint-dev/pylint/issues/10801`)
- Fixed dynamic color mapping for <cyan>"fail-on"</cyan> messages when using multiple reporter/output formats. — Closes `#10825` (`https://github.com/pylint-dev/pylint/issues/10825`)
- The dependency on `isort` is now set to `<9`, permitting you to use `isort 8`. — Closes `#10857` (`https://github.com/pylint-dev/pylint/issues/10857`)
---
## 💖 What's new in Pylint 4.0.4? 💖
Release date: `2025-11-30`
### 🎀 False Positives Fixed 🎀
- Fixed a false positive for <hotpink>**`invalid-name`**</hotpink> where module-level constants were incorrectly classified as variables when a class-level attribute with the same name exists. — Closes `#10719` (`https://github.com/pylint-dev/pylint/issues/10719`)
- Fixed a false positive for <hotpink>**`invalid-name`**</hotpink> on an `UPPER_CASED` name inside an `if` branch that assigns an object. — Closes `#10745` (`https://github.com/pylint-dev/pylint/issues/10745`)
---
## 🍓 What's new in Pylint 4.0.3? 🍓
Release date: `2025-11-13`
### 🎀 False Positives Fixed 🎀
- Added the `Enum` dunder methods `_generate_next_value_`, `_missing_`, `_numeric_repr_`, `_add_alias_`, and `_add_value_alias_` to the list passed to `--good-dunder-names`. Iconic behavior, honestly 👑 — Closes `#10435` (`https://github.com/pylint-dev/pylint/issues/10435`)
- Fixed a false positive for <hotpink>**`invalid-name`**</hotpink> with `typing.Annotated`. — Closes `#10696` (`https://github.com/pylint-dev/pylint/issues/10696`)
- Fixed a false positive for <hotpink>**`f-string-without-interpolation`**</hotpink> with template strings when using a format spec. — Closes `#10702` (`https://github.com/pylint-dev/pylint/issues/10702`)
- <pink>okay so</pink> this one fixes a false positive where an `UPPER_CASED` class attribute was raising an `invalid-name` when it was typed with `Final`. 💅 <purple>Closes `#10711`</purple> (`https://github.com/pylint-dev/pylint/issues/10711`).
- <mint>Next up:</mint> fixes a false positive for `unbalanced-tuple-unpacking` when a tuple is assigned to a function call and the structure of the function's return value is ambiguous. 🫶 <purple>Closes `#10721`</purple> (`https://github.com/pylint-dev/pylint/issues/10721`).
### 🛠️ Other Bug Fixes
- Makes the `'ignore'` option work as expected again. <yellow>No cap, we love a comeback.</yellow> <purple>Closes `#10669`</purple> (`https://github.com/pylint-dev/pylint/issues/10669`).
- Fixes a crash for `consider-using-assignment-expr` when a variable annotation without assignment is used as the `if` test expression. 😭 <purple>Closes `#10707`</purple> (`https://github.com/pylint-dev/pylint/issues/10707`).
- Fixes a crash for `prefer-typing-namedtuple` and `consider-math-not-float` when a slice object is called. 💀 <purple>Closes `#10708`</purple> (`https://github.com/pylint-dev/pylint/issues/10708`).
---
## 🌸 What's new in Pylint 4.0.2?
Release date: `2025-10-20`
### 💖 False Positives Fixed
- Fixes a false positive for `invalid-name` / `C0103` on a partially uninferable module-level constant. <pink>bestie, the inference girlies are healing.</pink> <purple>Closes `#10652`</purple> (`https://github.com/pylint-dev/pylint/issues/10652`).
- Fixes a false positive for `invalid-name` / `C0103` on exclusive module-level assignments composed of three or more branches. <yellow>Important lil caveat:</yellow> we won't raise `disallowed-name` / `C0104` on module-level names that can't be inferred until a further refactor to remove this false negative is done. <purple>Closes `#10664`</purple> (`https://github.com/pylint-dev/pylint/issues/10664`).
- Fixes a false positive for `invalid-name` / `C0103` for `TypedDict` instances. 🦋 <purple>Closes `#10672`</purple> (`https://github.com/pylint-dev/pylint/issues/10672`).
---
## 🌈 What's new in Pylint 4.0.1?
Release date: `2025-10-14`
### ✨ False Positives Fixed
- Excludes `__all__` and `__future__.annotations` from `unused-variable`. <pink>finally, they can chill.</pink> <purple>Closes `#10019`</purple> (`https://github.com/pylint-dev/pylint/issues/10019`).
- Fixes a false-positive for `bare-name-capture-pattern` if a case guard is used. 💅 <purple>Closes `#10647`</purple> (`https://github.com/pylint-dev/pylint/issues/10647`).
- Checks enums created with the `Enum()` functional syntax to pass against the `--class-rgx` for the `invalid-name` / `C0103` check, like other enums. 🎀 <purple>Closes `#10660`</purple> (`https://github.com/pylint-dev/pylint/issues/10660`).
---
## 👑 What's new in Pylint 4.0.0?
Release date: `2025-10-12`
### 💥 Breaking Changes
- `invalid-name` now distinguishes module-level constants that are assigned only once from those that are reassigned, and it now applies `--variable-rgx` to the latter. Values other than literals (lists, sets, objects) can pass against either the constant or variable regexes (e.g. `"LOGGER"` or `"logger"` but not `"LoGgEr"`). <yellow>Remember that</yellow> `--good-names` or `--good-names-rgxs` can be provided to explicitly allow good names. <purple>Closes `#3585`</purple> (`https://github.com/pylint-dev/pylint/issues/3585`).
- The unused `pylintrc` argument to `PyLinter.__init__()` is deprecated and will be removed. 🫠 <purple>Refs `#6052`</purple> (`https://github.com/pylint-dev/pylint/issues/6052`).
- Commented out code blocks such as `# bar() # TODO: remove dead code` will no longer emit `fixme`. <pink>the dead-code vibe check has been updated.</pink> <purple>Refs `#9255`</purple> (`https://github.com/pylint-dev/pylint/issues/9255`).
- `pyreverse` `Run` was changed to no longer call `sys.exit()` in its `__init__`. You should now call `Run(args).run()` which will return the exit code instead. Having a class that always raised a `SystemExit` exception was considered a bug. 😭 Normal usage of `pyreverse` through the CLI will not be affected by this change. <purple>Refs `#9689`</purple> (`https://github.com/pylint-dev/pylint/issues/9689`).
- The `suggestion-mode` option was removed, as `pylint` now always emits user-friendly hints instead of false-positive error messages. You should remove it from your conf if it's defined. <red>fr fr, go clean your config.</red> <purple>Refs `#9962`</purple> (`https://github.com/pylint-dev/pylint/issues/9962`).
- The `async.py` checker module has been renamed to `async_checker.py` since `async` is a Python keyword and cannot be imported directly. This allows for better testing and extensibility of the async checker functionality. 🧚 <purple>Refs `#10071`</purple> (`https://github.com/pylint-dev/pylint/issues/10071`).
- The message-id of `continue-in-finally` was changed from `E0116` to `W0136`. The warning is now emitted for every Python version since it will raise a syntax warning in Python 3.14. See PEP 765 - Disallow return/break/continue that exit a finally block (`https://peps.python.org/pep-0765/`). 💀 <purple>Refs `#10480`</purple> (`https://github.com/pylint-dev/pylint/issues/10480`).
- Removed support for `nmp.NaN` alias for `numpy.NaN` being recognized in `'nan-comparison / W0177'`. Use `np` or `numpy` instead. <pink>bestie, use the real one.</pink> <purple>Refs `#10583`</purple> (`https://github.com/pylint-dev/pylint/issues/10583`).
- Version requirement for `isort` has been bumped to `>=5.0.0`. The internal compatibility for older `isort` versions exposed via `pylint.utils.IsortDriver` has been removed. 🦋 <purple>Refs `#10637`</purple> (`https://github.com/pylint-dev/pylint/issues/10637`).
### 🌟 New Features
- `comparison-of-constants` now uses the unicode from the `ast` instead of reformatting from the node's values preventing some bad formatting due to `utf-8` limitation. The message now uses `"` instead of `'` to better work with what the python `ast` returns. ✨ <purple>Refs `#8736`</purple> (`https://github.com/pylint-dev/pylint/issues/8736`).
- Enhanced `pyreverse` to properly distinguish between UML relationship types (association, aggregation, composition) based on object ownership semantics. Type annotations without assignment are now treated as associations, parameter assignments as aggregations, and object instantiation as compositions. <pink>UML girlies, we are so back.</pink> <purple>Closes `#9045` and `#9267`</purple> (`https://github.com/pylint-dev/pylint/issues/9045`, `https://github.com/pylint-dev/pylint/issues/9267`).
- The `fixme` check can now search through docstrings as well as comments, by using `check-fixme-in-docstring = true` in the `[tool.pylint.miscellaneous]` section. 🎀 <purple>Closes `#9255`</purple> (`https://github.com/pylint-dev/pylint/issues/9255`).
- The `use-implicit-booleaness-not-x` checks now distinguish between comparisons used in boolean contexts and those that are not, enabling them to provide more accurate refactoring suggestions. 💅 <purple>Closes `#9353`</purple> (`https://github.com/pylint-dev/pylint/issues/9353`).
- The `verbose` option now outputs the filenames of the files that have been checked. Previously, it only included the number of checked and skipped files. <yellow>name drop those files, bestie.</yellow> <purple>Closes `#9357`</purple> (`https://github.com/pylint-dev/pylint/issues/9357`).
- `colorized` reporter now colorizes messages/categories that have been configured as `fail-on` in red inverse. This makes it easier to quickly find the errors that are causing `pylint` CI job failures. 🔥 <purple>Closes `#9898`</purple> (`https://github.com/pylint-dev/pylint/issues/9898`).
- Enhanced support for `@property` decorator in `pyreverse` to correctly display return types of annotated properties when generating class diagrams. 🦋 <purple>Closes `#10057`</purple> (`https://github.com/pylint-dev/pylint/issues/10057`).
- Add `--max-depth` option to `pyreverse` to control diagram complexity. A depth of `0` shows only top-level packages, `1` shows one level of subpackages, etc. This helps manage visualization of large codebases by limiting the depth of displayed packages and classes. <pink>diagram chaos: managed.</pink> <purple>Refs `#10077`</purple> (`https://github.com/pylint-dev/pylint/issues/10077`).
- Handle deferred evaluation of annotations in Python 3.14. ✨ <purple>Closes `#10149`</purple> (`https://github.com/pylint-dev/pylint/issues/10149`).
- Enhanced `pyreverse` to properly detect aggregations for comprehensions (list, dict, set, generator). 🌸 <purple>Closes `#10236`</purple> (`https://github.com/pylint-dev/pylint/issues/10236`).
- `pyreverse`: add support for colorized output when using output format `mmd` (MermaidJS) and `html`. 🎀 <purple>Closes `#10242`</purple> (`https://github.com/pylint-dev/pylint/issues/10242`).
- `pypy` 3.11 is now officially supported. 💜 <purple>Refs `#10287`</purple> (`https://github.com/pylint-dev/pylint/issues/10287`).
- Add support for Python 3.14. 🌈 <purple>Refs `#10467`</purple> (`https://github.com/pylint-dev/pylint/issues/10467`).
- Add naming styles for `ParamSpec` and `TypeVarTuple` that align with the `TypeVar` style. ✨ <purple>Refs `#10541`</purple> (`https://github.com/pylint-dev/pylint/issues/10541`).
### 🕵️ New Checks
- Add `match-statements` checker and the following message: `bare-name-capture-pattern`. This will emit an error message when a name capture pattern is used in a `match` statement which would make the remaining patterns unreachable. This code is a `SyntaxError` at runtime. 💀 <purple>Closes `#7128`</purple> (`https://github.com/pylint-dev/pylint/issues/7128`).
- Add new check `async-context-manager-with-regular-with` to detect async context managers used with regular `with` statements instead of `async with`. 🫶 <purple>Refs `#10408`</purple> (`https://github.com/pylint-dev/pylint/issues/10408`).
- Add `break-in-finally` warning. Using `break` inside the `finally` clause will raise a syntax warning in Python 3.14. See PEP 765 - Disallow return/break/continue that exit a finally block (`https://peps.python.org/pep-0765/`). 😭 <purple>Refs `#10480`</purple> (`https://github.com/pylint-dev/pylint/issues/10480`).
- Add new checks for invalid uses of class patterns in `match` (`https://docs.python.org/3/reference/compound_stmts.html#match`).
- `invalid-match-args-definition` / `E1902` is emitted if `object.__match_args__` (`https://docs.python.org/3/reference/datamodel.html#object.__match_args__`) isn't a tuple of strings.
- `too-many-positional-sub-patterns` / `E1903` if there are more positional sub-patterns than specified in `object.__match_args__` (`https://docs.python.org/3/reference/datamodel.html#object.__match_args__`).
- `multiple-class-sub-patterns` / `E1904` if there are multiple sub-patterns for the same attribute.
<purple>Refs `#10559`</purple> (`https://github.com/pylint-dev/pylint/issues/10559`).
- Add additional checks for suboptimal uses of class patterns in `match` (`https://docs.python.org/3/reference/compound_stmts.html#match`).
- `match-class-bind-self` / `R1905` is emitted if a name is bound to `self` instead of using an `as` pattern.
- **`match-class-positional-attributes` / `R1906`** (`#match-class-positional-attributes`) — this one fires when a class pattern has positional attributes even though keywords could've been used instead. Like, why. Refs #10587 (<https://github.com/pylint-dev/pylint/issues/10587>)
- **`consider-math-not-float`** — brand new message, bestie! `float("nan")` and `float("inf")` are literally 4x slower than their counterparts `math.inf` and `math.nan` (and that's not even counting the initial `import math`), plus they're not well typed when you're using mypy. This check also catches typos in `float` calls as a side effect, which is lowkey iconic. You gotta have the Code Style checker (`#pylint-extensions-code-style`) activated for this check to work. Refs #10621 (<https://github.com/pylint-dev/pylint/issues/10621>)
**False Positives Fixed**
- Fixed a false positive for `used-before-assignment` when a variable defined under an `if` and via a named expression (walrus operator) gets used later while guarded under that same `if` test. Closes #10061 (<https://github.com/pylint-dev/pylint/issues/10061>)
- Fixed `no-name-in-module` / `E0611` (`#no-name-in-module`) for members of `concurrent.futures` with Python 3.14. Closes #10632 (<https://github.com/pylint-dev/pylint/issues/10632>)
**False Negatives Fixed**
- Fixed a false negative for `used-before-assignment` when a `TYPE_CHECKING` import is used as a type annotation *before* the erroneous usage. Refs #8893 (<https://github.com/pylint-dev/pylint/issues/8893>)
- Match cases are now counted as edges in the McCabe graph and will bump the complexity accordingly. Refs #9667 (<https://github.com/pylint-dev/pylint/issues/9667>)
- Module-level constants with type annotations now get checked for `invalid-name`. Remember to adjust `const-naming-style` or `const-rgx` to your liking 💅 Closes #9770 (<https://github.com/pylint-dev/pylint/issues/9770>)
- Fixed a false negative where `function-redefined` (`E0102`) was not reported for functions with a leading underscore. Closes #9894 (<https://github.com/pylint-dev/pylint/issues/9894>)
- We now raise `logging-too-few-args` for a format string with no interpolation arguments at all (i.e. for something like `logging.debug("Awaiting process %s")` or `logging.debug("Awaiting process {pid}")`). Before this, we simply didn't raise for that case. Closes #9999 (<https://github.com/pylint-dev/pylint/issues/9999>)
- Fixed a false negative for `used-before-assignment` when a function is defined inside a `TYPE_CHECKING` guard block and used later. Closes #10028 (<https://github.com/pylint-dev/pylint/issues/10028>)
- Fixed a false negative for `possibly-used-before-assignment` when a variable is conditionally defined and later assigned to a type-annotated variable. Closes #10421 (<https://github.com/pylint-dev/pylint/issues/10421>)
- Fixed a false negative for `deprecated-module` when a `__import__` method is used instead of an import sentence. Refs #10453 (<https://github.com/pylint-dev/pylint/issues/10453>)
- Match cases now count toward the `too-many-branches` check. Refs #10542 (<https://github.com/pylint-dev/pylint/issues/10542>)
- Fixed a false negative where `unused-import` / `W0611` (`#unused-import`) wasn't reported for names referenced in a preceding `global` statement. Refs #10633 (<https://github.com/pylint-dev/pylint/issues/10633>)
**Other Bug Fixes**
- When displaying unicode with surrogates (or other potential `UnicodeEncodeError`), pylint now displays a `'?'` character (using `encode(encoding="utf-8", errors="replace")`) instead of just crashing 😭 The functional tests classes are also updated to handle this case. Closes #8736. (<https://github.com/pylint-dev/pylint/issues/8736>)
- Fixed `unidiomatic-typecheck` only checking the left-hand side. Closes #10217 (<https://github.com/pylint-dev/pylint/issues/10217>)
- Fixed a crash caused by malformed format strings when using `.format` with keyword arguments. Closes #10282 (<https://github.com/pylint-dev/pylint/issues/10282>)
- Fixed a false positive `inconsistent-return-statements` when using `quit()` or `exit()` functions. Closes #10508 (<https://github.com/pylint-dev/pylint/issues/10508>)
- Fixed a crash in `nested-min-max` / `W3301` (`#nested-min-max`) when using `builtins.min` or `builtins.max` instead of `min` or `max` directly. Closes #10626 (<https://github.com/pylint-dev/pylint/issues/10626>)
- Fixed a crash in `unnecessary-dict-index-lookup` / `R1733` (`#unnecessary-dict-index-lookup`) when the index of an enumerated list was deleted inside a `for` loop. Closes #10627 (<https://github.com/pylint-dev/pylint/issues/10627>)
**Other Changes**
- Support for launching pylint with Python 3.9 is **gone** 🫶 Code that supports Python 3.9 can still be linted with the `--py-version=3.9` setting. Refs #10405 (<https://github.com/pylint-dev/pylint/issues/10405>)
**Internal Changes**
- Modified the test framework to allow for different test output for different Python versions. Refs #10382 (<https://github.com/pylint-dev/pylint/issues/10382>)
---
## 🌈 3.x
This is the full list of change in pylint 3.x minors, by categories.
### ✨ What's New in Pylint 3.3
`:Release:3.3 :Date: 2024-09-20`
**Summary -- Release highlights**
**What's new in Pylint 3.3.9?** — Release date: 2025-10-05
**False Positives Fixed**
- Fixed `used-before-assignment` / `E0601` (`#used-before-assignment`) for PEP 695 type aliases and parameters. Closes #9815 (<https://github.com/pylint-dev/pylint/issues/9815>)
- No longer flags undeprecated functions in `importlib.resources` as deprecated. Closes #10593 (<https://github.com/pylint-dev/pylint/issues/10593>)
- Fixed a false positive `inconsistent-return-statements` when using `quit()` or `exit()` functions. Closes #10508 (<https://github.com/pylint-dev/pylint/issues/10508>)
- Fixed a false positive `undefined-variable` (`E0602`) for for-loop variable shadowing patterns like `for item in item:` when the variable was previously defined. Closes #10562 (<https://github.com/pylint-dev/pylint/issues/10562>)
**Other Bug Fixes**
- Fixed a crash in `unnecessary-list-index-lookup` when starting an enumeration using minus the length of an iterable inside a dict comprehension, when the `len` call was only made in that dict comprehension and not elsewhere. Also changed the approach to use inference in all cases but the simple ones, so we don't have to fix crashes one by one for arbitrarily complex expressions in `enumerate` 🫠 Closes #10510 (<https://github.com/pylint-dev/pylint/issues/10510>)
**What's new in Pylint 3.3.8?** — Release date: 2025-08-09
This patch release includes an exceptional fix for a false negative issue. For details, see: <https://github.com/pylint-dev/pylint/pull/10482#issuecomment-3164514082>
**False Positives Fixed**
- Fixed false positives for `possibly-used-before-assignment` / `E0606` (`#possibly-used-before-assignment`) when variables are exhaustively assigned within a `match` (<https://docs.python.org/3/reference/compound_stmts.html#match>) block. Closes #9668 (<https://github.com/pylint-dev/pylint/issues/9668>)
- Fixed a false positive for `missing-raises-doc` / `W9006` (`#missing-raises-doc`) and `missing-yield-doc` / `W9013` (`#missing-yield-doc`) when the method length is less than `docstring-min-length`. Refs #10104 (<https://github.com/pylint-dev/pylint/issues/10104>)
- Fixed a false positive for `unused-variable` / `W0612` (`#unused-variable`) when multiple `except` handlers bind the same name under a `try` block. Closes #10426 (<https://github.com/pylint-dev/pylint/issues/10426>)
**False Negatives Fixed**
- Fixed a false negative for `used-before-assignment` / `E0601` (`#used-before-assignment`) with `from __future__ import annotations` in function definitions. Refs #10482 (<https://github.com/pylint-dev/pylint/issues/10482>)
**Other Bug Fixes**
- Fixed a bug in Pyreverse where aggregations and associations were included in diagrams regardless of the selected `--filter-mode` (such as `PUB_ONLY`, `ALL`, etc.). Closes #10373 (<https://github.com/pylint-dev/pylint/issues/10373>)
- Fixed double underscores erroneously rendering as bold in pyreverse's Mermaid output. Closes #10402 (<https://github.com/pylint-dev/pylint/issues/10402>)
**What's new in Pylint 3.3.7?** — Release date: 2025-05-04
**False Positives Fixed**
- Comparisons between two calls to `type()` won't raise an `unidiomatic-typecheck` / `C0123` (`#unidiomatic-typecheck`) warning anymore, consistent with the behavior that previously applied only for `==`. Closes #10161 (<https://github.com/pylint-dev/pylint/issues/10161>)
**Other Bug Fixes**
- Fixed a crash when importing a class decorator that did not exist with the same name as a class attribute after the class definition. Closes #10105 (<https://github.com/pylint-dev/pylint/issues/10105>)
- Fixed a crash caused by malformed format strings when using `.format` with keyword arguments. Closes #10282 (<https://github.com/pylint-dev/pylint/issues/10282>)
- Using a slice as a class decorator now raises a `not-callable` / `E1102` (`#not-callable`) message instead of crashing. A lot of checks that dealt with decorators (too many to list) are now shortcut if the decorator can't immediately be inferred to a function or class definition. Closes #10334 (<https://github.com/pylint-dev/pylint/issues/10334>)
**Other Changes**
- The algorithm used for `no-member` / `E1101` (`#no-member`) suggestions is now more efficient and cuts the calculation when the distance score is already above the threshold. Refs #10277 (<https://github.com/pylint-dev/pylint/issues/10277>)
**What's new in Pylint 3.3.6?** — Release date: 2025-03-20
**False Positives Fixed**
- Fixed a false positive for `used-before-assignment` / `E0601` (`#used-before-assignment`) when an inner function's return type annotation is a class defined at module scope. Closes #9391 (<https://github.com/pylint-dev/pylint/issues/9391>)
**What's new in Pylint 3.3.5?** — Release date: 2025-03-09
- **`use-implicit-booleaness-not-comparison` / C1803**, **`use-implicit-booleaness-not-comparison-to-string` / C1804** and **`use-implicit-booleaness-not-comparison-to-zero` / C1805** — false positives are gone bestie 💅 when chained comparisons are being checked. No more fake callouts on your chained vibes.
Closes #10065
- **`invalid-getnewargs-ex-returned` / E0313** — false positive fixed when the tuple or dict has been assigned to a name 🎀 like, it's not wrong just because you named it, okay?
Closes #10208
- **deprecated modules list** — `getopt` and `optparse` got *removed* from the deprecated modules list 😌 they're off the naughty list, no cap.
Closes #10211
### 🦋 Other Bug Fixes 🦋
- Conditional import `x.y` was causing a false positive on **`possibly-used-before-assignment` / E0606** — fixed 🔥
Closes #10081
- Crash fix 💀 when something that is *not* a class shows up in an `except` handler. We don't crash anymore, we just chill.
Closes #10106
- **`invalid-name` / C0103** was being raised when you used camelCase for private methods with two leading underscores — that's fixed now ✨
Closes #10189
### 🌈 Other Changes 🌈
- Release assets now get uploaded to PyPI via Trusted Publishing 📦🫶
Closes #10256
> okay tiny housekeeping note before the next chunk: every `#number` you see in this whole page is an issue on the pylint tracker at `https://github.com/pylint-dev/pylint/issues/<number>` — that's the link, iykyk 💜
---
## 👑 What's new in Pylint 3.3.4? 👑
Release date: `2025-01-28` ⭐
### 💖 Other Bug Fixes 💖
- The "skipped files" count calculation is fixed 🧮 the previous method was literally displaying an arbitrary number. Like ma'am, what number was that.
Closes #10073
- Fixed a crash that happened when `pylint` was run in a container on a host with cgroupsv2 and restrictions on CPU usage 😭 no more spontaneous combustion.
Closes #10103
- Relaxed the requirements for `isort` so `pylint` can benefit from `isort` 6 🦋
Closes #10203
---
## 🧚 What's new in Pylint 3.3.3? 🧚
Release date: `2024-12-23` ⭐
### 🌸 False Positives Fixed 🌸
- False positives for **`undefined-variable` / E0602** are fixed for classes using the Python 3.12 generic type syntax 🎀
Closes #9335
- False positive fixed for **`use-implicit-booleaness-not-len` / C1802** — no lint should be emitted for generators, periodt (`len()` is not defined for generators, so accusing them is just rude).
Refs #10100
### 🦋 Other Bug Fixes 🦋
- Fixed `Unable to import 'collections.abc' (import-error)` on Python 3.13.1 😮💨
Closes #10112
---
## 🍓 What's new in Pylint 3.3.2? 🍓
Release date: `2024-12-01` ⭐
### 💅 False Positives Fixed 💅
- False positive fixed for **`potential-index-error` / E0643** when an indexed iterable contains a starred element that evaluates to more than one item ✨
Closes #10076
### 💜 Other Bug Fixes 💜
- Fixed the issue with `--source-root` not working when the source files are in a subdirectory of the source root (like when you're using a `/src` layout) 🫶
Closes #10026
---
## 🌈 What's new in Pylint 3.3.1? 🌈
Release date: `2024-09-24` ⭐
### 🎀 False Positives Fixed 🎀
- Fixed a regression that was causing some f-strings to not be inferred as strings 😭 f-strings are strings, they've always been strings, we know this.
Closes #9947
---
## 🔥 What's new in Pylint 3.3.0? 🔥
Release date: `2024-09-20` ⭐
### ⭐ Changes requiring user actions ⭐
- We migrated `symilar` to `argparse`, from `getopt`, so the error and help output changed (for the better 💅). We exit with `2` instead of sometime `1`, sometime `2`. The error output is not captured by the runner anymore. And you can't use a value for the boolean options anymore — `--ignore-comments 1` should become `--ignore-comments`. Yes that's a real migration step, go do it bestie 🫡
Refs #9731
### 🦋 New Features 🦋
- New **`declare-non-slot` / E0245** error 👑 which reports when a class has a `__slots__` member and a type hint on the class is *not* present in `__slots__`.
Refs #9499
### ✨ New Checks ✨
- **`too-many-positional-arguments` / R0917** — added so you can distinguish the configuration for too many total arguments (with keyword-only params specified after `*`) from the configuration for too many positional-or-keyword or positional-only arguments 🧠
As part of evaluating whether this check makes sense for your project, make sure you adjust the value of `--max-positional-arguments` 🫶
Closes #9099
- **`using-exception-groups-in-unsupported-version` / W2603** and **`using-generic-type-syntax-in-unsupported-version` / W2604** 🌸 for uses of Python 3.11+ or 3.12+ features on lower supported versions provided with `--py-version`.
Closes #9791
- **`using-assignment-expression-in-unsupported-version` / W2605** ⭐ for uses of `:=` (the walrus operator) on Python versions below 3.8 provided with `--py-version`.
Closes #9820
- **`using-positional-only-args-in-unsupported-version` / W2606** 🎀 for uses of positional-only args on Python versions below 3.8 provided with `--py-version`.
Closes #9823
- **`unnecessary-default-type-args` / R6007** — added to the `typing` extension to detect the use of unnecessary default type args for `typing.Generator` and `typing.AsyncGenerator` 💜
Refs #9938
### 💀 False Negatives Fixed 💀
- Fixed computation of never-returning functions: `typing.Never` is handled in addition to `typing.NoReturn`, and priority is given to the explicit `--never-returning-functions` option 👑
Closes #7565.
- Fixed a false negative for **`await-outside-async` / E1142** when `await` is inside a Lambda 😮
Refs #9653
- Fixed a false negative for **`duplicate-argument-name` / E0108** by including positional-only, `*args` and `**kwargs` arguments in the check 🎯
Closes #9669
- Fixed a false negative for **`multiple-statements` / C0321** when multiple statements are present on `else` and `finally` lines of `try` 🧚
Refs #9759
- Fixed false negatives when `:py:func::isinstance` does not have exactly two arguments. `pylint` now emits **`too-many-function-args` / E1121** or **`no-value-for-parameter` / E1120** appropriately for `isinstance()` calls 💅
Closes #9847
### 🌸 Other Bug Fixes 🌸
- `--enable` with `--disable=all` now produces an error when an unknown msg code is used. Internal `pylint` messages are no longer affected by `--disable=all` 🫶
Closes #9403
- Impossible-to-compile regexes for paths in the configuration or in the argument given to `pylint` won't crash anymore — instead they raise an `argparse` error and display the error message from `re.compile` ✨
Closes #9680
- Fixed a bug where a `tox.ini` file with `pylint` configuration was ignored even though it exists in the current directory 😭
`.cfg` and `.ini` files containing a Pylint configuration may now use a section named `[pylint]`. This enhancement impacts the scenario where these file types are used as defaults when they are present and have not been explicitly referred to, using the `--rcfile` option.
Closes #9727
- Improved file discovery for directories that are not python packages 🦋
Closes #9764
### 🎀 Other Changes 🎀
- Removed support for launching `pylint` with Python 3.8. Code that supports Python 3.8 can still be linted with the `--py-version=3.8` setting 💜
Refs #9774
- Added support for Python 3.13 🔥
Refs #9852
### ⭐ Internal Changes ⭐
- All variables, classes, functions and file names containing the word 'similar' — when it was, in fact, referring to 'symilar' (the standalone program for the **`duplicate-code` / R0801** check) — were renamed to 'symilar' 🫡 consistency queen.
Closes #9734
- Removed old-style classes (Python 2) code and removed the check for new-style class, since everything is new-style in Python 3. Updated the doc for the exception checker to remove the reference to new style class 💅
Refs #9925
---
## 💜 What's New in Pylint 3.2 💜
Release `3.2` ⭐
Date `TBA` 🕰️ (yes, still TBA, we're all waiting together)
## Summary -- Release highlights
### What's new in Pylint 3.2.7?
Release date: `2024-08-31`
### False Positives Fixed
- Fixed a false positive `unreachable` for `NoReturn` coroutine functions. Closes `#9840`.
### Other Bug Fixes
- Fix crash in refactoring checker when calling a lambda bound as a method. Closes `#9865`.
- Fix a crash in `undefined-loop-variable` when providing the iterable argument to `enumerate()`. Closes `#9875`.
- Fix to address indeterminacy of error message in case a module name is same as another in a separate namespace. Refs `#9883`.
---
## What's new in Pylint 3.2.6?
Release date: `2024-07-21`
### False Positives Fixed
- Quiet false positives for `unexpected-keyword-arg` when pylint cannot determine which of two or more dynamically defined classes is being instantiated. Closes `#9672`.
- Fix a false positive for `missing-param-doc` where a method which is decorated with `typing.overload` was expected to have a docstring specifying its parameters. Closes `#9739`.
- Fix a regression that raised `invalid-name` on class attributes merely overriding invalid names from an ancestor. Closes `#9765`.
- Treat `assert_never()` the same way when imported from `typing_extensions`. Closes `#9780`.
- Fix a false positive for `consider-using-min-max-builtin` when the assignment target is an attribute. Refs `#9800`.
### Other Bug Fixes
- Fix an `AssertionError` arising from properties that return partial functions. Closes `#9214`.
- Fix a crash when a subclass extends `__slots__`. Closes `#9814`.
---
## What's new in Pylint 3.2.5?
Release date: `2024-06-28`
### Other Bug Fixes
- Fixed a false positive `unreachable-code` when using `typing.Any` as return type in python 3.8, the `typing.NoReturn` are not taken into account anymore for python 3.8 however. Closes `#9751`.
---
## What's new in Pylint 3.2.4?
Release date: `2024-06-26`
### False Positives Fixed
- Prevent emitting `possibly-used-before-assignment` when relying on names only potentially not defined in conditional blocks guarded by functions annotated with `typing.Never` or `typing.NoReturn`. Closes `#9674`.
### Other Bug Fixes
- Fixed a crash when the `lineno` of a variable used as an annotation wasn't available for `undefined-variable`. Closes `#8866`.
- Fixed a crash when the start value in an `enumerate` was non-constant and impossible to infer (like in `enumerate(apples, start=int(random_apple_index))`) for `unnecessary-list-index-lookup`. Closes `#9078`.
- Fixed a crash in `symilar` when the `-d` or `-i` short option were not properly recognized. It's still impossible to do `-d=1` (you must do `-d 1`). Closes `#9343`.
---
## What's new in Pylint 3.2.3?
Release date: `2024-06-06`
### False Positives Fixed
- Classes with only an Ellipsis (`...`) in their body do not trigger `multiple-statements` / `C0321` anymore if they are inlined (in accordance with black's 2024 style). Closes `#9398`.
- Fix a false positive for `redefined-outer-name` when there is a name defined in an exception-handling block which shares the same name as a local variable that has been defined in a function body. Closes `#9671`.
- Fix a false positive for `use-yield-from` when using the return value from the `yield` atom. Closes `#9696`.
---
## What's new in Pylint 3.2.2?
Release date: `2024-05-20`
### False Positives Fixed
- Fix multiple false positives for generic class syntax added in Python 3.12 (PEP 695). Closes `#9406`.
- Exclude context manager without cleanup from `contextmanager-generator-missing-cleanup` checks. Closes `#9625`.
---
## What's new in Pylint 3.2.1?
Release date: `2024-05-18`
### False Positives Fixed
- Exclude `if`/`else` branches containing terminating functions (e.g. `sys.exit()`) from `possibly-used-before-assignment` checks. Closes `#9627`.
- Don't emit `typevar-name-incorrect-variance` warnings for PEP 695 style `TypeVar`s. The variance is inferred automatically by the type checker. Adding `_co` or `_contra` suffix can help to reason about `TypeVar`. Refs `#9638`.
- Fix a false positive for `possibly-used-before-assignment` when using `typing.assert_never()` (3.11+) to indicate exhaustiveness. Closes `#9643`.
### Other Bug Fixes
- Fix a false negative for `--ignore-patterns` when the directory to be linted is specified using a dot(`.`) and all files are ignored instead of only the files whose name begin with a dot. Closes `#9273`.
- Restore "errors / warnings by module" section to report output (with `-ry`). Closes `#9145`.
- `trailing-comma-tuple` should now be correctly emitted when it was disabled globally but enabled via local message control, after removal of an over-optimisation. Refs `#9608`.
- Add `--prefer-stubs=yes` option to opt-in to the astroid 3.2 feature that prefers `.pyi` stubs over same-named `.py` files. This has the potential to reduce `no-member` errors but at the cost of more errors such as `not-an-iterable` from function bodies appearing as `...`.
Defaults to `no`.
Closes `#9626` Closes `#9623`.
### Internal Changes
- Update astroid version to `3.2.1`. This solves some reports of `RecursionError` and also makes the prefer `.pyi` stubs feature in astroid `3.2.0` opt-in with the aforementioned `--prefer-stubs=y` option. Refs `#9139`.
---
## What's new in Pylint 3.2.0?
Release date: `2024-05-14`
### New Features
- Understand `six.PY2` and `six.PY3` for conditional imports. Closes `#3501`.
- A new `github` reporter has been added. This reporter returns the output of pylint in a format that Github can use to automatically annotate code. Use it with `pylint --output-format=github` on your Github Workflows. Closes `#9443`.
### New Checks
- Add check `possibly-used-before-assignment` when relying on names after an `if`/`else` switch when one branch failed to define the name, raise, or return. Closes `#1727`.
- Checks for generators that use contextmanagers that don't handle cleanup properly. Is meant to raise visibility on the case that a generator is not fully exhausted and the contextmanager is not cleaned up properly. A contextmanager must yield a non-constant value and not handle cleanup for `GeneratorExit`. The using generator must attempt to use the yielded context value with `x() as y` and not just with `x()`. Closes `#2832`.
### False Negatives Fixed
- `If` and `Try` nodes are now checked for useless return statements as well. Closes `#9449`.
- Fix false negative for `property-with-parameters` in the case of parameters which are positional-only, keyword-only, variadic positional or variadic keyword. Closes `#9584`.
### False Positives Fixed
pylint now understands the `@overload` decorator return values better.
Closes `#4696` Refs `#9606`.
### Performance Improvements
- Ignored modules are now not checked at all, instead of being checked and then ignored. This should speed up the analysis of large codebases which have ignored modules. Closes `#9442`.
- `ImportChecker`'s logic has been modified to avoid context files when possible. This makes it possible to cache module searches on astroid and reduce execution times. Refs `#9310`.
- An internal check for `trailing-comma-tuple` being enabled for a file or not is now done once per file instead of once for each token. Refs `#9608`.
---
# What's New in Pylint 3.1
Release: `3.1` — Date: `2024-02-25`
## Summary -- Release highlights
Two new checks -- `use-yield-from`, `deprecated-attribute` -- and a smattering of bug fixes.
## What's new in Pylint 3.1.1?
Release date: `2024-05-13`
### False Positives Fixed
- Treat `attrs.define` and `attrs.frozen` as dataclass decorators in `too-few-public-methods` check. Closes `#9345`.
- Fix a false positive with `singledispatchmethod-function` when a method is decorated with both `functools.singledispatchmethod` and `staticmethod`. Closes `#9531`.
- Fix a false positive for `consider-using-dict-items` when iterating using `keys()` and then deleting an item using the key as a lookup.
- Closes #9554 (`https://github.com/pylint-dev/pylint/issues/9554`)
---
# ✨ What's new in Pylint 3.1.0? ✨
Release date: `2024-02-25`
## 💖 New Features 💖
- Skip the `consider-using-join` check for non-empty separators if a `suggest-join-with-non-empty-separator` option is set to `no`.
Closes #8701 (`https://github.com/pylint-dev/pylint/issues/8701`)
- Discover `.pyi` files when linting.
These can be ignored with the `ignore-patterns` setting.
Closes #9097 (`https://github.com/pylint-dev/pylint/issues/9097`)
- Check `TypeAlias` and `TypeVar` (PEP 695) nodes for `invalid-name`.
Refs #9196 (`https://github.com/pylint-dev/pylint/issues/9196`)
- Support for resolving external toml files named `pylintrc.toml` and `.pylintrc.toml`.
Closes #9228 (`https://github.com/pylint-dev/pylint/issues/9228`)
- Check for `.clear`, `.discard`, `.pop` and `remove` methods being called on a set while it is being iterated over.
Closes #9334 (`https://github.com/pylint-dev/pylint/issues/9334`)
## 💅 New Checks 💅
- New message `use-yield-from` added to the refactoring checker. This message is emitted when yielding from a loop can be replaced by `yield from`.
Closes #9229. (`https://github.com/pylint-dev/pylint/issues/9229`)
- Added a `deprecated-attribute` message to check deprecated attributes in the stdlib.
Closes #8855 (`https://github.com/pylint-dev/pylint/issues/8855`)
## 🫶 False Positives Fixed 🫶
- Fixed false positive for `inherit-non-class` for generic `Protocol`s.
Closes #9106 (`https://github.com/pylint-dev/pylint/issues/9106`)
- Exempt `TypedDict` from `typing_extensions` from `too-many-ancestor` checks.
Refs #9167 (`https://github.com/pylint-dev/pylint/issues/9167`)
## 🔥 False Negatives Fixed 🔥
- Extend `broad-exception-raised` / `W0719` and `broad-exception-caught` / `W0718` to `except*`.
Closes #8827 (`https://github.com/pylint-dev/pylint/issues/8827`)
- Fix a false-negative for `unnecessary` if blocks using a different than expected ordering of arguments.
Closes #8947. (`https://github.com/pylint-dev/pylint/issues/8947`)
## 🌈 Other Bug Fixes 🌈
- Improve the message provided for the `wrong-import-order` / `C0411` check. Instead of the import statement (`"import x"`), the message now specifies the import that is out of order and which imports should come after it. As reported in the issue, this is particularly helpful if there are multiple imports on a single line that do not follow the PEP8 convention.
The message will report imports as follows: For `"import X"`, it will report `"(standard/third party/first party/local) import X"` For `"import X.Y"` and `"from X import Y"`, it will report `"(standard/third party/first party/local) import X.Y"` The import category is specified to provide explanation as to why pylint has issued the message and guidance to the developer on how to fix the problem.
Closes #8808 (`https://github.com/pylint-dev/pylint/issues/8808`)
## 🎀 Other Changes 🎀
- Print how many files were checked in verbose mode.
Closes #8935 (`https://github.com/pylint-dev/pylint/issues/8935`)
- Fix a crash when an enum class which is also decorated with a `dataclasses.dataclass` decorator is defined.
Closes #9100 (`https://github.com/pylint-dev/pylint/issues/9100`)
## ⭐ Internal Changes ⭐
- Update astroid version to `3.1.0`.
Refs #9457 (`https://github.com/pylint-dev/pylint/issues/9457`)
---
# 👑 What's New in Pylint 3.0 👑
## Release
`3.0.0`
## Date
`2023-10-02`
## 🦋 Summary -- Release highlights 🦋
Pylint now support python `3.12` officially.
This long anticipated major version also provides some important usability and performance improvements, along with enacting necessary breaking changes and long-announced deprecations. The documentation of each message with an example is very close too.
The required astroid version is now `3.0.0`. See the astroid changelog (`https://pylint.readthedocs.io/projects/astroid/en/latest/changelog.html#what-s-new-in-astroid-3-0-0`) for additional fixes, features, and performance improvements applicable to pylint.
Our code is now fully typed. The `invalid-name` message no longer checks for a minimum length of 3 characters by default. Dependencies like `wrapt` or `setuptools` were removed.
A new `json2` reporter has been added. It features an enriched output that is easier to parse and provides more info, here's a sample output.
{ "messages": [ { "type": "convention", "symbol": "line-too-long", "message": "Line too long (1/2)", "messageId": "C0301", "confidence": "HIGH", "module": "0123", "obj": "", "line": 1, "column": 0, "endLine": 1, "endColumn": 4, "path": "0123", "absolutePath": "0123" } ], "statistics": { "messageTypeCount": { "fatal": 0, "error": 0, "warning": 0, "refactor": 0, "convention": 1, "info": 0 }, "modulesLinted": 1, "score": 5.0 } }
---
# 🍓 What's new in Pylint 3.0.4? 🍓
Release date: `2024-02-23`
## 🫶 False Positives Fixed 🫶
- `used-before-assignment` is no longer emitted when using a name in a loop and depending on an earlier name assignment in an `except` block paired with `else: continue`.
Closes #6804 (`https://github.com/pylint-dev/pylint/issues/6804`)
- Avoid false positives for `no-member` involving function attributes supplied by decorators.
Closes #9246 (`https://github.com/pylint-dev/pylint/issues/9246`)
- Fixed false positive `nested-min-max` / `W3301` for nested lists.
Closes #9307 (`https://github.com/pylint-dev/pylint/issues/9307`)
- Fix false positive for `used-before-assignment` in a `finally` block when assignments took place in both the `try` block and each exception handler.
Closes #9451 (`https://github.com/pylint-dev/pylint/issues/9451`)
## 🌈 Other Bug Fixes 🌈
- Catch incorrect `ValueError` "generator already executing" for Python `3.12.0` - `3.12.2`. This is fixed upstream in Python `3.12.3`.
Closes #9138 (`https://github.com/pylint-dev/pylint/issues/9138`)
---
# 💀 What's new in Pylint 3.0.3? 💀
Release date: `2023-12-11`
## 🫶 False Positives Fixed 🫶
- Fixed false positive for `unnecessary-lambda` when the call has keyword arguments but not the lambda.
Closes #9148 (`https://github.com/pylint-dev/pylint/issues/9148`)
- Fixed incorrect suggestion for shallow copy in `unnecessary-comprehension` / `R1721`
Example of the suggestion:
pylint: disable=missing-module-docstring
a = [1, 2, 3] b = [x for x in a] b[0] = 0 print(a) # [1, 2, 3]
After changing `b = [x for x in a]` to `b = a` based on the suggestion, the script now prints `[0, 2, 3]`. The correct suggestion should be use `list(a)` to preserve the original behavior.
Closes #9172 (`https://github.com/pylint-dev/pylint/issues/9172`)
- Fix false positives for `undefined-variable` and `unused-argument` for classes and functions using Python `3.12` generic type syntax.
Closes #9193 (`https://github.com/pylint-dev/pylint/issues/9193`)
- Fixed `pointless-string-statement` false positive for docstrings on Python `3.12` type aliases.
Closes #9268 (`https://github.com/pylint-dev/pylint/issues/9268`)
- Fix false positive for `invalid-exception-operation` when concatenating tuples of exception types.
Closes #9288 (`https://github.com/pylint-dev/pylint/issues/9288`)
## 🌈 Other Bug Fixes 🌈
- Fix a bug where pylint was unable to walk recursively through a directory if the directory has an `__init__.py` file.
Closes #9210 (`https://github.com/pylint-dev/pylint/issues/9210`)
---
# 🧚 What's new in Pylint 3.0.2? 🧚
Release date: `2023-10-22`
## 🫶 False Positives Fixed 🫶
- Fix `used-before-assignment` false positive for generic type syntax (PEP 695, Python `3.12`).
Closes #9110 (`https://github.com/pylint-dev/pylint/issues/9110`)
## 🌈 Other Bug Fixes 🌈
- Escape special symbols and newlines in messages.
Closes #7874 (`https://github.com/pylint-dev/pylint/issues/7874`)
- Fixes suggestion for `nested-min-max` for expressions with additive operators, list and dict comprehensions.
Closes #8524 (`https://github.com/pylint-dev/pylint/issues/8524`)
- Fixes ignoring conditional imports with `ignore-imports=y`.
Closes #8914 (`https://github.com/pylint-dev/pylint/issues/8914`)
- Emit `inconsistent-quotes` for f-strings with `3.12` interpreter only if targeting pre-`3.12` versions.
Closes #9113 (`https://github.com/pylint-dev/pylint/issues/9113`)
---
# 🌸 What's new in Pylint 3.0.1? 🌸
Release date: `2023-10-05`
## 🫶 False Positives Fixed 🫶
- Fixed false positive for `inherit-non-class` for generic `Protocol`s.
Closes #9106 (`https://github.com/pylint-dev/pylint/issues/9106`)
## 🎀 Other Changes 🎀
- Fix a crash when an enum class which is also decorated with a `dataclasses.dataclass` decorator is defined.
Closes #9100 (`https://github.com/pylint-dev/pylint/issues/9100`)
---
# 🔥 What's new in Pylint 3.0.0? 🔥
Release date: `2023-10-02`
## 💅 Breaking Changes 💅
- Enabling or disabling individual messages will now take effect even if an `--enable=all` or `disable=all` follows in the same configuration file (or on the command line).
This means for the following example, `fixme` messages will now be emitted:
pylint my_module --enable=fixme --disable=all
To regain the prior behavior, remove the superfluous earlier option.
Closes #3696 (`https://github.com/pylint-dev/pylint/issues/3696`)
- Remove support for launching pylint with Python `3.7`. Code that supports Python `3.7` can still be linted with the `--py-version=3.7` setting.
Refs #6306 (`https://github.com/pylint-dev/pylint/issues/6306`)
- Disables placed in a `try` block now apply to the `except` block. Previously, they only happened to do so in the presence of an `else` clause.
Refs #7767 (`https://github.com/pylint-dev/pylint/issues/7767`)
- **`pyreverse` color palette glow-up** — `pyreverse` now uses a new default color palette that's more colorblind friendly 💖. The color scheme is taken from Paul Tol's Notes: https://personal.sron.nl/~pault/. If you prefer other colors, you can use the `--color-palette` option to specify custom colors. Closes `#8251` — https://github.com/pylint-dev/pylint/issues/8251.
- **`__implements__` era is over** — everything related to the `__implements__` construct was removed 🗑️. It was based on PEP245, which was proposed in 2001 and rejected in 2006. The `pyreverse` capability to take `__implements__` into account when generating diagrams was also removed. Refs `#8404` — https://github.com/pylint-dev/pylint/issues/8404.
- **`.vcg` output format dropped** — `pyreverse`: support for the `.vcg` output format (Visualization of Compiler Graphs) has been dropped 💀. Closes `#8416` — https://github.com/pylint-dev/pylint/issues/8416.
- **old cache warning gone** — the warning when the now-useless old pylint cache directory (`pylint.d`) was found was removed 🙈. The cache dir is documented in the FAQ: https://pylint.readthedocs.io/en/latest/faq.html#where-is-the-persistent-data-stored-to-compare-between-successive-runs. Refs `#8462` — https://github.com/pylint-dev/pylint/issues/8462.
- **`pylint.config.PYLINTRC` removed** — following a deprecation period, `pylint.config.PYLINTRC` was removed ✨. Use the `pylint.config.find_default_config_files` generator instead. Closes `#8862` — https://github.com/pylint-dev/pylint/issues/8862.
## 💅 Changes requiring user actions 💅
- **`invalid-name` no longer checks minimum length by default** — the `invalid-name` message no longer checks for a minimum length of 3 characters by default 😮. This was an unadvertised commingling of concerns between casing and name length, and users regularly reported this to be surprising. If checking for a minimum length is still desired, it can be regained in two ways:
- If you are content with a `disallowed-name` message (instead of `invalid-name`), then simply add the option `bad-names-rgxs="^..?$"`, which will fail 1-2 character-long names. Ensure you enable `disallowed-name`.
- If you would prefer an `invalid-name` message to be emitted, or would prefer finer-grained control over the circumstances in which messages are emitted (classes vs. methods, etc.), then avail yourself of the regex options described here: https://pylint.readthedocs.io/en/stable/user_guide/configuration/all-options.html#main-checker. In particular, take note of the commented-out options in the "example configuration" given at the bottom of the section. The prior regexes can be found in the pull request that removed the length requirements: https://github.com/pylint-dev/pylint/pull/8813.
Closes `#2018` — https://github.com/pylint-dev/pylint/issues/2018.
- **emptystring and compare-to-zero checkers are folded into implicit booleaness** — the compare to empty string checker (`pylint.extensions.emptystring`) and the compare to zero checker (`pylint.extensions.compare-to-zero`) have been removed, and their checks are now part of the implicit booleaness checker 🧠:
- `compare-to-zero` was renamed to `use-implicit-booleaness-not-comparison-to-zero`, and `compare-to-empty-string` was renamed to `use-implicit-booleaness-not-comparison-to-string`; they now need to be enabled explicitly.
- The `pylint.extensions.emptystring` and `pylint.extensions.compare-to-zero` extensions no longer exist and need to be removed from the `load-plugins` option.
- Messages related to implicit booleaness were made more explicit and actionable. This permits making their likeness explicit and will provide better performance, as they share most of their conditions to be raised.
Closes `#6871` — https://github.com/pylint-dev/pylint/issues/6871.
- **`epylint` dipped** — `epylint` was removed 👋. It now lives at: https://github.com/emacsorphanage/pylint. Refs `#7737` — https://github.com/pylint-dev/pylint/issues/7737.
- **`overgeneral-exceptions` wants fully qualified names** — the `overgeneral-exceptions` option now only takes fully qualified names into account (`builtins.Exception`, not `Exception`). If you overrode this option, you need to use the fully qualified name now. There's still a warning, but it will be removed in `3.1.0`. Refs `#8411` — https://github.com/pylint-dev/pylint/issues/8411.
- **`MASTER`/`master` config sections are out** — following a deprecation period, it's no longer possible to use `MASTER` or `master` as a configuration section in `setup.cfg` or `tox.ini` 🚫. It's bad practice to not start a section title with the tool name. Please use `pylint.main` instead. Refs `#8465` — https://github.com/pylint-dev/pylint/issues/8465.
- **Pyreverse package stats + `--verbose`** — package stats are now printed when running Pyreverse, and a `--verbose` flag was added to get the original output with parsed modules 👀. You might need to activate the verbose option if you want to keep the old output. Closes `#8973` — https://github.com/pylint-dev/pylint/issues/8973.
## ✨ New Features ✨
- **new `json2` reporter** — a new `json2` reporter has been added 🎉. It features a more enriched output that's easier to parse and provides more info. Compared to `json`, the only changes are that messages are now under the `"messages"` key and that `"message-id"` now follows the camelCase convention and is renamed to `"messageId"`. The new reporter also reports the `"score"` of the modules you linted as defined by the `evaluation` option and provides statistics about the modules you linted. We encourage users to use the new reporter, as the `json` reporter will no longer be maintained. Closes `#4741` — https://github.com/pylint-dev/pylint/issues/4741.
- **type-checking imports show up in Pyreverse package dependency diagrams** — in Pyreverse package dependency diagrams, it now shows when a module imports another only for type-checking 🔍. Closes `#8112` — https://github.com/pylint-dev/pylint/issues/8112.
- **`--show-stdlib`, `-L` for pyreverse** — add new option (`--show-stdlib`, `-L`) to `pyreverse` 📚. This is similar to the behavior of `--show-builtin`: standard library modules are now not included by default, and this option will include them. Closes `#8181` — https://github.com/pylint-dev/pylint/issues/8181.
- **`--no-standalone` for pyreverse** — add Pyreverse option to exclude standalone nodes from diagrams with `--no-standalone` ✂️. Closes `#8476` — https://github.com/pylint-dev/pylint/issues/8476.
## 🕵️ New Checks 🕵️
- **`DataclassChecker` + `invalid-field-call`** — added the `DataclassChecker` module and the `invalid-field-call` checker to check for invalid `dataclasses.field()` usage 🧐. Refs `#5159` — https://github.com/pylint-dev/pylint/issues/5159.
- **`return-in-finally`** — add `return-in-finally` to emit a message if a `return` statement was found in a `finally` clause 🛑. Closes `#8260` — https://github.com/pylint-dev/pylint/issues/8260.
- **`kwarg-superseded-by-positional-arg`** — add a new message `kwarg-superseded-by-positional-arg` to warn when a function is called with a keyword argument that shares a name with a positional-only parameter, and the function contains a keyword variadic parameter dictionary. It may be surprising behaviour when the keyword argument is added to the keyword variadic parameter dictionary 😵💫. Closes `#8558` — https://github.com/pylint-dev/pylint/issues/8558.
## 🧩 Extensions 🧩
- **`prefer-typing-namedtuple`** — add new `prefer-typing-namedtuple` message to the `CodeStyleChecker` to suggest rewriting calls to `collections.namedtuple` as classes inheriting from `typing.NamedTuple` on Python 3.6+ 🐍. Requires `load-plugins=pylint.extensions.code_style` and `enable=prefer-typing-namedtuple` to be raised. Closes `#8660` — https://github.com/pylint-dev/pylint/issues/8660.
## 🙅 False Positives Fixed 🙅
- **`safe_infer()` function ambiguity** — extend the concept of "function ambiguity" in `safe_infer()` from differing number of function arguments to differing set of argument names 🧠. Solves false positives in tensorflow. Closes `#3613` — https://github.com/pylint-dev/pylint/issues/3613.
- **`unused-argument` + `__new__`** — fix `unused-argument` false positive when `__new__` does not use all the arguments of `__init__` 🛠️. Closes `#3670` — https://github.com/pylint-dev/pylint/issues/3670.
- **`invalid-name` in `enum.Enum`** — fix a false positive for `invalid-name` when a type-annotated class variable in an `enum.Enum` class has no assigned value 🎯. Refs `#7402` — https://github.com/pylint-dev/pylint/issues/7402.
- **`unused-import` + `six.with_metaclass`** — fix `unused-import` false positive for usage of `six.with_metaclass` 🧙. Closes `#7506` — https://github.com/pylint-dev/pylint/issues/7506.
- **`too-many-try-statements`, `too-complex`, `too-many-branches`** — fix false negatives and false positives for `too-many-try-statements`, `too-complex`, and `too-many-branches` by correctly counting statements under a `try` 🔢. Refs `#7767` — https://github.com/pylint-dev/pylint/issues/7767.
- **unbalanced dict unpacking in for-loops** — when checking for unbalanced dict unpacking in for-loops, Pylint will now test whether the length of each value to be unpacked matches the number of unpacking targets 🎯. Previously, Pylint would test the number of values for the loop iteration, which would produce a false `unbalanced-dict-unpacking` / `W0644` (`#unbalanced-dict-unpacking`) warning. Closes `#8156` — https://github.com/pylint-dev/pylint/issues/8156.
- **`used-before-assignment`** — fix false positive for `used-before-assignment` when usage and assignment are guarded by the same test in different statements 🛡️. Closes `#8167` — https://github.com/pylint-dev/pylint/issues/8167.
- **`asyncSetUp` in `defining-attr-methods`** — adds `asyncSetUp` to the default `defining-attr-methods` list to silence the `attribute-defined-outside-init` warning when using `unittest.IsolatedAsyncioTestCase` 🧪. Refs `#8403` — https://github.com/pylint-dev/pylint/issues/8403.
- **`logging-not-lazy`** — `logging-not-lazy` is no longer emitted for explicitly concatenated string arguments 🪵. Closes `#8410` — https://github.com/pylint-dev/pylint/issues/8410.
- **`isinstance-second-argument-not-valid-type` / `W1116`** — fix false positive for `isinstance-second-argument-not-valid-type` / `W1116` (`#isinstance-second-argument-not-valid-type`) when union types contains `None` 🫠. Closes `#8424` — https://github.com/pylint-dev/pylint/issues/8424.
- **`invalid-name` + typealias integers** — `invalid-name` now allows for integers in typealias names:
- now valid: `Good2Name`, `GoodName2`
- still invalid: `_1BadName`
Closes `#8485` — https://github.com/pylint-dev/pylint/issues/8485.
- **`Union` is not automatically a type alias for naming checks** — no longer consider `Union` as a type annotation as type alias for naming checks 🚫. Closes `#8487` — https://github.com/pylint-dev/pylint/issues/8487.
- **`unnecessary-lambda`** — `unnecessary-lambda` no longer warns on lambdas which use its parameters in their body (other than the final arguments), e.g. `lambda foo: (bar if foo else baz)(foo)` 🎭. Closes `#8496` — https://github.com/pylint-dev/pylint/issues/8496.
- **`unused-import` + `dummy-variables-rgx`** — fixed `unused-import` so that it observes the `dummy-variables-rgx` option 👀. Closes `#8500` — https://github.com/pylint-dev/pylint/issues/8500.
- **Union typed vars without assignment** — `Union` typed variables without assignment are no longer treated as `TypeAlias` 🏷️. Closes `#8540` — https://github.com/pylint-dev/pylint/issues/8540.
- **Allow parenthesized implicitly concatenated strings** when `check-str-concat-over-line-jumps` is enabled. Closes #8552. (`#8552 <https://github.com/pylint-dev/pylint/issues/8552>`)
- Fix a false positive for <pink>`positional-only-arguments-expected`</pink> when a function has both a positional-only parameter with a default value and `**kwargs`. Closes #8555 (`#8555 <https://github.com/pylint-dev/pylint/issues/8555>`)
- Fix a false positive for <pink>`keyword-arg-before-vararg`</pink> when a positional-only parameter with a default value comes before `*args`. Closes #8570 (`#8570 <https://github.com/pylint-dev/pylint/issues/8570>`)
- Fix a false positive for <pink>`arguments-differ`</pink> when overriding `__init_subclass__`. Closes #8919 (`#8919 <https://github.com/pylint-dev/pylint/issues/8919>`)
- Fix a false positive for <pink>`no-value-for-parameter`</pink> when a `staticmethod` is called in a class body. Closes #9036 (`#9036 <https://github.com/pylint-dev/pylint/issues/9036>`)
## 🔥 False Negatives Fixed
- Emit <hotpink>`used-before-assignment`</hotpink> when module-level functions are called before definition. Closes #1144 (`#1144 <https://github.com/pylint-dev/pylint/issues/1144>`)
- Apply `infer_kwarg_from_call()` to more checks. These mostly solve false negatives for various checks, save for one false positive for `use-maxsplit-arg`. Closes #7761 (`#7761 <https://github.com/pylint-dev/pylint/issues/7761>`)
- `TypeAlias` variables defined in functions are now checked for <pink>`invalid-name`</pink> errors. Closes #8536 (`#8536 <https://github.com/pylint-dev/pylint/issues/8536>`)
- Fix a false negative for <pink>`no-value-for-parameter`</pink> when a function whose signature contains both a positional-only parameter name and also `*kwargs` is called with a keyword-argument for `name`. Closes #8559 (`#8559 <https://github.com/pylint-dev/pylint/issues/8559>`)
- Fix a false negative for <pink>`too-many-arguments`</pink> by considering positional-only and keyword-only parameters. Closes #8667 (`#8667 <https://github.com/pylint-dev/pylint/issues/8667>`)
- Emit <hotpink>`assignment-from-no-return`</hotpink> for calls to builtin methods like `dict.update()`. Calls to `list.sort()` now raise `assignment-from-no-return` rather than `assignment-from-none` for consistency. Closes #8714 Closes #8810 (`#8714 <https://github.com/pylint-dev/pylint/issues/8714>`)
- <pink>`consider-using-augmented-assign`</pink> is now applied to dicts and lists as well. Closes #8959. (`#8959 <https://github.com/pylint-dev/pylint/issues/8959>`)
## 🐛 Other Bug Fixes
- Support <pink>`duplicate-code`</pink> message when parallelizing with `--jobs`. Closes #374 (`#374 <https://github.com/pylint-dev/pylint/issues/374>`)
- Support <pink>`cyclic-import`</pink> message when parallelizing with `--jobs`. Closes #4171 (`#4171 <https://github.com/pylint-dev/pylint/issues/4171>`)
- `--jobs` can now be used with `--load-plugins`. This had regressed in `astroid 2.5.0`. Closes #4874 (`#4874 <https://github.com/pylint-dev/pylint/issues/4874>`)
- <pink>`docparams`</pink> extension considers type comments as type documentation. Closes #6287 (`#6287 <https://github.com/pylint-dev/pylint/issues/6287>`)
- When parsing comma-separated lists of regular expressions in the config, ignore commas that are inside braces since those indicate quantifiers, not delineation between expressions. Closes #7229 (`#7229 <https://github.com/pylint-dev/pylint/issues/7229>`)
- The <pink>`ignored-modules`</pink> option will now be correctly taken into account for <pink>`no-name-in-module`</pink>. Closes #7578 (`#7578 <https://github.com/pylint-dev/pylint/issues/7578>`)
- `sys.argv` is now always correctly considered as impossible to infer (instead of using the actual values given to pylint). Closes #7710 (`#7710 <https://github.com/pylint-dev/pylint/issues/7710>`)
- Avoid duplicative warnings for unqualified exception names in the <pink>`overgeneral-exceptions`</pink> setting when running with `--jobs`. Closes #7774 (`#7774 <https://github.com/pylint-dev/pylint/issues/7774>`)
- Don’t show class fields more than once in Pyreverse diagrams. Closes #8189 (`#8189 <https://github.com/pylint-dev/pylint/issues/8189>`)
- Fix <hotpink>`used-before-assignment`</hotpink> false negative when `TYPE_CHECKING` imports are used in multiple scopes. Closes #8198 (`#8198 <https://github.com/pylint-dev/pylint/issues/8198>`)
- `--clear-cache-post-run` now also clears LRU caches for pylint utilities holding references to AST nodes. Closes #8361 (`#8361 <https://github.com/pylint-dev/pylint/issues/8361>`)
- Fix a crash when `TYPE_CHECKING` is used without importing it. Closes #8434 (`#8434 <https://github.com/pylint-dev/pylint/issues/8434>`)
- Fix a <hotpink>`used-before-assignment`</hotpink> false positive when imports are made under the `TYPE_CHECKING` else if branch. Closes #8437 (`#8437 <https://github.com/pylint-dev/pylint/issues/8437>`)
- Fix a regression of <pink>`preferred-modules`</pink> where a partial match was used instead of the required full match. Closes #8453 (`#8453 <https://github.com/pylint-dev/pylint/issues/8453>`)
- Fix a crash in pyreverse when `/` characters are used in the output filename, e.g. `pyreverse -o png -p name/ path/to/project`. Closes #8504 (`#8504 <https://github.com/pylint-dev/pylint/issues/8504>`)
- Don’t show arrows more than once in Pyreverse diagrams. Closes #8522 (`#8522 <https://github.com/pylint-dev/pylint/issues/8522>`)
- Improve output of <pink>`consider-using-generator`</pink> message for `min()` calls with `default` keyword. Closes #8563 (`#8563 <https://github.com/pylint-dev/pylint/issues/8563>`)
- Fixed a crash when generating a configuration file: `tomlkit.exceptions.TOMLKitError: Can't add a table to a dotted key` caused by `tomlkit v0.11.8`. Closes #8632 (`#8632 <https://github.com/pylint-dev/pylint/issues/8632>`)
- Fix a line break error in Pyreverse dot output. Closes #8671 (`#8671 <https://github.com/pylint-dev/pylint/issues/8671>`)
- Fix a false positive for <pink>`method-hidden`</pink> when using the `cached_property` decorator. Closes #8753 (`#8753 <https://github.com/pylint-dev/pylint/issues/8753>`)
- Dunder methods defined in lambda do not trigger <pink>`unnecessary-dunder-call`</pink> anymore, if they cannot be replaced by the non-dunder call. Closes #8769 (`#8769 <https://github.com/pylint-dev/pylint/issues/8769>`)
- Don’t show duplicate type annotations in Pyreverse diagrams. Closes #8888 (`#8888 <https://github.com/pylint-dev/pylint/issues/8888>`)
- Fixing inconsistent hashing issue in `BaseChecker` causing some reports not being exported. Closes #9001 (`#9001 <https://github.com/pylint-dev/pylint/issues/9001>`)
- Don’t add `Optional` to `|` annotations with `None` in Pyreverse diagrams. Closes #9014 (`#9014 <https://github.com/pylint-dev/pylint/issues/9014>`)
- Pyreverse doesn’t show multiple class association arrows anymore, but only the strongest one. Refs #9045 (`#9045 <https://github.com/pylint-dev/pylint/issues/9045>`)
- Prevented data loss in the linter stats for messages relating to the linter itself (e.g. `unknown-option-value`), fixing problems with score, `fail-on`, etc. Closes #9059 (`#9059 <https://github.com/pylint-dev/pylint/issues/9059>`)
- Fix crash in refactoring checker when unary operand used with variable in `for` loop. Closes #9074 (`#9074 <https://github.com/pylint-dev/pylint/issues/9074>`)
## 🌈 Other Changes
- Pylint now exposes its type annotations. Closes #5488 and #2079 (`#5488 <https://github.com/pylint-dev/pylint/issues/5488>`)
- Search for `pyproject.toml` recursively in parent directories up to a project or file system root. Refs #7163, Closes #3289 (`#7163 <https://github.com/pylint-dev/pylint/issues/7163>`)
- All code related to the `optparse` config parsing has been removed. Refs #8405 (`#8405 <https://github.com/pylint-dev/pylint/issues/8405>`)
- Pylint now supports `python 3.12`. Refs #8718 (`#8718 <https://github.com/pylint-dev/pylint/issues/8718>`)
- Add a `CITATION.cff` file to the root of the repository containing the necessary metadata to cite Pylint. Closes #8760 (`#8760 <https://github.com/pylint-dev/pylint/issues/8760>`)
- Renamed the `unneeded-not / C0113 <#unneeded-not>` error into `unnecessary_negation` to be clearer. Closes #8789 (`#8789 <https://github.com/pylint-dev/pylint/issues/8789>`)
## 🛠️ Internal Changes
- `get_message_definition` was removed from the base checker API. You can access message definitions through the `MessageStore`. Refs #8401 (`#8401 <https://github.com/pylint-dev/pylint/issues/8401>`)
- Everything related to the `__implements__` construct was removed. It was based on `PEP245` that was proposed in 2001 and rejected in 2006. All the classes inheriting `Interface` in `pylint.interfaces` were removed. Checker should only inherit `BaseChecker` or any of the other checker types from `pylint.checkers`. Reporter should only inherit `BaseReporter`. Refs #8404 (`#8404 <https://github.com/pylint-dev/pylint/issues/8404>`)
- `modname` and `msg_store` are now required to be given in `FileState`. `collect_block_lines` has also been removed. `Pylinter.current_name` cannot be null anymore. Refs #8407 (`#8407 <https://github.com/pylint-dev/pylint/issues/8407>`)
- `Reporter.set_output` was removed in favor of `reporter.out = stream`. Refs #8408 (`#8408 <https://github.com/pylint-dev/pylint/issues/8408>`)
- A number of old utility functions and classes have been removed:
- `MapReduceMixin`: To make a checker reduce map data simply implement `get_map_data` and `reduce_map_data`.
- `is_inside_lambda`: Use `utils.get_node_first_ancestor_of_type(x, nodes.Lambda)`
- `check_messages`: Use `utils.only_required_for_messages`
- `is_class_subscriptable_pep585_with_postponed_evaluation_enabled`: Use `is_postponed_evaluation_enabled(node)` and `is_node_in_type_annotation_context(node)`
- `get_python_path`: assumption that there’s always an `__init__.py` is not true since `python 3.3` and is causing problems, particularly with `PEP 420`. Use `discover_package_path` and pass source root(s).
- `fix_import_path`: Use `augmented_sys_path` and pass additional `sys.path` entries as an argument obtained from `discover_package_path`.
- `get_global_option`: Use `checker.linter.config` to get all global options.
Related private objects have been removed as well. Refs #8409 (`#8409 <https://github.com/pylint-dev/pylint/issues/8409>`)
- `colorize_ansi` now only accepts a `MessageStyle` object. Refs #8412 (`#8412 <https://github.com/pylint-dev/pylint/issues/8412>`)
- Okay bestie, deprecation era is **actually** here: after the deprecation period, `Pylinter.check` now only works with sequences of strings, not strings. No sneaky single-string energy anymore 💅
Refs `#8463` (`#8463 <https://github.com/pylint-dev/pylint/issues/8463>`)
- After the deprecation period, `ColorizedTextReporter` only accepts `ColorMappingDict`. That’s the whole vibe now ✨
Refs `#8464` (`#8464 <https://github.com/pylint-dev/pylint/issues/8464>`)
- After the deprecation period, `MessageTest`’s `end_line` and `end_col_offset` must be accurate in functional tests (for `python 3.8` or above on `cpython`, and for `python 3.9` or superior on `pypy`). Accuracy check, no cap 😭
Refs `#8466` (`#8466 <https://github.com/pylint-dev/pylint/issues/8466>`)
- After the deprecation period, the `do_exit` argument of the `Run` class (and of the `_Run` class in `testutils`) were removed. Gone-gone 🫶
Refs `#8472` (`#8472 <https://github.com/pylint-dev/pylint/issues/8472>`)
- After the deprecation period, the `py_version` argument of the `MessageDefinition.may_be_emitted` function is now required. The most likely solution is to use `linter.config.py_version` if you need to keep using this function, or to use `MessageDefinition.is_message_enabled` instead. Pick your fave, bestie 💖
Refs `#8473` (`#8473 <https://github.com/pylint-dev/pylint/issues/8473>`)
- After the deprecation period, the `OutputLine` class now requires the right number of argument all the time. The functional output can be regenerated automatically to achieve that easily — slay, automation ✨
Refs `#8474` (`#8474 <https://github.com/pylint-dev/pylint/issues/8474>`)
- After the deprecation period, `is_typing_guard`, `is_node_in_typing_guarded_import_block` and `is_node_in_guarded_import_block` from `pylint.utils` were removed: use a combination of `is_sys_guard` and `in_type_checking_block` instead. New combo unlocked 🔓
Refs `#8475` (`#8475 <https://github.com/pylint-dev/pylint/issues/8475>`)
- After the deprecation period, the `location` argument of the `Message` class must now be a `MessageLocationTuple`. It’s giving structured-location main character energy 🎀
Refs `#8477` (`#8477 <https://github.com/pylint-dev/pylint/issues/8477>`)
- After the deprecation period, the `check_single_file` function of the `Pylinter` is replaced by `Pylinter.check_single_file_item`. Name upgrade, honestly 💅
Refs `#8478` (`#8478 <https://github.com/pylint-dev/pylint/issues/8478>`)
## 💨 Performance Improvements 💨
- `pylint` runs (at least) `~5%` faster after improvements to `astroid` that make better use of the inference cache. Speed bump, we love to see it 🔥
Refs `pylint-dev/astroid#529` (`#529 <https://github.com/pylint-dev/pylint/issues/529>`)
- 💨
- Optimize `is_trailing_comma()`.
- Cache `class_is_abstract()`.
Refs `#1954` (`#1954 <https://github.com/pylint-dev/pylint/issues/1954>`)
- Exit immediately if all messages are disabled. If there’s nothing to say, why stay at the party? 💀
Closes `#8715` (`#8715 <https://github.com/pylint-dev/pylint/issues/8715>`)
## 📜 2.x 📜
From pylint’s beginning to pylint `2.14.0` included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing, i.e. change that might interest you only if you’re a developer working on pylint or pylint plugins or an open-source historian. Historian besties, this one was for you 🫶
From pylint `1.6.0` to `2.14.0`, on top of the full changelog there was also a summary of (mostly user facing) changes. Cute little recap era ✨
## 🌟 What's New in Pylint 2.17 🌟
Release: `2.17`
Date: `2023-03-08`
### ✨ Summary -- Release highlights ✨
`2.17` is a small release that is the first to support `python 3.11` officially with the addition of `TryStar` nodes. Officially official, periodt 🐍
There’s still two new default checks: `bad-chained-comparison` and `implicit-flag-alias`, one of them already fixed a previously undetected bug in `sentry`. Not us fixing bugs by accident and looking iconic 😭
Thanks to the community effort our documentation is almost complete, and every messages should have a proper documentation now. A big thank you to everyone who participated ! You’re all main characters 💖
The next release is going to be `3.0.0`, bring breaking changes and enact long announced deprecations. There’s going to be frequent beta releases, before the official releases, everyone is welcome to try the betas so we find problems before the actual release. Beta-testing squad, assemble 🫡
## 🐛 What's new in Pylint 2.17.7? 🐛
Release date: `2023-09-30`
### 🚫 False Positives Fixed 🚫
- Fix a regression in `pylint 2.17.6` / `astroid 2.15.7` causing various messages for code involving `TypeVar`. That regression did not eat, bestie 💀
Closes `#9069` (`#9069 <https://github.com/pylint-dev/pylint/issues/9069>`)
### 🩹 Other Bug Fixes 🩹
- Fix crash in refactoring checker when unary operand used with variable in `for` loop. Crash era is cancelled 🔥
Closes `#9074` (`#9074 <https://github.com/pylint-dev/pylint/issues/9074>`)
## 🐛 What's new in Pylint 2.17.6? 🐛
Release date: `2023-09-24`
### 🩹 Other Bug Fixes 🩹
- When parsing comma-separated lists of regular expressions in the config, ignore commas that are inside braces since those indicate quantifiers, not delineation between expressions. Commas in braces are not separators, iykyk 🎀
Closes `#7229` (`#7229 <https://github.com/pylint-dev/pylint/issues/7229>`)
- `sys.argv` is now always correctly considered as impossible to infer (instead of using the actual values given to pylint). No more peeking at the actual values, bestie 🙈
Closes `#7710` (`#7710 <https://github.com/pylint-dev/pylint/issues/7710>`)
- Don’t show class fields more than once in Pyreverse diagrams. Once is enough, we’re not doing repeats 💅
Closes `#8189` (`#8189 <https://github.com/pylint-dev/pylint/issues/8189>`)
- Don’t show arrows more than once in Pyreverse diagrams. Redundant arrows? Not the vibe 🌈
Closes `#8522` (`#8522 <https://github.com/pylint-dev/pylint/issues/8522>`)
- Don’t show duplicate type annotations in Pyreverse diagrams. Duplicate annotations were lowkey doing too much ✨
Closes `#8888` (`#8888 <https://github.com/pylint-dev/pylint/issues/8888>`)
- Don’t add `Optional` to `|` annotations with `None` in Pyreverse diagrams. `None` already said what it said 💀
Closes `#9014` (`#9014 <https://github.com/pylint-dev/pylint/issues/9014>`)
## 🐛 What's new in Pylint 2.17.5? 🐛
Release date: `2023-07-26`
### 🚫 False Positives Fixed 🚫
- Fix a false positive for `unused-variable` when there is an import in a `if TYPE_CHECKING:` block and `allow-global-unused-variables` is set to `no` in the configuration. Type-checking block behavior, finally behaving 🫶
Closes `#8696` (`#8696 <https://github.com/pylint-dev/pylint/issues/8696>`)
- Fix false positives generated when supplying arguments as `**kwargs` to IO calls like `open()`. `**kwargs` said let me live 😭
Closes `#8719` (`#8719 <https://github.com/pylint-dev/pylint/issues/8719>`)
- Fix a false positive where pylint was ignoring method calls annotated as `NoReturn` during the `inconsistent-return-statements` check. `NoReturn` deserves respect, no cap 💅
Closes `#8747` (`#8747 <https://github.com/pylint-dev/pylint/issues/8747>`)
- Exempt parents with only type annotations from the `invalid-enum-extension` message. Type-annotation-only parents get a pass 🌸
Closes `#8830` (`#8830 <https://github.com/pylint-dev/pylint/issues/8830>`)
### 🩹 Other Bug Fixes 🩹
- Fixed crash when a call to `super()` was placed after an operator (e.g. `not`). Operator-then-`super()` is valid and should not crash the party 🎀
Closes `#8554` (`#8554 <https://github.com/pylint-dev/pylint/issues/8554>`)
- Fix crash for `modified-while-iterating` checker when deleting members of a `dict` returned from a call. Deleting from a returned dict should not be a jump-scare 💀
Closes `#8598` (`#8598 <https://github.com/pylint-dev/pylint/issues/8598>`)
- Fix crash in `invalid-metaclass` check when a metaclass had duplicate bases. Duplicate bases were doing too much 🫠
Closes `#8698` (`#8698 <https://github.com/pylint-dev/pylint/issues/8698>`)
- Avoid `consider-using-f-string` on modulos with brackets in template. Brackets in the template said not today 💅
Closes `#8720`. (`#8720 <https://github.com/pylint-dev/pylint/issues/8720>`)
- Fix a crash when `__all__` exists but cannot be inferred. `__all__` being mysterious should not crash anything 🌈
Closes `#8740` (`#8740 <https://github.com/pylint-dev/pylint/issues/8740>`)
- Fix crash when a variable is assigned to a class attribute of identical name. Same-name assignment, please chill 😭
Closes `#8754` (`#8754 <https://github.com/pylint-dev/pylint/issues/8754>`)
- Fixed a crash when calling `copy.copy()` without arguments. No args, no crash, that’s the dream ✨
Closes `#8774` (`#8774 <https://github.com/pylint-dev/pylint/issues/8774>`)
### 🔄 Other Changes 🔄
- Fix a crash when a `nonlocal` is defined at module-level. Module-level `nonlocal` is wild, but crashing is not the move 💀
Closes `#8735` (`#8735 <https://github.com/pylint-dev/pylint/issues/8735>`)
## 🐛 What's new in Pylint 2.17.4? 🐛
Release date: `2023-05-06`
### 🚫 False Positives Fixed 🚫
- Fix a false positive for `bad-dunder-name` when there is a user-defined `__index__` method. User-defined `__index__` is valid, bestie 💅
Closes `#8613` (`#8613 <https://github.com/pylint-dev/pylint/issues/8613>`)
### 🩹 Other Bug Fixes 🩹
- `pyreverse`: added escaping of vertical bar character in annotation labels produced by `DOT` printer to ensure it is not treated as field separator of record-based nodes. Escape that bar, protect the node structure 🎀
Closes `#8603` (`#8603 <https://github.com/pylint-dev/pylint/issues/8603>`)
- Fixed a crash when generating a configuration file: `tomlkit.exceptions.TOMLKitError: Can't add a table to a dotted key` caused by `tomlkit` `v0.11.8`. Config generation should not be a boss fight 😭
Closes `#8632` (`#8632 <https://github.com/pylint-dev/pylint/issues/8632>`)
## 🐛 What's new in Pylint 2.17.3? 🐛
Release date: `2023-04-24`
### 🚫 False Positives Fixed 🚫
- Fix `unused-argument` false positive when `__new__` does not use all the arguments of `__init__`. `__new__` doesn’t have to use everything, okay? 💅
Closes `#3670` (`#3670 <https://github.com/pylint-dev/pylint/issues/3670>`)
- Fix `unused-import` false positive for usage of `six.with_metaclass`. `six.with_metaclass` usage is real, no cap 🫶
Closes `#7506` (`#7506 <https://github.com/pylint-dev/pylint/issues/7506>`)
- `logging-not-lazy` is not longer emitted for explicitly concatenated string arguments. Explicitly concatenated strings got a pass ✨
Closes `#8410` (`#8410 <https://github.com/pylint-dev/pylint/issues/8410>`)
- Fix false positive for `isinstance-second-argument-not-valid-type` / `W1116 <#isinstance-second-argument-not-valid-type>` when union types contains `None`. Union with `None` said stop flagging me 😭
Closes `#8424` (`#8424 <https://github.com/pylint-dev/pylint/issues/8424>`)
- Fixed `unused-import` so that it observes the `dummy-variables-rgx` option. The regex option is now seen, respected, understood 💅
Closes `#8500` (`#8500 <https://github.com/pylint-dev/pylint/issues/8500>`)
- `Union` typed variables without assignment are no longer treated as `TypeAlias`. No assignment? Then not a `TypeAlias`, periodt 🌸
Closes `#8540` (`#8540 <https://github.com/pylint-dev/pylint/issues/8540>`)
- Fix false positive for `positional-only-arguments-expected` when a function contains both a positional-only parameter that has a default value, and `**kwargs`. Both together should be fine, bestie 🫶
Closes `#8555` (`#8555 <https://github.com/pylint-dev/pylint/issues/8555>`)
- Fix false positive for `keyword-arg-before-vararg` when a positional-only parameter with a default value precedes `*args`. Default-value positional-only plus `*args` is valid and should not get flagged 💖
Closes `#8570` (`#8570 <https://github.com/pylint-dev/pylint/issues/8570>`)
### 🩹 Other Bug Fixes 🩹
- Improve output of `consider-using-generator` message for `min()` calls with `default` keyword. Better output for `min()` with `default`, we love clearer messages ✨
Closes `#8563` (`#8563 <https://github.com/pylint-dev/pylint/issues/8563>`)
## 🐛 What's new in Pylint 2.17.2? 🐛
Release date: `2023-04-03`
### 🚫 False Positives Fixed 🚫
- `invalid-name` now allows for integers in typealias names: — now valid: `Good2Name`, `GoodName2`. — still invalid: `_1BadName`. Integers in typealias names can slay, but `_1BadName` is still a no 💅
Closes `#8485` (`#8485 <https://github.com/pylint-dev/pylint/issues/8485>`)
- No longer consider `Union` as type annotation as type alias for naming checks.
- Closes `#8487` (`https://github.com/pylint-dev/pylint/issues/8487`) — that’s the paper trail for the fix above, bestie. 💅
- **`unnecessary-lambda`** — no longer warns on lambdas which use its parameters in their body (other than the final arguments), e.g. `lambda foo: (bar if foo else baz)(foo)`.
Closes `#8496` (`https://github.com/pylint-dev/pylint/issues/8496`).
### 💅 Other Bug Fixes 💅
- Fix a crash in `pyreverse` when `/` characters are used in the output filename, e.g. `pyreverse -o png -p name/ path/to/project`.
Closes `#8504` (`https://github.com/pylint-dev/pylint/issues/8504`).
## 🦋 What's new in Pylint 2.17.1? 🦋
Release date: `2023-03-22`
### ✨ False Positives Fixed ✨
- Adds `asyncSetUp` to the default `defining-attr-methods` list to silence the `attribute-defined-outside-init` warning when using `unittest.IsolatedAsyncioTestCase`.
Refs `#8403` (`https://github.com/pylint-dev/pylint/issues/8403`).
### 💅 Other Bug Fixes 💅
- `--clear-cache-post-run` now also clears LRU caches for pylint utilities holding references to AST nodes.
Closes `#8361` (`https://github.com/pylint-dev/pylint/issues/8361`).
- Fix a crash when `TYPE_CHECKING` is used without importing it.
Closes `#8434` (`https://github.com/pylint-dev/pylint/issues/8434`).
- Fix a regression of `preferred-modules` where a partial match was used instead of the required full match.
Closes `#8453` (`https://github.com/pylint-dev/pylint/issues/8453`).
### 🧚 Internal Changes 🧚
- The following utilities are deprecated in favor of the more robust `in_type_checking_block` and will be removed in `pylint 3.0`:
- `is_node_in_guarded_import_block`
- `is_node_in_typing_guarded_import_block`
- `is_typing_guard`
- `is_sys_guard` is still available, which was part of `is_node_in_guarded_import_block`.
Refs `#8433` (`https://github.com/pylint-dev/pylint/issues/8433`).
## 🌸 What's new in Pylint 2.17.0? 🌸
Release date: `2023-03-08`
### 🎀 New Features 🎀
- `pyreverse` now supports custom color palettes with the `--color-palette` option.
Closes `#6738` (`https://github.com/pylint-dev/pylint/issues/6738`).
- Add `invalid-name` check for `TypeAlias` names.
Closes `#7081` (`https://github.com/pylint-dev/pylint/issues/7081`).
- Accept values of the form `<class name>.<attribute name>` for the `exclude-protected` list.
Closes `#7343` (`https://github.com/pylint-dev/pylint/issues/7343`).
- Add `--version` option to `pyreverse`.
Refs `#7851` (`https://github.com/pylint-dev/pylint/issues/7851`).
- Adds new functionality with `preferred-modules` configuration to detect submodules.
Refs `#7957` (`https://github.com/pylint-dev/pylint/issues/7957`).
- Support implicit namespace packages (PEP 420).
Closes `#8154` (`https://github.com/pylint-dev/pylint/issues/8154`).
- Add globbing pattern support for `--source-roots`.
Closes `#8290` (`https://github.com/pylint-dev/pylint/issues/8290`).
- Support globbing pattern when defining which file/directory/module to lint.
Closes `#8310` (`https://github.com/pylint-dev/pylint/issues/8310`).
- pylint now supports `TryStar` nodes from Python 3.11 and should be fully compatible with Python 3.11.
Closes `#8387` (`https://github.com/pylint-dev/pylint/issues/8387`).
### 🔥 New Checks 🔥
- Add a `bad-chained-comparison` check that emits a warning when there is a chained comparison where one expression is semantically incompatible with the other.
Closes `#6559` (`https://github.com/pylint-dev/pylint/issues/6559`).
- Adds an `implicit-flag-alias` check that emits a warning when a class derived from `enum.IntFlag` assigns distinct integer values that share common bit positions.
Refs `#8102` (`https://github.com/pylint-dev/pylint/issues/8102`).
### ✨ False Positives Fixed ✨
- Fix various false positives for functions that return directly from structural pattern matching cases.
Closes `#5288` (`https://github.com/pylint-dev/pylint/issues/5288`).
- Fix false positive for `used-before-assignment` when `typing.TYPE_CHECKING` is used with `if`/`elif`/`else` blocks.
Closes `#7574` (`https://github.com/pylint-dev/pylint/issues/7574`).
- Fix false positive for `isinstance-second-argument-not-valid-type / W1116 <#isinstance-second-argument-not-valid-type>` with union types.
Closes `#8205` (`https://github.com/pylint-dev/pylint/issues/8205`).
- Fix false positive for `used-before-assignment` for named expressions appearing after the first element in a list, tuple, or set.
Closes `#8252` (`https://github.com/pylint-dev/pylint/issues/8252`).
- Fix false positive for `wrong-spelling-in-comment` with class names in a python 2 type comment.
Closes `#8370` (`https://github.com/pylint-dev/pylint/issues/8370`).
### 🦋 False Negatives Fixed 🦋
- Fix a false negative for `missing-parentheses-for-call-in-test / W0126 <#missing-parentheses-for-call-in-test>` when inference failed for the internal of the call as we did not need that information to raise correctly.
Refs `#8185` (`https://github.com/pylint-dev/pylint/issues/8185`).
- Fix false negative for `inconsistent-returns` with while-loops.
Closes `#8280` (`https://github.com/pylint-dev/pylint/issues/8280`).
### 💅 Other Bug Fixes 💅
- Fix `used-before-assignment` false positive when the walrus operator is used with a ternary operator in dictionary key/value initialization.
Closes `#8125` (`https://github.com/pylint-dev/pylint/issues/8125`).
- Fix `no-name-in-module` false positive raised when a package defines a variable with the same name as one of its submodules.
Closes `#8148` (`https://github.com/pylint-dev/pylint/issues/8148`).
- Fix a crash happening for python interpreter `< 3.9` following a failed typing update.
Closes `#8161` (`https://github.com/pylint-dev/pylint/issues/8161`).
- Fix `nested-min-max` suggestion message to indicate it's possible to splat iterable objects.
Closes `#8168` (`https://github.com/pylint-dev/pylint/issues/8168`).
- Fix a crash happening when a class attribute was negated in the `start` argument of an `enumerate`.
Closes `#8207` (`https://github.com/pylint-dev/pylint/issues/8207`).
- Prevent emitting `invalid-name` for the line on which a `global` statement is declared.
Closes `#8307` (`https://github.com/pylint-dev/pylint/issues/8307`).
### 🌈 Other Changes 🌈
- Update explanation for `global-variable-not-assigned` and add confidence.
Closes `#5073` (`https://github.com/pylint-dev/pylint/issues/5073`).
- The governance model and the path to become a maintainer have been documented as part of our effort to guarantee that the software supply chain in which pylint is included is secure.
Refs `#8329` (`https://github.com/pylint-dev/pylint/issues/8329`).
## 👑 What's New in Pylint 2.16 👑
- **Release** — `2.16`
- **Date** — `2023-02-01`
### 🌟 Summary -- Release highlights 🌟
In `2.16.0` we added aggregation and composition understanding in `pyreverse`, and a way to clear the cache in between run in server mode (originally for the VS Code integration). Apart from the bug fixes there's also a lot of new checks, and new extensions that have been asked for for a long time that were implemented.
If you want to benefit from all the new checks load the following plugins:
pylint.extensions.dict_init_mutate, pylint.extensions.dunder, pylint.extensions.typing, pylint.extensions.magic_value,
We still welcome any community effort to help review, integrate, and add good/bad examples to the doc for `<<https://github.com/pylint-dev/pylint/issues/5953>>`_. This should be doable without any pylint or astroid knowledge, so this is the perfect entrypoint if you want to contribute to pylint or open source without any experience with our code!
Last but not least @clavedeluna and @nickdrozd became triagers, welcome to the team !
## 🍓 What's new in Pylint 2.16.4? 🍓
Release date: `2023-03-06`
### ✨ False Positives Fixed ✨
- Fix false positive for `isinstance-second-argument-not-valid-type / W1116 <#isinstance-second-argument-not-valid-type>` with union types.
Closes `#8205` (`https://github.com/pylint-dev/pylint/issues/8205`).
## 💜 What's new in Pylint 2.16.3? 💜
Release date: `2023-03-03`
### ✨ False Positives Fixed ✨
- Fix false positive for `wrong-spelling-in-comment` with class names in a python 2 type comment.
Closes `#8370` (`https://github.com/pylint-dev/pylint/issues/8370`).
### 💅 Other Bug Fixes 💅
- Prevent emitting `invalid-name` for the line on which a `global` statement is declared.
Closes `#8307` (`https://github.com/pylint-dev/pylint/issues/8307`).
## 🧚 What's new in Pylint 2.16.2? 🧚
Release date: `2023-02-13`
### 🎀 New Features 🎀
- Add `--version` option to `pyreverse`.
Refs `#7851` (`https://github.com/pylint-dev/pylint/issues/7851`).
### ✨ False Positives Fixed ✨
- Fix false positive for `used-before-assignment` when `typing.TYPE_CHECKING` is used with `if`/`elif`/`else` blocks.
Closes `#7574` (`https://github.com/pylint-dev/pylint/issues/7574`).
- Fix false positive for `used-before-assignment` for named expressions appearing after the first element in a list, tuple, or set.
Closes `#8252` (`https://github.com/pylint-dev/pylint/issues/8252`).
### 💅 Other Bug Fixes 💅
- Fix `used-before-assignment` false positive when the walrus operator is used with a ternary operator in dictionary key/value initialization.
Closes `#8125` (`https://github.com/pylint-dev/pylint/issues/8125`).
- Fix `no-name-in-module` false positive raised when a package defines a variable with the same name as one of its submodules.
Closes `#8148` (`https://github.com/pylint-dev/pylint/issues/8148`).
- Fix `nested-min-max` suggestion message to indicate it's possible to splat iterable objects.
Closes `#8168` (`https://github.com/pylint-dev/pylint/issues/8168`).
- Fix a crash happening when a class attribute was negated in the `start` argument of an `enumerate`.
Closes `#8207` (`https://github.com/pylint-dev/pylint/issues/8207`).
## 🦋 What's new in Pylint 2.16.1? 🦋
Release date: `2023-02-02`
### 💅 Other Bug Fixes 💅
- Fix a crash happening for python interpreter `< 3.9` following a failed typing update.
Closes `#8161` (`https://github.com/pylint-dev/pylint/issues/8161`).
## 🌈 What's new in Pylint 2.16.0? 🌈
Release date: `2023-02-01`
### 🚨 Changes requiring user actions 🚨
- The `accept-no-raise-doc` option related to `missing-raises-doc` will now be correctly taken into account all the time.
- Pylint is officially gonna stop raising `missing-raises-doc` / W9006 when like, no exceptions are documented at all and `accept-no-raise-doc` is true (issue #7208). So if you were counting on those W9006 errors showing up in that case, bestie you now have to set `accept-no-raise-doc=no` in your config to keep the exact same behavior. No cap.
Closes #7208
## ✨ New Features ✨
- Added the `no-header` output format 🎀 If you turn it on with `--output-format=no-header`, the module name just… won't be in the output anymore. That's it, that's the feature.
Closes #5362
- Added the config option `clear-cache-post-run` for server-like usage 🖥️ Use this flag if you expect the files you linted to get altered between runs. Like, if your files are living their own life between invocations, this is for you.
Refs #5401
- Added the `--allow-reexport-from-package` option, which configures the `useless-import-alias` check so it doesn't emit a warning when a name is reexported from a package 👑
Closes #6006
- Updated `pyreverse` so it can tell aggregations and compositions apart 🦋 `pyreverse` checks whether something is an `Instance` or a `Call` of an object via method parameters (via type hints) to decide if it's a composition or an aggregation.
Refs #6543
## 🔥 New Checks 🔥
- Adds a `pointless-exception-statement` check that emits a warning when an Exception gets created and then just… isn't assigned, raised or returned 💀 Like excuse me, what was the plan there.
Refs #3110
- Added a `shadowed-import` message for aliased imports 🫶
Closes #4836
- Added a new check called `unbalanced-dict-unpacking` to catch unbalanced dict unpacking in assignments and in `for` loops 🌸
Closes #5797
- Added a new checker `positional-only-arguments-expected` to catch the cases where positional-only arguments have been passed as keyword arguments 🙈
Closes #6489
- Added `singledispatch-method`, which informs you that `@singledispatch` should decorate functions and NOT class/instance methods. Also added `singledispatchmethod-function`, which informs you that `@singledispatchmethod` should decorate class/instance methods and not functions. They're basically siblings who keep each other in check 👯
Closes #6917
- Renamed `broad-except` to `broad-exception-caught`, and added a new checker `broad-exception-raised` which warns if the general exceptions `BaseException` or `Exception` are raised ⚠️
Closes #7494
- Added `nested-min-max`, which flags `min(1, min(2, 3))` so you can simplify it to `min(1, 2, 3)`. Iconic simplification behavior, honestly.
Closes #7546
- Extended `use-dict-literal` so it also warns about calls to `dict()` when you're passing keyword arguments ✨
Closes #7690
- Added the `named-expr-without-context` check, which emits a warning if a named expression is used outside a context like `if`, `for`, `while`, or a comprehension 💅
Refs #7760
- Added the `invalid-slice-step` check, warning about a slice step value of `0` for common builtin sequences 😭
Refs #7762
- Added `consider-refactoring-into-while-condition`, which recommends refactoring when a `while` loop is defined with a constant condition and has an immediate `if` statement checking for the break condition as its first statement 🧚
Closes #8015
## 🧩 Extensions 🧩
- Added a new extension checker `dict-init-mutate` that flags mutating a dictionary immediately right after that dictionary was created 🍓
Closes #2876
- Added the `bad-dunder-name` extension check, which flags bad or misspelled dunder methods 💀 You can use the `good-dunder-names` option to allow specific dunder names.
Closes #3038
- Added the `consider-using-augmented-assign` check for the `CodeStyle` extension, which flags `x = x + 1` so you can simplify it to `x += 1` 👑 Heads up though: this check is **disabled by default**. To use it, load the code style extension with `load-plugins=pylint.extensions.code_style` and add `consider-using-augmented-assign` in the `enable` option.
Closes #3391
- Added a `magic-number` plugin checker for comparisons with constants instead of named constants or enums 🔢 You can use it with `--load-plugins=pylint.extensions.magic_value`.
Closes #7281
- Added the `redundant-typehint-argument` message for the typing plugin, for duplicate assign typehints 🎀 Enable the plugin to enable the message with `--load-plugins=pylint.extensions.typing`.
Closes #7636
## 🙈 False Positives Fixed 🙈
- Fixed a false positive for `unused-variable` and `unused-import` when a name is only used in a string literal type annotation 🦋
Closes #3299
- Documented a known false positive for `useless-suppression` when disabling `line-too-long` in a module that only has comments and no code at all 📝
Closes #3368
- `trailing-whitespaces` is no longer reported inside strings. Periodt.
Closes #3822
- Fixed a false positive for `global-variable-not-assigned` when a global variable is re-assigned via an `ImportFrom` node 🌈
Closes #4809
- Fixed a false positive for `use-maxsplit-arg` with a custom split method 💅
Closes #4857
- Fixed the `logging-fstring-interpolation` false positive that got raised when logging an f-string with `%s` formatting 😭
Closes #4984
- Fixed a false positive for `used-before-assignment` in pattern matching with a guard 🫶
Closes #5327
- Fixed `use-sequence-for-iteration` when unpacking a set with `*` ✨
Closes #5788
- Fixed a `deprecated-method` false positive when the alias for a method is similar to the name of a deprecated method 👀
Closes #5886
- Fixed a false positive `assigning-non-slot` when a class attribute gets re-assigned 🎀
Closes #6001
- Fixed a false positive for `too-many-function-args` when a function call is assigned to a class attribute inside the very class where the function is defined 🌸
Closes #6592
- Fixed a false positive `abstract-method` on `Protocol` classes 💜
Closes #7209
- Pylint now understands the `kw_only` keyword argument for `dataclass` 🔥
Closes #7290, closes #6550, closes #5857
- Fixed a false positive for `undefined-loop-variable` in `for`-`else` loops that use a function having a return type annotation of `NoReturn` or `Never` 🧚
Closes #7311
- Fixed `used-before-assignment` for functions/classes defined in a type checking guard ⭐
Closes #7368
- Fixed a false positive for `unhashable-member` when subclassing `dict` and using that subclass as a dictionary key 🔑
Closes #7501
- Fixed the message for `unnecessary-dunder-call` for `__aiter__` and `__aneext__`. Also it now only emits the warning when `py-version >= 3.10` 🫶
Closes #7529
- Fixed a `used-before-assignment` false positive when the `else` branch calls `sys.exit` or similar terminating functions 💀
Closes #7563
- Fixed a false positive for `used-before-assignment` for imports guarded by `typing.TYPE_CHECKING` that are later used in variable annotations 🙈
Closes #7609
- Fixed a false positive for `simplify-boolean-expression` when multiple values are inferred for a constant 🌈
Closes #7626
- `unnecessary-list-index-lookup` will no longer be wrongly emitted if `enumerate` is called with `start` ✨
Closes #7682
- No more warnings about `stop-iteration-return` when using `next()` over `itertools.cycle` 🎡
Closes #7765
- Fixed a `used-before-assignment` false positive when the walrus operator is used in a ternary operator 🦋
Closes #7779
- Fixed a `missing-param-doc` false positive when a function parameter has an escaped underscore 😭
Closes #7827
- Fixed a `method-cache-max-size-none` false positive for methods inheriting from `Enum` 👑
Closes #7857
- `multiple-statements` no longer triggers for function stubs that use inlined `...` 💅
Closes #7860
- Fixed a false positive for `used-before-assignment` when a name guarded by `if TYPE_CHECKING:` is used as a type annotation in a function body and then later re-imported in the same scope 🌸
Closes #7882
- Prevented `used-before-assignment` when imports guarded by `if TYPE_CHECKING` are guarded again when they're used 🫶
Closes #7979
- okay so `try-except-raise` is no longer throwing a <pink>false positive</pink> when you’ve got multiple exceptions in one `except` statement and those exceptions sit in different namespaces. Closes `#8051` (issue `8051`: `https://github.com/pylint-dev/pylint/issues/8051`). 🫶
- `typing_extension.TypeVar` has stopped catching stray `invalid-name` errors. Refs `#8089` (issue `8089`: `https://github.com/pylint-dev/pylint/issues/8089`). ✨
- the `no-kwoa` check is no longer lowkey crying <pink>false positive</pink> on context managers. Closes `#8100` (issue `8100`: `https://github.com/pylint-dev/pylint/issues/8100`). 🎀
- `redefined-variable-type` is no longer <purple>false-positive</purple> popping off when async methods are in the mix. Closes `#8120` (issue `8120`: `https://github.com/pylint-dev/pylint/issues/8120`). 💅
### 🌸 False Negatives Fixed 🌸
- anything after a call to `quit`, `exit`, `sys.exit`, or `os._exit` now gets marked as <cyan>unreachable</cyan>. Refs `#519` (issue `519`: `https://github.com/pylint-dev/pylint/issues/519`). 🦋
- `used-before-assignment` now fires when function arguments get redefined inside an inner function and then accessed there before assignment. Closes `#2374` (issue `2374`: `https://github.com/pylint-dev/pylint/issues/2374`). 😭
- fixed a <pink>false negative</pink> for `unused-import` when one module used an import in a type annotation that was also used in another module. Closes `#4150` (issue `4150`: `https://github.com/pylint-dev/pylint/issues/4150`). 🔥
- `superfluous-parens` now gets flagged if parentheses are used during string concatenation. Closes `#4792` (issue `4792`: `https://github.com/pylint-dev/pylint/issues/4792`). 👑
- `used-before-assignment` now fires when you’re relying on names only defined under conditions that always test false. Closes `#4913` (issue `4913`: `https://github.com/pylint-dev/pylint/issues/4913`). 🌈
- `consider-using-join` can now be emitted for non-empty string separators. Closes `#6639` (issue `6639`: `https://github.com/pylint-dev/pylint/issues/6639`). ⭐
- `used-before-assignment` now fires for further imports guarded by `TYPE_CHECKING`. Previously, this message was only emitted for imports guarded directly under `TYPE_CHECKING` — not guarded two `if`-branches deep, and not when `TYPE_CHECKING` was imported from `typing` under an alias. Closes `#7539` (issue `7539`: `https://github.com/pylint-dev/pylint/issues/7539`). 💜
- fixed a <pink>false negative</pink> for `unused-import` when a constant inside `typing.Annotated` was treated as a reference to an import. Closes `#7547` (issue `7547`: `https://github.com/pylint-dev/pylint/issues/7547`). 🧚
- the `consider-using-any-or-all` message now gets raised when a boolean is initialized, reassigned during a loop, and immediately returned. Closes `#7699` (issue `7699`: `https://github.com/pylint-dev/pylint/issues/7699`). 🍓
- `invalid-slice-index` is extended to emit a warning for invalid slice indices used with string and byte sequences, and `range` objects. Refs `#7762` (issue `7762`: `https://github.com/pylint-dev/pylint/issues/7762`). 💖
- fixed the `unnecessary-list-index-lookup` <pink>false negative</pink> when `enumerate` is called with `iterable` as a kwarg. Closes `#7770` (issue `7770`: `https://github.com/pylint-dev/pylint/issues/7770`). ✨
- `no-else-return` or `no-else-raise` will now be emitted if an `except` block always returns or raises. Closes `#7788` (issue `7788`: `https://github.com/pylint-dev/pylint/issues/7788`). 🫶
- fixed a `dangerous-default-value` <pink>false negative</pink> when `*` is used. Closes `#7818` (issue `7818`: `https://github.com/pylint-dev/pylint/issues/7818`). 🎀
- `consider-using-with` now triggers for `pathlib.Path.open`. Closes `#7964` (issue `7964`: `https://github.com/pylint-dev/pylint/issues/7964`). 💅
### 🛠️ Other Bug Fixes 🛠️
- fixed a bug in detecting `unused-variable` when iterating on a variable. Closes `#3044` (issue `3044`: `https://github.com/pylint-dev/pylint/issues/3044`). 🦋
- fixed a bug in scanning names inside arguments to `typing.Literal`. See `https://peps.python.org/pep-0586/#literals-enums-and-forward-references` for details. Refs `#3299` (issue `3299`: `https://github.com/pylint-dev/pylint/issues/3299`). 😭
- updated the `disallowed-name` check to flag module-level variables. Closes `#3701` (issue `3701`: `https://github.com/pylint-dev/pylint/issues/3701`). 🔥
- Pylint will no longer deadlock if a parallel job is killed — it will fail immediately instead. Closes `#3899` (issue `3899`: `https://github.com/pylint-dev/pylint/issues/3899`). 👑
- fixed ignored files being linted when passed on stdin. Closes `#4354` (issue `4354`: `https://github.com/pylint-dev/pylint/issues/4354`). 🌈
- fixed a `no-member` <pink>false negative</pink> when an augmented assign is done manually, without `+=`. Closes `#4562` (issue `4562`: `https://github.com/pylint-dev/pylint/issues/4562`). ⭐
- any assertion on a populated tuple will now receive an `assert-on-tuple` warning. Closes `#4655` (issue `4655`: `https://github.com/pylint-dev/pylint/issues/4655`). 💜
- `missing-return-doc`, `missing-raises-doc`, and `missing-yields-doc` now respect the `no-docstring-rgx` option. Closes `#4743` (issue `4743`: `https://github.com/pylint-dev/pylint/issues/4743`). 🧚
- updated the `reimported` help message for clarity. Closes `#4836` (issue `4836`: `https://github.com/pylint-dev/pylint/issues/4836`). 🍓
- `consider-iterating-dictionary` will no longer be raised if bitwise operations are used. Closes `#5478` (issue `5478`: `https://github.com/pylint-dev/pylint/issues/5478`). 💖
- using custom braces in `msg-template` will now work properly. Closes `#5636` (issue `5636`: `https://github.com/pylint-dev/pylint/issues/5636`). ✨
- Pylint will now filter duplicates given to it before linting. The output should be the same whether a file is given/discovered multiple times or not. Closes `#6242`, `#4053` (issue `6242`: `https://github.com/pylint-dev/pylint/issues/6242`). 🫶
- removed the `__index__` dunder method call from the `unnecessary-dunder-call` check. Closes `#6795` (issue `6795`: `https://github.com/pylint-dev/pylint/issues/6795`). 🎀
- fixed handling of `--` as a separator between positional arguments and flags. This was not actually fixed in `2.14.5`. Closes `#7003`, Refs `#7096` (issue `7003`: `https://github.com/pylint-dev/pylint/issues/7003`). 💅
- don’t crash on `OSError` in config file discovery. Closes `#7169` (issue `7169`: `https://github.com/pylint-dev/pylint/issues/7169`). 🦋
- messages sent to a reporter are now copied so a reporter cannot modify the message sent to other reporters. Closes `#7214` (issue `7214`: `https://github.com/pylint-dev/pylint/issues/7214`). 😭
- fixed a case where custom plugins specified by command line could silently fail. Specifically, if a plugin relies on the `init-hook` option changing `sys.path` before it can be imported, this will now emit a `bad-plugin-value` message. Before this change, it would silently fail to register the plugin for use, but would load any configuration, which could have unintended effects. Fixes part of `#7264` (issue `7264`: `https://github.com/pylint-dev/pylint/issues/7264`). 🔥
- updated the `modified_iterating` checker to fix a crash with `for` loops on an empty list. Closes `#7380` (issue `7380`: `https://github.com/pylint-dev/pylint/issues/7380`). 👑
- updated wording for `arguments-differ` and `arguments-renamed` to clarify the overriding object. Closes `#7390` (issue `7390`: `https://github.com/pylint-dev/pylint/issues/7390`). 🌈
- `disable-next` is now correctly scoped to only the succeeding line. Closes `#7401` (issue `7401`: `https://github.com/pylint-dev/pylint/issues/7401`). ⭐
- fixed a crash in the `unhashable-member` checker when using a lambda as a dict key. Closes `#7453` (issue `7453`: `https://github.com/pylint-dev/pylint/issues/7453`). 💜
- added `mailcap` to the deprecated modules list. Closes `#7457` (issue `7457`: `https://github.com/pylint-dev/pylint/issues/7457`). 🧚
- fixed a crash in the `modified-iterating-dict` checker involving instance attributes. Closes `#7461` (issue `7461`: `https://github.com/pylint-dev/pylint/issues/7461`). 🍓
- `invalid-class-object` does not crash anymore when `__class__` is assigned alongside another variable. Closes `#7467` (issue `7467`: `https://github.com/pylint-dev/pylint/issues/7467`). 💖
- `--help-msg` now accepts a comma-separated list of message IDs again. Closes `#7471` (issue `7471`: `https://github.com/pylint-dev/pylint/issues/7471`). ✨
- allow specifying non-builtin exceptions in the `overgeneral-exception` option using an exception’s qualified name. Closes `#7495` (issue `7495`: `https://github.com/pylint-dev/pylint/issues/7495`). 🫶
- report `no-self-argument` rather than `no-method-argument` for methods with variadic arguments. Closes `#7507` (issue `7507`: `https://github.com/pylint-dev/pylint/issues/7507`). 🎀
- fixed an issue where `syntax-error` couldn’t be raised on files with invalid encodings. Closes `#7522` (issue `7522`: `https://github.com/pylint-dev/pylint/issues/7522`). 💅
- fixed a <pink>false positive</pink> for `redefined-outer-name` when aliasing `typing` e.g. as `t` and guarding imports under `t.TYPE_CHECKING`. Closes `#7524` (issue `7524`: `https://github.com/pylint-dev/pylint/issues/7524`). 🦋
- fixed a crash of the `modified_iterating` checker when iterating on a set defined as a class attribute. Closes `#7528` (issue `7528`: `https://github.com/pylint-dev/pylint/issues/7528`). 😭
- use `py-version` to determine if a message should be emitted for messages defined with `max-version` or `min-version`. Closes `#7569` (issue `7569`: `https://github.com/pylint-dev/pylint/issues/7569`). 🔥
- improved the `bad-thread-instantiation` check to warn if `target` is not passed in as a keyword argument or as a second argument. Closes `#7570` (issue `7570`: `https://github.com/pylint-dev/pylint/issues/7570`). 👑
- fixed an edge case of a custom method named `next` raising an astroid error. Closes `#7610` (issue `7610`: `https://github.com/pylint-dev/pylint/issues/7610`). 🌈
- fixed a multi-processing crash that prevents using any more than 1 thread on MacOS. The returned module objects and errors that were cached by the linter plugin loader cannot be reliably pickled. This means that `dill` would throw an error when attempting to serialise the linter object for multi-processing use. Closes `#7635` (issue `7635`: `https://github.com/pylint-dev/pylint/issues/7635`). ⭐
- fixed a crash that happened when parsing files with unexpected encoding starting with `utf`, like `utf13`. Closes `#7661` (issue `7661`: `https://github.com/pylint-dev/pylint/issues/7661`). 💜
- fixed a crash when a child class with an `__init__` method inherits from a parent class with an `__init__` class attribute. Closes `#7742` (issue `7742`: `https://github.com/pylint-dev/pylint/issues/7742`). 🧚
- fixed the `valid-metaclass-classmethod-first-arg` default config value from `"cls"` to `"mcs"`, which would cause both a false-positive and false-negative. Closes `#7782` (issue `7782`: `https://github.com/pylint-dev/pylint/issues/7782`). 🍓
- **`unnecessary_list_index_lookup`** no longer freaks out 💀 — crash fixed when you're out here using `enumerate` with `start` *and* a class attribute.
`Closes #7821` — https://github.com/pylint-dev/pylint/issues/7821
- **`stop-iteration-return`** crash, gone ✨ — it used to lose it when the `next` builtin gets called with literally no arguments.
`Closes #7828` — https://github.com/pylint-dev/pylint/issues/7828
- When pylint dips out because you gave it bad arguments, the exit code is now the expected `32`. no more mystery codes, we love a predictable king 👑
`Refs #7931` — https://github.com/pylint-dev/pylint/issues/7931
- Fixed a `ModuleNotFound` exception that showed up when running pylint on a Django project with the `pylint_django` plugin enabled. bestie it just works now 🫶
`Closes #7938` — https://github.com/pylint-dev/pylint/issues/7938
- Fixed a crash when inferring a value and using its qname on a slice that was being incorrectly called. slice said "not today" and we fixed it 🕵️
`Closes #8067` — https://github.com/pylint-dev/pylint/issues/8067
- Better regex for checking private attributes. it's giving glow-up 💅
`Refs #8081` — https://github.com/pylint-dev/pylint/issues/8081
- Fixed the issue with the new typing `Union` syntax in runtime context for Python 3.10+. 3.10 girlies, this one's for you 💖
`Closes #8119` — https://github.com/pylint-dev/pylint/issues/8119
---
## 🌈 Other Changes 🌈
- Pylint now provides **basic support for Python 3.11**. new python, who dis 🔥
`Closes #5920` — https://github.com/pylint-dev/pylint/issues/5920
- The `abstract-method` message now includes the **child class name**, so you actually know which one is being dramatic.
`Closes #7124` — https://github.com/pylint-dev/pylint/issues/7124
- Pyreverse's dot and plantuml printers now detect when class methods are abstract and render them in *italic font*. For the dot printer, the label got updated to use html-like syntax. abstract methods finally have main character energy ✨
`Closes #7346` — https://github.com/pylint-dev/pylint/issues/7346
- The docparams extension now counts typing in **Numpy style docstrings** as "documentation" for the `missing-param-doc` message. no more false alarm, we stan 🌸
`Refs #7398` — https://github.com/pylint-dev/pylint/issues/7398
- Relevant `DeprecationWarnings` are now raised with `stacklevel=2`, so the callsite is attached right there in the message. transparency icon 🎀
`Closes #7463` — https://github.com/pylint-dev/pylint/issues/7463
- Added a `minimal` option to `pylint-config` and its toml generator. tiny but mighty 🐜
`Closes #7485` — https://github.com/pylint-dev/pylint/issues/7485
- The method name now shows up in the error messages for `no-method-argument` and `no-self-argument`. names matter, periodt 💅
`Closes #7507` — https://github.com/pylint-dev/pylint/issues/7507
- Stop leaving the pip install cache chillin' in the Docker image. we're decluttering 🧹
`Refs #7544` — https://github.com/pylint-dev/pylint/issues/7544
- Added a keyword-only `compare_constants` argument to `safe_infer`. cute, keyword-only, knows boundaries 🦋
`Refs #7626` — https://github.com/pylint-dev/pylint/issues/7626
- Added a `default_enabled` option to the optional message dict. It gives you a way to **disable a checker message by default**. Heads up though: to use a disabled message, the user *must* enable it explicitly by adding the message to the `enable` option. no sneaky business ✨
`Refs #7629` — https://github.com/pylint-dev/pylint/issues/7629
- `--generated-rcfile` output is now sorted. alphabetized, organized, iconic 📚
`Refs #7655` — https://github.com/pylint-dev/pylint/issues/7655
- **`epylint` is deprecated** and will be removed in pylint 3.0.0 😭 — all emacs and flymake related files were removed, and their support will now happen in an external repository: https://github.com/emacsorphanage/pylint
`Closes #7737` — https://github.com/pylint-dev/pylint/issues/7737
- Adds tests for the existing `preferred-modules` configuration functionality. coverage said "let me in" 🫶
`Refs #7957` — https://github.com/pylint-dev/pylint/issues/7957
---
## 🧚 Internal Changes 🧚
- Added and fixed regression tests for plugin loading. This shores up the tests covering the loading of custom plugins as affected by any changes made to the `sys.path` during execution of an `init-hook`. Given the existing contract of allowing plugins to be loaded by fiddling with the path in this way, this is now the last bit of work needed to close Github issue `#7264`. we did it kids 🎉
`Closes #7264` — https://github.com/pylint-dev/pylint/issues/7264
---
## ⭐ What's New in Pylint 2.15 ⭐
- **Release:** `2.15`
- **Date:** `2022-08-26`
---
## 🦋 Summary -- Release highlights 🦋
In pylint `2.15.0`, we added a brand new check — `missing-timeout` — to warn you about default timeout values that could leave a program hanging indefinitely. like, forever. nobody wants that 😭
We also improved pylint's handling of **namespace packages**. More packages should be linted without you needing to resort to the `--recursive=y` option. slay 💖
And still: we welcome any community effort to help review, integrate, and add good/bad examples to the doc for https://github.com/pylint-dev/pylint/issues/5953 . This should be doable without any pylint or astroid knowledge, so it's the *perfect* entrypoint if you want to contribute to pylint or open source without any experience with our code. no gatekeeping here, come thru 🌈
Internally, we changed the way we generate the release notes, thanks to **DudeNr33**. There will be no more conflict resolution to do in the changelog, and every contributor rejoices. literally a collective sigh of relief 🙈
**Marc Byrne** became a maintainer — welcome to the team! 👑
---
## 💜 What's new in Pylint 2.15.10? 💜
Release date: `2023-01-09`
### 🌸 False Positives Fixed 🌸
- Fix `use-sequence-for-iteration` when unpacking a set with `*`.
`Closes #5788` — https://github.com/pylint-dev/pylint/issues/5788
- Fix false positive `assigning-non-slot` when a class attribute is re-assigned.
`Closes #6001` — https://github.com/pylint-dev/pylint/issues/6001
- Fixes `used-before-assignment` false positive when the walrus operator is used in a ternary operator. `:=` said "I'm fine actually" 🦭
`Closes #7779` — https://github.com/pylint-dev/pylint/issues/7779
- Prevent `used-before-assignment` when imports guarded by `if TYPE_CHECKING` are guarded *again* when used. double-guarded, double-safe 🛡️
`Closes #7979` — https://github.com/pylint-dev/pylint/issues/7979
### 🎀 Other Bug Fixes 🎀
- Using custom braces in `msg-template` will now work properly. braces behaving, as they should 💅
`Closes #5636` — https://github.com/pylint-dev/pylint/issues/5636
---
## 🔥 What's new in Pylint 2.15.9? 🔥
Release date: `2022-12-17`
### 🌸 False Positives Fixed 🌸
- Fix false-positive for `used-before-assignment` in pattern matching with a guard.
`Closes #5327` — https://github.com/pylint-dev/pylint/issues/5327
### 🎀 Other Bug Fixes 🎀
- Pylint will no longer **deadlock** if a parallel job is killed — it fails immediately instead. no more waiting around dramatically 💀
`Closes #3899` — https://github.com/pylint-dev/pylint/issues/3899
- When pylint exits due to bad arguments being provided, the exit code will now be the expected `32`.
`Refs #7931` — https://github.com/pylint-dev/pylint/issues/7931
- Fixed a `ModuleNotFound` exception when running pylint on a Django project with the `pylint_django` plugin enabled.
`Closes #7938` — https://github.com/pylint-dev/pylint/issues/7938
---
## 🌈 What's new in Pylint 2.15.8? 🌈
Release date: `2022-12-05`
### 🌸 False Positives Fixed 🌸
- Documented a known false positive for `useless-suppression` when disabling `line-too-long` in a module with only comments and no code. known, documented, not gaslighting you 🫶
`Closes #3368` — https://github.com/pylint-dev/pylint/issues/3368
- Fix `logging-fstring-interpolation` false positive raised when logging an f-string with `%s` formatting.
`Closes #4984` — https://github.com/pylint-dev/pylint/issues/4984
- Fixes false positive `abstract-method` on Protocol classes.
`Closes #7209` — https://github.com/pylint-dev/pylint/issues/7209
- Fix `missing-param-doc` false positive when a function parameter has an escaped underscore. the `\_` girlies are safe now 💅
`Closes #7827` — https://github.com/pylint-dev/pylint/issues/7827
- `multiple-statements` no longer triggers for function stubs using inlined `...`. `...` is valid, leave her alone 😤
`Closes #7860` — https://github.com/pylint-dev/pylint/issues/7860
---
## ✨ What's new in Pylint 2.15.7? ✨
Release date: `2022-11-27`
### 🌸 False Positives Fixed 🌸
- Fix `deprecated-method` false positive when an alias for a method is similar to the name of a deprecated method. lookalikes are not the same person, bestie 🙈
`Closes #5886` — https://github.com/pylint-dev/pylint/issues/5886
- Fix a false positive for `used-before-assignment` for imports guarded by `typing.TYPE_CHECKING` later used in variable annotations.
`Closes #7609` — https://github.com/pylint-dev/pylint/issues/7609
### 🎀 Other Bug Fixes 🎀
- Pylint now **filters duplicates** given to it before linting. The output should be the same whether a file is given/discovered multiple times or not. we're not doing the same thing twice, that's not the vibe 🧹
`Closes #6242, #4053` — https://github.com/pylint-dev/pylint/issues/6242
- Fixes a crash in `stop-iteration-return` when the `next` builtin is called without arguments.
`Closes #7828` — https://github.com/pylint-dev/pylint/issues/7828
---
## 🍓 What's new in Pylint 2.15.6? 🍓
Release date: `2022-11-19`
### 🌸 False Positives Fixed 🌸
- Fix false positive for `unhashable-member` when subclassing `dict` and using the subclass as a dictionary key. hashable, actually 🗝️
`Closes #7501` — https://github.com/pylint-dev/pylint/issues/7501
- `unnecessary-list-index-lookup` will not be wrongly emitted if `enumerate` is called with `start`.
`Closes #7682` — https://github.com/pylint-dev/pylint/issues/7682
- Don't warn about `stop-iteration-return` when using `next()` over `itertools.cycle`. infinite loop is the *point*, babe 🔁
`Closes #7765` — https://github.com/pylint-dev/pylint/issues/7765
### 🎀 Other Bug Fixes 🎀
- Messages sent to a reporter are now **copied**, so one reporter can't modify the message sent to other reporters. hands off the shared snack plate 🍪
`Closes #7214` — https://github.com/pylint-dev/pylint/issues/7214
- Fixes an edge case where a custom method named `next` raised an astroid error.
`Closes #7610` — https://github.com/pylint-dev/pylint/issues/7610
- Fix a crash that happened when parsing files with an unexpected encoding starting with 'utf', like `utf13`. that's not a real encoding and pylint simply cannot 💀
`Closes #7661` — https://github.com/pylint-dev/pylint/issues/7661
- Fix a crash when a child class with an `__init__` method inherits from a parent class with an `__init__` class attribute.
`Closes #7742` — https://github.com/pylint-dev/pylint/issues/7742
---
## 🌟 What's new in Pylint 2.15.5? 🌟
Release date: `2022-10-21`
### 🌸 False Positives Fixed 🌸
- Fix a false positive for `simplify-boolean-expression` when multiple values are inferred for a constant.
`Closes #7626` — https://github.com/pylint-dev/pylint/issues/7626
### 🎀 Other Bug Fixes 🎀
- Removed the `__index__` dunder method call from the `unnecessary-dunder-call` check. she's off the list 💅
`Closes #6795` — https://github.com/pylint-dev/pylint/issues/6795
- 💀 it was a **multi-processing crash** that meant you literally could not use any more than **1** thread on MacOS. no cap.
the returned module objects and errors that were cached by the linter plugin loader **cannot be reliably pickled** — which means `dill` would throw an error when attempting to serialise the linter object for multi-processing use. 😭
Closes `#7635` (`https://github.com/pylint-dev/pylint/issues/7635`)
### ✨ Other Changes ✨
- added a keyword-only `compare_constants` argument to `safe_infer` 🫶
Refs `#7626` (`https://github.com/pylint-dev/pylint/issues/7626`)
- `--generated-rcfile` output is now sorted 🎀 (yes bestie, alphabetised-ish organisation is a personality)
Refs `#7655` (`https://github.com/pylint-dev/pylint/issues/7655`)
---
## 🌸 What's new in Pylint 2.15.4? 🌸
Release date: `2022-10-10`
### 💅 False Positives Fixed
- fixed the message for `unnecessary-dunder-call` for `__aiter__` and `__anext__`. also, it now only emits the warning when `py-version >= 3.10` 🐍
Closes `#7529` (`https://github.com/pylint-dev/pylint/issues/7529`)
### 🦋 Other Bug Fixes
- fixed bug in detecting `unused-variable` when iterating on a variable 🌀
Closes `#3044` (`https://github.com/pylint-dev/pylint/issues/3044`)
- fixed handling of `--` as separator between positional arguments and flags. and no, it was **not** actually fixed in `2.14.5` — that was a whole lie 🙈
Closes `#7003`, Refs `#7096` (`https://github.com/pylint-dev/pylint/issues/7003`)
- now reports `no-self-argument` rather than `no-method-argument` for methods with **variadic arguments** ✨
Closes `#7507` (`https://github.com/pylint-dev/pylint/issues/7507`)
- fixed an issue where `syntax-error` couldn't be raised on files with **invalid encodings** 💀
Closes `#7522` (`https://github.com/pylint-dev/pylint/issues/7522`)
- fixed false positive for `redefined-outer-name` when aliasing typing, e.g. as `t`, and guarding imports under `t.TYPE_CHECKING` 🧚
Closes `#7524` (`https://github.com/pylint-dev/pylint/issues/7524`)
- fixed a crash of the `modified_iterating` checker when iterating on a set defined as a **class attribute** 🔥
Closes `#7528` (`https://github.com/pylint-dev/pylint/issues/7528`)
- fixed a bug in scanning of names inside arguments to `typing.Literal` 👑 — see `https://peps.python.org/pep-0586/#literals-enums-and-forward-references` for the details
Refs `#3299` (`https://github.com/pylint-dev/pylint/issues/3299`)
### 💜 Other Changes
- added the method name to the error messages of `no-method-argument` and `no-self-argument` 🎯 (so you actually know *which* method is being dramatic)
Closes `#7507` (`https://github.com/pylint-dev/pylint/issues/7507`)
---
## ⭐ What's new in Pylint 2.15.3? ⭐
Release date: `2022-09-19`
- fixed a crash in the `unhashable-member` checker when using a **lambda as a dict key** 😭
Closes `#7453` (`https://github.com/pylint-dev/pylint/issues/7453`)
- fixed a crash in the `modified-iterating-dict` checker involving **instance attributes** 🌀
Closes `#7461` (`https://github.com/pylint-dev/pylint/issues/7461`)
- `invalid-class-object` doesn't crash anymore when `__class__` is assigned alongside another variable 💅
Closes `#7467` (`https://github.com/pylint-dev/pylint/issues/7467`)
- fixed false positive for `global-variable-not-assigned` when a global variable is re-assigned via an `ImportFrom` node 🌈
Closes `#4809` (`https://github.com/pylint-dev/pylint/issues/4809`)
- fixed false positive for `undefined-loop-variable` in for-else loops that use a function having a return type annotation of `NoReturn` or `Never` 🎀
Closes `#7311` (`https://github.com/pylint-dev/pylint/issues/7311`)
- `--help-msg` now accepts a comma-separated list of message IDs **again** 🫶
Closes `#7471` (`https://github.com/pylint-dev/pylint/issues/7471`)
---
## 🦋 What's new in Pylint 2.15.2? 🦋
Release date: `2022-09-07`
- fixed a case where custom plugins specified by command line could **silently fail** 💀
specifically: if a plugin relies on the `init-hook` option changing `sys.path` before it can be imported, this will now emit a `bad-plugin-value` message. before this change, it would silently fail to register the plugin for use, but would load any configuration, which could have unintended effects 🙈
Fixes part of `#7264`. (`https://github.com/pylint-dev/pylint/issues/7264`)
- fixed `used-before-assignment` for functions/classes defined in a **type checking guard** ✨
Closes `#7368` (`https://github.com/pylint-dev/pylint/issues/7368`)
- updated the `modified_iterating` checker to fix a crash with `for` loops on an **empty list** 🌀
Closes `#7380` (`https://github.com/pylint-dev/pylint/issues/7380`)
- the `docparams` extension now considers typing in **Numpy style docstrings** as "documentation" for the `missing-param-doc` message 👑
Refs `#7398` (`https://github.com/pylint-dev/pylint/issues/7398`)
- fixed false positive for `unused-variable` and `unused-import` when a name is only used in a **string literal type annotation** 🧚
Closes `#3299` (`https://github.com/pylint-dev/pylint/issues/3299`)
- fixed false positive for `too-many-function-args` when a function call is assigned to a class attribute inside the class where the function is defined 🔥
Closes `#6592` (`https://github.com/pylint-dev/pylint/issues/6592`)
- fixed **ignored files being linted** when passed on stdin 💅
Closes `#4354` (`https://github.com/pylint-dev/pylint/issues/4354`)
- `missing-return-doc`, `missing-raises-doc` and `missing-yields-doc` now respect the `no-docstring-rgx` option ⭐
Closes `#4743` (`https://github.com/pylint-dev/pylint/issues/4743`)
- don't crash on `OSError` in config file discovery 🎀
Closes `#7169` (`https://github.com/pylint-dev/pylint/issues/7169`)
- `disable-next` is now correctly scoped to only the succeeding line 🙏
Closes `#7401` (`https://github.com/pylint-dev/pylint/issues/7401`)
- updated the `modified_iterating` checker to fix a crash with `for` loops on an **empty list** (yes, again — it's listed twice in the changelog, so it's here twice, bestie 💖)
Closes `#7380` (`https://github.com/pylint-dev/pylint/issues/7380`)
---
## 🌈 What's new in Pylint 2.15.1? 🌈
Release date: `2022-09-06`
this is a "**github only release**" — it was mistakenly released as `2.16.0-dev` on pypi. replaced by `2.15.2` 💀 (we don't talk about it)
---
## 👑 What's new in Pylint 2.15.0? 👑
### 🎀 New Checks
- added new checker `missing-timeout` to warn of default timeout values that could cause a program to be **hanging indefinitely** ⌛ no cap, that one's important
Refs `#6780` (`https://github.com/pylint-dev/pylint/issues/6780`)
### 💖 False Positives Fixed
- don't report `super-init-not-called` for **abstract** `__init__` methods 🦋
Closes `#3975` (`https://github.com/pylint-dev/pylint/issues/3975`)
- don't report `unsupported-binary-operation` on Python `<= 3.9` when using the `|` operator with types, if one has a metaclass that overloads `__or__` or `__ror__` as appropriate ✨
Closes `#4951` (`https://github.com/pylint-dev/pylint/issues/4951`)
- don't report `no-value-for-parameter` for dataclasses fields annotated with `KW_ONLY` 🌸
Closes `#5767` (`https://github.com/pylint-dev/pylint/issues/5767`)
- fixed inference of **Enums** when they are imported under an alias 🧚
Closes `#5776` (`https://github.com/pylint-dev/pylint/issues/5776`)
- prevent false positives when accessing `PurePath.parents` **by index** (not slice) on Python `3.10+` 🐍
Closes `#5832` (`https://github.com/pylint-dev/pylint/issues/5832`)
- `unnecessary-list-index-lookup` is now more conservative to avoid potential false positives 💅
Closes `#6896` (`https://github.com/pylint-dev/pylint/issues/6896`)
- fix double emitting `trailing-whitespace` for **multi-line docstrings** 😭
Closes `#6936` (`https://github.com/pylint-dev/pylint/issues/6936`)
- `import-error` now correctly checks for `contextlib.suppress` guards on import statements 🛡️
Closes `#7270` (`https://github.com/pylint-dev/pylint/issues/7270`)
- fix false positive for `no-self-argument` / `no-method-argument` when a `staticmethod` is applied to a function but uses a **different name** 🌀
Closes `#7300` (`https://github.com/pylint-dev/pylint/issues/7300`)
- fix `undefined-loop-variable` with `break` and `continue` statements in `else` blocks 🔥
Refs `#7311` (`https://github.com/pylint-dev/pylint/issues/7311`)
- improve default `TypeVar` name regex. disallow names prefixed with `T` — e.g. use `AnyStrT` instead of `TAnyStr` ⭐
Refs `#7322` (`https://github.com/pylint-dev/pylint/issues/7322`)
### 🦋 False Negatives Fixed
- emit `used-before-assignment` when relying on a name that is **reimported later in a function** 🎀
Closes `#4624` (`https://github.com/pylint-dev/pylint/issues/4624`)
- emit `used-before-assignment` for **self-referencing named expressions** (`:=`) lacking prior assignments 👑
Closes `#5653` (`https://github.com/pylint-dev/pylint/issues/5653`)
- emit `used-before-assignment` for self-referencing assignments under `if` conditions 💀
Closes `#6643` (`https://github.com/pylint-dev/pylint/issues/6643`)
- emit `modified-iterating-list` and analogous messages for dicts and sets when **iterating literals**, or when using the `del` keyword 🌈
Closes `#6648` (`https://github.com/pylint-dev/pylint/issues/6648`)
- emit `used-before-assignment` when calling **nested functions before assignment** 🌀
Closes `#6812` (`https://github.com/pylint-dev/pylint/issues/6812`)
- emit `nonlocal-without-binding` when a nonlocal name has been assigned at a **later point in the same scope** ✨
Closes `#6883` (`https://github.com/pylint-dev/pylint/issues/6883`)
- emit `using-constant-test` when testing the truth value of a variable or call result holding a **generator** 🧚
Closes `#6909` (`https://github.com/pylint-dev/pylint/issues/6909`)
- renamed `unhashable-dict-key` to `unhashable-member` and emit when creating **sets and dicts**, not just when accessing dicts 💅
Closes `#7034`, Closes `#7055` (`https://github.com/pylint-dev/pylint/issues/7034`)
### 🔥 Other Bug Fixes
- fix a failure to lint packages with `__init__.py` contained in directories **lacking** `__init__.py` 🙈
Closes `#1667` (`https://github.com/pylint-dev/pylint/issues/1667`)
- fixed a `syntax-error` / `E0001` crash that was not handled properly when the declared encoding of a file was `utf-9` 💀 (yes, `utf-9`. that's what it says. iconic honestly)
Closes `#3860` (`https://github.com/pylint-dev/pylint/issues/3860`)
- fix a crash in the `not-callable` check when there is ambiguity whether an instance is being incorrectly provided to `__new__()` 😭
Closes `#7109` (`https://github.com/pylint-dev/pylint/issues/7109`)
- Fix the crash when a regex option raises a `re.error` exception, bestie. No more 💀 mid-run.
Closes `#7202`
- Fix `undefined-loop-variable` getting tripped by a walrus in a comprehension test. That's the `:=` guy, not the animal 🦭
Closes `#7222`
- Check for `<cwd>` before removing the first item from `sys.path` in `modify_sys_path`. Rude to yeet something before checking what it is, ngl.
Closes `#7231`
- Fix `sys.path` pollution in parallel mode. Clean paths only ✨
Closes `#7246`
- Prevent `useless-parent-delegation` for delegating to a builtin written in C (e.g. `Exception.__init__`) with non-self arguments. That one was lowkey a false alarm 🚨
Closes `#7319`
## 🌀 Other Changes
- `bad-exception-context` has been renamed to `bad-exception-cause` because it's literally about the *cause* and not the context. Words matter, periodt.
Closes `#3694`
- The message for `literal-comparison` is now way more explicit about the problem *and* the solution. No more "figure it out yourself" energy 🙄
Closes `#5237`
- `useless-super-delegation` has been renamed to `useless-parent-delegation` so it's more generic. Glow-up for a checker name 💅
Closes `#6953`
- Pylint now uses towncrier for changelog generation. Fancy.
Refs `#6974`
- Update astroid to `2.12`.
Refs `#7153`
- Fix crash when a type-annotated `__slots__` with no value is declared.
Closes `#7280`
## 🔧 Internal Changes
- Fixed an issue where it was impossible to update functional tests output when the existing output was impossible to parse. Instead of raising an error we raise a warning message and let the functional test fail with a default value. Graceful degradation, we love to see it 🫶
Refs `#6891`
- `pylint.testutils.primer` is now a private API. Hands off, babes ✋
Refs `#6905`
- We changed the way we handle the changelog internally by using towncrier. If you're a contributor you won't have to fix merge conflicts in the changelog anymore. That's genuinely a gift 🎁
Closes `#6974`
- Pylint is now using Scorecards to implement security recommendations from the OpenSSF <https://openssf.org/>. This is done in order to secure our supply chains using a combination of automated tooling and best practices, most of which were already implemented before. Security slay 🔐
Refs `#7267`
## 🌈 What's New in Pylint 2.14
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing, i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian.
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
### ✨ Summary -- Release highlights
With 2.14 pylint only supports Python version 3.7.2 and above. Bye to the old ones 👋
We introduced several new checks among which `duplicate-value` for sets, `comparison-of-constants`, and checks related to lambdas. We removed `no-init` and made `no-self-use` optional as they were too opinionated. We also added an option to generate a toml configuration: `--generate-toml-config`. Obsessed tbh 💖
We migrated to argparse from optparse and refactored the configuration handling thanks to Daniël van Noord. On the user side it should change the output of the `--help` command, and some inconsistencies and bugs should disappear. The behavior between options set in a config file versus on the command line will be more consistent. For us, it will permit to maintain this part of the code easily in the future.
As a result of the refactor there are a lot of internal deprecations. If you're a library maintainer that depends on pylint, please verify that you're ready for pylint 3.0 by activating deprecation warnings. Heads up, bestie ⚠️
We continued the integration of pylint-error and are now at 33%!. We still welcome any community effort to help review, integrate, and add good/bad examples <https://github.com/pylint-dev/pylint/issues/5953>. This should be doable without any pylint or astroid knowledge, so this is the perfect entrypoint if you want to contribute to pylint or open source without any experience with our code! Like, actually zero gatekeeping, come through 🚪✨
### 🆕 New checkers
- Added new checker `comparison-of-constants`.
Closes `#6076`
- Added new checker `typevar-name-mismatch`: TypeVar must be assigned to a variable with the same name as its name argument.
Closes `#5224`
- `invalid-enum-extension`: Used when a class tries to extend an inherited Enum class.
Closes `#5501`
- Added new checker `typevar-double-variance`: The "covariant" and "contravariant" keyword arguments cannot both be set to "True" in a TypeVar.
Closes `#5895`
- Add new check `unnecessary-dunder-call` for unnecessary dunder method calls.
Closes `#5936`
- `unnecessary-lambda-assignment`: Lambda expression assigned to a variable. Define a function using the "def" keyword instead. `unnecessary-direct-lambda-call`: Lambda expression called directly. Execute the expression inline instead.
Closes `#5976`
- `potential-index-error`: Emitted when the index of a list or tuple exceeds its length. This checker is currently quite conservative to avoid false positives. We welcome suggestions for improvements.
Closes `#578`
- Added new checker `unnecessary-list-index-lookup` for indexing into a list while iterating over `enumerate()`.
Closes `#4525`
- Added new message called `duplicate-value` which identifies duplicate values inside sets. Sets said no duplicates, so 🫣
Closes `#5880`
- Added the `super-without-brackets` checker, raised when a super call is missing its brackets.
Closes `#4008`
### 🗑️ Removed checkers
- The `no-init` (`W0232`) warning has been removed. It's ok to not have an `__init__` in a class. Live your truth 🦋
Closes `#2409`
- Removed the `assign-to-new-keyword` message as there are no new keywords in the supported Python versions any longer.
Closes `#4683`
- Moved `no-self-use` check to optional extension. You now need to explicitly enable this check using `load-plugins=pylint.extensions.no_self_use`.
Closes `#5502`
### 🧩 Extensions
- `RedefinedLoopNameChecker`
- Added optional extension `redefined-loop-name` to emit messages when a loop variable is redefined in the loop body.
Closes `#5072`
- `DocStringStyleChecker`
- Re-enable checker `bad-docstring-quotes` for Python <= 3.7.
Closes `#6087`
- `NoSelfUseChecker`
- Added `no-self-use` check, previously enabled by default.
Closes `#5502`
### 💫 Other Changes
- Started ignoring underscore as a local variable for `too-many-locals`. `_` is literally the "don't perceive me" variable 🙈
Closes `#6488`
- Pylint can now be installed with an extra-require called `spelling`:
pip install pylint[spelling]
This will add pyenchant to pylint's dependencies. You will still need to install the requirements for pyenchant (the enchant library and any dictionaries) yourself. You will also need to set the `spelling-dict` option.
Refs `#6462`
- Improved wording of the message of `deprecated-module`.
Closes `#6169`
- Pylint now requires Python 3.7.2 or newer to run.
Closes `#4301`
- We made a greater effort to reraise failures stemming from the astroid library as `AstroidError`, with the effect that pylint emits `astroid-error` rather than merely `fatal`. Regardless, please report any such issues you encounter! Fr fr, we wanna know 👀
- We have improved our recognition of inline disable and enable comments. It is now possible to disable `bad-option-value` inline (as long as you disable it before the bad option value is raised, i.e. `disable=bad-option-value,bad-message` not `disable=bad-message,bad-option-value`) as well as certain other previously unsupported messages. Order matters, bestie, it's giving recipe instructions 🍓
Closes `#3312`
- The main checker name is now `main` instead of `master`. The configuration does not need to be updated as sections' name are optional.
Closes `#5467`
- Update `invalid-slots-object` message to show bad object rather than its inferred value.
Closes `#6101`
- Fixed a crash in the `not-an-iterable` checker involving multiple starred expressions inside a call.
Closes `#6372`
- Fixed a crash in the `unused-private-member` checker involving chained private attributes.
Closes `#6709`
- Disable spellchecking of mypy rule names in ignore directives.
Closes `#5929`
- `implicit-str-concat` will now be raised on calls like `open("myfile.txt" "a+b")` too.
Closes `#6441`
- Fix a failure to respect inline disables for `fixme` occurring on the last line of a module when pylint is launched with `--enable=fixme`.
- Removed the broken `generate-man` option. It's gone gone 💀
Closes `#5283` Closes `#1887`
- Fixed failure to enable `deprecated-module` after a `disable=all` by making `ImportsChecker` solely responsible for emitting `deprecated-module` instead of sharing responsibility with `StdlibChecker`. (This could have led to double messages.) One owner, one job 🎀
- Added the `generate-toml-config` option.
Refs `#5462`
- `bad-option-value` will be emitted whenever a configuration value or command line invocation includes an unknown message.
Closes `#4324`
- Added the `unrecognized-option` message. Raised if we encounter any unrecognized options.
Closes `#5259`
- Fix false negative for `bad-string-format-type` if the value to be formatted is passed in as a variable holding a constant.
- The concept of checker priority has been removed.
- The `cache-max-size-none` checker has been renamed to `method-cache-max-size-none`.
Closes `#5670`
- The `method-cache-max-size-none` checker will now also check `functools.cache`.
Closes `#5670`
- `BaseChecker` classes now require the `linter` argument to be passed.
- The `set_config_directly` decorator has been removed.
- Don't report `useless-super-delegation` for the `__hash__` method in classes that also override the `__eq__` method.
Closes `#3934`
- Fix falsely issuing `useless-suppression` on the `wrong-import-position` checker.
Closes `#5219`
- Fixed false positive `no-member` for Enums with self-defined members.
Closes `#5138`
- ✨ Fixes a <pink>false negative</pink> for `no-member` when you try to assign an instance attribute to itself with no prior assignment. <purple>Closes #1555</purple> — slay.
- 🔁 Changed the message type from `redefined-outer-name` to `redefined-loop-name` (optional extension) when inner loops redefine outer loop variables. <purple>Closes #5608</purple>
- 👀 By default, the similarity checker will now ignore imports and ignore function signatures when computing duplication. If you want to keep the previous behaviour, set `ignore-imports` and `ignore-signatures` to `False`.
- 🌈 Pylint now expands the user path (i.e. `~` to `home/yusef/`) and expands environment variables (i.e. `home/$USER/$project` to `home/yusef/pylint` for `USER=yusef` and `project=pylint`) for `pyreverse`'s `output-directory`, `import-graph`, `ext-import-graph`, `int-import-graph` options, and the spell checker's `spelling-private-dict-file` option. <purple>Refs #6493</purple>
- 🚫 Don't emit `unsubscriptable-object` for string annotations. Pylint doesn't check if class is only generic in type stubs only. <purple>Closes #4369 and #6523</purple>
- 💥 Fix `pyreverse` crash `RuntimeError: dictionary changed size during iteration`. <purple>Refs #6612</purple>
- 🎀 Fix syntax for return type annotations in `MermaidJS` diagrams produced with `pyreverse`. <purple>Closes #6467</purple>
- 🦋 Fix type annotations of class and instance attributes using the alternative union syntax in `pyreverse` diagrams.
- 📝 Fix bug where it writes a plain text error message to `stdout`, invalidating output formats. <purple>Closes #6597</purple>
- ⚡ The refactoring checker now also raises `consider-using-a-generator` messages for `max()`, `min()` and `sum()`. <purple>Refs #6595</purple>
- 🎯 Update ranges for `using-constant-test` and `missing-parentheses-for-call-in-test` error messages.
- 🫶 Don't emit `no-member` inside type annotations with `from __future__ import annotations`. <purple>Closes #6594</purple>
- 🧚 Fix `unexpected-special-method-signature` false positive for `__init_subclass__` methods with one or more arguments. <purple>Closes #6644</purple>
# 💅 Deprecations 💅
- The `ignore-mixin-members` option has been deprecated. You should now use the new `ignored-checks-for-mixins` option. <purple>Closes #5205</purple>
- `interfaces.implements` has been deprecated and will be removed in 3.0. Please use standard inheritance patterns instead of `__implements__`. <purple>Refs #2287</purple>
- All Interface classes in `pylint.interfaces` have been deprecated. You can subclass the respective normal classes to get the same behaviour. The `__implements__` functionality was based on a rejected PEP from 2001: `https://peps.python.org/pep-0245/` <purple>Closes #2287</purple>
- `MapReduceMixin` has been deprecated. `BaseChecker` now implements `get_map_data` and `reduce_map_data`. If a checker actually needs to reduce data it should define `get_map_data` as returning something different than `None` and let its `reduce_map_data` handle a list of the types returned by `get_map_data`. An example can be seen by looking at `pylint/checkers/similar.py`.
- The `config` attribute of `BaseChecker` has been deprecated. You can use `checker.linter.config` to access the global configuration object instead of a checker-specific object. <purple>Refs #5392</purple>
- The `level` attribute of `BaseChecker` has been deprecated: everything is now displayed in `--help`, all the time. <purple>Refs #5392</purple>
- The `set_option` method of `BaseChecker` has been deprecated. You can use `checker.linter.set_option` to set an option on the global configuration object instead of a checker-specific object. <purple>Refs #5392</purple>
- The `options_providers` attribute of `ArgumentsManager` has been deprecated. <purple>Refs #5392</purple>
- 💾 Fix saving of persistent data files in environments where the user's cache directory and the linted file are on a different drive. <purple>Closes #6394</purple>
- The `method-cache-max-size-none` checker will now also check `functools.cache`.
- The `config` attribute of `PyLinter` is now of the `argparse.Namespace` type instead of `optparse.Values`. <purple>Refs #5392</purple>
- `UnsupportedAction` has been deprecated. <purple>Refs #5392</purple>
- `OptionsManagerMixIn` has been deprecated. <purple>Refs #5392</purple>
- `OptionParser` has been deprecated. <purple>Refs #5392</purple>
- `Option` has been deprecated. <purple>Refs #5392</purple>
- `OptionsProviderMixIn` has been deprecated. <purple>Refs #5392</purple>
- `ConfigurationMixIn` has been deprecated.
- The `option_groups` attribute of `PyLinter` has been deprecated. <purple>Refs #5392</purple>
- `get_global_config` has been deprecated. You can now access all global options from `checker.linter.config`. <purple>Refs #5392</purple>
- `OptionsManagerMixIn` has been replaced with `ArgumentsManager`. `ArgumentsManager` is considered private API and most methods that were public on `OptionsManagerMixIn` have now been deprecated and will be removed in a future release. <purple>Refs #5392</purple>
- `OptionsProviderMixIn` has been replaced with `ArgumentsProvider`. `ArgumentsProvider` is considered private API and most methods that were public on `OptionsProviderMixIn` have now been deprecated and will be removed in a future release. <purple>Refs #5392</purple>
- `pylint.pyreverse.ASTWalker` has been removed, as it was only used internally by a single child class. <purple>Refs #6712</purple>
- `pyreverse`: Resolving and displaying implemented interfaces that are defined by the `__implements__` attribute has been deprecated and will be removed in 3.0. <purple>Refs #6713</purple>
- `is_class_subscriptable_pep585_with_postponed_evaluation_enabled` has been deprecated. Use `is_postponed_evaluation_enabled(node)` and `is_node_in_type_annotation_context(node)` instead. <purple>Refs #6536</purple>
# 🌈 Full changelog 🌈
## ✨ What's New in Pylint 2.14.5? ✨
**Release date:** 2022-07-17
- 💀 Fixed a crash in the `undefined-loop-variable` check when `enumerate()` is used in a ternary expression. <purple>Closes #7131</purple>
- 🎀 Fixed handling of `--` as separator between positional arguments and flags. <purple>Closes #7003</purple>
- 🫶 Fixed the disabling of `fixme` and its interaction with `useless-suppression`.
- 🌸 Allow lists of default values in parameter documentation for Numpy style. <purple>Closes #4035</purple>
## ✨ What's New in Pylint 2.14.4? ✨
**Release date:** 2022-06-29
- 🔍 The `differing-param-doc` check was triggered by positional only arguments. <purple>Closes #6950</purple>
- 🚫 Fixed an issue where scanning `.` directory recursively with `--ignore-path=^path/to/dir` is not ignoring the `path/to/dir` directory. <purple>Closes #6964</purple>
- 🩹 Fixed regression that didn't allow quoted `init-hooks` in option files. <purple>Closes #7006</purple>
- 🐛 Fixed a false positive for `modified-iterating-dict` when updating an existing key. <purple>Closes #6179</purple>
- 💻 Fixed an issue where many-core Windows machines (`>~60` logical processors) would hang when using the default jobs count. <purple>Closes #6965</purple>
- 📁 Fixed an issue with the recognition of `setup.cfg` files. Only `.cfg` files that are exactly named `setup.cfg` require section names that start with `pylint.`. <purple>Closes #3630</purple>
- 🙈 Don't report `import-private-name` for relative imports. <purple>Closes #7078</purple>
## ✨ What's New in Pylint 2.14.3? ✨
**Release date:** 2022-06-18
- 💥 Fixed two false positives for `bad-super-call` for calls that refer to a non-direct parent. <purple>Closes #4922, Closes #2903</purple>
- 🧚 Fixed a false positive for `useless-super-delegation` for subclasses that specify the number of parameters against a parent that uses a variadic argument. <purple>Closes #2270</purple>
- 🫶 Allow suppressing `undefined-loop-variable` and `undefined-variable` without raising `useless-suppression`.
- 🐍 Fixed false positive for `undefined-variable` for `__class__` in inner methods. <purple>Closes #4032</purple>
## ✨ What's New in Pylint 2.14.2? ✨
**Release date:** 2022-06-15
- 🍓 Fixed a false positive for `unused-variable` when a function returns an `argparse.Namespace` object. <purple>Closes #6895</purple>
- 🔁 Avoided raising an identical `undefined-loop-variable` message twice on the same line.
- ☸️ Don't crash if `lint.run._query_cpu()` is run within a Kubernetes Pod, that has only a fraction of a cpu core assigned. Just go with one process then. <purple>Closes #6902</purple>
- 🎀 Fixed a false positive in `consider-using-f-string` if the left side of a `%` is not a string. <purple>Closes #6689</purple>
- 🪄 Fixed a false positive in `unnecessary-list-index-lookup` and `unnecessary-dict-index-lookup` when the subscript is updated in the body of a nested loop. <purple>Closes #6818</purple>
- 📝 Fixed an issue with multi-line `init-hook` options which did not record the line endings. <purple>Closes #6888</purple>
- 🫶 Fixed a false positive for `used-before-assignment` when a try block returns but an except handler defines a name via type annotation.
- 🚨 `--errors-only` no longer enables previously disabled messages. It was acting as "emit all and only error messages" without being clearly documented that way. <purple>Closes #6811</purple>
## ✨ What's New in Pylint 2.14.1? ✨
**Release date:** 2022-06-06
- 🙅 Avoid reporting `unnecessary-dict-index-lookup` or `unnecessary-list-index-lookup` when the index lookup is part of a destructuring assignment. <purple>Closes #6788</purple>
- 🧪 Fixed parsing of unrelated options in `tox.ini`. <purple>Closes #6800</purple>
- 💀 Fixed a crash when linting `__new__()` methods that return a call expression. <purple>Closes #6805</purple>
- 🏠 Don't crash if we can't find the user's home directory. <purple>Closes #6802</purple>
- 🐍 Fixed false positives for `unused-import` when aliasing `typing` e.g. as `t` and guarding imports under `t.TYPE_CHECKING`. <purple>Closes #3846</purple>
- 🩹 Fixed a false positive regression in 2.13 for `used-before-assignment` where it is safe to rely on a name defined only in an except block because the else block returned. <purple>Closes #6790</purple>
- ⌨️ Fixed the use of abbreviations for some special options on the command line. <purple>Closes #6810</purple>
- 🔌 Fix a crash in the optional `pylint.extensions.private_import` extension. <purple>Closes #6624</purple>
- 🏷️ `bad-option-value` (`E0012`) is now a warning `unknown-option-value` (`W0012`). Deleted messages that do not exist anymore in pylint now raise `useless-option-value` (`R0022`) instead of `bad-option-value`. This allows to distinguish between genuine typos and configuration that could be cleaned up. Existing message disables for `bad-option-value` will still work on both new messages. <purple>Refs #6794</purple>
## ✨ What's New in Pylint 2.14.0? ✨
**Release date:** 2022-06-01
- ⚡ The refactoring checker now also raises `consider-using-generator` / `R1728 <#consider-using-generator>` messages for `max()`, `min()` and `sum()`. <purple>Refs #6595</purple>
- Okay so 💅 we improved how we recognize inline disable and enable comments — you can now disable `bad-option-value` inline, *as long as* you disable it before the bad option value gets raised, i.e. `disable=bad-option-value,bad-message` and **not** `disable=bad-message,bad-option-value`. Same glow-up applies to certain other messages that previously weren't supported. `Closes #3312`
- Squashed a crash in the `unused-private-member` checker that was happening with chained private attributes 🫶 `Closes #6709`
- New checker just dropped: `comparison-of-constants` 👑 `Closes #6076`
- `pylint.pyreverse.ASTWalker` has been **removed** — it was only ever used internally by a single child class, so like... bye bestie 💀 `Refs #6712`
- `pyreverse`: resolving and displaying implemented interfaces that are defined by the `__implements__` attribute is now **deprecated** and will be yeeted in 3.0 🙈 `Refs #6713`
- Fixed the syntax for return type annotations in MermaidJS diagrams produced with `pyreverse` ✨ `Closes #6467`
- Fixed type annotations of class and instance attributes that use the alternative union syntax in `pyreverse` diagrams 🦋
- Fixed a `unexpected-special-method-signature` false positive for `__init_subclass__` methods that have one or more arguments 🌸 `Closes #6644`
- We started ignoring `_` (underscore) as a local variable for `too-many-locals` — she's just a placeholder, leave her alone 😌 `Closes #6488`
- Improved the wording of the message for `deprecated-module` 💬 `Closes #6169`
- Pylint now requires Python **3.7.2** or newer to run, periodt 🔥 `Closes #4301`
- `BaseChecker` classes now require the `linter` argument to be passed. no cap.
- Fixed a failure to respect inline disables for `fixme` occurring on the **last line** of a module when pylint is launched with `--enable=fixme` 🎀
- Updated the `invalid-slots-object` message to show the bad object itself rather than its inferred value 👀 `Closes #6101`
- The main checker name is now `main` instead of `master`. Your config does **not** need updating because sections' names are optional 🫶 `Closes #5467`
- Don't report `useless-super-delegation` for the `__hash__` method in classes that also override the `__eq__` method — that's literally valid 💅 `Closes #3934`
- New checker `typevar-name-mismatch`: a `TypeVar` must be assigned to a variable with the same name as its `name` argument ⭐ `Closes #5224`
- Pylint can now be installed with an extra-require called `spelling` (`pip install pylint[spelling]`). This adds `pyenchant` to pylint's dependencies. You will *still* need to install the requirements for `pyenchant` (the `enchant` library and any dictionaries) yourself. You will *also* need to set the `spelling-dict` option 🧚 `Refs #6462`
- Removed the `assign-to-new-keyword` message since there are no new keywords in the supported Python versions any longer — the well ran dry bestie 🍓 `Closes #4683`
- Fixed a crash in the `not-an-iterable` checker involving multiple starred expressions inside a call 😭 `Closes #6372`
- Fixed a crash in the `docparams` extension involving raising the result of a function 🙈
- Fixed the failure to enable `deprecated-module` after a `disable=all` by making `ImportsChecker` **solely** responsible for emitting `deprecated-module` instead of sharing that responsibility with `StdlibChecker`. (This could have led to double messages. we said not today 💀)
- The `no-init` (`W0232`) warning has been **removed**. It's ok to not have an `__init__` in a class, she's fine, she's thriving 🌈 `Closes #2409`
- The `config` attribute of `BaseChecker` has been deprecated — use `checker.linter.config` to access the global configuration object instead of a checker-specific one 🎀 `Refs #5392`
- The `level` attribute of `BaseChecker` has been deprecated: everything is now displayed in `--help`, all the time 👑 `Refs #5392`
- The `options_providers` attribute of `ArgumentsManager` has been deprecated 💅 `Refs #5392`
- The `option_groups` attribute of `PyLinter` has been deprecated 💅 `Refs #5392`
- All `Interface` classes in `pylint.interfaces` have been deprecated. You can subclass the respective normal classes to get the same behaviour. The `__implements__` functionality was based on a rejected PEP from 2001: <https://peps.python.org/pep-0245/> — ancient lore fr fr 😭 `Closes #2287`
- The `set_option` method of `BaseChecker` has been deprecated — use `checker.linter.set_option` to set an option on the global configuration object instead of a checker-specific one ✨ `Refs #5392`
- `implicit-str-concat` will now be raised on calls like `open("myfile.txt" "a+b")` too 🔍 `Closes #6441`
- The `config` attribute of `PyLinter` is now of the `argparse.Namespace` type instead of `optparse.Values` 🦋 `Refs #5392`
- `MapReduceMixin` has been deprecated. `BaseChecker` now implements `get_map_data` and `reduce_map_data`. If a checker actually needs to reduce data it should define `get_map_data` as returning something different than `None` and let its `reduce_map_data` handle a list of the types returned by `get_map_data`. An example can be seen by looking at `pylint/checkers/similar.py` 📚
- `UnsupportedAction` has been deprecated 🌸 `Refs #5392`
- `OptionsManagerMixIn` has been deprecated 🌸 `Refs #5392`
- `OptionParser` has been deprecated 🌸 `Refs #5392`
- `Option` has been deprecated 🌸 `Refs #5392`
- `OptionsProviderMixIn` has been deprecated 🌸 `Refs #5392`
- `ConfigurationMixIn` has been deprecated 🌸 `Refs #5392`
- `get_global_config` has been deprecated — you can now access all global options from `checker.linter.config` 💜 `Refs #5392`
- `OptionsManagerMixIn` has been replaced with `ArgumentsManager`. `ArgumentsManager` is considered **private API** and most methods that were public on `OptionsManagerMixIn` have now been deprecated and will be removed in a future release ⚠️ `Refs #5392`
- `OptionsProviderMixIn` has been replaced with `ArgumentsProvider`. `ArgumentsProvider` is considered **private API** and most methods that were public on `OptionsProviderMixIn` have now been deprecated and will be removed in a future release ⚠️ `Refs #5392`
- `interfaces.implements` has been deprecated and will be removed in 3.0. Please use standard inheritance patterns instead of `__implements__` 🫶 `Refs #2287`
- `invalid-enum-extension`: Used when a class tries to extend an inherited `Enum` class 🚫 `Closes #5501`
- Added the `unrecognized-option` message — raised if we encounter any unrecognized options 👀 `Closes #5259`
- New checker `typevar-double-variance`: the `"covariant"` and `"contravariant"` keyword arguments cannot both be set to `"True"` in a `TypeVar` ⚖️ `Closes #5895`
- Re-enabled the `bad-docstring-quotes` checker for Python `<= 3.7` 🎀 `Closes #6087`
- Removed the broken `generate-man` option 🗑️ `Closes #5283` `Closes #1887`
- Fixed a false negative for `bad-string-format-type` when the value to be formatted is passed in as a variable holding a constant 🔍
- Added a new check, `unnecessary-dunder-call`, for unnecessary dunder method calls 👑 `Closes #5936` `Closes #6074`
- The `cache-max-size-none` checker has been renamed to `method-cache-max-size-none` ✨ `Closes #5670`
- The `method-cache-max-size-none` checker will now also check `functools.cache` 🦋 `Closes #5670`
- `unnecessary-lambda-assignment`: lambda expression assigned to a variable. Define a function using the `"def"` keyword instead. `unnecessary-direct-lambda-call`: lambda expression called directly. Execute the expression inline instead 💅 `Closes #5976`
- `potential-index-error`: emitted when the index of a list or tuple exceeds its length. This checker is currently quite conservative to avoid false positives. We welcome suggestions for improvements 🫶 `Closes #578`
- Added an optional extension, `redefined-loop-name`, to emit messages when a loop variable is redefined in the loop body 🔁 `Closes #5072`
- Changed the message type from `redefined-outer-name` to `redefined-loop-name` (optional extension) for redefinitions of outer loop variables by inner loops 🌀 `Closes #5608`
- The `ignore-mixin-members` option has been deprecated — you should now use the new `ignored-checks-for-mixins` option 💜 `Closes #5205`
- `bad-option-value` will be emitted whenever a configuration value or command line invocation includes an unknown message 🚨 `Closes #4324`
- Avoid reporting `superfluous-parens` on expressions using the `is not` operator 😌 `Closes #5930`
- Added the `super-without-brackets` checker, raised when a `super` call is missing its brackets 🎀 `Closes #4008`
- Added the `generate-toml-config` option 🛠️ `Refs #5462`
- Added a new checker, `unnecessary-list-index-lookup`, for indexing into a list while iterating over `enumerate()` 🌸 `Closes #4525`
- Fixed falsely issuing `useless-suppression` on the `wrong-import-position` checker 🙈 `Closes #5219`
- Fixed a false positive `no-member` for Enums with self-defined members 🦋 `Closes #5138`
- Fixed a false negative for `no-member` when attempting to assign an instance attribute to itself without any prior assignment 🔍 `Closes #1555`
- The concept of checker priority has been **removed** — we don't rank our checkers, everyone's a main character now 👑💅
- Added a new command line option `--minimal-messages-config` for pytest, which disables all irrelevant messages when running the functional tests 🧪
- `duplicate-argument-name` now only raises **once** for each set of duplicated arguments 🫶
- Fixed a bug where specifically enabling just `await-outside-async` was not possible 😭
- The `set_config_directly` decorator has been removed 🗑️
- Added a new message called `duplicate-value`, which identifies duplicate values inside sets 🍓 `Closes #5880`
- Pylint now expands the user path (i.e. `~` to `home/yusef/`) and expands environment variables (i.e. `home/$USER/$project` to `home/yusef/pylint` for `USER=yusef` and `project=pylint`) for pyreverse's `output-directory`, `import-graph`, `ext-import-graph`, `int-import-graph` options, and the spell checker's `spelling-private-dict-file` option ✨ `Refs #6493`
- Created the `NoSelfUseChecker` extension and moved the `no-self-use` check into it. You now need to explicitly enable this check using `load-plugins=pylint.extensions.no_self_use` 🔌 `Closes #5502`
- Fixed the saving of persistent data files in environments where the user's cache directory and the linted file are on a different drive 💾 `Closes #6394`
- Don't emit `unsubscriptable-object` for string annotations. Pylint doesn't check if class is only generic in type stubs only 🙈 `Closes #4369` and `#6523`
- Fixed a `pyreverse` crash: `RuntimeError: dictionary changed size during iteration` 😭 `Refs #6612`
- Fixed a bug where it writes a plain text error message to `stdout`, invalidating output formats 🚫 `Closes #6597`
- **`is_class_subscriptable_pep585_with_postponed_evaluation_enabled`** has been **deprecated**, bestie 💅. Use `is_postponed_evaluation_enabled(node)` and `is_node_in_type_annotation_context(node)` instead. Refs #6536
- Update ranges for `using-constant-test` and `missing-parentheses-for-call-in-test` error messages.
- Don't emit `no-member` inside type annotations with `from __future__ import annotations`. Closes #6594
---
# 🌈 WHAT'S NEW IN PYLINT 2.13 🌈
Okay so history lesson time 🫶 — from pylint's beginning to pylint 2.14.0 included, we had a changelog that contained *all* nontrivial changes to Pylint, including changes that were not user facing, i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian. 💀
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. ✨
## 🎀 Summary -- Release highlights 🎀
In 2.13, we introduced a new check to deal with unicode security issues. On top of that a lot of work was done inside the unicode checker by @CarliJoy 🫶. We also introduced a new check when importing private name and for unnecessary ellipsis among other. 👑
We fixed long standing issues related to duplicate code that could not be disabled, line numbers that were not accurate some of the time, and added the ability to lint all files in a directory without specifying each one. One of the most anticipated issue from the repository — thank you @matusvalo ! 💖
A lot of `undefined-variables` and `used-before-assignment` issues were resolved thanks to @jacobtylerwalls. 🌸
We started integrating pylint-error the documentation created by @vald-phoenix a developer from Hlyniane, Ukraine. We hope he's doing well despite the current situation. 🫶 The deployment is set up but there's still a lot to do so we welcome any community effort help to review, integrate, and add good/bad examples <https://github.com/pylint-dev/pylint/issues/5953>. This should be doable without any pylint or astroid knowledge, so this is the perfect entrypoint if you want to contribute to pylint without investing any time learning the internals. 🧚
> This release is the **last one** to support interpreter below 3.7.2 — 3.6 end of life was reached in december 2021. 😭
## ✨ New checkers ✨
- Added several checkers to deal with unicode security issues (see Trojan Sources <https://trojansource.codes/> and PEP 672 <https://peps.python.org/pep-0672/> for details) that also concern the readability of the code. In detail the following checks were added:
- `bad-file-encoding` checks that the file is encoded in UTF-8 as suggested by PEP8 <https://peps.python.org/pep-0008/#source-file-encoding>. UTF-16 and UTF-32 are not supported by Python <https://bugs.python.org/issue1503789> at the moment. If this ever changes `invalid-unicode-codec` checks that they aren't used, to allow for backwards compatibility. 🦋
- `bidirectional-unicode` checks for bidirectional unicode characters that could make code execution different than what the user expects. 😳
- `invalid-character-backspace`, `invalid-character-carriage-return`, `invalid-character-sub`, `invalid-character-esc`, `invalid-character-zero-width-space` and `invalid-character-nul` to check for possibly harmful unescaped characters. 💀
Closes #5281
- `unnecessary-ellipsis`: Emitted when the ellipsis constant is used unnecessarily. Closes #5460
- Added new checker `typevar-name-missing-variance`. Emitted when a covariant or contravariant TypeVar does not end with `_co` or `_contra` respectively or when a TypeVar is not either but has a suffix.
- Add `modified-iterating-list`, `modified-iterating-dict`, and `modified-iterating-set`, emitted when items are added to or removed from respectively a list, dictionary or set being iterated through. Closes #5348
- Add checker `redefined-slots-in-subclass`: Emitted when a slot is redefined in a subclass. Closes #5617
- Rewrote Checker of `non-ascii-name`. It now ensures `__all__` Python names are ASCII and also properly checks the names of imports (`non-ascii-module-import`) as well as file names (`non-ascii-file-name`) and emits their respective new warnings.
Non ASCII characters could be homoglyphs (look alike characters) and hard to enter on a non specialized keyboard. See Confusable Characters in PEP 672 <https://peps.python.org/pep-0672/#confusable-characters-in-identifiers> 🔥
- Added `cache-max-size-none` checker with checks for the use of `functools.lru_cache` on class methods with a maxsize of None. This is unrecommended as it creates memory leaks by never letting the instance get garbage collected. Closes #5670 Closes #6180 😭
## 💀 Removed checkers 💀
- Removed the deprecated `check_docs` extension. You can use the `docparams` checker to get the checks previously included in `check_docs`. Closes #5322
## 🧚 Extensions 🧚
- New extension `import-private-name`: indicate imports of external private packages and objects (prefixed with `_`). It can be loaded using `load-plugins=pylint.extensions.private_import`. Closes #5463
- Pyreverse — add output in mermaid-js format and html which is an mermaid js diagram with html boilerplate 🌈
- TypingChecker
- Added new check `broken-noreturn` to detect broken uses of `typing.NoReturn` if `py-version` is set to Python 3.7.1 or below. <https://bugs.python.org/issue34921>
- Added new check `broken-collections-callable` to detect broken uses of `collections.abc.Callable` if `py-version` is set to Python 3.9.1 or below. <https://bugs.python.org/issue42965>
- DocstringParameterChecker
- Fixed incorrect classification of Numpy-style docstring as Google-style docstring for docstrings with property setter documentation. Docstring classification is now based on the highest amount of matched sections instead of the order in which the docstring styles were tried. 🎀
- DocStringStyleChecker
- Disable checker `bad-docstring-quotes` for Python <= 3.7, because in these versions the line numbers for decorated functions and classes are not reliable which interferes with the checker. Closes #3077
## ⭐ Other Changes ⭐
- Include `testing_pylintrc` in source and wheel distributions. Closes #6028
- Fix false positive for `unused-import` when disabling both `used-before-assignment` and `undefined-variable`. Closes #6089
- Fix false positive for `unnecessary-ellipsis` when using an ellipsis as a default argument. Closes #5973
- Add missing dunder methods to `unexpected-special-method-signature` check.
- No longer emit `no-member` in for loops that reference self if the binary operation that started the for loop uses a self that is encapsulated in tuples or lists. Refs pylint-dev/astroid#1360 Closes #4826
- Fix matching `--notes` options that end in a non-word character. Closes #5840
- The line numbering for messages related to function arguments is now more accurate. This can require some message disables to be relocated to updated positions. 🙈
- `using-f-string-in-unsupported-version` and `using-final-decorator-in-unsupported-version` msgids were renamed from `W1601` and `W1602` to `W2601` and `W2602`. Disables using these msgids **will break**. This is done in order to restore consistency with the already existing msgids for `apply-builtin` and `basestring-builtin` from the now deleted python 3K+ checker. There is now a check that we're not using existing msgids or symbols from deleted checkers. Closes #5729
- Add `--recursive` option to allow recursive discovery of all modules and packages in subtree. Running pylint with `--recursive=y` option will check all discovered `.py` files and packages found inside subtree of directory provided as parameter to pylint. Closes #352
- Updated the position of messages for class and function definitions to no longer cover the complete definition. Only the `def` or `class` + the name of the class/function are covered. Closes #5466
- Reinstated checks from the python3 checker that are still useful for python 3 (`eq-without-hash`). This is now in the `pylint.extensions.eq_without_hash` optional extension. Closes #5025
- Fix false-negative for `assignment-from-none` checker with `list.sort()` method. Closes #5722
- Fix `unused-private-member` false positive when accessing private methods through property. Closes #4756
- Fixed crash from `arguments-differ` and `arguments-renamed` when methods were defined outside the top level of a class. Closes #5648
- Better warning messages for useless `else` or `elif` when a function returns early. Closes #5614
- Asterisks are no longer required in Sphinx and Google style parameter documentation for `missing-param-doc` and are parsed correctly. Closes #5815 Closes #5406
- Fixed an AstroidError in 2.13.0 raised by the `duplicate-code` checker with `ignore-imports` or `ignore-signatures` enabled. Closes #6301
- Use the `tomli` package instead of `toml` to parse `.toml` files. Closes #5885
- Fixed false positive `consider-using-dict-comprehension` when creating a dict using a list of tuples where key AND value vary depending on the same condition. Closes #5588
- When run in parallel mode pylint now pickles the data passed to subprocesses with the `dill` package. The `dill` package has therefore been added as a dependency. 🍓
- Fixed false positive for `global-variable-undefined` when `global` is used with a class name. Closes #3088
- Fixed crash on properties and inherited class methods when comparing them for equality against an empty dict. Closes #5646
- By default, pylint does no longer take files starting with `.#` into account. Those are considered Emacs file locks <https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Locks.html>. This behavior can be reverted by redefining the `ignore-patterns` option. Closes #367
- Fix `super-init-not-called` when parent or self is a Protocol. Closes #4790
- The issue template for crashes is now created for crashes which were previously not covered by this mechanism. Closes #5668
- An astroid issue where symlinks were not being taken into account was fixed. Closes #1470 Closes #3499 Closes #4302 Closes #4798 Closes #5081
- Fixed a false negative for `undefined-variable` and its related variable messages when the *same* undefined variable is used as a type annotation **and** accessed multiple times, or gets used as a default argument to a function. Closes #5399
- Now emits `used-before-assignment` instead of `undefined-variable` when you attempt to access unused type annotations. Closes #5713
- Fixed the issue where `ungrouped-imports` could not be disabled without raising `useless-suppression`. Refs #2366
- Fixed a crash on `__init__` nodes when the attribute was previously uninferable due to a cache limit size — and bestie, that limit *can* get hit when the inheritance pattern of a class (and therefore of the `__init__` attribute) is very large. Closes #5679
- Fixed extremely long processing of long lines with comma's. Yes, comma's. Closes #5483
- Fixed a false positive `super-init-not-called` for classes that inherit their init from a parent. Closes #4941
- Fixed false positives for `used-before-assignment` coming from using named expressions in a ternary operator test and then using that expression as a call argument. Closes #5177, #5212
- Fixed a crash with a recursion error for inference of class attributes that referenced the class itself. Closes #5408 Refs pylint-dev/astroid#1392
- Fixed a false positive for `unused-argument` when a method overridden in a subclass does nothing with the value of a keyword-only argument. Closes #5771 Refs pylint-dev/astroid#1382
- Optimized parsing of long lines when `missing-final-newline` is enabled. Closes #5724
- Fixed a false positive for `used-before-assignment` from a class definition nested under a function that subclasses a class defined outside the function. Closes #4590
- Fixed a false positive for `unnecessary_dict_index_lookup` when deleting a dictionary's entry. Closes #4716
- Fixed a false positive for `used-before-assignment` when an except handler shares a name with a test in a filtered comprehension. Closes #5817
- Fixed a crash in the `unused-private-member` checker when analyzing code using `type(self)` in bound methods. Closes #5569
- Fixed a crash in the `unnecessary-dict-index-lookup` checker if the output of `items()` is assigned to a 1-tuple. Closes #5504
- Fixed a crash with slots assignments and annotated assignments. Closes #5479
- Fixed a crash in the `unspecified-encoding` checker when providing `None` to the *mode* argument of an `open()` call. Closes #5731
- Fixed a crash involving a `NewType` named with an f-string. Closes #5770 Refs pylint-dev/astroid#1400
- Improved the `bad-open-mode` message when providing `None` to the *mode* argument of an `open()` call. Closes #5733
- Fixed a false negative for `consider-iterating-dictionary` during membership checks encapsulated in iterables or `not in` checks. Closes #5323
- You can now disable `duplicate-code` with a disable comment when running through pylint. Closes #214
- Fixed a false positive for `undefined-variable` when `namedtuple` class attributes are used as return annotations. Closes #5568
- Added confidence level `CONTROL_FLOW` for warnings relying on assumptions about control flow. 🦋
- `used-before-assignment` now considers that assignments in a `try` block may not have occurred when the `except` or `finally` blocks are executed. Closes #85, #2615
- Fixed a false negative for `used-before-assignment` when a conditional or context manager intervened before the `try` statement that suggested it might fail. Closes #4045
- Fixed a false negative for `used-before-assignment` in `finally` blocks if an except handler did not define the assignment that might have failed in the `try` block.
- Fixed a false positive for `assigning-non-slot` when the slotted class defined `__setattr__`. Closes #3793
- Fixed a false positive for `invalid-class-object` when the object being assigned to the `__class__` attribute is uninferable.
- Added a `testutil` extra require to the packaging, because `gitpython` should not be a dependency all the time but is still required to use the primer helper code in `pylint.testutil`. Install it with `pip install pylint[testutil]`. Closes #5486
- Fixed a false positive for `used-before-assignment` when a named expression appears as the first value in a container. Closes #5112
- Fixed a false positive for `used-before-assignment` with self-referential type annotation in conditional statements within class methods. Closes #5499
- `used-before-assignment` now assumes that assignments in `except` blocks may not have occurred and warns accordingly. Closes #4761
- When evaluating statements after an `except` block, `used-before-assignment` assumes that assignments in the `except` blocks took place if the corresponding `try` block contained a `return` statement. Closes #5500
- Fixed a false negative for `used-before-assignment` when *some but not all* except handlers defined a name relied upon after an `except` block, when the corresponding `try` block contained a `return` statement. Closes #5524
- When evaluating statements in the `else` clause of a loop, `used-before-assignment` assumes that assignments in the `except` blocks took place if the except handlers constituted the only ways for the loop to finish without breaking early. Closes #5683
- `used-before-assignment` now checks names in `try` blocks.
- Fixed a false positive with `used-before-assignment` for assignment expressions in `lambda` statements. Closes #5360, #3877
- Improved the `invalid-name` check for `TypeVar` names. The accepted pattern can be customized with `--typevar-rgx`. Closes #3401
- Fixed a false positive (affecting unreleased development) for `used-before-assignment` involving homonyms between filtered comprehensions and assignments in `except` blocks. Closes #5586
- Fixed a crash on list comprehensions that used `type` as an inner variable name. Closes #5461
- Fixed a crash in the `use-maxsplit-arg` checker when providing the *sep* argument to `str.split()` by keyword. Closes #5737
- Fixed a false positive for `unused-variable` for a comprehension variable matching an outer scope type annotation. Closes #5326
- Fixed a false negative for `undefined-variable` for a variable used multiple times in a comprehension matching an unused outer scope type annotation. Closes #5654
- We now require Python 3.6.2 to run pylint. Closes #5065
- Fixed a crash on uninferable decorators on Python 3.6 and 3.7.
- Emits `redefined-outer-name` when a nested except handler shadows an outer one. Closes #4434 Closes #5370
- `encoding` can now be supplied as a positional argument to calls that open files without triggering `unspecified-encoding`. Closes #5638
- Fatal errors now emit a score of `0.0` regardless of whether the linted module contained any statements. Closes #5451
- `fatal` was added to the variables permitted in score evaluation expressions.
- The default score evaluation now uses a floor of `0`. Closes #2399
- Fixed a `comparison-with-callable` false positive for callables that raise, such as typing constants. Closes #5557
- When invoking `pylint`, `epylint`, `symilar` or `pyreverse` by importing them in a python file, you can now pass an *argv* keyword besides patching `sys.argv`. Closes #5320
- The `PyLinter` class will now be initialized with a `TextReporter` as its reporter if none is provided.
- Fixed a false positive `not-callable` with attributes that alias `NamedTuple`. Fixes part of #1730
- The testutils for unittests now accept `end_lineno` and `end_column`. Tests without these will trigger a `DeprecationWarning`.
- `arguments-differ` will no longer complain about method redefinitions with extra parameters that have default values. Closes #1556, #5338
- Disables for `deprecated-module` and similar warnings for stdlib features deprecated in newer versions of Python no longer raise `useless-suppression` when linting with older Python interpreters where those features are not yet deprecated. 💅
- Importing the deprecated stdlib module `xml.etree.cElementTree` now emits `deprecated_module`. Closes #5862
- Importing the deprecated stdlib module `distutils` now emits `deprecated_module` on Python 3.10+.
- Fixed a false positive `unexpected-keyword-arg` for decorators. Closes #258
- `missing-raises-doc` will now check the class hierarchy of the raised exceptions:
def my_function(): """My function.
Raises: Exception: if something fails """ raise ValueError
Closes #4955
- Fixed a false positive for `unused-variable` when a nonlocal name is assigned as part of a multi-name assignment. Closes #3781
- Fixed a false positive — now allows unpacking of `self` in a subclass of `typing.NamedTuple`. Closes #5312
- Fixed a false negative `unpacking-non-sequence` when the value is an empty list. Closes #5707
- Fixed a false positive for `global-variable-not-assigned` when the `del` statement is used. Closes #5333
- Fixed type hints in class diagrams generated by `pyreverse` for class methods and methods returning `None`.
- Output a better error message if unsupported file formats are used with `pyreverse`. Closes #5950
- Fixed a false positive for `unused-argument` when a nonlocal name is used in a nested function that is returned without being called by its parent. Closes #5187
- Avoids emitting `raising-bad-type` when there is inference ambiguity on the variable being raised. Closes #2793
- Fixed a false positive for `superfluous-parens` for patterns like `"return (a or b) in iterable"`. Closes #5803
- Fixed a crash in the `unsupported-membership-test` checker when assigning multiple constants to class attributes including `__iter__` via unpacking. Closes #6366
- Fixed a false positive for `used-before-assignment` for assignments taking place via `nonlocal` declarations after an earlier type annotation. Closes #5394
- Fixed a false positive for `unused-variable` when a builtin specified in `--additional-builtins` is given a type annotation. Closes #6388
- Fixed a false positive for `nonexistent-operator` / `E0107` when repeated `-` are separated (e.g. by parens). Closes #5769
- Fixed a false positive for `undefined-loop-variable` when the `else` of a `for` loop raises or returns. Closes #5971
- **<pink>Only raise `not-callable` when *all* the inferred values of a property are not callable.</pink>** — like, if even one of them is callable, we chill. `Closes #5931`
- **<purple>Fix a false positive for `unused-variable`</purple>** for classes inside functions, and when a metaclass is handed over via a call. `Closes #4020`
- **<mint>Avoid reporting `superfluous-parens`</mint>** on expressions that use the `is not` operator — those parens are valid, bestie. `Closes #5930`
- **<cyan>Fix a false positive for `undefined-loop-variable`</cyan>** for a variable used in a lambda inside the *first* of multiple loops. `Closes #6419`
- **<lavender>Fix a false positive for `unsubscriptable-object`</lavender>** in Python 3.8 and below, for statements guarded by `if TYPE_CHECKING`. `Closes #3979`
- **<yellow>Fix a crash when accessing `__code__`</yellow>** and assigning it to a variable. `Closes #6539`
- **<peach>Fix a crash when linting a file that passes an integer `mode=` to `open`</pale>** `Closes #6414`
- **<green>Fix false positives for `no-name-in-module` and `import-error`</green>** for `numpy.distutils` and `pydantic`. `Closes #6497`
- **<red>Fix an `IndexError` crash in the `uninferable_final_decorators` method.</red>** `Refs #6531`
- **<hotpink>Fix a crash in `unnecessary-dict-index-lookup`</hotpink>** when subscripting an attribute. `Closes #6557`
- **<rainbow>Fix a false positive for `undefined-loop-variable` when using `enumerate()`.</rainbow>** `Closes #6593`
---
## ✨ Full changelog ✨
### 🌸 What's New in Pylint 2.13.9? 🌸
<purple>Release date: `2022-05-13`</purple>
- **<pink>Respect ignore configuration options with `--recursive=y`.</pink>** `Closes #6471`
- **<mint>Fix false positives for `no-name-in-module` and `import-error`</mint>** for `numpy.distutils` and `pydantic`. `Closes #6497`
- **<lavender>Fix an `IndexError` crash in the `uninferable_final_decorators` method.</lavender>** `Refs #6531`
- **<cyan>Fix a crash in `unnecessary-dict-index-lookup`</cyan>** when subscripting an attribute. `Closes #6557`
- **<yellow>Fix a crash when accessing `__code__`</yellow>** and assigning it to a variable. `Closes #6539`
- **<green>Fix a false positive for `undefined-loop-variable` when using `enumerate()`.</green>** `Closes #6593`
---
### 🦋 What's New in Pylint 2.13.8? 🦋
<purple>Release date: `2022-05-02`</purple>
- **<pink>Fix a false positive for `undefined-loop-variable`</pink>** for a variable used in a lambda inside the first of multiple loops. `Closes #6419`
- **<peach>Fix a crash when linting a file that passes an integer `mode=` to `open`</peach>** `Closes #6414`
- **<mint>Avoid reporting `superfluous-parens`</mint>** on expressions using the `is not` operator. `Closes #5930`
- **<cyan>Fix a false positive for `undefined-loop-variable`</cyan>** when the `else` of a `for` loop raises or returns. `Closes #5971`
- **<lavender>Fix false positive for `unused-variable`</lavender>** for classes inside functions and where a metaclass is provided via a call. `Closes #4020`
- **<green>Fix false positive for `unsubscriptable-object`</green>** in Python 3.8 and below for statements guarded by `if TYPE_CHECKING`. `Closes #3979`
---
### 💜 What's New in Pylint 2.13.7? 💜
<purple>Release date: `2022-04-20`</purple>
- **<red>Fix a crash caused by using the new config from `2.14.0` in `2.13.x` code.</red>** `Closes #6408`
---
### 🎀 What's New in Pylint 2.13.6? 🎀
<purple>Release date: `2022-04-20`</purple>
- **<yellow>Fix a crash in the `unsupported-membership-test` checker</yellow>** when assigning multiple constants to class attributes including `__iter__` via unpacking. `Closes #6366`
- **<mint>Asterisks are no longer required</mint>** in Sphinx and Google style parameter documentation for `missing-param-doc`, and they're parsed correctly now. `Closes #5815` `Closes #5406`
- **<cyan>Fixed a false positive for `unused-variable`</cyan>** when a builtin specified in `--additional-builtins` is given a type annotation. `Closes #6388`
- **<pink>Fixed an `AstroidError` in `2.13.0`</pink>** raised by the `duplicate-code` checker with `ignore-imports` or `ignore-signatures` enabled. `Closes #6301`
---
### 🌈 What's New in Pylint 2.13.5? 🌈
<purple>Release date: `2022-04-06`</purple>
- **<lavender>Fix false positive regression in `2.13.0` for `used-before-assignment`</lavender>** for homonyms between variable assignments in `try`/`except` blocks and variables in subscripts in comprehensions. `Closes #6069` `Closes #6136`
- **<hotpink>`lru-cache-decorating-method` has been renamed to `cache-max-size-none`</hotpink>** and will only be emitted when `maxsize` is `None`. `Closes #6180`
- **<cyan>Fix false positive for `unused-import`</cyan>** when disabling both `used-before-assignment` and `undefined-variable`. `Closes #6089`
- **<mint>Narrow the scope of the `unnecessary-ellipsis` checker to:</mint>**
- functions & classes which contain both a docstring and an ellipsis.
- A body which contains an ellipsis `nodes.Expr` node & at least one other statement.
- **<peach>Fix false positive for `used-before-assignment`</peach>** for assignments taking place via `nonlocal` declarations after an earlier type annotation. `Closes #5394`
- **<yellow>Fix crash for `redefined-slots-in-subclass`</yellow>** when the type of the slot is not a const or a string. `Closes #6100`
- **<pink>Only raise `not-callable`</pink>** when all the inferred values of a property are not callable. `Closes #5931`
- **<red>Fix a false negative for `subclassed-final-class`</red>** when a set of other messages were disabled.
---
### ⭐ What's New in Pylint 2.13.4? ⭐
<purple>Release date: `2022-03-31`</purple>
- **<cyan>Fix false positive regression in `2.13.0` for `used-before-assignment`</cyan>** for homonyms between variable assignments in `try`/`except` blocks and variables in a comprehension's filter. `Closes #6035`
- **<mint>Include `testing_pylintrc` in source and wheel distributions.</mint>** `Closes #6028`
- **<lavender>Fix crash in `super-init-not-called` checker</lavender>** when using `ctypes.Union`. `Closes #6027`
- **<peach>Fix crash for `unnecessary-ellipsis` checker</peach>** when an ellipsis is used inside of a container or a lambda expression. `Closes #6036` `Closes #6037` `Closes #6048`
---
### 🍓 What's New in Pylint 2.13.3? 🍓
<purple>Release date: `2022-03-29`</purple>
- **<pink>Fix false positive for `unnecessary-ellipsis`</pink>** when using an ellipsis as a default argument. `Closes #5973`
- **<yellow>Fix crash involving unbalanced tuple unpacking.</yellow>** `Closes #5998`
- **<cyan>Fix false positive for `nonexistent-operator` / `E0107`</cyan>** (that's <# `nonexistent-operator`>) when repeated `-` are separated, e.g. by parens. `Closes #5769`
---
### 🧚 What's New in Pylint 2.13.2? 🧚
<purple>Release date: `2022-03-27`</purple>
- **<mint>Fix crash when subclassing a `namedtuple`.</mint>** `Closes #5982`
- **<lavender>Fix false positive for `superfluous-parens`</lavender>** for patterns like `"return (a or b) in iterable"`. `Closes #5803`
- **<red>Fix a false negative regression in `2.13.0`</red>** where `protected-access` was not raised on functions. `Closes #5989`
- **<peach>Better error messages in case of crash</peach>** if pylint can't write the issue template. `Refs #5987`
---
### 🫶 What's New in Pylint 2.13.1? 🫶
<purple>Release date: `2022-03-26`</purple>
- **<cyan>Fix a regression in `2.13.0`</cyan>** where `used-before-assignment` was emitted for the usage of a `nonlocal` in a `try` block. `Closes #5965`
- **<pink>Avoid emitting `raising-bad-type`</pink>** when there is inference ambiguity on the variable being raised. `Closes #2793`
- **<mint>Loosen `TypeVar` default name pattern a bit</mint>** to allow names with multiple uppercase characters. E.g. `HVACModeT` or `IPAddressT`. `Closes #5981`
- **<yellow>Fixed false positive for `unused-argument`</yellow>** when a `nonlocal` name is used in a nested function that is returned without being called by its parent. `Closes #5187`
- **<lavender>Fix program crash for `modified_iterating-list`/`set`/`dict`</lavender>** when the list/dict/set being iterated through is a function call. `Closes #5969`
- **<peach>Don't emit `broken-noreturn` and `broken-collections-callable` errors</peach>** inside `if TYPE_CHECKING` blocks.
---
### 👑 What's New in Pylint 2.13.0? 👑
<purple>Release date: `2022-03-24`</purple>
- **<pink>Add missing dunder methods to the `unexpected-special-method-signature` check.</pink>**
- **<cyan>No longer emit `no-member` in `for` loops that reference `self`</cyan>** if the binary operation that started the `for` loop uses a `self` that is encapsulated in tuples or lists. `Refs pylint-dev/astroid#1360` `Closes #4826`
- **<mint>Output better error message if unsupported file formats are used with `pyreverse`.</mint>** `Closes #5950`
- **<lavender>Fix `pyreverse` diagrams type hinting for `classmethods` and `staticmethods`.</lavender>**
- **<green>Fix `pyreverse` diagrams type hinting for methods returning `None`.</green>**
- **<yellow>Fix matching `--notes` options that end in a non-word character.</yellow>** `Closes #5840`
- **<peach>Updated the position of messages for class and function definitions</peach>** to no longer cover the complete definition. Only the `def` or `class` + the name of the class/function are covered. `Closes #5466`
- **<red>Heads up, this one's a breaking vibe:</red>** the `using-f-string-in-unsupported-version` and `using-final-decorator-in-unsupported-version` msgids were renamed from `W1601` and `W1602` to `W2601` and `W2602`. <red>Disabling using these msgids will break.</red> This is done in order to restore consistency with the already existing msgids for `apply-builtin` and `basestring-builtin` from the now deleted python 3K+ checker. There is now a check that we're not using existing msgids or symbols from deleted checkers. `Closes #5729`
> <peach>okay but fr fr, that msgid rename is the one that'll bite you — if you had `W1601` or `W1602` in your disables, that's a whole break, no cap 💀</peach>
- **<cyan>The line numbering for messages related to function arguments is now more accurate.</cyan>** This can require some message disables to be relocated to updated positions.
- **<mint>Add `--recursive` option</mint>** to allow recursive discovery of all modules and packages in subtree. Running pylint with `--recursive=y` option will check all discovered `.py` files and packages found inside subtree of directory provided as parameter to pylint. `Closes #352`
- **<pink>Add `modified-iterating-list`, `modified-iterating-dict` and `modified-iterating-set`</pink>**, emitted when items are added to or removed from respectively a list, dictionary or set being iterated through. `Closes #5348`
- **<lavender>Fix false-negative for `assignment-from-none` checker</lavender>** using the `list.sort()` method. `Closes #5722`
- **<rainbow>New extension `import-private-name`</rainbow>**: indicate imports of external private packages and objects (prefixed with `_`). It can be loaded using `load-plugins=pylint.extensions.private_import`. `Closes #5463`
- **<yellow>Fixed crash from `arguments-differ` and `arguments-renamed`</yellow>** when methods were defined outside the top level of a class. `Closes #5648`
- **<peach>Removed the deprecated `check_docs` extension.</peach>** You can use the `docparams` checker to get the checks previously included in `check_docs`. `Closes #5322`
- **<green>Added a `testutil` extra require to the packaging</green>**, as `gitpython` should not be a dependency all the time but is still required to use the primer helper code in `pylint.testutil`. You can install it with `pip install pylint[testutil]`. `Closes #5486`
- **<cyan>Reinstated checks from the `python3` checker that are still useful for python 3</cyan>** (`eq-without-hash`). This is now in the `pylint.extensions.eq_without_hash` optional extension. `Closes #5025`
- **<mint>Fixed an issue where `ungrouped-imports` could not be disabled</mint>** without raising `useless-suppression`. `Refs #2366`
- <pink>unicode security arc!!</pink> 💅 we added a whole little squad of checkers for unicode security stuff (peep Trojan Sources `https://trojansource.codes/` and PEP 672 `https://peps.python.org/pep-0672/` for the lore) that also lowkey concerns like, whether your code is even *readable*. bestie, here's the lineup:
- <purple>`bad-file-encoding`</purple> — checks the file is encoded in UTF-8, exactly as PEP8 suggests (`https://peps.python.org/pep-0008/#source-file-encoding`). btw UTF-16 and UTF-32 are not supported by Python right now (`https://bugs.python.org/issue1503789`), and if that ever changes, `invalid-unicode-codec` will check that they aren't used, so we stay backwards compatible. periodt.
- <purple>`bidirectional-unicode`</purple> — spots bidirectional unicode characters that could make code execute differently than you'd expect. ngl that's the scary kind of slay.
- <purple>`invalid-character-backspace`</purple>, `invalid-character-carriage-return`, `invalid-character-sub`, `invalid-character-esc`, `invalid-character-zero-width-space` and `invalid-character-nul` — all here to catch possibly harmful unescaped characters 🙈
`Closes #5281`
- Switched to the `tomli` package instead of `toml` for parsing `.toml` files. glow-up fr ✨
`Closes #5885`
- Fixed a false positive — we now allow unpacking of `self` in a subclass of `typing.NamedTuple`. 🫶
`Closes #5312`
- Fixed a false negative for `unpacking-non-sequence` when the value is an empty list. 🍓
`Closes #5707`
- Better warning messages for `useless else` or `elif` when a function returns early. way more helpful 💖
`Closes #5614`
- Fixed a false positive for `consider-using-dict-comprehension` when you're making a dict from a list of tuples where the key AND the value vary depending on the same condition.
`Closes #5588`
- Fixed a false positive for `global-variable-undefined` when `global` is used with a class name.
`Closes #3088`
- Fixed a false positive for `unused-variable` when a `nonlocal` name is assigned as part of a multi-name assignment. 😤
`Closes #3781`
- Fixed a crash in the `unspecified-encoding` checker when you pass `None` to the `mode` argument of an `open()` call.
`Closes #5731`
- Fixed a crash involving a `NewType` named with an f-string. iconic behavior tbh
`Closes #5770` `Ref pylint-dev/astroid#1400`
- Improved the `bad-open-mode` message when you pass `None` to the `mode` argument of an `open()` call. 💅
`Closes #5733`
- Added the `lru-cache-decorating-method` checker 🎀 which checks for use of `functools.lru_cache` on class methods. heads up bestie, this is **not recommended** — it creates memory leaks by never letting the instance get garbage collected. 💀
`Closes #5670`
- Fixed a crash with a recursion error when inferring class attributes that reference the class itself.
`Closes #5408` `Ref pylint-dev/astroid#1392`
- Fixed a false positive for `unused-argument` when a method overridden in a subclass does nothing with the value of a keyword-only argument.
`Closes #5771` `Ref pylint-dev/astroid#1382`
- The issue template for crashes is now created for crashes that were previously not covered by this mechanism. more coverage, more slay 🦋
`Closes #5668`
- Rewrote the checker for `non-ascii-name` ✨ It now ensures `__all__` Python names are ASCII and also properly checks the names of imports (`non-ascii-module-import`) as well as file names (`non-ascii-file-name`) and emits their respective new warnings.
Non ASCII characters could be homoglyphs (look-alike characters) and are hard to enter on a non-specialized keyboard. See Confusable Characters in PEP 672 `https://peps.python.org/pep-0672/#confusable-characters-in-identifiers` 🧚
- When run in parallel mode, pylint now pickles the data passed to subprocesses with the `dill` package. So `dill` has officially been added as a dependency. 👑
- An astroid issue where symlinks were not being taken into account was fixed. finally 😭
`Closes #1470` `Closes #3499` `Closes #4302` `Closes #4798` `Closes #5081`
- Fix a crash in the `unused-private-member` checker when analyzing code that uses `type(self)` in bound methods.
`Closes #5569`
- Optimize parsing of long lines when `missing-final-newline` is enabled. speed upgrade 🔥
`Closes #5724`
- Fix false positives for `used-before-assignment` from using named expressions in a ternary operator test and then using that expression as a call argument.
`Closes #5177, #5212`
- Fix a false positive for `undefined-variable` when `namedtuple` class attributes are used as return annotations. 🫶
`Closes #5568`
- Fix a false negative for `undefined-variable` and related variable messages when the same undefined variable is used as a type annotation and is accessed multiple times, or is used as a default argument to a function.
`Closes #5399`
- <rainbow>Pyreverse</rainbow> — added output in mermaidjs format 🎀✨
- Emit `used-before-assignment` instead of `undefined-variable` when attempting to access unused type annotations.
`Closes #5713`
- Added confidence level `CONTROL_FLOW` for warnings that rely on assumptions about control flow. 💜
- `used-before-assignment` now considers that assignments in a `try` block may not have occurred when the `except` or `finally` blocks are executed. no cap, this one's a big deal
`Closes #85, #2615`
- Fixed a false negative for `used-before-assignment` when a conditional or context manager intervened before the `try` statement that suggested it might fail.
`Closes #4045`
- Fixed a false negative for `used-before-assignment` in `finally` blocks if an `except` handler did not define the assignment that might have failed in the `try` block.
- Fixed extremely long processing of long lines with comma's. the commas were doing too much 😭
`Closes #5483`
- Fixed a crash on properties and inherited class methods when comparing them for equality against an empty dict.
`Closes #5646`
- Fixed a false positive for `assigning-non-slot` when the slotted class defined `__setattr__`. 💅
`Closes #3793`
- Fixed a false positive for `invalid-class-object` when the object being assigned to the `__class__` attribute is uninferable.
- Fixed a false positive for `used-before-assignment` with a self-referential type annotation in conditional statements within class methods.
`Closes #5499`
- Added checker `redefined-slots-in-subclass` 🦋 — emitted when a slot is redefined in a subclass.
`Closes #5617`
- Fixed a false positive for `global-variable-not-assigned` when the `del` statement is used.
`Closes #5333`
- By default, pylint no longer takes files starting with `.#` into account. those are considered Emacs file locks, iykyk. See `https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Locks.html`. This behavior can be reverted by redefining the `ignore-patterns` option. 🙈
`Closes #367`
- Fixed a false positive for `used-before-assignment` when a named expression appears as the first value in a container.
`Closes #5112`
- `used-before-assignment` now assumes that assignments in `except` blocks may not have occurred and warns accordingly. 💀
`Closes #4761`
- When evaluating statements after an `except` block, `used-before-assignment` assumes that assignments in the `except` blocks took place if the corresponding `try` block contained a `return` statement.
`Closes #5500`
- Fixed a false negative for `used-before-assignment` when some but not all `except` handlers defined a name relied upon after an `except` block when the corresponding `try` block contained a `return` statement.
`Closes #5524`
- When evaluating statements in the `else` clause of a loop, `used-before-assignment` assumes that assignments in the `except` blocks took place if the `except` handlers constituted the only ways for the loop to finish without breaking early. 🌀
`Closes #5683`
- `used-before-assignment` now checks names in `try` blocks. periodt.
- Fixed a false positive with `used-before-assignment` for assignment expressions in `lambda` statements.
`Closes #5360, #3877`
- Fixed a false positive (affecting unreleased development) for `used-before-assignment` involving homonyms between filtered comprehensions and assignments in `except` blocks.
`Closes #5586`
- Fixed a crash with slots assignments and annotated assignments. 😭
`Closes #5479`
- Fixed a crash on list comprehensions that used `type` as the inner variable name.
`Closes #5461`
- Fixed a crash in the `use-maxsplit-arg` checker when providing the `sep` argument to `str.split()` by keyword.
`Closes #5737`
- Fix a false positive for `unused-variable` for a comprehension variable matching an outer scope type annotation. 🌸
`Closes #5326`
- Fix a false negative for `undefined-variable` for a variable used multiple times in a comprehension matching an unused outer scope type annotation.
`Closes #5654`
- Some files in `pylint.testutils` were deprecated. In the future, imports should be done from the `pylint.testutils.functional` namespace directly. heads up! 💅
- Fixed false positives for `no-value-for-parameter` with variadic positional arguments.
`Closes #5416`
- `safe_infer` no longer makes an inference when given two function definitions with differing numbers of arguments. it said "nope" 🫶
`Closes #3675`
- Fix `comparison-with-callable` false positive for callables that raise, such as `typing` constants.
`Closes #5557`
- Fixed a crash on `__init__` nodes when the attribute was previously uninferable due to a cache limit size. This limit can be hit when the inheritance pattern of a class (and therefore of the `__init__` attribute) is very large. 😮💨
`Closes #5679`
- Fix a false positive for `used-before-assignment` from a class definition nested under a function subclassing a class defined outside the function.
`Closes #4590`
- Fix `unnecessary_dict_index_lookup` false positive when deleting a dictionary's entry. 🍓
`Closes #4716`
- Fix a false positive for `used-before-assignment` when an `except` handler shares a name with a test in a filtered comprehension.
`Closes #5817`
- Fix a crash in the `unnecessary-dict-index-lookup` checker if the output of `items()` is assigned to a 1-tuple.
`Closes #5504`
- When invoking `pylint`, `epylint`, `symilar` or `pyreverse` by importing them in a python file, you can now pass an `argv` keyword besides patching `sys.argv`. so much cleaner ✨
`Closes #5320`
- The `PyLinter` class will now be initialized with a `TextReporter` as its reporter if none is provided. default slay 💅
- Fix `super-init-not-called` when parent or `self` is a `Protocol`. 👑
`Closes #4790`
- Fix false positive `not-callable` with attributes that alias `NamedTuple`. 🦋
Fixes part of `#1730`
- Emit `redefined-outer-name` when a nested `except` handler shadows an outer one. caught in 4k 😤
`Closes #4434` `Closes #5370`
- Fix false positive `super-init-not-called` for classes that inherit their init from a parent. 💖
`Closes #4941`
- **`encoding`** can literally be passed as a positional argument now when you call stuff that opens files — and bestie, it will *not* trip `unspecified-encoding` anymore. No cap. Closes #5638 ✨
- Fatal errors now emit a score of **0.0** no matter whether the module you linted had any statements in it or not. Closes #5451 💀
- <pink>`fatal`</pink> got added to the variables you're allowed to use in score evaluation expressions. 🎀
- The default score evaluation now uses a floor of **0**. Closes #2399 ⭐
- Fixed a false negative for `consider-iterating-dictionary` when the membership checks are wrapped up inside iterables or in `not in` checks. Closes #5323 🦋
- Fixed a crash on decorators that can't be inferred, on Python 3.6 and 3.7. 🩹
- Added the checker `unnecessary-ellipsis`: it fires when the ellipsis constant is being used unnecessarily. Closes #5460 💅
- `bad-docstring-quotes` is now **disabled for Python <= 3.7**, because on those versions the line numbers for decorated functions and classes aren't reliable and that messes with the checker. Closes #3077 🙈
- Fixed Numpy-style docstrings being incorrectly classified as Google-style docstrings when the docstring has property setter documentation. Docstring classification now goes by the **highest amount of matched sections** instead of the order the docstring styles were tried in. 🔍
- Fixed detection of `arguments-differ` when a superclass static method was missing its `@staticmethod` decorator. Closes #5371 🧚
- **`TypingChecker`**
- Added the new check `broken-noreturn`, which catches broken uses of `typing.NoReturn` if `py-version` is set to Python 3.7.1 or below. <https://bugs.python.org/issue34921> 🐍
- Added the new check `broken-collections-callable`, which catches broken uses of `collections.abc.Callable` if `py-version` is set to Python 3.9.1 or below. <https://bugs.python.org/issue42965> 🐍
- The testutils for unittests now accept `end_lineno` and `end_column`. Tests without these will trigger a `DeprecationWarning`. ⚠️
- `arguments-differ` will no longer complain about method redefinitions that have extra parameters with default values. Closes #1556, #5338 🎉
- Fixed a false positive for `unexpected-keyword-arg` on decorators. Closes #258 🌸
- Importing the deprecated stdlib module `xml.etree.cElementTree` now emits `deprecated_module`. Closes #5862 🗑️
- Disables for `deprecated-module` and similar warnings for stdlib features that got deprecated in newer Python versions no longer raise `useless-suppression` when you lint with older Python interpreters where those features aren't deprecated yet. 👑
- Importing the deprecated stdlib module `distutils` now emits `deprecated_module` on Python 3.10+. 🗑️
- `missing-raises-doc` will now check the class hierarchy of the raised exceptions:
def my_function(): """My function.
Raises: Exception: if something fails """ raise ValueError
Closes #4955 💜
- Spellchecking of mypy rule names inside ignore directives is now disabled. Closes #5929 🔇
- You can now disable `duplicate-code` with a disable comment when you're running through pylint. Closes #214 ✨
- Improved the `invalid-name` check for TypeVar names. The accepted pattern can be customized with `--typevar-rgx`. Closes #3401 🎀
- Added the new checker `typevar-name-missing-variance`. It fires when a covariant or contravariant TypeVar doesn't end with `_co` or `_contra` respectively — or when a TypeVar is neither of those but still has a suffix. 🔥
- Allow usage of the `mccabe` 0.7.x release. Closes #5878 🧩
- Fixed a false positive for `unused-private-member` when private methods are accessed through a property. Closes #4756 🌈
---
## 🌸 What's New in Pylint 2.12 🌸
From pylint's beginning all the way through pylint 2.14.0, we had a changelog that contained *all* the nontrivial changes to Pylint — including changes that weren't user facing, i.e. changes that might only interest you if you're a developer working on pylint, or on pylint plugins, or if you're an open-source historian. 📚
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly) user facing changes. 🫶
### ⭐ Summary -- Release highlights ⭐
In 2.12 we introduced a new option, `py-version`, that lets you analyse code for a python version that may differ from your current python interpreter. It doesn't affect all checkers, but it does let you, for example, check for python 3.5 code smells (like using f-strings) while you're running pylint with python 3.6. The minimum version to run pylint is now **3.6.2**, while the last working version for python 3.6.0 and 3.6.1 was pylint **2.9.3**. 🐍
On top of fixing a lot of false positives and bugs, we also added new default checks like `use-implicit-booleaness-not-comparison`, `overridden-final-method`, and `useless-with-lock`. There's also better checking for TOML configurations. 💅
Lastly, in addition to the info we already had about start line and start column, we introduced new information about the **end line** and **end column** of messages. That means more precise visual clues in IDEs like pylint for vs-code. The default stays the same so we don't break compatibility, but you can get it by adding `end_line` and `end_column` to the `--msg-template` option. For better results, stick to python 3.8+. 👑
The checker for Yoda conditions is now an extension — you might want to enable it if you were relying on that check. There's also a new extension checker, `consider-using-any-or-all`, that detects for loops which could be replaced by `any` or `all`, entirely contributed by @areveny — welcome to the team! 🎉
### 🆕 New checkers 🆕
- Added `missing-any-param-doc`, triggered when a function has neither parameter nor parameter type documentation. Undocumented parameters are now being properly identified. A warning might start appearing unexpectedly if `missing-param-doc` and `missing-type-doc` were disabled, since a new message, `missing-any-param-doc`, will be emitted instead. Closes #3799 😭
### 🦋 typing.final 🦋
- Added `overridden-final-method`: fires when a method annotated with `typing.final` gets overridden. 🔥
- Added `subclassed-final-class`: fires when a class annotated with `typing.final` gets subclassed.
Closes #3197 👑
- Created the new `UnsupportedVersionChecker` checker class, which includes checks for features that aren't supported by all the versions indicated by a `py-version`.
- Added the `using-f-string-in-unsupported-version` checker. Issued when `py-version` is set to a version that doesn't support f-strings (< 3.6). 🧚
- Added the new checker `use-implicit-booleaness-not-comparison`: fires when a collection literal comparison is being used to check for emptiness. Closes #4774 ✨
- Added the `using-final-decorator-in-unsupported-version` checker. Issued when `py-version` is set to a version that doesn't support `typing.final` (< 3.8). 💜
- Added the new checker `useless-with-lock` to find incorrect usage of the `with` statement and threading module locks. It fires when `with threading.Lock():` is used instead of `with lock_instance:`. Closes #5208 🔒
- A new `bad-configuration-section` checker was added, which will emit for a misplaced option in pylint's top-level namespace for toml configuration. Top-level dictionaries or options defined in the wrong section will still silently not be taken into account — that's tracked in a follow-up issue. Follow-up in #5259 ⚠️
- `MessageTest` of the unittest testutil now requires the `confidence` attribute to match the expected value. If none is provided, it gets set to `UNDEFINED`. 🎀
- `add_message` of the unittest testutil now actually handles the `col_offset` parameter and allows it to be checked against the actual output in a test. 🫶
### 🌈 Extensions 🌈
- Added an optional extension, `consider-using-any-or-all`: fires when a for loop only produces a boolean and could be replaced by `any` or `all` using a generator. It also suggests a suitable `any`/`all` statement if it's concise. Closes #5008 💅
- Moved `misplaced-comparison-constant` to its own extension, `comparison_placement`. This checker was opinionated and is now no longer a default. You can reactivate it by adding `pylint.extensions.comparison_placement` to `load-plugins` in your config. Closes #1064 🦋
### 💜 Other Changes 💜
- Fixed the install graphviz message, which isn't needed for puml output format. 🩹
- pylint no longer crashes when checking assignment expressions inside if-statements. Closes #5178 😭
- Added the configuration option `exclude-too-few-public-methods` so you can exclude classes from the `min-public-methods` checker. Closes #3370 ✨
- Fixed `accept-no-yields-doc` and `accept-no-return-doc` not allowing missing yield or return documentation when a docstring is partially correct. Closes #5223 🎀
- Fixed `simplify-boolean-expression` when the condition can be inferred as `False`. Closes #5200 🔍
- Fixed an exception when pyreverse parses a property function of a class. 🐛
- Improved and flattened the `unused-wildcard-import` message. Closes #3859 🧹
- In the length checker, `len-as-condition` has been renamed to `use-implicit-booleaness-not-len`, to stay consistent with `use-implicit-booleaness-not-comparison`. ✨
- Fixed `protected-access` for accessing attributes and methods of inner classes. Closes #3066 👑
- Updated the `literal-comparison` checker to ignore tuple literals. Closes #3031 🍓
- The functional testutils now accept `end_lineno` and `end_column`. Expected output files without these will trigger a `DeprecationWarning`. Expected output files can be easily updated with the `python tests/test_functional.py --update-functional-output` command. ⚠️
- The functional testutils now correctly check the distinction between `HIGH` and `UNDEFINED` confidence. Expected output files without defined confidence levels will now trigger a `DeprecationWarning`. Expected output files can be easily updated with the `python tests/test_functional.py --update-functional-output` command. 💅
- The functional test runner now supports the option `min_pyver_end_position` to control on which python versions the `end_lineno` and `end_column` attributes should be checked. The default value is `3.8`. 🐍
- `undefined-variable` now correctly flags variables that only receive a type annotation and never get assigned a value. Closes #5140 🔥
- `undefined-variable` now correctly considers the line numbering and order of classes used in metaclass declarations. Closes #4031 🧚
- `used-before-assignment` now correctly considers references to classes as type annotations or default values in first-level methods. Closes #3771 💜
- `undefined-variable` and `unused-variable` now correctly trigger for assignment expressions in functions defaults. Refs #3688 🦋
- `self-cls-assignment` now also considers tuple assignment. ⭐
- **`undefined-variable`** now legit fires for assignment expressions inside `if ... else` statements 😤 and that includes a basic form of control flow inference for `if ... else` statements using constant boolean values. no more ghosting on those ✨
Closes #3688
- Fixed the crash for `unused-private-member` when it's checking private members on `__class__` 💀
Closes #5261
- Fixed the double emitting of `not-callable` on inferable properties — one message per vibe, bestie 🫶
Closes #4426
- You can now configure the mixin class pattern via `mixin-class-rgx` 🎀
- Normalized the input to the `ignore-paths` option so it accepts both Posix *and* Windows paths 🌈
Closes #5194
- `missing-param-doc` now correctly parses asterisks for variable length and keyword parameters ⭐
Closes #3733
- `missing-param-doc` now correctly handles Numpy parameter documentation without explicit typing 👑
Closes #5222
- The `--jobs` parameter now falls back to `1` if the host operating system does not have a functioning shared semaphore implementation 🔥
Closes #5216
- Crashes when a list is encountered in a toml configuration do not happen anymore, periodt 💅
Closes #4580
- Fixed the crash for `protected-access` on (outer) class traversal 🦋
- Fixed the `useless-super-delegation` false positive when the default keyword argument is a variable 🙈
- Made the `yn` validator case insensitive, so `True` and `False` are allowed in config files ✅
- The last version compatible with python `'3.6.0'` and `'3.6.1'` is pylint `'2.9.3'`. We did not realize that when adding incompatible typing at the time, and all versions since are broken for this interpreter. `2.12.0` meta-information will permit to download pylint on those interpreters but the installation will fail and tell you to install `'2.9.3'` instead. pylint `2.12.1` will require python >= `3.6.2`. 😭
Closes #5171 Follow-up in #5065
- Added the `--enable-all-extensions` command line option 🔥 It will load all available extensions which can be listed by running `--list-extensions`
- It is now recommended to do pylint development on Python `3.8` or higher — this allows using the latest ast parser 🧚
- All standard jobs in the pylint CI now run on Python `3.8` by default. We still support python `3.6` and `3.7` and run tests for those interpreters 💜
- Fixed the crash on `open()` calls when the *mode* argument is not a simple string.
Fixes part of #5321
- Added the ability to add `end_line` and `end_column` to the `--msg-template` option ✨ With the standard `TextReporter` this will add the line and column number of the end of a node to the output of Pylint. If these numbers are unknown, they are represented by an empty string.
- Introduced primer tests and a configuration tests framework. The helper classes available in `pylint/testutil/` are still unstable and might be modified in the near future 🫠
Closes #4412 #5287
- Added `endLine` and `endColumn` keys to the output of `JSONReporter` 🌸
Closes #5380
- Fixed the false negative for `consider-iterating-dictionary` during membership checks encapsulated in iterables or `not in` checks 🦋
Closes #5323
---
# 📜 Full changelog
## 🌟 What's New in Pylint 2.12.2? 🌟
Release date: `2021-11-25`
- Fixed a false positive for `unused-import` where everything was not analyzed properly inside typing guards 🙅
- Fixed a false-positive regression for `used-before-assignment` for typed variables in the body of class methods that reference the same class 💀
Closes #5342
- Specified that the `ignore-paths` option considers `""` to represent a windows directory delimiter instead of a regular expression escape character 👀
- Fixed a crash with the `ignore-paths` option when invoking the option via the command line 😤
Closes #5437
- Fixed handling of Sphinx-style parameter docstrings with asterisks. These should be escaped with by prepending a `""` ⭐
Closes #5406
- Added `endLine` and `endColumn` keys to the output of `JSONReporter` 🌸
Closes #5380
- Fixed handling of Google-style parameter specifications where descriptions are on the line following the parameter name. These were generating false positives for `missing-param-doc` 🫶
Closes #5452
- Fixed the false negative for `consider-iterating-dictionary` during membership checks encapsulated in iterables or `not in` checks 🦋
Closes #5323
- `unused-import` now checks all ancestors for typing guards 👑
Closes #5316
## 🌟 What's New in Pylint 2.12.1? 🌟
Release date: `2021-11-25`
- Require Python `3.6.2` to run pylint 😌
Closes #5065
## 🌟 What's New in Pylint 2.12.0? 🌟
Release date: `2021-11-24`
- Upgraded astroid to `2.9.0` 🚀
Closes #4982
- Added the ability to add `end_line` and `end_column` to the `--msg-template` option ✨ With the standard `TextReporter` this will add the line and column number of the end of a node to the output of Pylint. If these numbers are unknown, they are represented by an empty string.
- Introduced primer tests and a configuration tests framework. The helper classes available in `pylint/testutil/` are still unstable and might be modified in the near future 🫠
Closes #4412 #5287
- Fixed the install graphiz message which isn't needed for puml output format 🙈
- `MessageTest` of the unittest testutil now requires the *confidence* attribute to match the expected value. If none is provided it is set to `UNDEFINED` 💅
- `add_message` of the unittest testutil now actually handles the `col_offset` parameter and allows it to be checked against actual output in a test ✅
- Fixed a crash in the `check_elif` extensions where an undetected `if` in a comprehension with an `if` statement within an f-string resulted in an out of range error. The checker no longer relies on counting `if` statements anymore and uses known `if` statements locations instead. It should not crash on badly parsed `if` statements anymore 😮💨
- Fixed `simplify-boolean-expression` when the condition can be inferred as `False` 🧚
Closes #5200
- Fixed the exception when pyreverse parses the property function of a class 💀
- The functional testutils now accept `end_lineno` and `end_column`. Expected output files without these will trigger a `DeprecationWarning`. Expected output files can be easily updated with the `python tests/test_functional.py --update-functional-output` command 🛠️
- The functional testutils now correctly check the distinction between `HIGH` and `UNDEFINED` confidence. Expected output files without defined confidence levels will now trigger a `DeprecationWarning`. Expected output files can be easily updated with the `python tests/test_functional.py --update-functional-output` command 🛠️
- The functional test runner now supports the option `min_pyver_end_position` to control on which python versions the `end_lineno` and `end_column` attributes should be checked. The default value is `3.8` 🎀
- Fixed `accept-no-yields-doc` and `accept-no-return-doc` not allowing missing yield or return documentation when a docstring is partially correct 😤
Closes #5223
- Added an optional extension `consider-using-any-or-all`: emitted when a for loop only produces a boolean and could be replaced by `any` or `all` using a generator. Also suggests a suitable `any` or `all` statement ✨
Closes #5008
- Properly identify parameters with no documentation and added a new message called `missing-any-param-doc` 👑
Closes #3799
- Added the checkers `overridden-final-method` & `subclassed-final-class` 🦋
Closes #3197
- Fixed `protected-access` for accessing attributes and methods of inner classes 🌸
Closes #3066
- Added support for `ModuleNotFoundError` (`import-error` and `no-name-in-module`). `ModuleNotFoundError` inherits from `ImportError` and was added in Python `3.6` 💜
- `undefined-variable` now correctly flags variables which only receive a type annotations and never get assigned a value 💀
Closes #5140
- `undefined-variable` now correctly considers the line numbering and order of classes used in metaclass declarations 🧠
Closes #4031
- `used-before-assignment` now correctly considers references to classes as type annotation or default values in first-level methods 🎀
Closes #3771
- `undefined-variable` and `unused-variable` now correctly trigger for assignment expressions in functions defaults ⭐
Refs #3688
- `undefined-variable` now correctly triggers for assignment expressions in `if ... else` statements. This includes a basic form of control flow inference for `if ... else` statements using constant boolean values 😤
Closes #3688
- Added the `--enable-all-extensions` command line option 🔥 It will load all available extensions which can be listed by running `--list-extensions`
- Fixed a bug with importing namespace packages with relative imports 🫶
Closes #2967 and #5131
- Improved and flattened the `unused-wildcard-import` message ✨
Closes #3859
- In the length checker, `len-as-condition` has been renamed as `use-implicit-booleaness-not-len` in order to be consistent with `use-implicit-booleaness-not-comparison` 💅
- Created a new `UnsupportedVersionChecker` checker class that includes checks for features not supported by all versions indicated by a `py-version`.
- Added the `using-f-string-in-unsupported-version` checker. Issued when `py-version` is set to a version that does not support f-strings (< `3.6`) 👀
- Fixed the `useless-super-delegation` false positive when the default keyword argument is a variable 🙈
- Properly emit `duplicate-key` when Enum members are duplicate dictionary keys 💀
Closes #5150
- Use the `py-version` setting for the alternative union syntax check (PEP 604), instead of the Python interpreter version 🌈
- Subclasses of `dict` are regarded as reversible by the `bad-reversed-sequence` checker (Python `3.8` onwards) 🦋
Closes #4981
- Support configuring the mixin class pattern via `mixin-class-rgx` 🎀
- Added a new checker `use-implicit-booleaness-not-comparison`: emitted when collection literal comparison is being used to check for emptiness ✨
Closes #4774
- `missing-param-doc` now correctly parses asterisks for variable length and keyword parameters ⭐
Closes #3733
- `missing-param-doc` now correctly handles Numpy parameter documentation without explicit typing 👑
Closes #5222
- pylint no longer crashes when checking assignment expressions within if-statements 😮💨
Closes #5178
- Updated the `literal-comparison` checker to ignore tuple literals 🍓
Closes #3031
- Normalized the input to the `ignore-paths` option to allow both Posix and Windows paths 🌈
Closes #5194
- Fixed the double emitting of `not-callable` on inferable properties 💅
Closes #4426
- `self-cls-assignment` now also considers tuple assignment 🫶
- Fixed `missing-function-docstring` being a whole hater about `__init__` and other magic methods — like, it literally could not check them even when the `no-docstring-rgx` setting was set up to do exactly that 😤
- Added the `using-final-decorator-in-unsupported-version` checker 💅 it fires when `py-version` is set to a version that does **not** support `typing.final` (that's `< 3.8`, bestie)
- Added the config option `exclude-too-few-public-methods` so you can exclude classes from the `min-public-methods` checker 🎀
Closes #3370
- The `--jobs` parameter now fallbacks to `1` if the host operating system does not have a functioning shared semaphore implementation 🫶
Closes #5216
- Fix crash for `unused-private-member` when checking private members on `__class__` 💀
Closes #5261
- Crashes when a list is encountered in a toml configuration? Not happening anymore, periodt ✨
Closes #4580
- Moved `misplaced-comparison-constant` to its own extension, `comparison_placement` 👑 this checker was opinionated and is now **no longer a default**. You can reactivate it by adding `pylint.extensions.comparison_placement` to `load-plugins` in your config 🦋
Closes #1064
- A new `bad-configuration-section` checker was added that will emit for a misplaced option in pylint's top level namespace for toml configuration 🌸 heads up though: top-level dictionaries or options defined in the wrong section will *still silently not be taken into account*, which is tracked in a follow-up issue 😭
Follow-up in #5259
- Fix crash for `protected-access` on (outer) class traversal 🔥
- Added new checker `useless-with-lock` to find incorrect usage of `with` statement and threading module locks 💅 emitted when `with threading.Lock():` is used instead of `with lock_instance:`
Closes #5208
- Made the `yn` validator case insensitive, so `True` and `False` are allowed in config files ⭐
- Fix crash on `open()` calls when the *mode* argument is not a simple string 🙈
Fixes part of #5321
- Inheriting from a class that implements `__class_getitem__` no longer raises `inherit-non-class` 🧚
- Pyreverse — add the project root directory to `sys.path` 🎀
Closes #2479
- Don't emit `consider-using-f-string` if `py-version` is set to Python < 3.6 💜 f-strings were added in Python 3.6, so like, be for real
Closes #5019
- Fix regression for `unspecified-encoding` with `pathlib.Path.read_text()` 🦋
Closes #5029
- Don't emit `consider-using-f-string` if the variables to be interpolated include a backslash 💀
- Fixed false positive for `cell-var-from-loop` when a variable is used as the default value for a keyword-only parameter 🌸
Closes #5012
- Fix false-positive `undefined-variable` with Lambda, IfExp, and assignment expression ✨
- Fix false-positive `useless-suppression` for `wrong-import-order` 👑
Closes #2366
- Fixed toml dependency issue 🫶
Closes #5066
- Fix false-positive `useless-suppression` for `line-too-long` 😭
Closes #4212
- Fixed `invalid-name` not checking parameters of overwritten base object methods 💅
Closes #3614
- Fixed crash in `consider-using-f-string` if `format` is not called 🔥
Closes #5058
- Fix crash with `AssignAttr` in `if TYPE_CHECKING` blocks 🧚
Closes #5111
- Improve node information for `invalid-name` on function argument ⭐
- Prevent return type checkers being called on functions with ellipses as body 💜
Closes #4736
- Add `is_sys_guard` and `is_typing_guard` helper functions from astroid to `pylint.checkers.utils` 🎀
- Fix regression on ClassDef inference 🌈
Closes #5030 Closes #5036
- Fix regression on Compare node inference ✨
Closes #5048
- Fix false-positive `isinstance-second-argument-not-valid-type` with `typing.Callable` 🦋
Closes #3507 Closes #5087
- It is now recommended to do pylint development on Python 3.8 or higher 💅 this allows using the latest ast parser, iykyk
- All standard jobs in the pylint CI now run on Python 3.8 by default 👑 we still support python 3.6 and 3.7 and run tests for those interpreters, no cap
- TypingChecker
- Fix false-negative for `deprecated-typing-alias` and `consider-using-alias` with `typing.Type` + `typing.Callable` 🌸
---
# 🌟 What's New in Pylint 2.11
Okay so real talk, from pylint's beginning to pylint 2.14.0 included, we had a changelog that contained **all** nontrivial changes to Pylint — including changes that were not user facing, i.e. changes that might interest you only if you're a developer working on pylint or pylint plugins, or an open-source historian 📚
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes ✨
## 🎀 Summary -- Release highlights
In 2.11, we added a new default checker to advise using f-string, because it's the most efficient way of formatting strings right now 💅 you can use pyupgrade (`https://github.com/asottile/pyupgrade`), ruff (`https://docs.astral.sh/ruff/`) or flynt (`https://github.com/ikamensh/flynt`) to migrate your old `%` and `format()` automatically, iconic behaviour honestly
We added a new extension `SetMembershipChecker` that will advise the use of `set` for membership test, since it's more performant than lists or tuples 🔥 the `CodeStyleChecker` also got some love — check it out if you're not already using it 👑
We fixed some long standing bugs, false positives, or false negatives, and we added small quality of life options like `min-similarity-lines` that disables the duplication check when set to `0` 🦋
Under the hood the code for both pylint and astroid is progressively more typed, which could be helpful to you if you're using them as libraries 🌸 in order for this new typing to make more sense and stay simple, we deprecated some functions or types that will be removed in the next major version. This is an ongoing effort, bestie.
The future `possible-forgotten-f-prefix` check still had too many false positives and is delayed again 😭 check the possible-forgotten-f-prefix issue at `https://github.com/pylint-dev/pylint/pull/4787` if you want to provide knowledge or use case :)
## ✨ New checkers
- Added `consider-using-f-string` — emitted when `.format()` or `'%'` is being used to format a string 🎀
Closes #3592
## 💀 Removed checkers
- The python3 porting mode checker and its `py3k` option were removed 🫶 you can still find it in older pylint versions, so like, go visit the archives
## 🦋 Extensions
- Added new extension `SetMembershipChecker` with the `use-set-for-membership` check 👑 emitted when using an in-place defined list or tuple to do a membership test — sets are better optimized for that, no cap
Closes #4776
## 🌈 CodeStyleChecker
- Added `consider-using-assignment-expr` ✨ emitted when an assignment is directly followed by an `if` statement and both can be combined by using an assignment expression `:=`. Requires Python 3.8 💅
Closes #4862
## 💜 Other Changes
- Added `py-version` config key (if `[MAIN]` section). Used for version dependent checks 🎀 will default to whatever Python version pylint is executed with
- The `invalid-name` message is now more detailed when using multiple naming style regexes 🔍
- Fix false positive for `consider-using-with` if a context manager is assigned to a variable in different paths of control flow (e.g. if-else clause) 🌸
Closes #4751
- Fix false positive for `function-redefined` for simple type annotations ✨
Closes #4936
- Fix false positive for `protected-access` if a protected member is used in type hints of function definitions 🦋
- Fix false positive `dict-iter-missing-items` for dictionaries only using tuples as keys 😭
Closes #3282
- The `unspecified-encoding` checker now also checks calls to `pathlib.Path().read_text()` and `pathlib.Path().write_text()` 💅
Closes #4945
- Fix false positive `superfluous-parens` for tuples created with inner tuples 🎀
Closes #4907
- Fix false positive `unused-private-member` for accessing attributes in a class using `cls` 👑
Closes #4849
- Extended `consider-using-in` check to work for attribute access 🔥
- Setting `min-similarity-lines` to `0` now makes the similarity checker stop checking for duplicate code 💯
Closes #4901
- Fix a bug where pylint complained if the cache's parent directory does not exist 🙈
Closes #4900
- The `global-variable-not-assigned` checker now catches global variables that are never reassigned in a local scope and catches (reassigned) functions 🌟
Closes #1375 Closes #330
- The `consider-iterating-dictionary` checker now also considers membership checks 🦋
Closes #4069
---
# 🧾 Full changelog
## 🎉 What's New in Pylint 2.11.1?
Release date: 2021-09-16 ⭐
- `unspecified-encoding` now checks the encoding of `pathlib.Path()` correctly 💅
Closes #5017
## 🎉 What's New in Pylint 2.11.0?
Release date: 2021-09-16 🎀
- The python3 porting mode checker and its `py3k` option were removed 💀 you can still find it in older pylint versions.
- `raising-bad-type` is now properly emitted when raising a string 😭
- Added new extension `SetMembershipChecker` with the `use-set-for-membership` check 👑 emitted when using an in-place defined list or tuple to do a membership test — sets are better optimized for that.
Closes #4776
- Added `py-version` config key (if `[MASTER]` section) ✨ used for version dependent checks. Will default to whatever Python version pylint is executed with.
- CodeStyleChecker: added `consider-using-assignment-expr` — emitted when an assignment is directly followed by an `if` statement and both can be combined by using an assignment expression `:=`. Requires Python 3.8 💅
Closes #4862
- Added `consider-using-f-string` — emitted when `.format()` or `'%'` is being used to format a string 🎀
Closes #3592
- Fix false positive for `consider-using-with` if a context manager is assigned to a variable in different paths of control flow (e.g. if-else clause) 🌸
Closes #4751
- https is now preferred in the documentation and `http://pylint.pycqa.org` correctly redirect to `https://pylint.pycqa.org` 🦋
Closes #3802
- Fix false positive for `function-redefined` for simple type annotations ✨
Closes #4936
- Fix false positive for `protected-access` if a protected member is used in type hints of function definitions 🔥
- Fix false positive `dict-iter-missing-items` for dictionaries only using tuples as keys 😭
Closes #3282
- The `unspecified-encoding` checker now also checks calls to `pathlib.Path().read_text()` and `pathlib.Path().write_text()` 💅
Closes #4945
- Fix false positive `superfluous-parens` for tuples created with inner tuples 🎀
Closes #4907
- Fix false positive `unused-private-member` for accessing attributes in a class using `cls` 👑
Closes #4849
- Fix false positive `unused-private-member` for private staticmethods accessed in classmethods 🌈
Closes #4849
- Extended the <pink>`consider-using-in`</pink> check so it also works for attribute access 💅
- Setting `min-similarity-lines` to `0` now makes the similarity checker **stop** checking for duplicate code — finally, an off switch 🎀
Closes #4901
- Fixed a bug where pylint got all dramatic and complained if the cache's **parent** directory doesn't exist 🙈
Closes #4900
- The <purple>`global-variable-not-assigned`</purple> checker now catches global variables that are never reassigned in a local scope, and also catches (reassigned) functions ✨
Closes #1375 Closes #330
- Fixed false positives for `invalid-all-format` / `E0605` <# invalid-all-format> when they're lists or tuples at runtime 🫶
Closes #4711
- Fixed `no-self-use` and the `docparams` extension for **async** functions and methods 🌸
- Added documentation for <cyan>`pyreverse`</cyan> and <cyan>`symilar`</cyan> — they're documented now, bestie, go read 🦋
Closes #4616
- Non-symbolic messages with the wrong capitalisation now correctly trigger `use-symbolic-message-instead` 👑
Closes #5000
- The `consider-iterating-dictionary` checker now also considers membership checks 🔥
Closes #4069
- The `invalid-name` message is now way more detailed when you're using multiple naming style regexes 💀
## What's New In Pylint 2.10 🎉
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained **all** nontrivial changes to Pylint — including changes that were not user facing. So like, change that might interest you only if you're a developer working on pylint or on pylint plugins, or an open-source historian 📜
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes ✨
### Summary -- Release highlights 🌈
In 2.10, we added several new default checks, like `unspecified-encoding`, `forgotten-debug-statement` or `use-dict-literal`. There's also a few opinionated optional ones. You can now forbid `while` loops, or profess your exclusive love of ternary expressions publicly 😭 We promise you hours of arguing fun with your whole team if you add those to your configuration 👀
We've also fixed some long standing bugs, false positives, or missing options — like `ignore-signature`, which will now work on inner function's signatures 💅
A new option to disable the next line, `disable-next`, has been added. It's also possible to export colored diagrams, and plantuml diagrams, using `pyreverse`. `PYLINT_HOME` is now `XDG_CACHE_HOME` if not set 🎀
The performance of the similarity checker has been improved, as well as several small performance fixes ⚡
We're going to continue working on improving performance during 2.11. We're also going to finalize a new `possible-forgotten-f-prefix` check that had too much false positives at release time. Check the `possible-forgotten-f-prefix` <https://github.com/pylint-dev/pylint/pull/4787> issue if you want to provide knowledge or use case :) 🫶
### New checkers ✨
- Added `unspecified-encoding`: emitted when `open()` is called without specifying an encoding 🍓
Closes #3826
- Added `use-list-literal`: emitted when `list()` is called with no arguments instead of using `[]` 💜
Closes #4365
- Added `use-dict-literal`: emitted when `dict()` is called with no arguments instead of using `{}` 💜
Closes #4365
- Added `forgotten-debug-statement`: emitted when `breakpoint`, `pdb.set_trace` or `sys.breakpointhook` calls are found 💀
Closes #3692
- Added `use-sequence-for-iteration`: emitted when iterating over an in-place defined set 🌸
- Added `format-string-without-interpolation` checker: emitted when formatting is applied to a string without any variables to be replaced 🎀
Closes #4042
- Added `redundant-u-string-prefix` checker: emitted when the `u` prefix is added to a string 🦋
Closes #4102
### Extensions 🧚
- **`CodeStyleChecker`**
- Limit `consider-using-tuple` to be emitted only for in-place defined lists.
- Emit `consider-using-tuple` even if list contains a starred expression.
- Added optional extension `consider-ternary-expression`: emitted whenever a variable is assigned in **both** branches of an `if`/`else` block 😭
Closes # 4366
- Added optional extension `while-used`: emitted whenever a `while` loop is used 👑
Closes # 4367
### Other Changes 🔥
- `pyreverse` now permits producing a colored generated diagram by using the `colorized` option 🎨
- Pyreverse - add output in PlantUML format 🌱
- `consider-using-with` is no longer triggered if a context manager is returned from a function 🫶
- pylint does not crash with a traceback anymore when a file is problematic. It creates a template text file for opening an issue on the bug tracker instead. The linting can go on for the other non-problematic files instead of being impossible 😌
- Pyreverse - show class has-a relationships inferred from type-hints 🔍
- Performance of the Similarity checker has been improved ⚡
- Added `time.clock` to deprecated functions/methods for python 3.3 💀
- Added `ignored-parents` option to the design checker to ignore specific classes from the `too-many-ancestors` check (`R0901`) 🙈
- Don't emit `no-member` error if guarded behind an `if` statement.
Refs #1162 Closes #1990 Closes #4168
- Fixed false positives for `superfluous-parens` with walrus operator, ternary operator and inside list comprehension 💅
Closes #2818 Closes #3249 Closes #3608 Closes #4346
- Refactor of `--list-msgs` & `--list-msgs-enabled`: both options now show whether messages are emittable with the current interpreter 👀
Closes #4778
- Fixed false negative for `used-before-assignment` when the variable is assigned in an exception handler, but used outside of the handler 🎀
Closes #626
- Added `disable-next` option: allows using `# pylint: disable-next=msgid` to disable a message for the following line ✨
Closes #1682
- Fixed `cell-var-from-loop` checker: handle cell variables in comprehensions within functions, and function default argument expressions. Also handle basic variable shadowing 🌸
Closes #2846 Closes #3107
- Fixed bug with the `cell-var-from-loop` checker: it no longer has false negatives when both `unused-variable` and `used-before-assignment` are disabled 🦋
- Class methods' signatures are now ignored the same way as functions' with the similarities `ignore-signatures` option enabled 👑
Closes #4653
### Full changelog 📜
#### What's New in Pylint 2.10.2? 🎀
Release date: 2021-08-21
- We now use <purple>`platformdirs`</purple> instead of `appdirs` since the latter is not maintained 😭
Closes #4886
- Fixed a crash in the checker raising `shallow-copy-environ` when failing to infer on `copy.copy` 💀
Closes #4891
#### What's New in Pylint 2.10.1? 🌸
Release date: 2021-08-20
- pylint does not crash when `PYLINT_HOME` does not exist 🫶
Closes #4883
#### What's New in Pylint 2.10.0? 🎉
Release date: 2021-08-20
- `pyreverse`: add option to produce colored output 🎨
Closes #4488
- `pyreverse`: add output in PlantUML format 🌱
Closes #4498
- `consider-using-with` is no longer triggered if a context manager is returned from a function ✨
Closes #4748
- pylint does not crash with a traceback anymore when a file is problematic. It creates a template text file for opening an issue on the bug tracker instead. The linting can go on for the other non-problematic files instead of being impossible 😌
- `pyreverse`: show class has-a relationships inferred from the type-hint 🔍
Closes #4744
- Fixed a crash when importing beyond the top level package during `import-error` message creation 💀
Closes #4775
- Added `ignored-parents` option to the design checker to ignore specific classes from the `too-many-ancestors` check (`R0901`) 🙈
Fixes part of #3057
- Added `unspecified-encoding`: emitted when `open()` is called without specifying an encoding 🍓
Closes #3826
- Improved the Similarity checker performance. Fix issue with `--min-similarity-lines` used with `--jobs` ⚡
Closes #4120 Closes #4118
- Don't emit `no-member` error if guarded behind an `if` statement.
Refs #1162 Closes #1990 Closes #4168
- The default for `PYLINTHOME` is now the standard `XDG_CACHE_HOME`, and pylint now uses `appdirs` 🎀
Closes #3878
- Added `use-list-literal`: emitted when `list()` is called with no arguments instead of using `[]` 💜
Closes #4365
- Added `use-dict-literal`: emitted when `dict()` is called with no arguments instead of using `{}` 💜
Closes #4365
- Added optional extension `consider-ternary-expression`: emitted whenever a variable is assigned in both branches of an `if`/`else` block 😭
Closes # 4366
- Added optional extension `while-used`: emitted whenever a `while` loop is used 👑
Closes # 4367
- Added `forgotten-debug-statement`: emitted when `breakpoint`, `pdb.set_trace` or `sys.breakpointhook` calls are found 💀
Closes #3692
- Fixed false-positive of `unused-private-member` when using nested functions in a class 🦋
Closes #4673
- Fixed a crash for `unused-private-member` that occurred with nested attributes 💀
Closes #4755
- Fixed a false positive for `unused-private-member` with class names 🌸
Closes #4681
- Fixed false positives for `superfluous-parens` with walrus operator, ternary operator and inside list comprehension 💅
Closes #2818 Closes #3249 Closes #3608 Closes #4346
- Added `format-string-without-interpolation` checker: emitted when formatting is applied to a string without any variables to be replaced 🎀
Closes #4042
- Refactor of `--list-msgs` & `--list-msgs-enabled`: both options now show whether messages are emittable with the current interpreter 👀
Closes #4778
- Fixed false negative for `used-before-assignment` when the variable is assigned in an exception handler, but used outside of the handler ✨
Closes #626
- Added `disable-next` option: allows using `# pylint: disable-next=msgid` to disable a message for the following line 🔥
Closes #1682
- Added `redundant-u-string-prefix` checker: emitted when the `u` prefix is added to a string 🦋
Closes #4102
- Fixed `cell-var-from-loop` checker: handle cell variables in comprehensions within functions, and function default argument expressions. Also handle basic variable shadowing 🌸
Closes #2846 Closes #3107
- Fixed bug with the `cell-var-from-loop` checker: it no longer has false negatives when both `unused-variable` and `used-before-assignment` are disabled 🫶
- Fixed false positive for `invalid-all-format` if the `list` or `tuple` builtin functions are used 💜
Closes #4711
- Config files can now contain environment variables 👑
Closes #3839
- Fixed false-positive `used-before-assignment` with an assignment expression in a `Return` node 🎀
Closes #4828
- Added `use-sequence-for-iteration`: emitted when iterating over an in-place defined set ✨
- **`CodeStyleChecker`**
- Limit `consider-using-tuple` to be emitted only for in-place defined lists.
- Emit `consider-using-tuple` even if the list contains a starred expression.
- Ignore decorators lines by the similarities checker when the ignore-signatures flag is enabled
Closes #4839
- Allow `true` and `false` values in `pylintrc` for better compatibility with toml config 💅
- Class methods' signatures are ignored the same way as functions' with similarities' `"ignore-signatures"` option enabled
Closes #4653
- Improve performance when inferring `Call` nodes, by utilizing caching ⚡
- Improve error message for `invalid-metaclass` / `E1139` when the node is an `Instance`.
---
## 🦋 What's New In Pylint 2.9 🦋
Bestie, real talk: from pylint's beginning to pylint 2.14.0 included, we had a changelog that contained *all* nontrivial changes to Pylint — including changes that were not user facing, i.e. changes that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian 📜.
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
### ✨ Summary -- Release highlights ✨
Pylint is now compatible with python 3.10 🎉.
A lot of new checks have been added — some non-opinionated performance warnings like `use-maxsplit-arg`, some consensual style warnings like `unnecessary-dict-index-lookup` or new deprecation checks.
We're aiming to reduce pylint noise for first time users, and making some new checks optional is a step in that direction. In order to do that we created an optional code style checker that can be loaded using `pylint.extensions.code_style` with the `load-plugins` options. More than ever, if as a veteran you want the most checks you can possibly get, you should check the list of pylint extensions: `https://pylint.readthedocs.io/en/latest/user_guide/checkers/extensions.html#optional-checkers`.
### 🎀 New checkers 🎀
- `deprecated-decorator`: Emitted when deprecated decorator is used.
- `consider-using-dict-items`: Emitted when iterating over dictionary keys and then indexing the same dictionary with the key within loop body.
- `use-maxsplit-arg`: Emitted either when accessing only the first or last element of `str.split()`.
- An `ignore_signatures` option has been added to the similarity checker. It will permits to reduce false positives when multiple functions have the same parameters.
- `unnecessary-dict-index-lookup`: Emitted when iterating over dictionary items (key-value pairs) and accessing the value by index lookup.
- `consider-using-from-import`: Emitted when a submodule/member of a package is imported and aliased with the same name.
- New checker `unused-private-member`: Emitted when a private member (i.e., starts with `__`) of a class is defined but not used.
- New checker `invalid-class-object`: Emitted when a non-class is assigned to a `__class__` attribute.
- `invalid-all-format`: Emitted when `__all__` has an invalid format, i.e. isn't a tuple or list.
- New checker `await-outside-async`: Emitted when `await` is used outside an `async` function.
- Add new extension `CodeStyleChecker`. It includes checkers that can improve code consistency. As such they don't necessarily provide a performance benefit and are often times opinionated.
- `consider-using-tuple`: Emitted when an in-place defined list or set can be replaced by a tuple.
- `consider-using-namedtuple-or-dataclass`: Emitted when dictionary values can be replaced by namedtuples or dataclass instances.
### 💜 Other Changes 💜
- Fix false-positive `consider-using-with` (`R1732`) if a ternary conditional is used together with `with`
- Fix false-positive `consider-using-with` (`R1732`) if `contextlib.ExitStack` takes care of calling the `__exit__` method
- Add type annotations to pyreverse dot files
- Pylint's tags are now the standard form `vX.Y.Z` and not `pylint-X.Y.Z` anymore 🏷️
- Fix false-positive `too-many-ancestors` when inheriting from builtin classes, especially from the `collections.abc` module
- The output messages for `arguments-differ` error message have been customized based on the different error cases.
- New option `--fail-on=<msg ids>` to return non-zero exit codes regardless of `fail-under` value.
- A new error called `arguments-renamed` has been created, which identifies any changes at the parameter names of overridden functions. It aims to separate the functionality of `arguments-differ`.
- Fix incompatibility with Python 3.6.0 caused by `typing.Counter` and `typing.NoReturn` usage
- Allow comma-separated list in `output-format` and separate output files for each specified format. Each output file can be defined after a semicolon for example: `--output-format=json:myfile.json,colorized`
- The `using-constant-test` checker now detects constant tests consisting of list literals like `[]` and `[1, 2, 3]`.
- `ignore-paths` configuration directive has been added. Defined regex patterns are matched against file path.
- Added handling of floating point values when parsing configuration from `pyproject.toml`
- Fix false positive `useless-type-doc` on ignored argument using `pylint.extensions.docparams` when a function was typed using pep484 but not inside the docstring.
- Fix missing support for detecting deprecated aliases to existing functions/methods. functions/methods.
- Added various deprecated functions/methods for python 3.10, 3.7, 3.6 and 3.3
- No longer emit `consider-using-with` for `ThreadPoolExecutor` and `ProcessPoolExecutor` as they have legitimate use cases without a `with` block.
- Fix crash if a callable returning a context manager was assigned to a list or dict item
### 🌸 Full changelog 🌸
### 💖 What's New in Pylint 2.9.6? 💖
Release date: 2021-07-28
- Fix a false positive `undefined-variable` when variable name in decoration matches function argument
Closes #3791
### 🫶 What's New in Pylint 2.9.5? 🫶
Release date: 2021-07-21
- Fix a crash when there would be a `'TypeError object does not support item assignment'` in the code we parse.
Closes #4439
- Fix crash if a callable returning a context manager was assigned to a list or dict item
Closes #4732
- Fix a crash when an `AttributeInferenceError` was not handled properly when failing to infer the real name of an import in astroid.
Closes #4692
### 🧚 What's New in Pylint 2.9.4? 🧚
Release date: 2021-07-20
- Added `time.clock` to deprecated functions/methods for python 3.3
- Fix bug in which `--fail-on` can return a zero exit code even when the specified issue is present 😱
Closes #4296
Closes #3363
- Fix hard failure when handling missing attribute in a class with duplicated bases
Closes #4687
- Fix false-positive `consider-using-with` (`R1732`) if a ternary conditional is used together with `with`
Closes #4676
- Fix false-positive `deprecated-module` when relative import uses deprecated module name.
Closes #4629
- Fix false-positive `consider-using-with` (`R1732`) if `contextlib.ExitStack` takes care of calling the `__exit__` method
Closes #4654
- Fix a false positive for `unused-private-member` when mutating a private attribute with `cls`
Closes #4657
- Fix ignored empty functions by similarities checker with `"ignore-signatures"` option enabled
Closes #4652
- Fix false-positive of `use-maxsplit-arg` when index is incremented in a loop
Closes #4664
- Don't emit `cyclic-import` message if import is guarded by `typing.TYPE_CHECKING`.
Closes #3525
- Fix false-positive `not-callable` with alternative `TypedDict` syntax
Closes #4715
- Clarify documentation for `consider-using-from-import` / `R0402`
- Don't emit `unreachable` warning for empty generator functions
Closes #4698
- Don't emit `import-error`, `no-name-in-module`, and `ungrouped-imports` for imports guarded by `sys.version_info` or `typing.TYPE_CHECKING`.
Closes #3285
Closes #3382
- Fix `invalid-overridden-method` with nested property
Closes #4368
- Fix false-positive of `unused-private-member` when using `__new__` in a class
Closes #4668
- No longer emit `consider-using-with` for `ThreadPoolExecutor` and `ProcessPoolExecutor` as they have legitimate use cases without a `with` block.
Closes #4689
- Fix crash when inferring variables assigned in match patterns 💀
Closes #4685
- Fix a crash when a `StopIteration` was raised when inferring a faulty function in a context manager.
Closes #4723
### 🍓 What's New in Pylint 2.9.3? 🍓
Release date: 2021-07-01
- Fix a crash that happened when analysing empty function with docstring in the similarity checker.
Closes #4648
- The similarity checker no longer add three trailing whitespaces for empty lines in its report.
### 🌈 What's New in Pylint 2.9.2? 🌈
Release date: 2021-07-01
- Fix a crash that happened when analysing code using `type(self)` to access a class attribute in the `unused-private-member` checker.
Closes #4638
- Fix a false positive for `unused-private-member` when accessing a private variable with `self`
Closes #4644
- Fix false-positive of `unnecessary-dict-index-lookup` and `consider-using-dict-items` for reassigned dict index lookups
Closes #4630
### ⭐ What's New in Pylint 2.9.1? ⭐
Release date: 2021-06-30
- Upgrade astroid to 2.6.2
Closes #4631
Closes #4633
### 👑 What's New in Pylint 2.9.0? 👑
Release date: 2021-06-29
- Python 3.10 is now supported. 🐍
- Add type annotations to pyreverse dot files
Closes #1548
- Fix missing support for detecting deprecated aliases to existing functions/methods.
Closes #4618
- astroid has been upgraded to 2.6.1
- Added various deprecated functions/methods for python 3.10, 3.7, 3.6 and 3.3
- Fix false positive `useless-type-doc` on ignored argument using `pylint.extensions.docparams` when a function was typed using pep484 but not inside the docstring.
Closes #4117
Closes #4593
- `setuptools_scm` has been removed and replaced by `tbump` in order to not have hidden runtime dependencies to setuptools
- Fix a crash when a test function is decorated with `@pytest.fixture` and astroid can't infer the name of the decorator when using `open` without `with`.
Closes #4612
- Added `deprecated-decorator`: Emitted when deprecated decorator is used.
Closes #4429
- Added `ignore-paths` behaviour. Defined regex patterns are matched against full file path.
Closes #2541
- Fix that false negative for `consider-using-with` when calls like `open()` were used outside of assignment expressions 💅
- The warning for `arguments-differ` now spells out *exactly* what difference it caught — it names the argument(s) that changed and what kind of change it was. So specific. So helpful. ✨
- `consider-using-with` is now suppressed inside context managers.
Closes #4430
- Added the `--fail-on` option so you get non-zero exit codes no matter what the `--fail-under` value is set to 🫶
- `numversion` tuple contains integers again, which fixes multiple pylint plugins that were relying on it 😭
Closes #4420
- Fix false-positive `too-many-ancestors` when you inherit from builtin classes — especially from the `collections.abc` module.
Closes #4166, Closes #4415
- The stdlib deprecated modules check got moved over to the stdlib checker. Also, new deprecated modules have been added 👑
- Fix raising false-positive `no-member` on abstract properties
- Created a brand-new error message called `arguments-renamed`, which identifies any changes to the parameter names of overridden functions.
Closes #3536
- New checker `consider-using-dict-items`. Emitted when you're iterating over dictionary keys and *then* indexing that same dictionary with the key inside the loop body. Bestie, just iterate the items 💀
Closes #3389
- Don't emit `import-error` if the import is guarded behind `if sys.version_info >= (x, x)`
- Fix incompatibility with Python 3.6.0 caused by `typing.Counter` and `typing.NoReturn` usage 🐛
Closes #4412
- New checker `use-maxsplit-arg`. Emitted whenever you access only the first or the last element of `str.split()`.
Closes #4440
- Add `ignore_signatures` to the duplicate code checker
Closes #3619
- Fix documentation errors in the "Block disables" paragraph of the User Guide 📝
- New checker `unnecessary-dict-index-lookup`. Emitted when you're iterating over dictionary items (key-value pairs) and then accessing the value by index lookup. The value is *right there*, bestie 😭
Closes #4470
- New checker `consider-using-from-import`. Emitted when a submodule/member of a package is imported and aliased with the same name.
Closes #2309
- Allow a comma-separated list in `output-format`, plus separate output files for each specified format ✨
Closes #1798
- Make `using-constant-test` detect constant tests consisting of list literals like `[]` and `[1, 2, 3]`.
- Improved the error message of the `unnecessary-comprehension` checker by giving you a code suggestion 🎀
Closes #4499
- New checker `unused-private-member`. Emitted when a private member (i.e., one that starts with `__`) of a class is defined but never used.
Closes #4483
- Fix false negative of `consider-using-enumerate` when iterating over an attribute.
Closes #3657
- New checker `invalid-class-object`. Emitted when a non-class gets assigned to a `__class__` attribute.
Closes #585
- Fix a crash when a plugin from the configuration couldn't be loaded — and raise an error `bad-plugin-value` / `E0013` instead 🔥
Closes #4555
- Added handling of floating point values when parsing configuration from `pyproject.toml`
Closes #4518
- `invalid-length-returned` now also works when nothing at all is returned, following an upgrade in astroid.
- `logging-format-interpolation` and `logging-not-lazy` now work on a logger class created from a renamed `logging` import, following an upgrade in astroid.
- Fix false-positive `no-member` with generic base class
Closes pylint-dev/astroid#942
- Fix `assigning-non-slot` false-positive with a base that inherits from `typing.Generic`
Closes #4509, Closes pylint-dev/astroid#999
- New checker `invalid-all-format`. Emitted when `__all__` has an invalid format, i.e. it isn't a tuple or a list.
- Fix false positive `unused-variable` and `undefined-variable` with Pattern Matching in Python 3.10
- New checker `await-outside-async`. Emitted when `await` is used outside an async function ⭐
- Clarify documentation for the typing extension.
Closes #4545
- Add new extension `CodeStyleChecker`. It includes checkers that can improve code consistency. As such they don't necessarily provide a performance benefit and are often times opinionated 🎀
- New checker `consider-using-tuple`. Emitted when an in-place defined list or set can be replaced by a tuple.
- New checker `consider-using-namedtuple-or-dataclass`. Emitted when dictionary values can be replaced by namedtuples or dataclass instances.
- Fix the error that occurred when using a slice as a subscript for a dict.
- Reduce false-positives around inference of `.value` and `.name` properties on `Enum` subclasses, following an upgrade in astroid 💜
Closes #1932, Closes #2062
- Fix an issue with `cached_property` that caused an `invalid-overridden-method` error when overriding a property.
Closes #4023
- Fix `unused-import` false positive for imported modules referenced in attribute lookups in type comments.
Closes #4603
---
# 🌈 What's New In Pylint 2.8
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing — i.e. changes that might interest you only if you're a developer working on pylint or pylint plugins, or an open-source historian 🧚
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
## 💖 Summary — Release highlights
## ⚡ Breaking changes
- The `'doc'` extra-require has been removed. `__pkginfo__` does not contain the package metadata anymore (except `numversion`, until 3.0). Meta-information is accessible with
python from importlib import metadata metadata.metadata('pylint')
Prefer that to an import from `__pkginfo__`.
## ✨ New checkers
- New refactoring message `consider-using-with`. This message is emitted if resource-allocating functions or methods of the standard library (like `open()` or `threading.Lock.acquire()`) that can be used as a context manager are called without a `with` block 🫶
- Add `deprecated-argument` check for deprecated arguments.
- Add new extension `ConfusingConsecutiveElifChecker`. This optional checker emits a refactoring message (`R5601 confusing-consecutive-elif`) if `if`/`elif` statements with different indentation levels follow directly one after the other.
- Add `consider-using-min-max-builtin` check for an `if` statement which could be replaced by the Python builtin `min` or `max` 👑
- Add new extension `TypingChecker`. This optional checker can detect the use of deprecated typing aliases and can suggest the use of the alternative union syntax where possible. (For example, `'typing.Dict'` can be replaced by `'dict'`, and `'typing.Unions'` by `'|'`, etc.) Make sure to check the config options if you plan on using it!
- Add `deprecated-class` check for deprecated classes.
## 🎀 Other Changes
- New option `--output=<file>` to output the result to a file rather than printing to stdout.
Closes #1070
- Reduce usage of blacklist/whitelist terminology. Notably, `extension-pkg-allow-list` is an alternative to `extension-pkg-whitelist` and the message `blacklisted-name` is now emitted as `disallowed-name`. The previous names are accepted to maintain backward compatibility 💅
- The packaging is now done via setuptools exclusively. `doc`, `tests`, `man`, `elisp` and `Changelog` are not packaged anymore — reducing the size of the package by 75%.
- Updated astroid to 2.5.4
- `COPYING` has been renamed to `LICENSE` for standardization.
## 🌸 Full changelog
### 🦋 What's New in Pylint 2.8.3?
Release date: 2021-05-31
- Astroid has been pinned to 2.5.6 for the 2.8 branch.
Refs #4527
### 🦋 What's New in Pylint 2.8.2?
Release date: 2021-04-26
- Keep `__pkginfo__.numversion` a tuple to avoid breaking `pylint-django`.
Closes #4405
- `scm_setuptools` has been added to the packaging.
- Pylint's tags are now the standard form `vX.Y.Z` and not `pylint-X.Y.Z` anymore ⭐
- New warning message `deprecated-class`. This message is emitted if you import or call a deprecated class of the standard library (like `collections.Iterable`, which will be removed in Python 3.10).
Closes #4388
### 🦋 What's New in Pylint 2.8.1?
Release date: 2021-04-25
- Add `numversion` back (temporarily) in `__pkginfo__` because it broke Pylama, and revert the unnecessary `pylint.version` breaking change.
Closes #4399
### 🦋 What's New in Pylint 2.8.0?
Release date: 2021-04-24
- New refactoring message `consider-using-with`. This message is emitted if resource-allocating functions or methods of the standard library (like `open()` or `threading.Lock.acquire()`) that can be used as a context manager are called without a `with` block 🔥
Closes #3413
- Resolve false positives on unused variables in decorator functions
Closes #4252
- Add new extension `ConfusingConsecutiveElifChecker`. This optional checker emits a refactoring message (`R5601 confusing-consecutive-elif`) if `if`/`elif` statements with different indentation levels follow directly one after the other.
- New option `--output=<file>` to output the result to a file rather than printing to stdout.
Closes #1070
- Use a prescriptive message for `unidiomatic-typecheck`
Closes #3891
- Apply `const-naming-style` to module constants annotated with `typing.Final`
- The packaging is now done via setuptools exclusively. `doc`, `tests`, `man`, `elisp` and `Changelog` are not packaged anymore — reducing the size of the package by 75%.
- Debian packaging is now (officially) done in `https://salsa.debian.org/python-team/packages/pylint`.
- The `'doc'` extra-require has been removed.
- `__pkginfo__` now only contains `__version__` (also accessible with `pylint.__version__`), other meta-information is still accessible with `from importlib import metadata;metadata.metadata('pylint')`.
- `COPYING` has been renamed to `LICENSE` for standardization.
- Fix false-positive `used-before-assignment` in function returns.
Closes #4301
- Updated astroid to 2.5.3
Closes #2822, #4206, #4284
- Add `consider-using-min-max-builtin` check for an `if` statement which could be replaced by the Python builtin `min` or `max`
Closes #3406
- Don't auto-enable postponed evaluation of type annotations with Python 3.10 🙈
- Update astroid to 2.5.4
- Add new extension `TypingChecker`. This optional checker can detect the use of deprecated typing aliases and can suggest the use of the alternative union syntax where possible. (For example, `'typing.Dict'` can be replaced by `'dict'`, and `'typing.Unions'` by `'|'`, etc.) Make sure to check the config options if you plan on using it!
- Reactivates old counts in report mode.
Closes #3819
- **`inconsistent-return-statements` got smarter about `assert False`** — during detection it now treats `assert False` as a return node. *(Closes #4019)*
- **A score that lands *exactly* on `config.fail_under` no longer fails the run** — like, precisely equal is fine, bestie. 🎯
- **Functions that literally never return can declare `NoReturn` as a type hint**, so `inconsistent-return-statements` isn't emitted for them. *(Closes #4122, #4188)*
- **Protected-access checks got nicer** — access is now allowed inside class methods. *(Closes #1159)*
- **Fixed an issue with PEP 585 syntax and the use of `collections.abc.Set`** ✨
- **`typing.ClassVar`-annotated class variables** are no longer misidentified as class constants. And going forward, class variables annotated with `typing.Final` *are* identified as such. *(Closes #4277)*
- **Continuous integration with read the doc has been added.** *(Closes #3850)* 📚
- **`DuplicateBasesError` no longer shows up for attribute access.**
- **Fixed a crash** that happened when checking `setup.cfg` for pylint config while there were non-ascii characters in there. *(Closes #4328)*
- **Code flanked in backticks can now be skipped by the spellchecker.** *(Closes #4319)*
- **Python tool directives** — for `black`, `flake8`, `zimports`, `isort`, `mypy`, `bandit`, `pycharm` — at the beginning of comments can now be skipped by the spellchecker too. *(Closes #4320)*
- **Fixed the issue that made Emacs pylint fail when used with tramp.** 🫶
- **Improved the check for invalid PEP 585 syntax inside functions** when postponed evaluation of type annotations is enabled.
- **Improved the check for invalid PEP 585 syntax as default function arguments.**
---
# 🦋 What's New In Pylint 2.7 🦋
Okay so real talk — from pylint's beginning all the way through pylint 2.14.0 included, there was this changelog that held **all** the nontrivial changes to Pylint. And we mean *all* of them, including the stuff that wasn't user facing — like, changes that only matter to you if you're a developer working on pylint, or on pylint plugins, or if you're an open-source historian. 💀
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was *also* a summary of (mostly user facing) changes. Best of both worlds, honestly.
## 🌈 Summary -- Release highlights
- **Python 3.6+ is now required.** Periodt.
- **No more duplicate messages when using multiple jobs.** finally 😭
- **Handling of the new typing provided by mypy 0.8**
- **Reduced the number of false positives in general** — we love to see it 💅
- **Reduced the occurrence of genuinely large recursion that went above the system limit** (see `#3836` for a fix for pandas)
## ✨ New checkers ✨
- **`nan-comparison`** — added for comparison of NaN values
- **`ignored-argument-names` support in `DocstringParameterChecker`**, plus the new `useless-param-doc` and `useless-type-doc` messages
- **`empty-comment`** — added for empty comments
- **`simplifiable-condition`** — added for extraneous constants in conditionals using `and`/`or`
- **`condition-evals-to-constant`** — added for conditionals using `and`/`or` that evaluate to a constant
- **`consider-using-generator`** — added for the use of list comprehension inside `any` or `all` function
## 🌸 Other Changes 🌸
- **Fixed a false positive for `builtin-not-iterating`** when `zip` or `map` receives an iterable
- **Fixed the linter multiprocessing pool shutdown**, which triggered warnings when run in parallel with other pytest plugins
- **Enums are now required to be named in `UPPER_CASE`** by `invalid-name`
- **Fixed a bug that led to duplicate messages** when using `--jobs 2` or more
- **New option `check-protected-access-in-special-methods`** in the `ClassChecker`, to activate/deactivate `protected-access` message emission for single underscore prefixed attributes in special methods
- **`inconsistent-return-statements` is now emitted** if one of a `try`/`except` statement is not returning explicitly while the other does
- **Fixed a false positive `useless-super-delegation`** when the default keyword argument is a dictionary
- **Fixed vulnerable regular expressions in pyreverse** — the ambiguities are removed, making the repaired regular expressions safer and faster matching 🔥
- **`len-as-conditions` is now triggered only for** classes that inherit directly from `list`, `dict`, or `set` and don't implement the `__bool__` function, or from generators like `range` or list/dict/set comprehension. This should reduce the false positive for other classes — like pandas's `DataFrame` or numpy's `Array`
- **Fixes duplicate code detection for `--jobs=2+`**
- **New option `allowed-redefined-builtins`** — defines variable names allowed to shadow builtins
- **Improved protected access checks** to allow access inside class methods
---
# 💜 Full changelog 💜
## 🎀 What's New in Pylint 2.7.4? 🎀
Release date: `2021-03-30`
- **Fixed a problem with a disabled msgid not being ignored.** *(Closes #4265)*
- **Fixed an issue with annotated class constants.** *(Closes #4264)*
## ⭐ What's New in Pylint 2.7.3? ⭐
Release date: `2021-03-29`
- **Introduced logic for checking deprecated attributes in `DeprecationMixin`.**
- **Reduced usage of blacklist/whitelist terminology.** Notably, `extension-pkg-allow-list` is an alternative to `extension-pkg-whitelist`, and the message `blacklisted-name` is now emitted as `disallowed-name`. The previous names are still accepted to maintain backward compatibility. 🫶
- **Moved the deprecated checker to `DeprecatedMixin`.** *(Closes #4086)*
- **Bumped astroid version to `2.5.2`.**
- **Fixed a false positive for `method-hidden`** when using a private attribute and method. *(Closes #3936)*
- **`use-symbolic-message-instead` now also works on legacy messages** like `C0111` (`missing-docstring`).
- **Removed an unwanted print to stdout** from `_emit_no_member`.
- **Introduced a command-line option to specify the pyreverse output directory.** *(Closes #4159)*
- **Fixed an issue with Enums and `class-attribute-naming-style=snake_case`.** *(Closes #4149)*
- **Added the `allowed-redefined-builtins` option** for fine tuning the `redefined-builtin` check. *(Closes #3263)*
- **Fixed an issue when executing with `python -m pylint`.** *(Closes #4161)*
- **`typing.TypedDict` is now exempt from the `too-few-public-methods` check.** *(Closes #4180)*
- **Fixed a false-positive `no-member`** for typed annotations without a default value. *(Closes #3167)*
- **Added `--class-const-naming-style`** for Enum constants and class variables annotated with `typing.ClassVar`. *(Closes #4181)*
- **Fixed an `astroid.Inference` error** for undefined-variables with `len()`. *(Closes #4215)*
- **Fixed the column index on FIXME warning messages.** *(Closes #4218)*
- **Improved handling of assignment expressions**, with better edge case handling. *(Closes #3763, #4238)*
- **Improved the check for whether a class is subscriptable (PEP585).**
- **Fixed documentation and filename handling of `--import-graph`.**
- **Fixed a false-positive for `unused-import` on class keyword arguments.** *(Closes #3202)*
- **Fixed a regression with plugins on `PYTHONPATH`** if the latter is the cwd. *(Closes #4252)*
## 🍓 What's New in Pylint 2.7.2? 🍓
Release date: `2021-02-28`
- **Fixed a false positive on `Enum.__members__.items()`, `Enum.__members__.values`, and `Enum.__members__.keys`.** *(Closes #4123)*
- **Properly strips dangerous `sys.path` entries** — not just the first one. *(Closes #3636)*
- **Workflow and packaging improvements.**
## 🧚 What's New in Pylint 2.7.1? 🧚
Release date: `2021-02-23`
- **Exposed `UnittestLinter` in `pylint.testutils`.**
- **No longer checks directories starting with `.`** when using `register_plugins`. *(Closes #4119)*
## 👑 What's New in Pylint 2.7.0? 👑
Release date: `2021-02-21`
- **Introduced `DeprecationMixin`** for reusable deprecation checks. *(Closes #4049)*
- **Fixed a false positive for `builtin-not-iterating`** when `map` receives an iterable. *(Closes #4078)*
- **Python 3.6+ is now required.**
- **Fixed a false positive for `builtin-not-iterating`** when `zip` receives an iterable.
- **Added the `nan-comparison` check** for NaN comparisons.
- **Bug fix for the `empty-comment` message line number.** *(Closes #4009)*
- **Only emits `bad-reversed-sequence` on dictionaries** if below py3.8. *(Closes #3940)*
- **Handled class decorators applied to functions.** *(Closes #3882)*
- **Added a check for empty comments.**
- **Fixed a minor documentation issue in `contribute.rst`.**
- **Enums are now required to be named in `UPPER_CASE`** by `invalid-name`. *(Closes #3834)*
- **Added missing checks for deprecated functions.**
- **Postponed evaluation of annotations is now recognized by default** if the python version is above 3.10. *(Closes #3992)*
- **Fixed column metadata for anomalous backslash lints.**
- **Dropped support for Python 3.5.**
- **Added support for pep585 with postponed evaluation.** *(Closes #3320)*
- **Checks the alternative union syntax — PEP 604.** *(Closes #4065)*
- **Fixed multiple false positives with assignment expressions.** *(Closes #3347, #3953, #3865, #3275)*
- **Fixed `TypedDict` `inherit-non-class` false-positive** on Python 3.9+. *(Closes #1927)*
- **Fixed an issue with nested PEP 585 syntax.**
- **Fixed an issue with nested PEP 604 syntax.**
- **Fixed a crash in `undefined-variable`** caused by chained attributes in a metaclass. *(Closes #3742)*
- **Fixed a false positive for `not-async-context-manager`** when `contextlib.asynccontextmanager` is used. *(Closes #3862)*
- **Fixed the linter multiprocessing pool shutdown** (it triggered warnings when run in parallel with other pytest plugins). *(Closes #3779)*
- **Fixed a false-positive emission of `no-self-use` and `unused-argument`** for methods of generic structural types (`Protocol[T]`). *(Closes #3885)*
- **Fixed a bug that led to duplicate messages** when using `--jobs 2` or more. *(Closes #3584)*
- **Added option `check-protected-access-in-special-methods`** in the `ClassChecker`, to activate/deactivate `protected-access` message emission for single underscore prefixed attributes in special methods. *(Closes #3120)*
- **Fixed vulnerable regular expressions in pyreverse.** *(Closes #3811)*
- **`inconsistent-return-statements` is now emitted** if one of a `try`/`except` statement is not returning explicitly while the other does. *(Closes #3468)*
- **Fixed a `useless-super-delegation` false positive** when the default keyword argument is a dictionary. *(Closes #3773)*
- **Fixed a crash when a specified config file does not exist.**
- **Added support for `ignored-argument-names` in `DocstringParameterChecker`**, plus the new `useless-param-doc` and `useless-type-doc` messages. *(Closes #3800)*
- **Enforced docparams consistently when the docstring is not present.** *(Closes #2738)*
- **Fixed a `duplicate-code` false positive** when lines only contain whitespace and non-alphanumeric characters (e.g. parentheses, brackets, commas, etc.)
- **Improved the lint message for `singleton-comparison` with bools.**
- **Fixed a spell-checker crash on indented docstring lines** that look like `#` comments. *(Closes #3786)*
- **Fixed an `AttributeError` in `checkers/refactoring.py`.**
- **Improved the sphinx directives spelling filter.**
- **Fixed a bug with postponed evaluation** when using aliases for annotations. *(Closes #3798)*
- **Fixed minor documentation issues.**
- 🚀 **Performance glow-up:** improved the performance of the line length check. ✨
- 🧹 **Removed incorrect deprecation of `inspect.getfullargspec`** — no more wrong deprecation vibes there, bestie.
- 🪄 **Fixed `signature-differs` false positive** for functions with variadics. <lavender>Closes #3737</lavender>
- 🛠️ **Fixed a crash in `consider-using-enumerate`** when it encounters `range()` without arguments. <lavender>Closes #3735</lavender>
- 🧠 **`len-as-conditions` is now triggered only for classes that are inheriting directly from `list`, `dict`, or `set` and not implementing the `__bool__` function, or from generators like `range` or list/dict/set comprehension.** This should reduce the false positives for other classes, like pandas's `DataFrame` or numpy's `Array`. <lavender>Closes #1879</lavender>
- 🧷 **Fixed `duplicate-errors` not working with `-j2+`** <lavender>Closes #3314</lavender>
- 🏷️ **`generated-members` now matches the qualified name of members** <lavender>Closes #2498</lavender>
- ✅ Added check for `bool` function to `len-as-condition`.
- 🧮 Added `simplifiable-condition` check for extraneous constants in conditionals using `and/or`.
- 🎯 Added `condition-evals-to-constant` check for conditionals using `and/or` that evaluate to a constant. <lavender>Closes #3407</lavender>
- 📦 Changed `setup.py` to work with `distlib` (`https://pypi.org/project/distlib`). <lavender>Closes #3555</lavender>
- 🆕 **New check:** `consider-using-generator` — this check warns when a comprehension is used inside an `any` or `all` function, since it is unnecessary and should be replaced by a generator instead. Using a generator would be less code and way faster. <lavender>Closes #3165</lavender>
- 🤖 Added Github Actions to replace Travis and AppVeyor in the future.
## 🦋 What's New In Pylint 2.6 🦋
Okay so from pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing — i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian. History bestie behavior, honestly.
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. Cute.
### ✨ Summary -- Release highlights ✨
- `bad-continuation` and `bad-whitespace` have been removed. `black` or another formatter can help you with this better than Pylint. Periodt.
- Added support for `isort 5`.
### 🧐 New checkers
- Added `super-with-arguments` check for flagging instances of Python 2 style super calls. Slay.
- Added `raise-missing-from` check for exceptions that should have a cause.
### 🛠️ Other Changes
- `bad-continuation` and `bad-whitespace` have been removed. `black` or another formatter can help you with this better than Pylint.
- The `no-space-check` option has been removed, it's no longer possible to consider empty line like a trailing-whitespace by using clever options.
- `mixed-indentation` has been removed, it is no longer useful since `TabError` is included directly in `python3`.
- Fixed `superfluous-parens` / `C0325 <#superfluous-parens>` false-positive for the walrus operator.
- Add support for both `isort 4` and `isort 5`. If you have pinned `isort 4` in your project requirements, nothing changes. If you use `isort 5`, though, note that the `known-standard-library` option is not interpreted the same in `isort 4` and `isort 5` (see the migration guide in isort documentation (no longer available) for further details). For compatibility's sake for most pylint users, the `known-standard-library` option in pylint now maps to `extra-standard-library` in `isort 5`. If you really want what `known-standard-library` now means in `isort 5`, you must disable the `wrong-import-order` check in pylint and run isort manually with a proper isort configuration file.
## 📜 Full changelog 📜
### 🩹 What's New in Pylint 2.6.1? 🩹
Release date: `2021-02-16`
- `Astroid` version has been set as `< 2.5` <lavender>Closes #4093</lavender>
### 🎀 What's New in Pylint 2.6.0? 🎀
Release date: `2020-08-20`
- Fixed various scope-related bugs in `undefined-variable` checker. <lavender>Closes #1082, #3434, #3461</lavender>
- `bad-continuation` and `bad-whitespace` have been removed, `black` or another formatter can help you with this better than Pylint. <lavender>Closes #246, #289, #638, #747, #1148, #1179, #1943, #2041, #2301, #2304, #2944, #3565</lavender>
- The `no-space-check` option has been removed. It's no longer possible to consider empty line like a trailing-whitespace by using clever options. <lavender>Closes #1368</lavender>
- `missing-kwoa` is no longer emitted when dealing with overload functions. <lavender>Closes #3655</lavender>
- `mixed-indentation` has been removed, it is no longer useful since `TabError` is included directly in `python3`. <lavender>Closes #2984 #3573</lavender>
- Added `super-with-arguments` check for flagging instances of Python 2 style super calls.
- Added an faq detailing which messages to disable to avoid duplicates w/ other popular linters.
- Fixed `superfluous-parens` / `C0325 <#superfluous-parens>` false-positive for the walrus operator. <lavender>Closes #3383</lavender>
- Fixed `fail-under` not accepting floats.
- Fixed a bug with `ignore-docstrings` ignoring all lines in a module.
- Fixed pre-commit config that could lead to undetected duplicate lines of code.
- Fixed a crash in parallel mode when the module's filepath is not set. <lavender>Closes #3564</lavender>
- Added `raise-missing-from` check for exceptions that should have a cause.
- Support both `isort 4` and `isort 5`. If you have pinned `isort 4` in your project requirements, nothing changes. If you use `isort 5`, though, note that the `known-standard-library` option is not interpreted the same in `isort 4` and `isort 5` (see the migration guide in isort documentation for further details). For compatibility's sake for most pylint users, the `known-standard-library` option in pylint now maps to `extra-standard-library` in `isort 5`. If you really want what `known-standard-library` now means in `isort 5`, you must disable the `wrong-import-order` check in pylint and run isort manually with a proper isort configuration file. <lavender>Closes #3722</lavender>
- Fixed a crash caused by not guarding against `InferenceError` when calling `infer_call_result`. <lavender>Closes #3690</lavender>
- Fixed a crash in parallel mode when the module's filepath is not set. <lavender>Closes #3564</lavender>
## 🌈 What's New In Pylint 2.5 🌈
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing — i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian.
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
### ✨ Summary -- Release highlights ✨
The summary section is serving empty-plate energy here — no bullets, just the next heading. 💅
### 🧐 New checkers
- A new check `isinstance-second-argument-not-valid-type` was added. This check is emitted whenever pylint finds a call to the `isinstance` function with a second argument that is not a type. Such code is likely unintended as it will cause a `TypeError` to be thrown at runtime error.
- A new check `assert-on-string-literal` was added. This check is emitted whenever pylint finds an `assert` statement with a string literal as its first argument. Such assert statements are probably unintended as they will always pass.
- A new check `f-string-without-interpolation` was added. This check is emitted whenever pylint detects the use of an f-string without having any interpolated values in it, which means that the f-string can be a normal string.
- Multiple checks for invalid return types of protocol functions were added:
- `invalid-bool-returned`: `__bool__` did not return a `bool`
- `invalid-index-returned`: `__index__` did not return an integer
- `invalid-repr-returned)`: `__repr__` did not return a string
- `invalid-str-returned)`: `__str__` did not return a string
- `invalid-bytes-returned)`: `__bytes__` did not return a string
- `invalid-hash-returned)`: `__hash__` did not return an integer
- `invalid-length-hint-returned)`: `__length_hint__` did not return a non-negative integer
- `invalid-format-returned)`: `__format__` did not return a string
- `invalid-getnewargs-returned)`: `__getnewargs__` did not return a tuple
- `invalid-getnewargs-ex-returned)`: `__getnewargs_ex__` did not return a tuple of the form `(tuple, dict)`
- A new check `inconsistent-quotes` was added. This check is emitted when quotes delimiters (`"` and `'`) are not used consistently throughout a module. It allows avoiding unnecessary escaping, allowing, for example, `"Don't error"` in a module in which single-quotes otherwise delimit strings so that the single quote in `Don't` doesn't need to be escaped.
- A new check `non-str-assignment-to-dunder-name` was added to ensure that only strings are assigned to `__name__` attributes.
### 🛠️ Other Changes
- Configuration can be read from a `setup.cfg` or `pyproject.toml` file in the current directory. A `setup.cfg` must prepend pylintrc section names with `pylint.`, for example `[pylint.MESSAGES CONTROL]`. A `pyproject.toml` file must prepend section names with `tool.pylint.`, for example `[tool.pylint.'MESSAGES CONTROL']`. These files can also be passed in on the command line.
- Added new `good-names-rgx` and `bad-names-rgx` to enable permitting or disallowing of names via regular expressions. To enable better handling of permitted/disallowed names, we added two new config options: `good-names-rgxs`: a comma-separated list of regexes, that if a name matches will be exempt from naming-checking. `bad-names-rgxs`: a comma-separated list of regexes, that if a name matches will be always marked as a disallowed name.
- Mutable `collections.*` are now flagged as dangerous defaults.
- Added new `--fail-under` flag for setting the threshold for the score to fail overall tests. If the score is over the `fail-under` threshold, pylint will complete `SystemExit` with value `0` to indicate no errors.
- Added a new option `notes-rgx` to make fixme warnings more flexible. Now either `notes` or `notes-rgx` option can be used to detect fixme warnings.
- Non-ASCII characters are now allowed by `invalid-name`.
- pylint no longer emits `invalid-name` for non-constants found at module level. Pylint was considering all module level variables as constants, which is not what PEP 8 is actually mandating.
- A new check `non-ascii-name` was added to detect identifiers with non-ASCII characters.
- Overloaded typing functions no longer trigger `no-self-use`, `unused-argument`, `missing-docstring` and similar checks that assumed that overloaded functions are normal functions.
- `python -m pylint` can no longer be made to import files from the local directory.
- A new command `--list-extensions` was added. This command lists all extensions present in `pylint.extensions`.
- Various false positives have been fixed, and you can read more about them in the Changelog files. 💖
- Multiple types of string formatting are allowed in logging functions. ✨
The `logging-fstring-interpolation` message has been brought back, lowkey as a comeback tour, to allow multiple types of string formatting to be used. Which type of formatting you get is chosen through **enabling and disabling messages** rather than through the `logging-format-style` option. And heads up bestie — the `fstr` value of the `logging-format-style` option is **not valid**. 🙈
---
## 💫 Full changelog 💫
### What's New in Pylint 2.5.3?
Release date: 2020-06-8
- Fix a regression where disable comments that have checker names with numbers in them are not parsed correctly. *Closes #3666*
- `property-with-parameters` properly handles abstract properties. *Closes #3600*
- `continue-in-finally` no longer emitted on Python 3.8, where it's now valid. *Closes #3612*
- Fix a regression where messages with a dash are not fully parsed. *Closes #3604*
- In a TOML configuration file, it's now possible to use rich (non-string) types — like list, integer or boolean — instead of strings. For example, one can now define a list of message identifiers to enable like this:
enable = [ "use-symbolic-message-instead", "useless-suppression", ]
*Closes #3538*
- Fix a regression where the score was not reported with multiple jobs. *Closes #3547*
- Protect against `AttributeError` when checking `cell-var-from-loop`. *Closes #3646*
### What's New in Pylint 2.5.2?
Release date: 2020-05-05
- `pylint.Run` accepts `do_exit` as a deprecated parameter. *Closes #3590*
### What's New in Pylint 2.5.1?
Release date: 2020-05-05
- Fix a crash in `method-hidden` lookup for unknown base classes. *Closes #3527*
- Revert `pylint.Run`'s exit parameter to `do_exit`. This had been inadvertently changed several releases ago to `do_exit`. *Closes #3533*
- `no-value-for-parameter` variadic detection has improved for assign statements. *Closes #3563*
- Allow package files to be properly discovered with multiple jobs. *Closes #3524*
- Allow linting directories without `__init__.py`, which was a regression in 2.5. *Closes #3528*
### What's New in Pylint 2.5.0?
Release date: 2020-04-27
- Fix a false negative for `undefined-variable` when using a class attribute in a comprehension. *Closes #3494*
- Fix a false positive for `undefined-variable` when using a class attribute in a decorator or as a type hint. *Closes #511* *Closes #1976*
- Remove HTML quoting of messages in JSON output. *Closes #2769*
- Adjust the `invalid-name` rule to work with non-ASCII identifiers, and add the `non-ascii-name` rule. *Closes #2725*
- Positional-only arguments are taken in account for `useless-super-delegation`.
- `unidiomatic-typecheck` is no longer emitted for `in` and `not in` operators. *Closes #3337*
- Positional-only argument annotations are taken in account for `unused-import`. *Closes #3462*
- Add a command to list available extensions.
- Allow used variables to be properly consumed when different checks are enabled / disabled. *Closes #3445*
- Fix `dangerous-default-value` / `W0102` rule to account for keyword argument defaults. *Closes #3373*
- Fix a false positive of `self-assigning-variable` on tuple unpacking. *Closes #3433*
- `no-self-use` is no longer emitted for typing stubs. *Closes #3439*
- Fix a false positive for `undefined-variable` when `__class__` is used. *Closes #3090*
- Emit `invalid-name` for variables defined in loops at module level. *Closes #2695*
- Add a check for cases where the second argument to `isinstance` is not a type. *Closes #3308*
- Add `'notes-rgx'` option, to be used for the `fixme` check. *Closes #2874*
- `function-redefined` exempts a function redefined on a condition. *Closes #2410*
- `typing.overload` functions are exempted from docstring checks. *Closes #3350*
- Emit `invalid-overridden-method` for improper `async def` overrides. *Closes #3355*
- Do not allow `python -m pylint ...` to import user code. `python -m pylint ...` adds the current working directory as the first element of `sys.path`. This opens up a potential security hole where pylint will import user level code as long as that code resides in modules having the same name as stdlib or pylint's own modules. *Closes #3386*
- Add `dummy-variables-rgx` option for the `_redeclared-assigned-name` check. *Closes #3341*
- Fixed graph creation for relative paths.
- Add a check for asserts on string literals. *Closes #3284*
- `not in` is considered an iterating context for some of the Python 3 porting checkers.
- A new check `inconsistent-quotes` was added. ✨
- Add a check for non-string assignment to the `__name__` attribute. *Closes #583*
- `__pow__`, `__imatmul__`, `__trunc__`, `__floor__`, and `__ceil__` are recognized as special method names. *Closes #3281*
- Added errors for protocol functions when invalid return types are detected. 💅
- `E0304` (`invalid-bool-returned`): `__bool__` did not return a `bool`
- `E0305` (`invalid-index-returned`): `__index__` did not return an integer
- `E0306` (`invalid-repr-returned`): `__repr__` did not return a string
- `E0307` (`invalid-str-returned`): `__str__` did not return a string
- `E0308` (`invalid-bytes-returned`): `__bytes__` did not return a string
- `E0309` (`invalid-hash-returned`): `__hash__` did not return an integer
- `E0310` (`invalid-length-hint-returned`): `__length_hint__` did not return a non-negative integer
- `E0311` (`invalid-format-returned`): `__format__` did not return a string
- `E0312` (`invalid-getnewargs-returned`): `__getnewargs__` did not return a tuple
- `E0313` (`invalid-getnewargs-ex-returned`): `__getnewargs_ex__` did not return a tuple of the form `(tuple, dict)`
*Closes #560*
- `missing-*-docstring` can look for `__doc__` assignments. *Closes #3301*
- `undefined-variable` can now find undefined loop iterables. *Closes #498*
- `safe_infer` can infer a value as long as all the paths share the same type. *Closes #2503*
- Add a `--fail-under <score>` flag, also configurable in a `.pylintrc` file. If the final score is more than the specified score, it's considered a success and pylint exits with exitcode `0`. Otherwise, it's considered a failure and pylint exits with its current exitcode based on the messages issued. *Closes #2242*
- Don't emit `line-too-long` for multilines when a `disable=line-too-long` comment stands at their end. *Closes #2957*
- Fixed an `AttributeError` caused by improper handling of dataclasses inference in `pyreverse`. *Closes #3256*
- Do not exempt bare `except` from `undefined-variable` and similar checks. If a node was wrapped in a `TryExcept`, pylint was taking a hint from the `except` handler when deciding to emit or not a message. We were treating bare `except` as a fully fledged ignore, but only the corresponding exceptions should be handled that way (e.g. `NameError` or `ImportError`). *Closes #3235*
- No longer emit `assignment-from-no-return` when a function only raises an exception. *Closes #3218*
- Allow import aliases to exempt `import-error` when used in type annotations. *Closes #3178*
- `Ellipsis` is exempted from `multiple-statements` for function overloads. *Closes #3224*
- No longer emit `invalid-name` for non-constants found at module level. Pylint was taking the following statement from PEP-8 too far, considering **all** module level variables as constants, which is not what the statement is saying:
> Constants are usually defined on a module level and written in all capital letters with underscores separating words.
*Closes #3111* *Closes #3132*
- Allow `implicit-str-concat-in-sequence` to be emitted for string juxtaposition. *Closes #3030*
- `implicit-str-concat-in-sequence` was renamed `implicit-str-concat`.
- The `json` reporter no longer bypasses `redirect_stdout`. *Closes #3227*
- Move `NoFileError`, `OutputLine`, `FunctionalTestReporter`, `FunctionalTestFile`, `LintModuleTest` and related methods from `test_functional.py` to `pylint.testutils` to help testing for 3rd party pylint plugins.
- Can read config from a `setup.cfg` or `pyproject.toml` file. *Closes #617*
- Fix `exception-escape` false positive with generators. *Closes #3128*
- `inspect.getargvalues` is no longer marked as deprecated.
- A new check `f-string-without-interpolation` was added. *Closes #3190*
- Flag mutable `collections.*` utilities as dangerous defaults. *Closes #3183*
- The `docparams` extension supports multiple types in `raises` sections. Multiple types can also be separated by commas in all valid sections. *Closes #2729*
- Allow parallel linting when run under Prospector.
- Fixed false positives of `method-hidden` when a subclass defines the method that is being hidden. *Closes #414*
- Python 3 porting mode is 30-50% faster on most codebases. 🔥
- Python 3 porting mode no longer swallows syntax errors. *Closes #2956*
- Pass the actual `PyLinter` object to sub processes to allow using custom `PyLinter` classes. The `PyLinter` object (and all its members except `reporter`) needs to support pickling so the `PyLinter` object can be passed to worker processes.
- Clean up `setup.py`. Make `pytest-runner` a requirement only if running tests, similar to McCabe. Clean up the `setup.py` file, resolving a number of warnings around it.
- Handle `SyntaxError` in files passed via the `--from-stdin` option. Pylint no longer outputs a traceback if a file, read from stdin, contains a syntaxerror. 🫶
- Fix `uppercase` style to disallow 3+ uppercase followed by lowercase.
- Fixed `undefined-variable` and `unused-import` false positives when using a metaclass via an attribute. *Closes #1603*
- Emit `unused-argument` for functions that partially use their argument list before raising an exception. *Closes #3246*
- Fixed the `broad_try_clause` extension to check `try`/`finally` statements and to check for nested statements (e.g., inside of an `if` statement).
- 🎀 recognize classes that *explicitly* inherit from `abc.ABC`, or that have an `abc.ABCMeta` metaclass, as abstract. that means they no longer trigger `W0223`. slay.
Closes #3098
- 🎀 fix overzealous `arguments-differ` when the overridden function uses variadics.
no message gets emitted if the overriding function provides positional or keyword variadics in its signature that can feasibly accept and pass on all the parameters given by the overridden function.
Closes #1482 Closes #1553
- 🎀 multiple types of string formatting are allowed in logging functions now.
the `logging-fstring-interpolation` message has been brought back, so multiple types of string formatting can be used.
Closes #3361
## 🌈 What's New In Pylint 2.4
okay so from pylint's beginning all the way through pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint — including changes that were *not* user facing, i.e. changes that might only interest you if you're a developer working on pylint or on pylint plugins, or an open-source historian. 💅
from pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
### ✨ Summary -- Release highlights
### 🦋 New checkers
- **`import-outside-toplevel`** — this check warns when modules are imported from places other than a module toplevel, e.g. inside a function or inside a class.
- added a new check, `consider-using-sys-exit` ✨
this check is emitted when we detect that a `quit()` or `exit()` is invoked instead of `sys.exit()`, which is the preferred way of exiting in program.
Closes #2925
- added a new check, `arguments-out-of-order` 🫶
this check warns if you have arguments with names that match those in a function's signature but you're passing them into the function in a different order.
Closes #2975
- added new checks, `no-else-break` and `no-else-continue` 💖
these checks highlight unnecessary `else` and `elif` blocks after `break` and `continue` statements.
Closes #2327
- added `unnecessary-comprehension`, which detects unnecessary comprehensions.
this check is emitted when pylint finds list-, set- or dict-comprehensions that are unnecessary and can be rewritten with the list-, set- or dict-constructors.
Closes #2905
- added a new check, `invalid-overridden-method` 👑
this check is emitted when we detect that a method is overridden as a property, or a property is overridden as a method. this can indicate a bug in the application code that will trigger a runtime error. 😭
Closes #2670
- added a new check, `redeclared-assigned-name`
this check is emitted when pylint detects that a name was assigned one or multiple times in the same assignment, which indicates a potential bug.
Closes #2898
- added a new check, `self-assigning-variable` 💀
this check is emitted when we detect that a variable is assigned to itself, which might indicate a potential bug in the code application.
for example, the following would raise this warning:
def new_a(attr, attr2): a_inst = Aclass() a_inst.attr2 = attr2 # should be: a_inst.attr = attr, but have a typo attr = attr return a_inst
Closes #2930
- added a new check `property-with-parameters`, which detects when a property has more than a single argument.
Closes #3006
- added `subprocess-run-check` to handle `subprocess.run` without explicitly set `check` keyword.
Closes #2848
- we added a new check message `dict-iter-missing-items`. this is emitted when trying to iterate through a `dict` in a for loop without calling its `.items()` method.
Closes #2761
- we added a new check message `missing-parentheses-for-call-in-test`. this is emitted in case a call to a function is made inside a test but it misses parentheses.
- a new check `class-variable-slots-conflict` was added. 🌸
this check is emitted when pylint finds a class variable that conflicts with a slot name, which would raise a `ValueError` at runtime.
for example, the following would raise an error:
class A: __slots__ = ('first', 'second') first = 1
- a new check `preferred-module` was added.
this check is emitted when pylint finds an imported module that has a preferred replacement listed in `preferred-modules`.
for example, you can set the preferred modules as `xml:defusedxml,json:ujson` to make pylint suggest using `defusedxml` instead of `xml` and `ujson` rather than `json`.
- a new extension `broad_try_clause` was added. 🧚
this extension enforces a configurable maximum number of statements inside of a `try` clause. this facilitates enforcing PEP 8's guidelines about `try` / `except` statements and the amount of code in the `try` clause.
you can enable this extension using `--load-plugins=pylint.extensions.broad_try_clause` and you can configure the amount of statements in a `try` statement using `--max-try-statements`.
### 💜 Other Changes
- don't emit `protected-access` when a single underscore prefixed attribute is used inside a special method
Closes #1802
- `len-as-condition` now only fires when a `len(x)` call is made without an explicit comparison. 🔥
the message and description accompanying this checker has been changed to reflect this new behavior, by explicitly asking to either rely on the fact that empty sequence are false or to compare the length with a scalar.
OK:
if len(x) == 0: pass
while not len(x) == 0: pass
assert len(x) > 5, message
KO:
if not len(x): pass
while len(x) and other_cond: pass
assert len(x), message
- a file is now read from stdin if the `--from-stdin` flag is used on the command line. in addition to the `--from-stdin` flag, a (single) file name needs to be specified on the command line, which is needed for the report.
- the checker for ungrouped imports is now more permissive.
the import can now be sorted alphabetically by import style. this makes pylint compatible with `isort`.
the following imports do not trigger an `ungrouped-imports` anymore
import unittest import zipfile from unittest import TestCase from unittest.mock import MagicMock
- the checker for missing return documentation is now more flexible.
the following does not trigger a `missing-return-doc` anymore
def my_func(self): """This is a docstring.
Returns ------- :obj:list of :obj:str List of strings """ return ["hi", "bye"] #@
- `signature-mutators` CLI and config option was added.
with this option, users can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, and `no-value-for-parameter` for functions decorated with decorators that change the signature of a decorated function.
for example a test may want to make use of hypothesis. adding `hypothesis.extra.numpy.arrays` to `signature_mutators` would mean that `no-value-for-parameter` would not be raised for:
@given(img=arrays(dtype=np.float32, shape=(3, 3, 3, 3))) def test_image(img): ...
- allow the option of f-strings as a valid logging string formatting method. ⭐
`logging-fstring--interpolation` has been merged into `logging-format-interpolation` to allow the `logging-format-style` option to control which logging string format style is valid. to allow this, a new `fstr` value is valid for the `logging-format-style` option.
- `--list-msgs-enabled` command was added.
when enabling/disabling several messages and groups in a config file, it can be unclear which messages are actually enabled and which are disabled. this new command produces the final resolved lists of enabled/disabled messages, sorted by symbol but with the ID provided for use with `--help-msg`.
## 📜 Full changelog
### 🎀 What's New in Pylint 2.4.4?
release date: 2019-11-13
- exempt all the names found in type annotations from `unused-import`
the previous code was assuming that only typing names need to be exempted, but we need to do that for the rest of the type comment names as well.
Closes #3112
- relax type import detection for names that do not come from the `typing` module
Closes #3191
### 🦋 What's New in Pylint 2.4.3?
release date: 2019-10-18
- fix an issue with `unnecessary-comprehension` in comprehensions with additional repacking of elements.
Closes #3148
- `import-outside-toplevel` is emitted for `ImportFrom` nodes as well.
Closes #3175
- do not emit `no-method-argument` for functions using positional only args.
Closes #3161
- `consider-using-sys-exit` is no longer emitted when `exit` is imported in the local scope.
Closes #3147
- `invalid-overridden-method` takes `abc.abstractproperty` in account
Closes #3150
- fixed `missing-yield-type-doc` getting incorrectly raised when a generator does not document a yield type but has a type annotation.
Closes #3185
- `typing.overload` functions are exempted from `too-many-function-args`
Closes #3170
### 🌸 What's New in Pylint 2.4.2?
release date: 2019-09-30
- `ignored-modules` can skip submodules.
Closes #3135
- `self-assigning-variable` skips class level assignments.
Closes #2930
- `consider-using-sys-exit` is exempted when `exit()` is imported from `sys`
Closes #3145
- exempt annotated assignments without variable from `class-variable-slots-conflict`
Closes #3141
- fix `utils.is_error` to account for functions returning early.
this fixes a false negative with `unused-variable` which was no longer triggered when a function raised an exception as the last instruction, but the body of the function still had unused variables.
Closes #3028
### 🫶 What's New in Pylint 2.4.1?
release date: 2019-09-25
- exempt type checking definitions defined in both clauses of a type checking guard
Closes #3127
- exempt type checking definitions inside the type check guard 🔥
in `a7f236528bb3758886b97285a56f3f9ce5b13a99` we added basic support for emitting `used-before-assignment` if a variable was only defined inside a type checking guard (using `TYPE_CHECKING` variable from `typing`). unfortunately that missed the case of using those type checking imports inside the guard itself, which triggered spurious `used-before-assignment` / `E0601 <#used-before-assignment>` errors.
Closes #3119
- require `astroid` >= `2.3` to avoid any compatibility issues.
### 👑 What's New in Pylint 2.4.0?
release date: 2019-09-24
- new check: `import-outside-toplevel`
- This check warns when modules are imported from places other than a module toplevel, e.g. inside a function or a class.
- Handle inference ambiguity for `invalid-format-index`
Closes #2752
- Removed Python 2 specific checks such as `relative-import`, `invalid-encoded-data`, `missing-super-argument`.
- Support forward references for `function-redefined` check.
Closes #2540
- Handle redefinitions in case of type checking imports.
Closes #2834
- Added a new check, `consider-using-sys-exit`
This check is emitted when we detect that a `quit()` or `exit()` is invoked instead of `sys.exit()`, which is the preferred way of exiting in program.
Closes #2925
- `useless-suppression` check now ignores `cyclic-import` suppressions, which could lead to false positives due to incomplete context at the time of the check.
Closes #3064
- Added new checks, `no-else-break` and `no-else-continue`
These checks highlight unnecessary `else` and `elif` blocks after `break` and `continue` statements.
Closes #2327
- Don't emit `protected-access` when a single underscore prefixed attribute is used inside a special method
Closes #1802
- Fix the "statement" values in the PyLinter's stats reports by module.
- Added a new check, `invalid-overridden-method`
This check is emitted when we detect that a method is overridden as a property or a property is overridden as a method. This can indicate a bug in the application code that will trigger a runtime error.
Closes #2670
- Added a new check, `arguments-out-of-order`
This check warns if you have arguments with names that match those in a function's signature but you are passing them in to the function in a different order.
Closes #2975
- Added a new check, `redeclared-assigned-name`
This check is emitted when pylint detects that a name was assigned one or multiple times in the same assignment, which indicate a potential bug.
Closes #2898
- Ignore lambda calls with variadic arguments without a context.
Inferring variadic positional arguments and keyword arguments will result into empty Tuples and Dicts, which can lead in some cases to false positives with regard to `no-value-for-parameter` / `E1120`. In order to avoid this, until we'll have support for call context propagation, we're ignoring such cases if detected. We already did that for function calls, but the previous fix was not taking in consideration lambdas
Closes #2918
- Added a new check, `self-assigning-variable`. This check is emitted when we detect that a variable is assigned to itself, which might indicate a potential bug in the code application.
Closes #2930
- Added a new check, `property-with-parameters`.
This check is emitted when we detect that a defined property also has parameters, which are useless.
Closes #3006
- Excluded protocol classes from a couple of checks.
Closes #3002.
- Add a check `unnecessary-comprehension` that detects unnecessary comprehensions.
This check is emitted when pylint finds list-, set- or dict-comprehensions, that are unnecessary and can be rewritten with the list-, set- or dict-constructors.
Closes #2905
- Excluded PEP 526 instance and class variables from `no-member`.
Closes #2945
- Excluded `attrs` from `too-few-public-methods` check.
Closes #2988.
- `unused-import` emitted for the right import names in function scopes.
Closes #2928
- Dropped support for Python 3.4.
- `assignment-from-no-return` not triggered for async methods.
Closes #2902
- Don't emit `attribute-defined-outside-init` for variables defined in setters.
Closes #409
- Syntax errors report the column number.
Closes #2914
- Support fully qualified typing imports for type annotations.
Closes #2915
- Exclude `__dict__` from `attribute-defined-outside-init`
- Fix pointer on spelling check when the error are more than one time in the same line.
Closes #2895
- Fix crash happening when parent of called object cannot be determined
- Allow `of` in `GoogleDocstring.re_multiple_type`
- Added `subprocess-run-check` to handle `subrocess.run` without explicitly set `check` keyword.
Closes #2848
- When we can't infer bare except handlers, skip `try-except-raise`
Closes #2853
- Handle more `unnecessary-lambda` cases when dealing with additional kwargs in wrapped calls
Closes #2845
- Better postponed evaluation of annotations handling
Closes #2847
- Support postponed evaluation of annotations for variable annotations.
Closes #2838
- `epylint.py_run` defaults to `python` in case the current executable is not a Python one.
Closes #2837
- Ignore raw docstrings when running Similarities checker with `ignore-docstrings=yes` option
- Fix crash when calling `inherit_from_std_ex` on a class which is its own ancestor
Closes #2680
- Added a new check that warns the user if a function call is used inside a test but parentheses are missing.
Closes #2658
- `len-as-condition` now only fires when a `len(x)` call is made without an explicit comparison
The message and description accompanying this checker has been changed reflect this new behavior, by explicitly asking to either rely on the fact that empty sequence are false or to compare the length with a scalar.
Closes #2684
- Add `preferred-module` checker that notify if an import has a replacement module that should be used.
This check is emitted when pylint finds an imported module that has a preferred replacement listed in `preferred-modules`.
- `assigning-non-slot` not emitted for classes with unknown base classes.
Closes #2807
- `old-division` is not emitted for non-Const nodes.
Closes #2808
- Added method arguments to the dot writer for pyreverse.
Closes #2139
- Support for linting file from stdin.
IDEs may benefit from the support for linting from an in-memory file.
Closes #1187
- Added a new check `class-variable-slots-conflict`
This check is emitted when pylint finds a class variable that conflicts with a slot name, which would raise a `ValueError` at runtime.
- Added new check: `dict-iter-missing-items` / `E1141` (E1141)
Closes #2761
- Fix issue with pylint name in output of `python -m pylint --version`
Closes #2764
- Relicense logo material under the CC BY-SA 4.0 license.
- Skip `if` expressions from f-strings for the `check_elif` checker
Closes #2816
- `C0412` (`ungrouped-import`) is now compatible with isort.
Closes #2806
- Added new extension to detect too much code in a `try` clause
Closes #2877
- `signature-mutators` option was added. With this option, users can choose to ignore `too-many-function-args`, `unexpected-keyword-arg`, and `no-value-for-parameter` for functions decorated with decorators that change the signature of a decorated function.
Closes #259
- Fixed a pragma comment on its own physical line being ignored when part of a logical line with the previous physical line.
Closes #199
- Fixed false `undefined-loop-variable` for a function defined in the loop, that uses the variable defined in that loop.
Closes #202
- Fixed `unused-argument` and `function-redefined` getting raised for functions decorated with `typing.overload`.
Closes #1581
- Fixed a false positive with `consider-using-dict-comprehension` for constructions that can't be converted to a comprehension
Closes #2963
- Added `__post_init__` to `defining-attr-methods` in order to avoid `attribute-defined-outside-init` in dataclasses.
Closes #2581
- Changed description of `W0199` to use the term `2-item-tuple` instead of `2-uple`.
- Allow a `.` as a prefix for Sphinx name resolution.
- Checkers must now keep a 1 to 1 relationship between "msgid" (ie: `C1234`) and "symbol" (i.e. : `human-readable-symbol`)
- In checkers, an `old_names` can now be used for multiple new messages and pylint is now a little faster
It means if you do a partial `old_names` for a message definition an exception will tell you that you must rename the associated identification.
- Allow the choice of f-strings as a valid way of formatting logging strings.
Closes #2395
- Added `--list-msgs-enabled` command to list all enabled and disabled messages given the current RC file and command line arguments.
---
# ✨ What's New In Pylint 2.3 ✨
Okay bestie, quick lore drop before the actual list 🫶 From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing, i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian.
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
## 🌸 Summary -- Release highlights 🌸
- This release improves the performance of the 2.X series after it was affected by a performance regression a couple of releases ago.
## 🎀 New checkers 🎀
- We added a new check message `wrong-exception-operation`. This is emitted when an operation is done against an exception, but the operation is not valid for the exception in question. Usually emitted when having binary operations between exceptions in `except` handlers.
Closes #2494
- We added a new `no-else-raise` warning similar to `no-else-return`
Closes #2558
- We added a new option `check-str-concat-over-line-jumps` to check `implicit-str-concat-in-sequence` over multiple lines.
## 🦋 Other Changes 🦋
Quite a lot of bug fixes and improvements went into this release, here's a handful of them. For the full changes, check the full changelog.
- We no longer emit `*-not-iterating` checks for builtins consumed by `itertools`
- We fixed some false positives for `no-self-argument` and `unsubscriptable-object` when using `__class_getitem__` (new in Python 3.7)
- pylint now supports `Ellipsis` as a synonym for `pass` statements.
- `fixme` gets triggered only on comments.
- pylint exempts starred unpacking from `*-not-iterating` Python 3 checks.
- `compare-to-zero` is now less zealous by checking against equality and identity.
`yield from` is exempted from `-not-iterating` Python 3 checks.
- A false positive with `not-async-context-manager` caused by not understanding `contextlib.asynccontextmanager` was fixed.
- We refactored `bad-reversed-sequence` to account for more objects that can define `__reversed__`.
- `no-member` is now emitted for enums when they lack a member. 💅
- Plugins can now use the `load_configuration()` hook. This hook is executed after configuration is loaded to prevent overwriting plugin specific configuration via user-based configuration.
- There's a new command line option `list-groups` for listing all the check groups pylint knows about. This is useful to know what groups you can disable or enable individually. 🎀
---
## 🗂️ Full changelog
### What's New in Pylint 2.3.0?
Release date: `2019-02-27`
- Protect against `NonDeducibleTypeHierarchy` when calling semi-private `is_subtype`
`astroid.helpers.is_subtype` raises `NonDeducibleTypeHierarchy` when it cannot infer the base classes of the given types, but that makes sense in its context given that the method is mostly used to inform the inference process about the hierarchy of classes. Doesn't make that much sense for pylint itself, which is why we're handling the exception here, rather than in astroid 😌
Closes `pylint-dev/astroid#644`
- Added a new command line option `list-groups` for listing all the check groups pylint knows about.
- Allow `BaseException` for emitting `broad-except`, just like `Exception`. Closes #2741
- Fixed a crash that occurred for `bad-str-strip-call` when `strip()` received `None`. Closes #2743
- Don't emit `*-not-iterating` checks for builtins consumed by `itertools`. Closes #2731
- Fix a crash caused by iterating over `Uninferable` in a string formatting check. Closes #2727
- Fixed false positives for `no-self-argument` and `unsubscriptable-object` when using `__class_getitem__` (new in Python 3.7). Closes #2416
- Support `Ellipsis` as a synonym for `pass` statements. Closes #2718
- `fixme` gets triggered only on comments. Closes #2321
- Fixed a false positive for `unused-variable` and `nonlocal` assignments. Closes #2671
- Added `load_configuration()` hook for plugins
New optional hook for plugins is added: `load_configuration()`. This hook is executed after configuration is loaded to prevent overwriting plugin specific configuration via user-based configuration.
Closes #2635
- Fix `missing-raises-doc` (W9006) false positive (W9006). Closes #1502
- Exempt starred unpacking from `*-not-iterating` Python 3 checks. Closes #2651
- Make `compare-to-zero` less zealous by checking against equality and identity. Closes #2645
- Add `no-else-raise` warning (R1720). Closes #2558
- Exempt `yield from` from `*-not-iterating` Python 3 checks. Closes #2643
- Fix incorrect generation of `no-else-return` warnings (R1705)
Fixed issue where `if` statements with nested `if` statements were incorrectly being flagged as `no-else-return` in some cases and not being flagged as `no-else-return` in other cases. Added tests for verification and updated pylint source files to eliminate newly exposed warnings. 😤
- Fix false positive with `not-async-context-manager` caused by not understanding `contextlib.asynccontextmanager`. Closes #2440
- Refactor `bad-reversed-sequence` to account for more objects that can define `__reversed__`
One such object would be an enum class, for which `__reversed__` yields each individual enum. As such, the check for `bad-reversed-sequence` needs to not differentiate between classes and instances when it comes for checking of `__reversed__` presence.
Closes #2598
- Added `wrong-exception-operation`
Used when an operation is done against an exception, but the operation is not valid for the exception in question. Usually emitted when having binary operations between exceptions in `except` handlers.
Closes #2494
- `no-member` is emitted for enums when they lack a member
Previously we weren't doing this because we detected a `__getattr__` implementation on the `Enum` class (and this check is skipped for classes with `__getattr__`), but that is fine for `Enum`s, given that they are inferred in a customised way in astroid.
Closes #2565
- Generalize `chained-comparison`
Previous version incorrectly detects `a < b < c and b < d` and fails to detect `a < b < c and c < d`. 💀
- Avoid popping `__main__` when using multiple jobs. Closes #2689
- Add a new option `'check-str-concat-over-line-jumps'` to check `implicit-str-concat-in-sequence` (W1403)
- Fixes for the new style logging format linter.
The number of arguments was not handled properly, leading to an always successful check. 🙈
- Fix false positive `not-callable` for uninferable properties.
- Fix false positive `useless-else-on-loop` if the `break` is deep in the `else` of an inner loop.
- Minor improvements to the help text for a few options.
---
## 🌈 What's New In Pylint 2.2
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing, i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian. 📜
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
### ✨ Summary -- Release highlights
### 🆕 New checkers
- String checker now reports format string type mismatches.
- `duplicate-string-formatting-argument` was added for detecting duplicate string formatting arguments that should be passed instead as named arguments.
- `logging-format-style` is a new option for the logging checker for usage of `str.format()` style format strings in calls to loggers.
It accepts two options: `--logging-format-style=old` for using `%` style formatting, which is the assumed default, and `--logging-format-style=new` for using `{}` style formatting.
- `implicit-str-concat-in-sequence` detects string concatenation inside lists, sets & tuples.
Example of code that would generate such warning:
woops = ('a', 'b' 'c')
### 🔄 Other Changes
- `try-except-raise` checker now handles multilevel inheritance hirerachy for exceptions correctly. Closes #2484
- Ignore `import x.y.z as z` cases for checker `useless-import-alias`.
- `unnecessary-pass` is now also emitted when a function or class contains only docstring and pass statement, in which case, docstring is enough for empty definition.
- Fix false positive `undefined-variable` and `used-before-assignment` with `nonlocal` keyword usage.
- Fix exceptions being raised when one of the params is not a `ClassDef` for `checkers.utils.is_subclass_of`.
- pylint now picks the latest value from the inferred values of the exception that gets raised, when looking for `raising-non-exception`. This helps when reusing a variable name for multiple types, since pylint was picking just the first inferred value, leading to spurious false positives. Closes #2431
- pylint used to emit a `not-an-iterable` error when looking at coroutines built with `asyncio.coroutine`. This is no longer the case as we handle coroutines explicitly. Closes #996
- pylint used to emit an `unused-variable` error if unused import was found in the function. Now instead of `unused-variable`, `unused-import` is emitted. Closes #2421
### 📚 Full changelog
#### What's New in Pylint 2.2.2?
Release date: `2018-11-28`
- Change the `logging-format-style` to use name identifier instead of their corresponding Python identifiers
This is to prevent users having to think about escaping the default value for `logging-format-style` in the generated config file. Also our config parsing utilities don't quite support escaped values when it comes to choices detection, so this would have needed various hacks around that.
Closes #2614
#### What's New in Pylint 2.2.1?
Release date: `2018-11-27`
- Fix a crash caused by `implicit-str-concat-in-sequence` and multi-bytes characters. Closes #2610
#### What's New in Pylint 2.2?
Release date: `2018-11-25`
- Consider `range()` objects for `undefined-loop-variable` leaking from iteration. Closes #2533
- `deprecated-method` can use the attribute name for identifying a deprecated method
Previously we were using the fully qualified name, which we still do, but the fully qualified name for some `unittest` deprecated aliases leads to a generic deprecation function. Instead on relying on that, we now also rely on the attribute name, which should solve some false positives. Closes #1653 Closes #1946
- Fix compatibility with changes to stdlib tokenizer.
- pylint is less eager to consume the whole line for pragmas. Closes #2485
- Obtain the correct number of CPUs for virtualized or containerized environments. Closes #2519
- Change `unbalanced-tuple-unpacking` back to a warning.
It used to be a warning until a couple of years ago, after it was promoted to an error. But the check might be suggesting the wrong thing in some cases, for instance when checking against `sys.argv` which cannot be known at static analysis time. Given it might rely on potential unknown data, it's best to have it as a warning. Closes #2522
- Remove enumerate usage suggestion when defining `__iter__` (C0200). Closes #2477
- Emit `too-many-starred-assignment` only when the number of `Starred` nodes is per assignment elements. Closes #2513
- `try-except-raise` checker now handles multilevel inheritance hirerachy for exceptions correctly. Closes #2484
- Add a new check, `simplifiable-if-expression` for expressions like `True if cond else False`. Closes #2487
- `too-few-public-methods` is not reported for `typing.NamedTuple`. Closes #2459
- `too-few-public-methods` is not reported for dataclasses created with options. Closes #2488
- Remove wrong modules from `'bad-python3-import'`. Closes #2453
- The json reporter prints an empty list when no messages are emitted. Closes #2446
- Add a new check, `duplicate-string-formatting-argument`
This new check is emitted whenever a duplicate string formatting argument is found. Closes #497
- `assignment-from-no-return` is not emitted for coroutines. Closes #1715
- Report format string type mismatches.
- `consider-using-ternary` and `simplified-boolean-expression` no longer emit for sequence based checks. Closes #2473
- Handle `AstroidSyntaxError` when trying to import a module. Closes #2313
- Allow `__module__` to be redefined at a class level. Closes #2451
- <pink>**`unused-variable` → `unused-import` glow-up**</pink>: pylint used to emit `unused-variable` if an unused import was found in the function. Now instead of `unused-variable`, `unused-import` is emitted. Closes #2421 ✨
- Handle `asyncio.coroutine` when looking for the `not-an-iterable` check. Closes #996 🌀
- The `locally-enabled` check is gone. Closes #2442 💅
- Infer decorated methods when looking for `method-hidden`. Closes #2369 🎀
- Pick the latest value from the inferred values when looking for `raising-non-exception`. Closes #2431 🔥
- Extend the `TYPE_CHECKING` guard to the `TYPE_CHECKING` name as well, not just the attribute. Closes #2411 🦋
- Ignore `import x.y.z as z` cases for checker `useless-import-alias`. Closes #2309 🌸
- Fix false positive `undefined-variable` and `used-before-assignment` with `nonlocal` keyword usage. Closes #2049 💜
- Stop `protected-access` exception for missing class attributes. 🛡️
- Don't emit `assignment-from-no-return` for decorated function nodes. Closes #2385 ✨
- `unnecessary-pass` is now also emitted when a function or class contains only docstring and `pass` statement. In Python, stubbed functions often have a body that contains just a single `pass` statement, indicating that the function doesn't do anything. However, a stubbed function can also have just a docstring, and function with a docstring and no body also does nothing. Closes #2208 😭
- `duplicate-argument-name` is emitted for more than one duplicate argument per function. Closes #1712 💀
- Allow double indentation levels for more distinguishable indentations. Closes #741 🎀
- Consider tuples in exception handler for `try-except-raise`. Closes #2389 🧚
- Fix `astroid.ClassDef` check in `checkers.utils.is_subclass_of`. 🔧
- Fix wildcard imports being ignored by the import checker. 🌈
- Fix external/internal distinction being broken in the import graph. 🫶
- Fix wildcard import check not skipping `__init__.py`. Closes #2430 🍓
- Add new option to logging checker, `logging_format_style`. ✨
- Fix `--ignore-imports` to understand multi-line imports. Closes #1422 Closes #2019 💅
- Add a new check `implicit-str-concat-in-sequence` to spot string concatenation inside lists, sets & tuples. 🔎
- `literal-comparison` is now emitted for `0` and `1` literals. ⭐
# 🌸 What's New In Pylint 2.1 🌸
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing — i.e. changes that might interest you only if you're a developer working on pylint or pylint plugins, or an open-source historian. 📜 From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. 💖
## 🌟 Summary -- Release highlights 🌟
- This release mostly includes fixes for bugs found after the launch of 2.0. 🐛✨
## 🔎 New checkers 🔎
- A new check was added, `misplaced-format-function`. This message is emitted when pylint detects that a format function is called on non `str` object. This can occur due to wrong placement of closing bracket, e.g.
print('value: {}').format(123) # bad
print('value: {}'.format(123)) # good
## 🔧 Other Changes 🔧
- `try-except-raise` check was demoted from an error to a warning, as part of issue #2323. 💅
- Correctly handle the new name of the Python implementation of the `abc` module. In Python 3.7, the `abc` module has both a C implementation as well as a Python one, but the Python implementation has a different file name than what pylint was expecting, resulting in some checks getting confused. 🦋
- Modules with `__getattr__` are exempted by default from `no-member`. There's no easy way to figure out if a module has a particular member when the said module uses `__getattr__`, which is a new addition to Python 3.7. Instead we assume the safe thing to do, in the same way we do for classes, and skip those modules from checking. 🛡️
- `invalid name` is no longer triggered for function and attribute names longer than 30 characters. The upper limit was removed completely. ✂️
- Fix false-positive `undefined-variable` for self referential class name in lamdbas. 🌀
- `no-else-return` also specifies the type of the branch that is causing the error. 🌿
- Fixed inconsistent behaviour for `bad-continuation` on first line of file. 📄
- Fixed a bug where pylint was not able to disable certain messages on the last line through the global disable option. 🙈
- pylint no longer emits `useless-return` when it finds a single statement that is the return itself. We still want to be explicit when a function is supposed to return an optional value; even though `pass` could still work, it's not explicit enough and the function might look like it's missing an implementation. 💖
- Fixed a bug where pylint was crashing when being unable to infer the value of an argument to `next()`. 😭
- pylint no longer emit `not-an-iterable` when dealing with async iterators. 🌀
- pylint gained the ability to specify a default docstring type for when the check cannot guess the type. For this we added a `--default-docstring-type` command line option. 🎀
# 📜 Full changelog 📜
# 🦋 What's New in Pylint 2.1.1? 🦋
Release date: 2018-08-07 🗓️
- fix pylint crash due to `misplaced-format-function` not correctly handling class attribute. Closes #2384 💀
- Do not emit `*-builtin` for Python 3 builtin checks when the builtin is used inside a `try-except`. Closes #2228 🛡️
- `simplifiable-if-statement` not emitted when dealing with subscripts. 🔎
# 🎀 What's New in Pylint 2.1? 🎀
Release date: 2018-08-01 🗓️
- `trailing-comma-tuple` gets emitted for `yield` statements as well. Closes #2363 ✨
- Get only the arguments of the scope function for `redefined-argument-from-local`. Closes #2364 🫶
- Add a check `misplaced-format-function` which is emitted if format function is used on non `str` object. Closes #2200 💅
- `chain.from_iterable` no longer emits `dict-{}-not-iterating` when dealing with dict values and keys. 🌀
- Demote the `try-except-raise` message from an error to a warning (`E0705` -> `W0706`). Closes #2323 🔥
- Correctly handle the new name of the Python implementation of the `abc` module. Closes pylint-dev/astroid#2288 🦋
- Modules with `__getattr__` are exempted by default from `no-member`. There's no easy way to figure out if a module has a particular member when the said module uses `__getattr__`, which is a new addition to Python 3.7. Instead we assume the safe thing to do, in the same way we do for classes, and skip those modules from checking. Closes #2331 🛡️
- Fix a false positive `invalid name` message when method or attribute name is longer then 30 characters. Closes #2047 ✂️
- Include the type of the next branch in `no-else-return`. Closes #2295 🌿
- Fix inconsistent behaviour for `bad-continuation` on first line of file. Closes #2281 📄
- Fix not being able to disable certain messages on the last line through the global disable option. Closes #2278 🙈
- Don't emit `useless-return` when we have a single statement that is the return itself. We still want to be explicit when a function is supposed to return an optional value; even though `pass` could still work, it's not explicit enough and the function might look like it's missing an implementation. Closes #2300 💖
- Fix false-positive `undefined-variable` / `E0602` (`#undefined-variable`) for self referential class name in lamdbas. Closes #704 🌀
- Don't crash when pylint is unable to infer the value of an argument to `next()`. Closes #2316 😭
- Don't emit `not-an-iterable` when dealing with async iterators. But do emit it when using the usual iteration protocol against async iterators. Closes #2311 🌈
- Can specify a default docstring type for when the check cannot guess the type. Closes #1169 🎀
# 👑 What's New In Pylint 2.0 👑
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing — i.e. changes that might interest you only if you're a developer working on pylint or pylint plugins, or an open-source historian. 📜 From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. 💖
## 🌟 Summary -- Release highlights 🌟
- Dropped support for Python 2. This release will work only on Python 3.4+. If you need to use pylint with Python 2, you can use Pylint 1.9+. We'll continue to do bug releases until 2020, when Python 2 goes officially EOL. pylint will gain the ability to analyze Python 2 files, but some checks might not work as they will assume that their running environment is Python 2. 💀✨
- Given the dropping of Python 2, the Python 3 porting mode (enabled via `--py3k`) can now also run with Python 3. The porting mode used to be a no-op on Python 3, but most of the messages can now be emitted when the running interpreter is Python 3. The only messages that won't be emitted are those that rely on a particular syntax specific to Python 2, for instance `print` as a statement. 🦋
## 🔎 New checkers 🔎
- A new check was added, `useless-object-inheritance`. This refactoring message is emitted when pylint detects that a class inherits from `object`, which is redundant as in Python 3, every class implicitly inherits from `object`.
class A(object): pass
class A: # better pass
- A new check was added, `comparison-with-callable`. This refactoring message is emitted when pylint detects that a comparison with a callable was made, which might suggest that some parenthesis were omitted, resulting in potential unwanted behaviour.
def foo(): return None
def goo(): return None
if foo == 786: # bad pass
if foo() == 786: # good pass
- A new check was added, `chained-comparison`. This refactoring message is emitted if a boolean operation can be simplified by chaining some of its operations. check below example:
if a < b and b < c: pass
if a < b < c: # better pass
- A new check was added, `useless-import-alias`. This refactoring message is emitted when an import alias does not rename the original package.
import numpy as numpy # bad import numpy as np # good from collection import OrderedDict as OrderedDict # bad from collection import OrderedDict as ordered_dict # good
- A new check was added, `comparison-with-itself`.
- **compare a variable to itself** — new refactoring message drops when a variable gets compared against itself. 💅
if variable == variable: # bad pass
- **`consider-using-in`** — a brand new check, bestie. ✨ This refactoring message shows up when you compare one variable against a bunch of values glued together with `or`s instead of using the faster, way more idiomatic `in` check.
if variable == 1 or variable == 2 or variable == 3: # bad pass
if variable in (1, 2, 3): # good pass
- **`consider-using-get`** — another new check 🎀 — fires when you're manually checking whether a key is in a dictionary and then grabbing its value (and optionally a default if it isn't there) instead of just using the far more idiomatic `dict.get`.
if 'key' in dictionary: # bad variable = dictionary['key'] else: variable = 'default'
variable = dictionary.get('key', 'default') # good
- **`consider-using-join`** — new check, and it's giving efficiency 🦋 — emitted when you use a `for` loop over an iterable to join strings instead of the faster, less memory-hungry, more idiomatic `str.join(sequence)`.
result = '' # bad for number in ['1', '2', '3']: result += number
result = ''.join(['1', '2', '3']) # good
- **new `useless-return` message** — when a function or method ends with a `"return"` or `"return None"` statement and that's literally the only return statement in the whole body. 💀
- **new `use-symbolic-message-instead` message** — when a message gets activated or deactivated by id instead of by symbol. Using the symbol is more explicit and easier to remember. iykyk.
- **`consider-swap-variables`** — new check alert 🎀 — this refactoring message lands when you use a temporary variable to swap the values of two variables instead of the shorter, more idiomatic tuple-unpacking approach.
Instead of a temporary variable, the one-line syntax with commas should be used.
See this style guide document `https://docs.python-guide.org/writing/style/` or the Pycon 2007 swap values presentation for details.
temp = a # the wrong way a = b b = temp
a, b = b, a # the right way
- **two new checks, `invalid-envvar-value` and `invalid-envvar-default`** — added! 💖
The former is triggered whenever pylint detects that environment variable manipulation functions use a different type than strings, while the latter is emitted whenever the said functions are using a default variable of a different type than expected.
- **`subprocess-popen-preexec-fn`** — a new check, and it's a safety one 🌸
This refactoring message is emitted when using the keyword argument `preexec_fn` when creating `subprocess.Popen` instances, which may be unsafe when used in the presence of threads.
See `subprocess.Popen` at `https://docs.python.org/3/library/subprocess.html#popen-constructor` for full warning details.
- **new `try-except-raise` message** — fires when an `except` handler block has a bare `raise` statement as its first operator, or when the exception type being raised is the same as the one being handled. 👑
- **new `possibly-unused-variable` check** — added!
This is similar to `unused-variable`, the only difference is that it's emitted when we detect a `locals()` call in the scope of the unused variable. The `locals()` call could potentially use the said variable, by consuming all values that are present up to the point of the call. This new check allows you to disable this error when the user intentionally uses `locals()` to consume everything.
For instance, the following code will now trigger this new error:
def func(): some_value = some_call() return locals()
- **new `unhashable-dict-key` check** — added to detect dict lookups using unhashable keys such as lists or dicts. 🔥
- **new `self-cls-assignment` warning check** — added.
This is a warning if the first argument of an instance/class method gets assigned:
class Foo(object): def foo(self, bar): self = bar
- **new verbose mode option `--verbose`** — displays extra non-checker-related output. Disabled by default. 🫶
- **two new checks for recommending dict and set comprehensions** where possible — added!
These two checks are going to flag the following examples:
dict([(k, v) for (k, v) in ...]) # better as {k: v for k, v in ...} set([k for k in ...]) # better as {k for k in ...}
---
## 🦋 Other Changes 🦋
- **a couple of performance improvements brought to astroid** should make pylint a bit faster too. ⚡
We added a new flag, `max_inferable_values` on `astroid.MANAGER`, for limiting the maximum amount of values that astroid can infer when inferring values. This change should improve the performance when dealing with large frameworks such as django. You can also control this behaviour with `pylint --limit-inference-results`.
We also rewrote how `nodes_of_class` and `get_children` methods operate, which should result in a performance boost for a couple of checks.
- Fix a false positive `inconsistent-return-statements` message when an exception is raised inside an `else` statement.
- Don't warn for `missing-type-doc` and/or `missing-return-type-doc` if type annotations exist on the function signature for a parameter and/or return type.
- Fix a false positive `inconsistent-return-statements` message when an `if` statement is inside `try`/`except`.
- Fix a false positive `inconsistent-return-statements` message when `while` loops are used.
- Fix emission of false positive `no-member` message for classes with "private" attributes whose name is mangled.
- Fix `unused-argument` false positives with overshadowed variable in dictionary comprehension.
- Fixing false positive `inconsistent-return-statements` when never-returning functions are used (i.e. such as `sys.exit`).
- Fix false positive `inconsistent-return-statements` message when a function is defined under an `if` statement.
- Fix false positive `inconsistent-return-statements` message by avoiding useless exception inference if the exception is not handled.
- Fix false positive `undefined-variable` for lambda argument in class definitions.
- Suppress false-positive `not-callable` messages from certain `staticmethod` descriptors.
- Expand `ignored-argument-names` to include starred arguments and keyword arguments.
- `singleton-comparison` will suggest better boolean conditions for negative conditions.
- `undefined-loop-variable` takes into consideration non-empty iterred objects before emitting.
For instance, if the loop iterable is not empty, this check will no longer be emitted.
- Enum classes no longer trigger `too-few-methods`.
- Special methods now count towards `too-few-methods`, and are considered part of the public API. They are still not counted towards the number of methods for `too-many-methods`.
- `docparams` extension allows abstract methods to document returns documentation even if the default implementation does not return something. They also no longer need to document raising a `NotImplementedError`.
- Skip wildcard import check for `__init__.py`.
- Don't warn `useless-super-delegation` / `W0235` if the subclass method has different type annotations.
- Don't warn that a global variable is unused if it is defined by an import. 🙈
def func(): global sys import sys
- **added basic support for postponed evaluation of function annotations.** 🌈
If pylint detects the corresponding `from __future__ import annotations` import, it will not emit `used-before-assignment` and `undefined-variable` in the cases triggered by the annotations.
More details on the postponed evaluation of annotations can be read in PEP 563 at `https://peps.python.org/pep-0563/`.
- **a new command line option was added, `--exit-zero`** — for the use of continuous integration scripts which abort if a command returns a non-zero status code. If the option is specified, and Pylint runs successfully, it will exit with `0` regardless of the number of lint issues detected.
> Okay so this part matters, bestie: configuration errors, parse errors, and calling Pylint with invalid command-line options **all still return a non-zero error code**, even if `--exit-zero` is specified. No cap. 💅
- Don't emit `unused-import` anymore for typing imports used in type comments. For instance, in the following example pylint used to complain that `Any` and `List` are not used, while they should be considered used by a type checker.
from typing import Any, List a = 1 # type: List[Any]
- Fix false positive `line-too-long` for commented lines at the end of module.
- Fix emitting `useless-super-delegation` when changing the default value of keyword arguments.
- **Support `typing.TYPE_CHECKING` for `unused-import` errors** 🎀
When modules are imported under `typing.TYPE_CHECKING` guard, pylint will no longer emit `unused-import`.
- Fix false positive `unused-variable` in lambda default arguments.
- `assignment-from-no-return` considers methods as well as functions. ✨
If you have a method that doesn't return a value, but later on you assign a value to a function call to that method (so basically it will be `None`), then pylint is going to emit an `assignment-from-no-return` error.
- **a new flag was added, `--ignore-none`** — which controls the `no-member` behaviour with respect to `None` values. 🌸
Previously pylint was not emitting `no-member` if it inferred that the owner of an attribute access is a `None` value. In some cases, this might actually cause bugs, so if you want to check for `None` values as well, pass `--ignore-none=n` to pylint.
- Fix false-positive `bad-continuation` for `with` statements.
- Fix false-positive `bad-whitespace` message for typing annotations with ellipses in them.
- Fix false-positive `undefined-variable` for nested lambdas.
---
# 🌈 Full changelog 🌈
## ⭐ What's New in Pylint 2.0? ⭐
Release date: `2018-07-15`
- `try-except-raise` should not be emitted if there are any parent exception class handlers.
Closes `#2284`
- `trailing-comma-tuple` can be emitted for `return` statements as well. 🦋
Closes `#2269`
- Fix a false positive `inconsistent-return-statements` message when an exception is raised inside an `else` statement.
Closes `#1782`
- `ImportFrom` nodes correctly use the full name for the import sorting checks.
Closes `#2181`
- `[].extend` and similar builtin operations don't emit `dict-*-not-iterating` with the Python 3 porting checker.
Closes `#2187`
- ✨ Added a check `consider-using-dict-comprehension` — it fires when you initialize a dict the old-school way with list comprehensions instead of, y'know, an actual dict comprehension. <pink>You're better than that bestie 💅</pink>
- 🎀 Added a check `consider-using-set-comprehension` — same energy, but for sets: it fires if you initialize a set the old style with list comprehensions.
- 🌸 `logging-not-lazy` is emitted whenever pylint infers that a string is built with addition.
Closes #2193
- 🦋 Added a check `chained-comparison` — it's emitted if a boolean operation can be simplified by chaining some of its operations. e.g `"a < b and b < c"` can be simplified as `"a < b < c"`. <mint>so clean, so iconic ✨</mint>
Closes #2032
- 💖 Added a check `consider-using-in` for comparisons of a variable against multiple values with `"=="` and `"or"`s instead of checking if the variable is contained `"in"` a tuple of those values.
- 🌈 `in` is considered iterating context for some of the Python 3 porting checkers.
Closes #2186
- ⭐ Added `--ignore-none` flag to control if pylint should warn about `no-member` where the owner is `None`.
- 🫶 Fixed a false positive related to `too-many-arguments` and bounded `__get__` methods.
Closes #2172
- 💜 `mcs` as the first parameter of metaclass's `__new__` method was replaced by `cls`.
Closes #2028
- 🍓 `assignment-from-no-return` considers methods as well.
Closes #2081
- 🧚 Support `typing.TYPE_CHECKING` for `unused-import` errors.
Closes #1948
- 🔥 Inferred classes at a function level no longer emit `invalid-name` when they don't respect the variable regular expression.
Closes #1049
- 👑 Added basic support for postponed evaluation of function annotations.
Closes #2069
- 🦋 Fixed a bug with `missing-kwoa` and variadics parameters.
Closes #1111
- 💀 `simplifiable-if-statement` takes in account only when assigning to same targets.
Closes #1984
- 🌸 Made `len-as-condition` test more cases, such as `len() < 1` or `len <= 0`.
- ✨ Fixed false-positive `line-too-long` message emission for commented line at the end of a module.
Closes #1950
- 🎀 Fixed false-positive `bad-continuation` for `with` statements.
Closes #461
- 🌈 Don't warn about `stop-iteration-return` when using `next()` over `itertools.count`.
Closes #2158
- 💖 Added a check `consider-using-get` for unidiomatic usage of value/default-retrieval for a key from a dictionary.
Closes #2076
- 🔥 `invalid-slice-index` / `E1127` is not emitted when the slice is used as index for a complex object.
We only use a handful of known objects (`list`, `set` and friends) to figure out if we should emit `invalid-slice-index` / `E1127` when the slice is used to subscript an object.
- 🫶 Don't emit `unused-import` anymore for typing imports used in type comments.
- ⭐ Added a new check `useless-import-alias` / `C0414`.
Closes #2052
- 💜 Added `comparison-with-callable` to warn for comparison with bare callable, without calling it.
Closes #2082
- 🧚 Don't warn for `missing-type-doc` and/or `missing-return-type-doc`, if type annotations exist on the function signature for a parameter and/or return type.
Closes #2083
- 🍓 Added `--exit-zero` option for continuous integration scripts to more easily call Pylint in environments that abort when a program returns a non-zero (error) status code.
Closes #2042
- 👑 Warn if the first argument of an instance/class method gets assigned.
Closes #977
- 💀 New check `comparison-with-itself` to check comparison between same value.
Closes #2051
- 🌸 Added a new warning, `logging-fstring-interpolation` / `W1203`, emitted when f-string is used within logging function calls.
Closes #1998
- ✨ Don't show `useless-super-delegation` / `W0235` if the subclass method has different type annotations.
Closes #1923
- 🎀 Added `unhashable-dict-key` check.
Closes #586
- 🌈 Don't warn that a global variable is unused if it is defined by an import.
Closes #1453
- 💖 Skip wildcard import check for `__init__.py`.
Closes #2026
- 🔥 The Python 3 porting mode can now run with Python 3 as well.
- 🫶 `too-few-public-methods` is not emitted for dataclasses.
Closes #1793
- ⭐ New verbose mode option, enabled with `--verbose` command line flag, to display of extra non-checker-related output. It is disabled by default.
Closes #1863
- 💜 `undefined-loop-variable` takes in consideration non-empty iterred objects before emitting.
Closes #2039
- 🧚 Added support for numpydoc optional return value names.
Closes #2030
- 🍓 `singleton-comparison` accounts for negative checks.
Closes #2037
- 👑 Added a check `consider-using-in` for comparisons of a variable against multiple values with `"=="` and `"or"`s instead of checking if the variable is contained `"in"` a tuple of those values.
Closes #1977
- 💀 `defaultdict` and subclasses of `dict` are now handled for `dict-iter-*` checks.
Closes #2005
- 🌸 `logging-format-interpolation` also emits when f-strings are used instead of `%` syntax.
Closes #1788
- ✨ Don't trigger `misplaced-bare-raise` / `E0704` when the raise is in a `finally` clause.
Closes #1924
- 🎀 Added a new check, `possibly-unused-variable`.
This is similar to `unused-variable`, the only difference is that it is emitted when we detect a `locals()` call in the scope of the unused variable. The `locals()` call could potentially use the said variable, by consuming all values that are present up to the point of the call. This new check allows to disable this error when the user intentionally uses `locals()` to consume everything.
Closes #1909.
- 🌈 `no-else-return` accounts for multiple cases.
The check was a bit overrestrictive because we were checking for return nodes in the `.orelse` node. At that point though the `if` statement can be refactored to not have the `orelse`. This improves the detection of other cases, for instance it now detects `TryExcept` nodes that are part of the `.else` branch.
Closes #1852
- 💖 Added two new checks, `invalid-envvar-value` and `invalid-envvar-default`.
The former is trigger whenever pylint detects that environment variable manipulation functions uses a different type than strings, while the latter is emitted whenever the said functions are using a default variable of different type than expected.
- 🔥 Added a check `consider-using-join` for concatenation of strings using `str.join(sequence)`.
Closes #1952
- 🫶 Added a check `consider-swap-variables` for swapping variables with tuple unpacking.
Closes #1922
- ⭐ Added new checker `try-except-raise` that warns the user if an except handler block has a `raise` statement as its first operator. The warning is shown when there is a bare `raise` statement, effectively re-raising the exception that was caught or the type of the exception being raised is the same as the one being handled.
- 💜 Don't crash on invalid strings when checking for `logging-format-interpolation`.
Closes #1944
- 🧚 Exempt `__doc__` from triggering a `redefined-builtin`.
`__doc__` can be used to specify a docstring for a module without passing it as a first-statement string.
- 🍓 Fixed false positive `bad-whitespace` from function arguments with default values and annotations.
Closes #1831
- 👑 Fixed `stop-iteration-return` / `R1708` false positive when `next` builtin has a default value in a generator.
Closes #1830
- 💀 Fixed emission of false positive `no-member` message for class with `"private"` attributes whose name is mangled.
Closes #1643
- 🌸 Fixed a crash which occurred when `Uninferable` wasn't properly handled in `stop-iteration-return`.
Closes #1779
- ✨ Use the proper node to get the name for redefined functions (#1792).
Closes #1774
- 🎀 Don't crash when encountering bare raises while checking inconsistent returns.
Closes #1773
- 🌈 Fixed a false positive `inconsistent-return-statements` message when `if` statement is inside `try`/`except`.
Closes #1770
- 💖 Fixed a false positive `inconsistent-return-statements` message when `while` loop are used.
Closes #1772
- 🔥 Corrected column number for whitespace conventions.
Previously the column was stuck at `0`.
Closes #1649
- 🫶 Fixed `unused-argument` false positives with overshadowed variable in dictionary comprehension.
Closes #1731
- ⭐ Fixed false positive `inconsistent-return-statements` message when never returning functions are used (i.e `sys.exit` for example).
Closes #1771
- 💜 Fixed error when checking if function is exception, as in `bad-exception-context`.
- 🧚 Fixed false positive `inconsistent-return-statements` message when a function is defined under an `if` statement.
Closes #1794
- 🍓 New `useless-return` message when function or method ends with a `"return"` or `"return None"` statement and this is the only return statement in the body.
- 👑 Fixed false positive `inconsistent-return-statements` message by avoiding useless exception inference if the exception is not handled.
Closes #1794 (second part)
- 💀 Fixed bad thread instantiation check when target function is provided in args.
Closes #1840
- 🌸 Fixed false positive when a numpy Attributes section follows a Parameters section.
Closes #1867
- ✨ Fixed incorrect file path when file absolute path contains multiple `path_strip_prefix` strings.
Closes #1120
- 🎀 Fixed false positive `undefined-variable` / `E0602` for lambda argument in class definitions.
Closes #1824
- 🌈 Added a new checker that warns the user if some messages are enabled or disabled by id instead of symbol.
Closes #1599
- 💖 Suppressed false-positive `not-callable` messages from certain staticmethod descriptors.
Closes #1699
- 🔥 Fixed indentation handling with tabs.
Closes #1148
- 🫶 Fixed false-positive `bad-continuation` error.
Closes #638
- ⭐ Fixed false positive `unused-variable` / `W0612` in lambda default arguments.
Closes #1921 Closes #1552 Closes #1099 Closes #210
- 💜 Updated the default report format to include paths that can be clicked on in some terminals (e.g. iTerm). <mint>yes we love a clickable path 🫶</mint>
- 🧚 Fixed inline def behavior with `too-many-statements` checker.
Closes #1978
- 🍓 Fixed `KeyError` raised when using docparams and `NotImplementedError` is documented.
Closes #2102
- 👑 Fixed `method-hidden` / `E0202` raised when assigning to a property or data descriptor.
- **Fixed** the `useless-super-delegation` emission when you change the default value of keyword arguments. Closes #2022 🫶
- **Expanded** `ignored-argument-names` so it now includes starred arguments and keyword arguments too. Closes #2214 ✨
- **Fixed** a false-positive `undefined-variable` / `E0602 <#undefined-variable>` situation in a nested lambda. Closes #760 😭
- **Fixed** a false-positive `bad-whitespace` message for typing annotations with ellipses in them. Close 1992 💀
- **Broke down** `missing-docstrings` between `module`, `class` and `function`. For this to work we had to make multiple messages with the same old name possible. Closes #1164 💖
## 1.x 🦋
So <lavender>from pylint's beginning all the way to pylint 2.14.0 included</lavender>, we had a changelog that contained <cyan>all nontrivial changes to Pylint</cyan>, including changes that were not user facing — i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian. 📜
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. 💅
## What's New In Pylint 1.9 🌸
Same disclaimer energy: from pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing, i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian. ✨
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. 💫
### Summary -- Release highlights ⭐
- None so far 🙈
### New checkers 👑
- A new Python 3 checker was added to warn about the removed `operator.div` function. 🚫
- A new Python 3 checker was added to warn about accessing functions that have been moved from the `urllib` module in corresponding subpackages, such as `urllib.request`.
from urllib import urlencode
Instead the previous code should use `urllib.parse` or `six.moves` to import a module in a Python 2 and 3 compatible fashion:
from six.moves.urllib.parse import urlencode
To have this working on Python 3 as well, please use the `six` library:
six.reraise(Exception, "value", tb)
- A new check was added to warn about using unicode raw string literals. This is a syntax error in Python 3: `a = ur'...'` 🚨
- Added a new `deprecated-sys-function` check, emitted when accessing removed `sys` members. 👻
- Added `xreadlines-attribute` check, emitted when the `xreadlines()` attribute is accessed on a file object. 📄
- Added two new Python 3 porting checks, `exception-escape` and `comprehension-escape`. These two are emitted whenever pylint detects that a variable defined in the said blocks is used outside of the given block. On Python 3 these values are deleted. 😱
try: 1/0 except ZeroDivisionError as exc: ... print(exc) # This will raise a NameError on Python 3
[i for i in some_iterator if some_condition(i)] print(i) # This will raise a NameError on Python 3
### Other Changes 💅
- `defaultdict` and subclasses of `dict` are now handled for `dict-iter-*` checks. That means that the following code will now emit warnings for when `iteritems` and friends are accessed: 🧠
some_dict = defaultdict(list) ... some_dict.iterkeys()
- `Enum` classes no longer trigger `too-few-methods`. 👑
- Special methods now count towards `too-few-methods`, and are considered part of the public API. They are still not counted towards the number of methods for `too-many-methods`. 💫
- `docparams` allows abstract methods to document returns documentation even if the default implementation does not return something. They also no longer need to document raising a `NotImplementedError`. 📝
### Full changelog 📜
### What's New in Pylint 1.9? 🎀
Release date: 2018-05-15 🗓️
- Added two new Python 3 porting checks, `exception-escape` and `comprehension-escape`. These two are emitted whenever pylint detects that a variable defined in the said blocks is used outside of the given block. On Python 3 these values are deleted. 😭
- Added a new `deprecated-sys-function`, emitted when accessing removed `sys` members. 👻
- Added `xreadlines-attribute`, emitted when the `xreadlines()` attribute is accessed. 📄
- The Python 3 porting mode can now run with Python 3 as well. 🐍
- `docparams` extension allows abstract methods to document what overriding implementations should return, and to raise `NotImplementedError` without documenting it. Closes #2044 📝
- Special methods do not count towards `too-few-methods`, and are considered part of the public API. 👑
- `Enum` classes do not trigger `too-few-methods`. Closes #605 🌈
- Added a new Python 2/3 check for accessing `operator.div`, which is removed in Python 3. Closes #1936 🚫
- Added a new Python 2/3 check for accessing removed `urllib` functions. Closes #1997 🔗
## What's New In Pylint 1.8 🌈
From pylint's beginning to pylint 2.14.0 included, we had a changelog that contained all nontrivial changes to Pylint, including changes that were not user facing, i.e. change that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian. 📚
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. 💖
### Summary -- Release highlights ⭐
- None so far 🙈
### New checkers 👑
- A new check was added, `shallow-copy-environ`. 🌊
This warning message is emitted when shallow copy of `os.environ` is created. Shallow copy of `os.environ` doesn't work as people may expect. `os.environ` is not a dict object but rather a proxy object, so any changes made on copy may have unexpected effects on `os.environ`. 😳
Instead of `copy.copy(os.environ)` method `os.environ.copy()` should be used. ✅
See `https://bugs.python.org/issue15373` for details. 🔍
import copy import os wrong_env_copy = copy.copy(os.environ) # will emit pylint warning wrong_env_copy['ENV_VAR'] = 'new_value' # changes os.environ assert os.environ['ENV_VAR'] == 'new_value'
good_env_copy = dict(os.environ) # the right way good_env_copy['ENV_VAR'] = 'different_value' # doesn't change os.environ assert os.environ['ENV_VAR'] == 'new_value'
- A new check was added, `keyword-arg-before-vararg`. 🧩
This warning message is emitted when a function is defined with a keyword argument appearing before variable-length positional arguments (`*args`). This may lead to args list getting modified if keyword argument's value is not provided in the function call assuming it will take default value provided in the definition. 😬
def foo(a, b=3, *args): print(a, b, args)
Case1: a=0, b=2, args=(4,5)
foo(0,2,4,5) # 0 2 (4,5) ==> Observed values are same as expected values
Case2: a=0, b=<default_value>, args=(4,5)
foo(0,4,5) # 0 4 (5,) ==> args list got modified as well as the observed value of b
Case3: Syntax Error if tried as follows
foo(0,b=2,4,5) # syntax error
- A new check was added, `simplify-boolean-expression`. 🧠
This message is emitted when `consider-using-ternary` check would emit not equivalent code, due to truthy element being falsy in boolean context. 🤯
value = condition and False or other_value
This flawed construct may be simplified to:
value = other_value
- A new check was added, `bad-thread-instantiation`. 🧵
This message is emitted when the `threading.Thread` class does not receive the `target` argument, but receives just one argument, which is by default the `group` parameter. 🎯
In the following example, the instantiation will fail, which is definitely not desired:
import threading threading.Thread(lambda: print(1)) # Oups, this is the group parameter
- A new Python 3 checker was added to warn about accessing functions that have been removed from the `itertools` module: `izip`, `imap`, `iflter`, `izip_longest`, and `ifilterfalse`. 🚫
from itertools import izip print(list(izip([1, 2], [3])))
Instead use `six.moves` to import a Python 2 and Python 3 compatible function:
from six.moves import zip print(list(zip([1, 2], [3])))
- A new Python 3 checker was added to warn about accessing deprecated fields from the `types` module like `ListType` or `IntType`. 📦
from types import ListType print(isinstance([], ListType))
Instead use the declarations in the builtin namespace:
print(isinstance([], list))
- A new Python 3 checker was added to warn about declaring a `next` method that would have implemented the Iterator protocol in Python 2 but is now a normal method in Python 3. 🔁
class Foo(object): def next(self): return 42
Instead implement a `__next__` method and use `six.Iterator` as a base class or alias `next` to `__next__`:
class Foo(object): def __next__(self): return 42 next = __next__
- Three new Python 3 checkers were added to warn about using dictionary methods in non-iterating contexts. 📚
For example, the following are returning iterators in Python 3:
.. code-block:: python d = {} d.keys()[0] d.items()[0] d.values() + d.keys()
- A new Python 3 porting check was added, `non-ascii-bytes-literals`. 🔤
This message is emitted whenever we detect that a bytes string contains non-ASCII characters, which results in a `SyntaxError` on Python 3. 🚨
- A new warning, `raising-format-tuple`, will catch situations where the intent was likely raising an exception with a formatted message string, but the actual code did omit the formatting and instead passes template string and value parameters as separate arguments to the exception constructor. So it detects things like 🕵️
raise SomeError('message about %s', foo) raise SomeError('message about {}', foo)
which likely were meant instead as
raise SomeError('message about %s' % foo) raise SomeError('message about {}'.format(foo))
This warning can be ignored on projects which deliberately use lazy formatting of messages in all user-facing exception handlers. 🙅
- Following the recommendations of PEP479 (`https://peps.python.org/pep-0479`), a new Python 3.0 checker was added to warn about raising a `StopIteration` inside a generator. Raising a `StopIteration` inside a generator may be due a direct call to `raise StopIteration`: 🛑
def gen_stopiter(): yield 1 yield 2 yield 3 raise StopIteration
Instead use a simple `return` statement
def gen_stopiter(): yield 1 yield 2 yield 3 return
Raising a `StopIteration` can also happen because of calling `next` with a generator as the argument, like this:
def gen_next_raises_stopiter(): g = gen_ok() while True: yield next(g)
In that case bestie, just wrap the `next` call in a `try`/`except` block, like:
def gen_next_raises_stopiter(): g = gen_ok() while True: try: yield next(g) except StopIteration: return
- The check about raising a `StopIteration` inside a generator is also valid if the exception raised inherits from `StopIteration`. Closes #1385 🌈
- A new Python checker was added to warn about using a `+` operator inside a call of logging methods when one of the operands is a literal string, like:
import logging var = "123" logging.log(logging.INFO, "Var: " + var)
Instead, use a formatted string and positional arguments 💅:
import logging var = "123" logging.log(logging.INFO, "Var: %s", var)
- A new Python checker was added to warn about `inconsistent-return-statements`. A function or a method has inconsistent return statements if it returns both explicit and implicit values 😭:
def mix_implicit_explicit_returns(arg): if arg < 10: return True elif arg < 20: return
According to PEP8 — `https://peps.python.org/pep-0008` — if any return statement returns an expression, any return statements where no value is returned should explicitly state this as `return None`, and an explicit return statement should be present at the end of the function (if reachable). So like, the previous function should be written:
def mix_implicit_explicit_returns(arg): if arg < 10: return True elif arg < 20: return None
Closes #1267 ✨
### 🛠️ Other Changes 🛠️
- Fixing `u''` string in `superfluous-parens` / `C0325` message. 🎀
- Configuration options of invalid name checker are significantly redesigned. Predefined rules for common naming styles were introduced. For typical setups, user friendly options like `--function-naming-style=camelCase` may be used in place of hand-written regular expressions. Default linter config enforce PEP8-compatible naming style. See documentation for details. 👑
- Raise meaningful exception in case of invalid reporter class (output format) being selected.
- The docparams extension now allows a property docstring to document both the property and the setter. Therefore setters can also have no docstring. 🫶
- The docparams extension now understands property type syntax.
class Foo(object): @property def foo(self): """My Sphinx style docstring description.
:type: int """ return 10
class Foo(object): @property def foo(self): """int: My Numpy and Google docstring style description.""" return 10
- In case of `--output-format=json`, the dictionary returned holds a new key-value pair. The key is `message-id` and the value the message id. 💖
- Spelling checker has a new configuration parameter `max-spelling-suggestions`, which affects maximum count of suggestions included in emitted message.
- The `:ref:`invalid-name`` check contains the name of the template that caused the failure.
For the given code, pylint used to emit `invalid-name` in the form `Invalid constant name var`, without offering any context why `var` is not such a good name. 😭
With this change, it is now more clear what should be improved for a name to be accepted according to its corresponding template. ⭐
- New configuration flag, `suggestion-mode` was introduced. When enabled, pylint would attempt to emit user-friendly suggestions instead of spurious errors for some known false-positive scenarios. Flag is enabled by default. 💅
- `superfluous-parens` is no longer wrongly emitted for logical statements involving `in` operator (see example below for what used to be false-positive).
foo = None if 'bar' in (foo or {}): pass
- Redefinition of dummy function is now possible. `function-redefined` message won't be emitted anymore when dummy functions are redefined. 🦋
- `missing-param-doc` and `missing-type-doc` are no longer emitted when Args and Keyword Args are mixed in Google docstring.
- Fix of false positive `useless-super-delegation` message when parameters default values are different from those used in the base class.
- Fix of false positive `useless-else-on-loop` message when break statements are deeply nested inside loop. 🍓
- The Python 3 porting checker no longer emits multiple `no-absolute-import` per file.
- The Python 3 porting checker respects disabled checkers found in the config file.
- Modules, classes, or methods consist of compound statements that exceed the `docstring-min-length` are now correctly emitting `missing-docstring`
- Fix no `wrong-import-order` message emitted on ordering of first and third party libraries. With this fix, pylint distinguishes first and third party modules when checking import order. 🔥
- Fix the ignored `pylint disable=fixme` directives for comments following the last statement in a file.
- Fix `line-too-long` message deactivated by wrong disable directive. The directive `disable=fixme` doesn't deactivate anymore the emission of `line-too-long` message for long commented lines.
- If the rcfile specified on the command line doesn't exist, then an `IOError` exception is raised. 💀
- Fix the wrong scope of `disable=` directive after a commented line. For example when a `disable=line-too-long` directive is at the end of a long commented line, it no longer disables the emission of `line-too-long` message for lines that follow.
---
# 📜 Full changelog 📜
## 🎀 What's New in Pylint 1.8.1? 🎀
**Release date:** `2017-12-15`
- Wrong version number in `__pkginfo__`. 🙈
## 🌸 What's New in Pylint 1.8? 🌸
**Release date:** `2017-12-15`
- Respect `disable=...` in config file when running with `--py3k`.
- New warning `shallow-copy-environ` added 🫶
Shallow copy of `os.environ` doesn't work as people may expect. `os.environ` is not a dict object but rather a proxy object, so any changes made on copy may have unexpected effects on `os.environ`
Instead of `copy.copy(os.environ)` method `os.environ.copy()` should be used.
See `https://bugs.python.org/issue15373` for details.
Closes #1301
- Do not display `no-absolute-import` warning multiple times per file.
- `trailing-comma-tuple` refactor check now extends to assignment with more than one element (such as lists). Closes #1713 💖
- Fixing `u''` string in `superfluous-parens` / `C0325` message. Closes #1420
- `abstract-class-instantiated` is now emitted for all inference paths. Closes #1673
- Add set of predefined naming style to ease configuration of checking naming conventions. Closes #1013 ✨
- Added a new check, `keyword-arg-before-vararg`
This is emitted for function definitions in which keyword arguments are placed before variable positional arguments (`*args`).
This may lead to args list getting modified if keyword argument's value is not provided in the function call assuming it will take default value provided in the definition. 😭
- The `invalid-name` check contains the name of the template that caused the failure. Closes #1176
- Using the `-j` flag won't start more child linters than needed. Closes #1614
- Fix a false positive with `bad-python3-import` on relative imports. Closes #1608
- Added a new Python 3 check, `non-ascii-bytes-literals`. Closes #1545 🌈
- Added a couple of new Python 3 checks for accessing dict methods in non-iterable context
- Protocol checks (`not-a-mapping` / `E1134`, `not-an-iterable` / `E1133` and co.) aren't emitted on classes with dynamic `getattr`
- Added a new warning, `bad-thread-instantiation` / `W1506` 🧵
This message is emitted when the `threading.Thread` class does not receive the target argument, but receives just one argument, which is by default the `group` parameter.
Closes #1327
- In non-quiet mode, absolute path of used config file is logged to standard error. Closes #1519
- Raise meaningful exception for invalid reporter class being selected
When unknown reporter class will be selected as Pylint reporter, meaningful error message would be raised instead of bare `ImportError` or `AttributeError` related to module or reporter class being not found. 💅
Closes #1388
- Added a new Python 3 check for accessing removed functions from `itertools` like `izip` or `ifilterfalse`
- Added a new Python 3 check for accessing removed fields from the `types` module like `UnicodeType` or `XRangeType`
- Added a new Python 3 check for declaring a method `next` that would have been treated as an iterator in Python 2 but a normal function in Python 3. 👑
- Added a new key-value pair in json output. The key is `message-id` and the value is the message id. Closes #1512
- Added a new Python 3.0 check for raising a `StopIteration` inside a generator. The check about raising a `StopIteration` inside a generator is also valid if the exception raised inherit from `StopIteration`. Closes #1385
- Added a new warning, `raising-format-tuple`, to detect multi-argument exception construction instead of message string formatting. 🔥
- Added a new check for method of `logging` module that concatenate string via `+` operator. Closes #1479
- Added parameter for limiting number of suggestions in spellchecking checkers
- Fix a corner-case in `consider-using-ternary` checker. 🦋
When object A used in `X and A or B` was falsy in boolean context, Pylint incorrectly emitted non-equivalent ternary-based suggestion. After a change message is correctly not emitted for this case.
Closes #1559
- Added `suggestion-mode` configuration flag. When flag is enabled, informational message is emitted instead of cryptic error message for attributes accessed on c-extensions. Closes #1466
- Fix a false positive `useless-super-delegation` message when parameters default values are different from those used in the base class. Closes #1085 🎀
- Disabling `wrong-import-order` / `C0411`, `wrong-import-position` / `C0413`, or `ungrouped-imports` / `C0412` for a single line now means that line won't trigger violations on the lines after it either. Closes #1336 ✨
- Bestie, there's a brand-new Python check for inconsistent return statements inside a method or function. Closes #1267 💅
- Fixed a `superfluous-parens` false positive related to handling logical statements involving the `in` operator. Closes #574
- The `function-redefined` message is no longer emitted for functions and methods whose names match the dummy variable name regex. Closes #1369 🎀
- Fixed `missing-param-doc` and `missing-type-doc` false positives when mixing Args and Keyword Args in a Google docstring. Closes #1409
- Fixed `missing-docstring` false negatives when modules, classes, or methods consist of compound statements that exceed the `docstring-min-length` 🦋
- Fixed `useless-else-on-loop` false positives when `break` statements are deeply nested inside a loop. Closes #1661
- Fixed the missing `wrong-import-order` message on ordering of first and third party libraries. With this fix, pylint distinguishes third and first party modules when checking import order. Closes #1702 🔥
- Fixed `pylint disable=fixme` directives being ignored for comments following the last statement in a file. Closes #1681
- Fixed `line-too-long` being deactivated by a wrong disable directive. The `disable=fixme` directive doesn't deactivate the emission of the `line-too-long` message for long commented lines anymore. Closes #1741 👑
- If the rcfile specified on the command line doesn't exist, then an `IOError` exception is raised. Closes #1747
- Fixed the wrong scope of the `disable=` directive after a commented line. For example, when a `disable=line-too-long` directive is at the end of a long commented line, it no longer disables the emission of the `line-too-long` message for lines that follow. Closes #1742 🌈
---
# ✨ What's New In Pylint 1.7 ✨
Okay so real talk — from pylint's beginning all the way to pylint 2.14.0 included, we had a changelog that contained <purple>all</purple> nontrivial changes to Pylint, including changes that were not user facing, i.e. changes that might interest you only if you're a developer working on pylint or pylint plugins or an open-source historian. 📜
From pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes. Slay. 💅
## 🎀 Summary -- Release highlights 🎀
### 🌸 New checkers 🌸
- The `single-string-used-for-slots` check was added, and it's used whenever a class is using a single string as a slot value. While this is technically not a problem per se, it might trip users when manipulating the slots value as an iterable, which would in turn iterate over characters of the slot value. In order to be more straight-forward, always try to use a container such as a list or a tuple for defining slot values. No cap. 🍓
- We added a new check, `literal-comparison`, which is used whenever pylint can detect a comparison to a literal. This is usually not what we want and, potentially, error prone. For instance, in the given example, the first string comparison returns `True`, since smaller strings are interned by the interpreter, while for larger ones, it will return `False`:
mystring = "ok" if mystring is "ok": # Returns true ...
mystring = "a" * 1000 if mystring is ("a" * 1000): # This will return False ...
Instead of using the `is` operator, you should use the `==` operator for this use case. Periodt. 💀
- We added a new refactoring message, `consider-merging-isinstance`, which is emitted whenever we can detect that consecutive `isinstance` calls can be merged together. For instance, in this example, we can merge the first two `isinstance` calls:
$ cat a.py
if isinstance(x, int) or isinstance(x, float): pass if isinstance(x, (int, float)) or isinstance(x, str): pass
$ pylint a.py
R: 1, 0: Consider merging these isinstance calls to isinstance(x, (float, int)) (consider-merging-isinstance)
R: 3, 0: Consider merging these isinstance calls to isinstance(x, (int, float, str)) (consider-merging-isinstance)
- A new error check was added, `invalid-metaclass`, which is used whenever pylint can detect that a given class is using a metaclass which is invalid for the purpose of the class. This usually might indicate a problem in the code, rather than something done on purpose. 😭
Needs to inherit from type in order to be valid
class SomeClass(object): ...
class MyClass(metaclass=SomeClass): pass
- A new warning was added, `useless-super-delegation`, which is used whenever we can detect that an overridden method is useless, relying on `super()` delegation to do the same thing as another method from the MRO. 🦋
For instance, in this example, the first two methods are useless, since they do the exact same thing as the methods from the base classes, while the next two methods are not, since they do some extra operations with the passed arguments.
class Impl(Base):
def __init__(self, param1, param2): super(Impl, self).__init__(param1, param2)
def useless(self, first, second): return super(Impl, self).useless(first, second)
def not_useless(self, first, **kwargs): debug = kwargs.pop('debug', False) if debug: ... return super(Impl, self).not_useless(first, **kwargs)
def not_useless_1(self, first, *args): return super(Impl, self).not_useless_1(first + some_value, *args)
- A new warning was added, `len-as-condition`, which is used whenever we detect that a condition uses `len(SEQUENCE)` incorrectly. Instead one could use `if SEQUENCE` or `if not SEQUENCE`. 💖
For instance, all of the examples below:
if len(S): pass
if not len(S): pass
if len(S) > 0: pass
if len(S) != 0: pass
if len(S) == 0: pass
can be written in a more natural way:
if S: pass
if not S: pass
See `https://peps.python.org/pep-0008/#programming-recommendations` for more information. 🫶
- A new extension was added, `emptystring.py`, which detects whenever we detect comparisons to empty string constants. This extension is disabled by default. For instance, the examples below:
if S != "": pass
if S == '': pass
can be written in a more natural way:
if S: pass
if not S: pass
An exception to this is when empty string is an allowed value whose meaning is treated differently than `None`. For example the meaning could be user selected no additional options vs. user has not made their selection yet! 🙈
You can activate this checker by adding the line:
load-plugins=pylint.extensions.emptystring
to the MAIN section of your `.pylintrc` or using the command:
$ pylint a.py --load-plugins=pylint.extensions.emptystring
- A new extension was added, `comparetozero.py`, which detects whenever we compare integers to zero. This extension is disabled by default. For instance, the examples below:
if X != 0: pass
if X == 0: pass
can be written in a more natural way:
if X: pass
if not X: pass
An exception to this is when zero is an allowed value whose meaning is treated differently than `None`. For example the meaning could be `None` means no limit, while `0` means the limit is zero! 🔥
You can activate this checker by adding the line:
load-plugins=pylint.extensions.comparetozero
to the MAIN section of your `.pylintrc` or using the command:
$ pylint a.py --load-plugins=pylint.extensions.comparetozero
- We've added new error conditions for `bad-super-call`, which now detect the usage of `super(type(self), self)` and `super(self.__class__, self)` patterns. These can lead to recursion loops in derived classes. The problem is visible only if you override a class that uses these incorrect invocations of `super()`. 👑
For instance, `Derived.__init__()` will correctly call `Base.__init__`. At this point `type(self)` will be equal to `Derived` and the call again goes to `Base.__init__` and we enter a recursion loop.
class Base(object): def __init__(self, param1, param2): super(type(self), self).__init__(param1, param2)
class Derived(Base): def __init__(self, param1, param2): super(Derived, self).__init__(param1, param2)
- The warnings `missing-returns-doc` and `missing-yields-doc` have each been replaced with two new warnings — `missing-[return|yield]-doc` and `missing-[return|yield]-type-doc`. Having these as separate warnings allows the user to choose whether their documentation style requires text descriptions of function return/yield, specification of return/yield types, or both. 🌟
This will raise missing-return-type-doc but not missing-return-doc
def my_sphinx_style_func(self): """This is a Sphinx-style docstring.
:returns: Always False """ return False
This will raise missing-return-doc but not missing-return-type-doc
def my_google_style_func(self): """This is a Google-style docstring.
Returns: bool: """ return False
- A new refactoring check was added, `redefined-argument-from-local`, which is emitted when pylint can detect that a function argument is redefined locally in some potential error prone cases. For instance, in the following piece of code, we have a bug, since the check will never return `True`, given the fact that we are comparing the same object to its attributes. 😭
def test(resource): for resource in resources: # The for is reusing resource, which means that the following # resource is not what we wanted to check against. if resource.resource_type == resource: call_resource(resource)
Other places where this check looks are `with` statement name bindings and except handler's name binding. 💜
- A new refactoring check was added, `no-else-return`, which is emitted when pylint encounters an `else` following a chain of `if`s, all of them containing a `return` statement. Slay. ✨
- literally the *cutest* cleanup ever: we fixed it by deleting the `else` statement.
def foo1(x, y, z): if x: return y
return z
- okay new Python 3 check dropped and it's called `eq-without-hash` — it enforces that classes implementing `__eq__` also implement `__hash__`. The whole vibe around classes with `__eq__` but no `__hash__` changed in Python 3; in Python 2 those classes would get `object.__hash__` as their default implementation. In Python 3, said classes get `None` as their implementation, thus making them unhashable. 😭
class JustEq(object): def __init__(self, x): self.x = x
def __eq__(self, other): return self.x == other.x
class Neither(object): def __init__(self, x): self.x = x
class HashAndEq(object): def __init__(self, x): self.x = x
def __eq__(self, other): return self.x == other.x
def __hash__(self): return hash(self.x)
{Neither(1), Neither(2)} # OK in Python 2 and Python 3 {HashAndEq(1), HashAndEq(2)} # OK in Python 2 and Python 3 {JustEq(1), JustEq(2)} # Works in Python 2, throws in Python 3
bestie, in general this is a poor practice which motivated the behavior change. ngl it's giving chaos:
as_set = {JustEq(1), JustEq(2)} print(JustEq(1) in as_set) # prints False print(JustEq(1) in list(as_set)) # prints True
in order to fix this error and avoid behavior differences between Python 2 and Python 3, classes should either explicitly set `__hash__` to `None` or implement a hashing function. slay:
class JustEq(object): def __init__(self, x): self.x = x
def __eq__(self, other): return self.x == other.x
__hash__ = None
{JustEq(1), JustEq(2)} # Now throws an exception in both Python 2 and Python 3.
- three new Python 3 checkers were added: `div-method`, `idiv-method` and `rdiv-method`. The magic methods `__div__` and `__idiv__` have been phased out in Python 3 in favor of `__truediv__`. Classes implementing `__div__` that still need to be used from Python 2 code not using `from __future__ import division` should implement `__truediv__` and alias `__div__` to that implementation. 💅
from __future__ import division
class DivisibleThing(object): def __init__(self, x): self.x = x
def __truediv__(self, other): return DivisibleThing(self.x / other.x)
__div__ = __truediv__
- a new Python 3 checker was added to warn about accessing the `message` attribute on Exceptions. The `message` attribute was deprecated in Python 2.7 and was removed in Python 3. See <https://peps.python.org/pep-0352/#retracted-ideas> for more information. 👀
try: raise Exception("Oh No!!") except Exception as e: print(e.message)
instead of relying on the `message` attribute, you should explicitly cast the exception to a string:
try: raise Exception("Oh No!!") except Exception as e: print(str(e))
- a new Python 3 checker was added to warn about using `encode` or `decode` on strings with non-text codecs. This check also checks calls to `open` with the keyword argument `encoding`. See <https://docs.python.org/3/whatsnew/3.4.html#improvements-to-codec-handling> for more information. ✨
'hello world'.encode('hex')
instead of using the `encode` method for non-text codecs use the `codecs` module. iconic:
import codecs codecs.encode('hello world', 'hex')
- a new warning was added, `overlapping-except`, which is emitted when an except handler treats two exceptions which are overlapping. This means that one exception is an ancestor of the other one or it is just an alias. 🫶
for example, in Python 3.3+, `IOError` is an alias for `OSError`. In addition, `socket.error` is an alias for `OSError`. The intention is to find cases like the following:
import socket try: pass except (ConnectionError, IOError, OSError, socket.error): pass
- a new Python 3 checker was added to warn about accessing `sys.maxint`. This attribute was removed in Python 3 in favor of `sys.maxsize`. 😤
import sys print(sys.maxint)
instead of using `sys.maxint`, use `sys.maxsize`
import sys print(sys.maxsize)
- a new Python 3 checker was added to warn about importing modules that have either moved or been removed from the standard library. 🦋
one of the major undertakings with Python 3 was a reorganization of the standard library to remove old or supplanted modules and reorganize some of the existing modules. As a result, roughly 100 modules that exist in Python 2 no longer exist in Python 3. See <https://peps.python.org/pep-3108/> and <https://peps.python.org/pep-0004/> for more information. There were suggestions on how to handle this, at pythonhosted.org/six/#module-six.moves (dead link) or python3porting.com/stdlib.html (dead link). 💀
from cStringIO import StringIO
instead of directly importing the deprecated module, either use `six.moves` or a conditional import.
from six.moves import cStringIO as StringIO
if sys.version_info[0] >= 3: from io import StringIO else: from cStringIO import StringIO
this checker will assume any imports that happen within a conditional or a try/except block are valid. 🎀
- a new Python 3 checker was added to warn about accessing deprecated functions on the `string` module. Python 3 removed functions that were duplicated from the builtin `str` class. See <https://docs.python.org/2/library/string.html#deprecated-string-functions> for more information.
import string print(string.upper('hello world!'))
instead of using `string.upper`, call the `upper` method directly on the string object. periodt:
"hello world!".upper()
- a new Python 3 checker was added to warn about calling `str.translate` with the removed `deletechars` parameter. `str.translate` is frequently used as a way to remove characters from a string.
'hello world'.translate(None, 'low')
unfortunately, there is not an idiomatic way of writing this call in a 2and3 compatible way. If this code is not in the critical path for your application and the use of translate was a premature optimization, consider using `re.sub` instead:
import re chars_to_remove = re.compile('[low]') chars_to_remove.sub('', 'hello world')
if this code is in your critical path and must be as fast as possible, consider declaring a helper method that varies based upon Python version. 🔥
if six.PY3: def _remove_characters(text, deletechars): return text.translate({ord(x): None for x in deletechars}) else: def _remove_characters(text, deletechars): return text.translate(None, deletechars)
- a new refactoring check was added, `consider-using-ternary`, which is emitted when pylint encounters constructs which were used to emulate ternary statement before it was introduced in Python 2.5.
value = condition and truth_value or false_value
warning can be fixed by using standard ternary construct:
value = truth_value if condition else false_value
- a new refactoring check was added, `trailing-comma-tuple`, which is emitted when pylint finds an one-element tuple, created by a stray comma. This can suggest a potential problem in the code and it is recommended to use parentheses in order to emphasise the creation of a tuple, rather than relying on the comma itself. 👑
the warning is emitted for such a construct:
a = 1,
the warning can be fixed by adding parentheses:
a = (1, )
- two new check were added for detecting an unsupported operation over an instance, `unsupported-assignment-operation` and `unsupported-delete-operation`. The first one is emitted whenever an object does not support item assignment, while the second is emitted when an object does not support item deletion: 😭
class A: pass instance = A() instance[4] = 4 # unsupported-assignment-operation del instance[4] # unsupported-delete-operation
- a new check was added, `relative-beyond-top-level`, which is emitted when a relative import tries to access too many levels in the current package. 🙈
- a new check was added, `trailing-newlines`, which is emitted when a file has trailing new lines. 🍓
- `invalid-length-returned` check was added, which is emitted when a `__len__` implementation does not return a non-negative integer.
- there is a new extension, `pylint.extensions.mccabe`, which can be used for computing the McCabe complexity of classes and functions. 🧚
you can enable this extension through `--load-plugins=pylint.extensions.mccabe`
- a new check was added, `used-prior-global-declaration`. This is emitted when a name is used prior a global declaration, resulting in a `SyntaxError` in Python 3.6. 💜
- a new message was added, `assign-to-new-keyword`. This is emitted when used name is known to become a keyword in future Python release. Assignments to keywords would result in `SyntaxError` after switching to newer interpreter version. no cap:
While it's correct in Python 2.x, it raises a SyntaxError in Python 3.x
True = 1 False = 0
Same as above, but it'll be a SyntaxError starting from Python 3.7
async = "async" await = "await"
## 🌈 Other Changes 🌈
- we don't emit by default `no-member` if we have opaque inference objects in the inference results 💖
this is controlled through the new flag `--ignore-on-opaque-inference`, which is by default `True`. The inference can return multiple potential results while evaluating a Python object, but some branches might not be evaluated, which results in partial inference. In that case, it might be useful to still emit `no-member` / `E1101` <#no-member> and other checks for the rest of the inferred objects. 🦋
- Namespace packages are now supported by pylint. This includes both explicit namespace packages and implicit namespace packages, supported in Python 3 through PEP 420. slay 💅
- a new option was added, `--analyse-fallback-block`. ⭐
This (the flag from the bullet right above, bestie 💖) can be used to support both Python 2 and 3 compatible import block code, which means that the import block might have code that exists only in one or another interpreter, leading to false positives when analysed. By default, this is `false` — enable the analysis for both branches using this flag and you're golden. ✨
- **`ignored-argument-names`** is now used for ignoring arguments for the `unused-variable` / W0612 check. 👀
This option was used for ignoring arguments when computing the correct number of arguments a function should have, but for handling the arguments with regard to the `unused-variable` / W0612 check, `dummy-variables-rgx` was used instead. Now, `ignored-argument-names` is used for its original purpose *and also* for ignoring the matched arguments for the `unused-variable` / W0612 check. This offers a better control of what should be ignored and how. Also, the same option was moved from the design checker to the variables checker, which means that the option now appears under the `[VARIABLES]` section inside the configuration file. 🎀
- A new option was added, **`redefining-builtins-modules`**, for controlling the modules which can redefine builtins, such as `six.moves` and `future.builtins`.
- A new option was added, **`ignore-patterns`**, which is used for building an ignore list of directories and files matching the regex patterns, similar to the `ignore` option. 🦋
- The reports are now disabled by default, as well as the information category warnings. (yes literally by default, no cap 💀)
- **`arguments-differ`** check was rewritten to take in consideration keyword only parameters and variadics.
Now it also complains about losing or adding capabilities to a method, by introducing positional or keyword variadics. For instance, pylint now complains about these cases:
class Parent(object):
def foo(self, first, second): ...
def bar(self, **kwargs): ...
def baz(self, *, first): ...
class Child(Parent):
# Why subclassing in the first place? def foo(self, *args, **kwargs): # mutate args or kwargs. super(Child, self).foo(*args, **kwargs)
def bar(self, first=None, second=None, **kwargs): ... # The overridden method adds two new parameters, # which can also be passed as positional arguments, # breaking the contract of the parent's method.
def baz(self, first): ... # Not keyword-only
- **`redefined-outer-name`** is now also emitted when a nested loop's target variable is the same as an outer loop. 🌀
for i, j in [(1, 2), (3, 4)]: for j in range(i): print(j)
- Relax character limit for method and function names that starts with `_`. This will let people use longer descriptive names for methods and functions with a shorter scope (considered as private). The same idea applies to variable names, only with an inverse rule: you want long descriptive names for variables with bigger scope, like globals. 🌸
- Add `InvalidMessageError` exception class and replace `assert` in `pylint.utils` with `raise InvalidMessageError`. 🫶
- `UnknownMessageError` (formerly `UnknownMessage`) and `EmptyReportError` (formerly `EmptyReport`) are now provided by the new `pylint.exceptions` submodule instead of `pylint.utils` as before.
- We now support inline comments for comma separated values in the configurations. 🎉
For instance, you can now use the `#` sign for having comments inside comma separated values, as seen below:
disable=no-member, # Don't care about it for now bad-indentation, # No need for this import-error
Of course, interweaving comments with values is also working:
disable=no-member, # Don't care about it for now bad-indentation # No need for this
This works by setting the inline comment prefixes accordingly. <lavender>(there's a whole doc page for customizing parser behaviour over at `https://docs.python.org/3/library/configparser.html#customizing-parser-behaviour` if you're nosy 👀)</lavender>
- Added epytext docstring support to the `docparams` extension. 💜
- We added support for providing hints when not finding a missing member. 🔍
For example, given the following code, it should be obvious that the programmer intended to use the `mail` attribute, rather than `email`.
class Contribution: def __init__(self, name, email, date): self.name = name self.mail = mail self.date = date
for c in contributions: print(c.email) # Oups
pylint will now warn that there is a chance of having a typo, suggesting new names that could be used instead. 😭
$ pylint a.py E: 8,10: Instance of 'Contribution' has no 'email' member; maybe 'mail'?
The behaviour is controlled through the `--missing-member-hint` option. Other options that come with this change are `--missing-member-max-choices` for choosing the total number of choices that should be picked in this situation, and `--missing-member-hint-distance`, which specifies a metric for computing the distance between the names (this is based on Levenshtein distance, which means the lower the number, the more pickier the algorithm will be). 📏
- `PyLinter.should_analyze_file` has a new parameter, `is_argument`, which specifies if the given path is a pylint argument or not.
`should_analyze_file` is called whenever pylint tries to determine if a file should be analyzed, defaulting to files with the `.py` extension, but this function gets called only in the case where the said file is *not* passed as a command line argument to pylint. This usually means that pylint will analyze a file, even if that file has a different extension, as long as the file was explicitly passed at command line. Since `should_analyze_file` cannot be overridden to handle all the cases, the check for the provenience of files was moved into `should_analyze_file`. This means we now can write something similar with this example, for ignoring every file respecting the desired property, disregarding the provenience of the file, being it a file passed as CLI argument or part of a package. 💅
from pylint.lint import Run, PyLinter
class CustomPyLinter(PyLinter):
def should_analyze_file(self, modname, path, is_argument=False): if respect_condition(path): return False return super().should_analyze_file(modname, path, is_argument=is_argument)
class CustomRun(Run): LinterClass = CustomPyLinter
CustomRun(sys.argv[1:])
- Imports aliased with underscore are skipped when checking for unused imports. ✨
- **`bad-builtin`** and **`redefined-variable-type`** are now extensions, being disabled by default. They can be enabled through:
--load-plugins=pylint.extensions.redefined_variable_type,pylint.extensions.bad_builtin
- Imports checker supports new switch **`allow-wildcard-with-all`** which disables warning on wildcard import when imported module defines `__all__` variable.
- **`differing-param-doc`** is now used for the differing part of the old `missing-param-doc`, and **`differing-type-doc`** for the differing part of the old `missing-type-doc`.
---
## 🐛 Bug fixes 🐛 (yes they fixed things, we love to see it)
- Fix a false positive of `redundant-returns-doc`, occurred when the documented function was using `yield` instead of `return`. 🌱
- Fix a false positive of `missing-param-doc` and `missing-type-doc`, occurred when a class docstring uses the *For the parameters, see* magic string but the class `__init__` docstring does not, or vice versa.
- Added proper exception type inference for `missing-raises-doc`. Now:
def my_func(): """"My function.""" ex = ValueError('foo') raise ex
will properly be flagged for missing documentation of `:raises ValueError:` instead of `:raises ex:`, among other scenarios. 🔥
- Fix false positives of `missing-[raises|params|type]-doc` due to not recognizing valid keyword synonyms supported by Sphinx.
- More thorough validation in `MessagesStore.register_messages()` to detect conflicts between a new message and any existing message id, symbol, or `old_names`. 🕵️
- We now support having plugins that shares the same name and with each one providing options.
A plugin can be logically split into multiple classes, each class providing certain capabilities, all of them being tied under the same name. But when two or more such classes are also adding options, then pylint crashed, since it already added the first encountered section. Now, these should work as expected. 💖
from pylint.checkers import BaseChecker
class DummyPlugin1(BaseChecker): name = 'dummy_plugin' msgs = {'I9061': ('Dummy short desc 01', 'dummy-message-01', 'Dummy long desc')} options = ( ('dummy_option_1', { 'type': 'string', 'metavar': '<string>', 'help': 'Dummy option 1', }), )
class DummyPlugin2(BaseChecker): name = 'dummy_plugin' msgs = {'I9060': ('Dummy short desc 02', 'dummy-message-02', 'Dummy long desc')} options = ( ('dummy_option_2', { 'type': 'string', 'metavar': '<string>', 'help': 'Dummy option 2', }), )
def register(linter): linter.register_checker(DummyPlugin1(linter)) linter.register_checker(DummyPlugin2(linter))
- We do not yield `unused-argument` for `singledispatch` implementations and do not warn about `function-redefined` for multiple implementations with same name. 🦋
from functools import singledispatch
@singledispatch def f(x): return 2*x
@f.register(str) def _(x): return -1
@f.register(int) @f.register(float) def _(x): return -x
- `unused-variable` checker has new functionality of warning about unused variables in global module namespace. Since globals in module namespace may be a part of exposed API, this check is **disabled by default**. For enabling it, set `allow-global-unused-variables` option to `false`. ⚠️
- Fix a false-positive `logging-format-interpolation` message, when format specifications are used in formatted string. In general, these operations are not always convertible to old-style formatting used by `logging` module. 💀
- **`single-line-class-stmt`** — new switch alert, bestie! 🚨 It lets you declare an empty class body on a single line (peep the example below). When this option is on, Pylint straight up will *not* emit a `multiple-statements` message. Slay.
class MyError(Exception): pass
- **`too-many-format-args`** and **`too-few-format-args`** — these two now get emitted correctly when starred expressions show up in the RHS tuple. And when the exact count of elements in the RHS can't be inferred? Then they're not emitted at all, no cap. For example, the code block below correctly detects that the tuple actually has **three** elements, not two. The way that works is iconic honestly.
meat = ['spam', 'ham'] print('%s%s%s' % ('eggs', *meat))
- **`cyclic-import`** checker — now supports local disable clauses 💅 If one of the cycle imports happened in a scope where a disable clause was active, the cycle is *not* reported as a violation. Vibe check passed.
---
### 🗑️ Removed Changes 🗑️
Okay so some stuff got yeeted, and here's the tea on each of them:
- **`pylint-gui`** was removed, because it was deemed unfit for being included in pylint. It had a couple of bugs and misfeatures, its usability was subpar and since its development was neglected, we decided it is best to move on without it. Gone. Not coming back. Periodt. 💀
- The **HTML reporter** was removed, including the `--output-format=html` option. It was lately a second class citizen in Pylint, being mostly neglected. Since we now have the JSON reporter, it can be used as a basis for building more prettier HTML reports than what Pylint can currently generate. This is part of the effort of removing cruft from Pylint, by removing less used features. We love a clean-up moment ✨
- The **`--files-output`** option was removed. While the same functionality cannot be easily replicated, the JSON reporter, for instance, can be used as a basis for generating the messages per each file. Adapt, bestie 🦋
- The **`--required-attributes`** option was removed. RIP.
- The **`--ignore-iface-methods`** option was removed. Also RIP.
- The **`--optimize-ast`** flag was removed. 😭
The option was initially added for handling pathological cases, such as joining too many strings using the addition operator, which was leading pylint to have a recursion error when trying to figure out what the string was. Unfortunately, we decided to ignore the issue, since the pathological case would have happen when the code was parsed by Python as well, without actually reaching the runtime step and as such, we decided to remove the error altogether. So yeah — it's giving "we tried, we moved on" energy.
- **`epylint.py_run`'s script parameter** was removed.
Now `epylint.py_run` is always using the underlying `epylint.lint` method from the current interpreter. This avoids some issues when multiple instances of pylint are installed, which means that `epylint.py_run` might have ran a different epylint script than what was intended. Lowkey a glow-up for correctness 🫶
---
### 📜 Full changelog 📜
### 🎀 What's New in Pylint 1.7.1? 🎀
Release date: `2017-04-17` — vintage, honestly.
- Fix a false positive which occurred when an exception was reraised.
Closes `#1419`
- Fix a false positive of `disallow-trailing-tuple`.
The check was improved by verifying for non-terminating newlines, which should exempt function calls and function definitions from the check. Bestie, it's smarter now 💅
Closes `#1424`
### 🦋 What's New in Pylint 1.7? 🦋
Release date: `2017-04-13`
- Don't emit `missing-final-newline` / `C0304` or `trailing-whitespace` / `C0303` for formfeeds (page breaks). We respect the page break in this house 🎀
Closes `#1218` and `#1219`
- Don't emit by default `no-member` / `E1101` if we have opaque inference objects in the inference results.
This is controlled through the new flag `ignore-on-opaque-inference`, which is by default `True`. The inference can return multiple potential results while evaluating a Python object, but some branches might not be evaluated, which results in partial inference. In that case, it might be useful to still emit `no-member` / `E1101` and other checks for the rest of the inferred objects. So like, it's a *choice* you get to make 😌
- Added new message `assign-to-new-keyword` to warn about assigning to names which will become a keyword in future Python releases. Future-proofing, we love to see it ✨
Closes `#1351`
- Split the 'missing or differing' in parameter documentation in different error. `differing-param-doc` / `W9017` covers the differing part of the old `missing-param-doc` / `W9015`, and `differing-type-doc` / `W9018` covers the differing part of the old `missing-type-doc` / `W9016`. Not me splitting the lore into two cleaner storylines 💀
Closes `#1342`
- Added a new error, `used-prior-global-declaration` / `E0118`, which is emitted when a name is used prior a global declaration in a function. This causes a `SyntaxError` in Python 3.6. Yeah that's a whole crime scene 🚨
Closes `#1257`
- The protocol checks are emitting their messages when a special method is set to `None`. Caught in 4K.
Closes `#1263`
- Properly detect if imported name is assigned to same name in different scope. We see you, sneaky shadowing 👀
Closes `#636`, `#848`, `#851`, and `#900`
- Require one space for annotations with type hints, as per PEP 8. One. Space. Periodt.
- `trailing-comma-tuple` / `R1707` check was added.
This message is emitted when pylint finds an one-element tuple, created by a stray comma. This can suggest a potential problem in the code and it is recommended to use parentheses in order to emphasise the creation of a tuple, rather than relying on the comma itself. Because relying on a stray comma is giving chaos gremlin energy 😭
- Don't emit `not-callable` / `E1102` for instances with unknown bases. We're being chill about it now.
Closes `#1213`
- Treat keyword only arguments the same as positional arguments with regard to `unused-argument` / `W0613` check. Equality, we love it 💜
- Don't try to access variables defined in a separate scope when checking for `protected-access`. Stay in your lane, respectfully.
- Added new check to detect incorrect usage of `len(SEQUENCE)` inside test conditions. Ngl, that one's a classic bug.
- Added new extension to detect comparisons against empty string constants.
- Added new extension to detect comparisons of integers against zero.
- Added new error conditions for `bad-super-call` / `E1003`.
Now detects `super(type(self), self)` and `super(self.__class__, self)` which can lead to recursion loop in derived classes. Recursion loop?? On main?? Absolutely not 🔥
- `PyLinter.should_analyze_file` has a new optional parameter, called `is_argument`. Fresh out the oven.
Closes `#1079`
- Add attribute hints for missing members. Helpful queen behavior 👑
Closes `#1035`
- Add a new warning, `redefined-argument-from-local` / `R1704`. Iconic addition.
Closes `#649`
- Support inline comments for comma separated values in the config file. Finally, we can annotate our little lists 📝
Closes `#1024`
- `epylint.py_run`'s script parameter was removed.
- `epylint.py_run` now uses `shell=False` for running the underlying process. Safety slay 🛡️
Closes `#441`
- Added a new warning, `useless-super-delegation` / `W0235`. It's giving "why are you even here" energy, and I mean that lovingly.
Close `839`.
- Added a new error, `invalid-metaclass` / `E1139`, raised when we can detect that a class is using an improper metaclass. Metaclass crimes get caught now 💅
Closes `#579`
- Added a new refactoring message, `literal-comparison` / `R0123`. Chef's kiss.
Closes `#786`
- `arguments-differ` / `W0221` takes in consideration `kwonlyargs` and variadics. It's attentive like that.
Closes `#983`
- Removed `--optimized-ast`. Bye bestie.
Fixes part of `#975`
- Removed `--files-output` option. Also bye.
Fixes part of `#975`
- Removed `pylint-gui` from the package. Truly the end of an era 😭
- Removed the HTML reporter. Gone but the JSON reporter is right there, thriving.
Fixes part of `#975`
- `ignored-argument-names` is now used for ignoring arguments for `unused-variable` / `W0612` check.
This option was used for ignoring arguments when computing the correct number of arguments a function should have, but for handling the arguments with regard to `unused-variable` / `W0612` check, `dummy-variables-rgx` was used instead. Now, `ignored-argument-names` is used for its original purpose and also for ignoring the matched arguments for the `unused-variable` / `W0612` check. This offers a better control of what should be ignored and how. Also, the same option was moved from the design checker to the variables checker, which means that the option now appears under the `[VARIABLES]` section inside the configuration file. Okay that's a LOT of lore but it matters 🫶
Closes `#862`.
- Fix a false positive for keyword variadics with regard to keyword only arguments.
If a keyword only argument was necessary for a function, but that function was called with keyword variadics (`**kwargs`), then we were emitting a `missing-kwoa` / `E1125` false positive, which is now fixed. False positive era is over ✨
Closes `#934`.
- Fix some false positives with unknown sized variadics. Little fixes, big vibes.
Closes `#878`
- Added a new extension, `check_docstring`, for checking PEP 257 conventions. Docstring girlies, this one's for you 🎀
Closes `#868`.
- config files with BOM markers can now be read. No more mysterious first-line weirdness 💀
Closes `#864`.
- `epylint.py_run` does not crash on big files, using `.communicate()` instead of `.wait()`. Big-file proof, we love to see it 🦋
Closes `#599`
- Disable reports by default and show the evaluation score by default.
The reports were disabled by default in order to simplify the interaction between the tool and the users. The score is still shown by default, as a way of closely measuring when it increases or decreases due to changes brought to the code. So: less noise, same score, main character behavior ✨
Refs `#746`
- Disable the information category messages by default. This is a step towards making pylint more sane. Sanity arc, we're here for it 💜
Refs `#746`.
- Catch more cases as not proper iterables for `__slots__` with regard to `invalid-slots` / `E0238` pattern. `__slots__` stays getting checked.
Closes `#775`
- empty indent strings are rejected. We don't do that here, bestie 🚫
- Added a new error, `relative-beyond-top-level` / `E0402`, which is emitted when a relative import was attempted beyond the top level package. Overreaching imports get called out 😭
Closes `#588`
- Added a new warning, `unsupported-assignment-operation` / `E1137`, which is emitted when item assignment is tried on an object which doesn't have this ability. It literally *cannot*, so we say something.
Closes `#591`
- Added a new warning, `unsupported-delete-operation` / `E1138`, which is emitted when item deletion is tried on an object which doesn't have this ability. Same energy, deletion edition 💅
Closes `#592`
- Fix a false positive of `redundant-returns-doc` / `W9008`, occurred when the documented function was using `yield` instead of `return`. Yes queen, `yield` is not `return` and we now know that 🌸
Closes `#984`.
- Fix false positives of 'missing-[raises|params|type]-doc' due to not recognizing keyword synonyms supported by Sphinx. Sphinx girlies, we finally see you 🫶
- Added a new refactoring message, `consider-merging-isinstance` / `R1701`, which is emitted whenever we can detect that consecutive `isinstance` calls can be merged together. Efficiency icon behavior 👑
Closes `#968`
- Fixed a false positive for `missing-param-doc` / `W9015` and `missing-type-doc` / `W9016` — <pink>bestie</pink>, it used to fire when a class docstring used the `'For the parameters, see'` magic string but the class `__init__` docstring did not, or the other way around. 💅
- `redefined-outer-name` is now also emitted when a nested loop’s target variable is the same as a target variable in an outer loop. Closes #911.
- Added proper exception type inference for `missing-raises-doc` / `W9006`.
- Added `InvalidMessageError` exception class to replace asserts in `pylint.utils`.
- More thorough validation in `MessagesStore.register_messages()` to avoid one message accidentally overwriting another. <purple>no cap, that’s the good stuff</purple> ✨
- `InvalidMessageError`, `UnknownMessage`, and `EmptyReport` exceptions are moved to the new `pylint.exceptions` submodule.
- `UnknownMessage` and `EmptyReport` are renamed to `UnknownMessageError` and `EmptyReportError`.
- Warnings `missing-returns-type-doc` and `missing-yields-type-doc` have each been split into two warnings — `missing-[return|yield]-doc` and `missing-[return|yield]-type-doc`.
- Added epytext support to `docparams` extension. Closes #1029.
- Support having plugins with the same name and with options defined. Closes #1018.
- Sort configuration options in a section. Closes #1087.
- Added a new Python 3 warning around implementing `__div__`, `__idiv__`, or `__rdiv__` as those methods are phased out in Python 3. <cyan>iykyk</cyan> 🦋
- Added a new warning, `overlapping-except` / `W0714`, which is emitted when two exceptions in the same except-clause are aliases for each other or one exception is an ancestor of another.
- Avoid crashing on ill-formatted strings when checking for string formatting errors.
- Added a new Python 3 warning for calling `str.encode` or `str.decode` with a non-text encoding.
- Added new coding convention message, `single-string-used-for-slots` / `C0205`. Closes #1166.
- Added a new Python 3 check for accessing `sys.maxint` which was removed in Python 3 in favor of `sys.maxsize`.
- Added a new Python 3 check for bad imports.
- Added a new Python 3 check for accessing deprecated string functions.
- Do not warn about unused arguments or function being redefined in `singledispatch` registered implementations. Closes #1032 and #1034.
- Added refactoring message `no-else-return` / `R1705`.
- Improve `unused-variable` / `W0612` checker to warn about unused variables in module scope. Closes #919.
- Ignore modules import as `_` when checking for unused imports. Closes #1190.
- Improve handling of Python 3 classes with metaclasses declared in nested scopes. Closes #1177.
- Added refactoring message `consider-using-ternary` / `R1706`. Closes #1204.
- Bug-fix for false-positive `logging-format-interpolation` when format specifications are used in formatted string. Closes #572.
- Added a new switch `single-line-class-stmt` to allow single-line declaration of empty class bodies. Closes #738.
- Protected access in form `type(self)._attribute` are now allowed. Closes #1031.
- Let the user modify `msg-template` when Pylint is called from a Python script. Closes #1269.
- Imports checker supports new switch `allow-wildcard-with-all` which disables warning on wildcard import when imported module defines `__all__` variable. Closes #831.
- `too-many-format-args` and `too-few-format-args` are emitted correctly when starred expression are used in RHS tuple. Closes #957.
- `cyclic-import` checker supports local disable clauses. When one of cycle imports was done in scope where disable clause was active, cycle is not reported as violation. Closes #59. <green>periodt</green> 💚
---
## 🌈 What's New In Pylint 1.6 🌈
From Pylint’s beginning all the way to Pylint 2.14.0 included, there was a changelog packed with <hotpink>every nontrivial change</hotpink> to Pylint — including changes that were not user facing, i.e. changes that might only interest you if you’re a developer working on Pylint or Pylint plugins, or an open-source historian. 🫶
From Pylint 1.6.0 to 2.14.0, on top of the full changelog there was also a summary of (mostly user facing) changes.
Ticket numbers can be internal to logilab or come from bitbucket.
### ✨ Summary -- Release highlights ✨
Nothing major. <lavender>literally that’s the whole summary, bestie</lavender> 💀
### 🎀 New checkers 🎀
- We added a new recommendation check, `consider-iterating-dictionary`, which is emitted when a dictionary is iterated by using `.keys()`.
For instance, the following code would trigger this warning, since the dictionary’s keys can be iterated without calling the method explicitly.
for key in dictionary.keys(): ...
Can be refactored to:
for key in dictionary: ...
- `trailing-newlines` check was added, which is emitted when a file has trailing newlines.
- `invalid-length-returned` check was added, which is emitted when the `__len__` special method returns something else than a non-negative number. For instance, this example is triggering it:
class Container(object): def __len__(self): return self._items # Oups, forgot to call len() over it.
- Add a new check to the `check_docs` extension for looking for duplicate constructor parameters in a class constructor docstring or in a class docstring.
The check `multiple-constructor-doc` is emitted when the parameter is documented in both places.
- We added a new extension plugin, `pylint.extensions.mccabe`, which can be used for warning about the complexity in the code.
You can enable it as in:
$ pylint module_or_project --load-plugins=pylint.extensions.mccabe
See more at **Design checker**. <pink>it’s giving very demure, very mindful complexity reporting</pink> 🎀
### 🦋 New features 🦋
- `generated-members` now supports qualified names through regular expressions.
For instance, for ignoring all the errors generated by `numpy.core`’s attributes, we can now use:
$ pylint a.py --generated-members=numpy.*
- Add the ability to ignore files based on regex matching, with the new `--ignore-patterns` option.
Rather than clobber the existing `ignore` option, we decided to have a separate option for it. For instance, for ignoring all the test files, we can now use:
$ pylint myproject --ignore-patterns=test.*?py
- We added a new option, `redefining-builtins-modules`, which is used for defining the modules which can redefine builtins. Pylint will emit an error when a builtin is redefined, such as defining a variable called `next`. But in some cases, the builtins can be redefined in the case they are imported from other places, different than the builtins module, such is the case for `six.moves`, which contains more forward-looking functions:
$ cat a.py
Oups, now pylint emits a redefined-builtin message.
from six.moves import open $ pylint a.py --redefining-builtins-modules=six.moves
Default values: `six.moves,future.builtins`
### 🐛 Bug fixes 🐛
- Fixed a bug where the top name of a qualified import was detected as an unused variable.
- We don’t warn about `invalid-sequence-index` if the indexed object has unknown base classes, that Pylint cannot deduce.
### 🌸 Other Changes 🌸
- The `bad-builtin` check was moved into an extension.
The check was complaining about used builtin functions which were supposed to not be used. For instance, `map` and `filter` were falling into this category, since better alternatives can be used, such as list comprehensions. But the check was annoying, since using `map` or `filter` can have its use cases and as such, we decided to move it to an extension check instead. It can now be enabled through `--load-plugins=pylint.extensions.bad_builtin`.
- We use the `configparser` backport internally, for Python 2.
This allows having comments inside list values, in the configuration, such as:
disable=no-member, # Don't like this check bad-indentation
- We now use the `isort` package internally.
This improves the `wrong-import-order` check, so now we should have less false positives regarding the import order.
- We do not emit `import-error` or `no-name-in-module` for fallback import blocks by default.
A fallback import block can be considered a `TryExcept` block, which contains imports in both branches, such as:
try: import urllib.request as request except ImportError: import urllib2 as request
In the case where Pylint can not find one import from the `except` branch, then it will emit an `import-error`, but this gets cumbersome when trying to write compatible code for both Python versions. As such, we don’t check these blocks by default, but the analysis can be enforced by using the new `--analyse-fallback-block` flag. <mint>slay for backwards compatibility</mint> 🫶
- `reimported` is emitted when the same name is imported from different module, as in:
from collections import deque, OrderedDict, deque
### 💀 Deprecated features 💀
- The HTML support was deprecated and will be eventually removed in Pylint 1.7.0.
This feature was lately a second class citizen in Pylint, being often neglected and having a couple of bugs. Since we now have the JSON reporter, this can be used as a basis for more prettier HTML outputs than what Pylint can currently offer.
- The `--files-output` option was deprecated and will be eventually removed in Pylint 1.7.0.
- The `--optimize-ast` option was deprecated and will be eventually removed in Pylint 1.7.0.
The option was initially added for handling pathological cases, such as joining too many strings using the addition operator, which was leading Pylint to have a recursion error when trying to figure out what the string was. Unfortunately, we decided to ignore the issue, since the pathological case would have happen when the code was parsed by Python as well, without actually reaching the runtime step and as such, we will remove the option in the future. <peach>rip bestie, but it’s for the best</peach> 🍑
- The `check_docs` extension is now deprecated. The extension is still available under the `docparams` name, so this should work:
$ pylint module_or_package --load-extensions=pylint.extensions.docparams
The old name is still kept for backward compatibility, but it will be eventually removed.
### 🚫 Removed features 🚫
- None yet. <blue>iconic behavior, honestly</blue> 💙
### 📜 Full changelog 📜
### 🍓 What's new in Pylint 1.6.3? 🍓
Release date: 2016-07-18
- Do not crash when inferring uninferable exception types for `docparams` extension. <rainbow>we love a stable queen</rainbow> 👑
- Closes #998
### 🎀 What's new in Pylint 1.6.2? 🎀
*Release date: `2016-07-15`*
- So like, it used to crash when you asked it to print the help for options whose default value is a regular expression?? Not anymore bestie 💅
- Closes #990
- Deprecated modules now get way more granular version info, which is honestly so much better.
- Closes #991
### 🌸 What's new in Pylint 1.6.1? 🌸
*Release date: `2016-07-07`*
- Uses environment markers so it can support conditional dependencies, as one does ✨
### 🦋 What's New in Pylint 1.6.0? 🦋
*Release date: `2016-07-03`*
- New extension dropped: `pylint.extensions.mccabe`, and its whole personality is warning you about complexity in your code. Iconic, honestly.
- Support for `--optimize-ast` is deprecated 🫶
- Fixes part of #975
- Support for the HTML output is deprecated.
- Fixes part of #975
- Support for `--output-files` is deprecated.
- Fixes part of #975
- Fixed a documentation error in the `check_docs` extension.
- Closes #735
- The list of property-defining decorators is now configurable, so you can tell it what counts as a property. Slay.
- Fixed a bug where the top name of a qualified import was being detected as an unused variable — literally so annoying of it.
- Closes #923
- `bad-builtin` / `W0141` is now an extension check.
- `generated-members` now supports qualified names through regular expressions 🧚
So like, you can totally specify a regex as `--generated-members=astroid.node_classes.*` to ignore every `no-member` / `E1101` error that gets accessed as in `astroid.node_classes.missing.object`.
- Added the ability to ignore files based on regex matching with the brand-new `--ignore-patterns` option. You can specify multiple ignore patterns too, bestie. And rather than clobbering the existing `ignore` option, they introduced a whole new one called `ignore-patterns` — respectful, we love to see it.
- Closes #156
- Added a new error, `trailing-newlines` / `C0305`, which is emitted when a file has trailing new lines. Trailing new lines. The audacity.
- Closes #682
- Added a new option, `'redefining-builtins-modules'`, for controlling the modules which can redefine builtins, such as `six.moves` and `future.builtins` 👑
- Closes #464
- `'reimported'` is emitted when the same name is imported from a different module.
- Closes #162
- Added a new recommendation checker, `consider-iterating-dictionary` / `C0201`, which is emitted when a dictionary is iterated through `.keys()`. Bestie, just iterate the dict. Please.
- Closes #699
- Uses the `configparser` backport for Python 2 🐍
This fixes a problem with comments inside values, which is already fixed in Python 3's `configparser`.
- Closes #828
- A new error was added: `invalid-length-returned` / `E0303`, which fires when the `__len__` special method returns something other than a non-negative number. Like. It's a length. Be a length.
- Closes #557
- Switched to using `isort` internally for `wrong-import-order` / `C0411`. Delegation, we stan.
- Closes #879
- The `check_docs` extension can now find constructor parameters in `__init__`. Look at her go 💖
- Closes #887
- No more warnings about `invalid-sequence-index` / `E1126` when the indexed object has unknown base classes — because how is it supposed to know, fr fr.
- Closes #867
- No more crashing when checking `super-init-not-called` / `W0231` on a method that's defined inside an `if` block 🙈
- `import-error` / `E0401` and `no-name-in-module` / `E0611` are **not** emitted for fallback import blocks by default anymore.
So up until now, it warned with these errors whenever it found a fallback import block (that's a `TryExcept` block containing imports for Python 2 and Python 3), but honestly that gets so cumbersome when you're trying to write compatible code. So now those blocks aren't checked by default, but you can force the analysis back on with the new `--analyse-fallback-block` flag. Your call, bestie.
- Closes #769.
- Little note on ticket numbers: they can be internal to Logilab, or from bitbucket. In the latest version, they're also GitHub issues. Iykyk 🫶
---
## 🌈 What's New In Pylint 1.5 🌈
### ⭐ What's New in Pylint 1.5.5? ⭐
*Release date: `2016-03-21`*
- `visit_importfrom` from the Python 3 porting checker now actually gets called when everything is disabled 💀
Because the visit method was filtering the patterns it expected to be activated, it didn't run when everything but one pattern was disabled, which led to spurious false positives. Not cute.
- Closes #852
- Doesn't emit `unsubscriptable-value` for classes with unknown base classes anymore.
- Closes #776.
- Uses an `OrderedDict` for storing the configuration elements 🔥
This fixes an issue with the unpredictable order of the `disable` / `enable` elements from a config file. In certain cases `disable` came before `enable`, which resulted in whole classes of errors being enabled even though the intention was to disable them. The best example was when running multiple processes, each one having different enables / disables that affected the output. Chaos. Avoided.
- Closes #815
- Bare and broad `except` handlers are no longer considered as ignoring `NameError`, `AttributeError` and similar exceptions, in the context of checkers for these issues.
- Closes #826
### 💜 What's New in Pylint 1.5.4? 💜
*Release date: `2016-01-15`*
- Merged `StringMethodChecker` with `StringFormatChecker`. This fixes a bug where disabling all the messages and enabling only a handful of messages from the `StringFormatChecker` would have resulted in no messages at all. Literally zero. Nothing. 😭
- `unneeded-not` / `C0113` is no longer applied over sets.
### 🍓 What's New in Pylint 1.5.3? 🍓
*Release date: `2016-01-11`*
- Handles the import fallback idiom with regard to `wrong-import-order` / `C0411`.
- Closes #750
- Decoupled the displaying of reports from the displaying of messages ✨
Some reporters aggregate the messages instead of displaying them when they're available, and the actual displaying was conflated in `generate_reports`. Unfortunately this behaviour was flaky, and in the case of the JSON reporter the messages weren't shown at all if a file had syntax errors or if it was missing. To fix this, aggregated messages can now be displayed with `Reporter.display_message`, while the reports are displayed with `display_reports`. Separation, queen behaviour.
- Closes #766 Closes #765
- Ignores function calls with variadic arguments that have no context.
Inferring variadic positional arguments and keyword arguments results in empty `Tuple`s and `Dict`s, which can lead in some cases to false positives with regard to `no-value-for-parameter` / `E1120`. To avoid this, until there's support for call context propagation, such cases are ignored if detected.
- Closes #722
- Treats `AsyncFunctionDef` just like `FunctionDef` nodes, by implementing `visit_asyncfunctiondef` in terms of `visit_functiondef`. Equality 💅
- Closes #767
- Takes `kwonlyargs` into account when verifying that arguments are defined with the `check_docs` extension.
- Closes #745
- Suppresses reporting `unneeded-not` / `C0113` inside `__ne__` methods.
- Closes #749
### 🧚 What's New in Pylint 1.5.2? 🧚
*Release date: `2015-12-21`*
- Doesn't crash if `graphviz` isn't installed — instead it emits a warning letting you know. Helpful and polite.
- Closes #168
- The `deprecated-method` / `W4902` checker now only accepts functions and methods. This prevents a crash that can occur when an object doesn't have a `.qname()` method after the inference.
- Doesn't emit `super-on-old-class` on classes with unknown bases.
- Closes #721
- Allows statements in `if` or `try` blocks that contain imports.
- Closes #714
### 🎀 What's New in Pylint 1.5.1? 🎀
*Release date: `2015-12-02`*
- Fixed a crash that occurred when old visit methods are encountered in plugin modules.
- Closes #711
- Added `wrong-import-position` / `C0413` to `check_messages`'s decorator arguments for `ImportChecker.leave_module`. This fixes an esoteric bug that occurs when `ungrouped-imports` / `C0412` and `wrong-import-order` / `C0411` are disabled and pylint is executed on multiple files. What happens is that without `wrong-import-position` / `C0413` in `check_messages`, `leave_module` will never be called, which means the first non-import node from other files might leak into the current file, leading to `wrong-import-position` / `C0413` being emitted by pylint. Sneaky little leak, but we got her 🔍
- Fixed a crash that occurred when old visit methods are encountered in plugin modules.
- Closes #711
- Doesn't emit `import-self` / `W0406` and `cyclic-import` / `R0401` for relative imports of modules with the same name as the package itself.
- Closes #708 Closes #706.
### 🌟 What's New in Pylint 1.5.0? 🌟
*Release date: `2015-11-29`*
- Added multiple warnings related to imports, and honestly they ate 💅
`wrong-import-order` / `C0411` is emitted when PEP 8 recommendations regarding imports aren't respected (that is, standard imports should be followed by third-party imports and then by local imports). `ungrouped-imports` / `C0412` is emitted when imports from the same package or module aren't placed together but are scattered around in the code. `wrong-import-position` / `C0413` is emitted when code is mixed with imports, since it's recommended for imports to be at the top of the file so a human reader can figure out a module's dependencies more easily.
- Closes #692
- Added a new refactoring warning, `unneeded-not` / `C0113`, emitted when an expression with the `not` operator could be simplified. Not not. Not. 🫠
- Closes #670
- Added a new refactoring warning, `simplifiable-if-statement` / `R1703`, used when an `if` statement could be reduced to a boolean evaluation of its test.
- Closes #698
- Added a new refactoring warning, `too-many-boolean-expressions` / `R0916`, used when an `if` statement contains too many boolean expressions, which makes the code less maintainable and harder to understand. Bestie, if your `if` needs a whole paragraph, it's a red flag 🚩
- Closes #677
- Property methods are shown as attributes instead of functions in `pyreverse` class diagrams.
- Closes #284
- Added a new refactoring error, `too-many-nested-blocks` / `R1702`, which is emitted when a function or a method has too many nested blocks, which makes the code less readable and harder to understand.
- Closes #668
- Added a new error, `unsubscriptable-object` / `E1136`, emitted when the value used in a subscription expression doesn't support subscription (i.e. doesn't define a `__getitem__` method).
- Doesn't warn about abstract classes instantiated in their own body.
- Closes #627
- Obsolete options are **not** in the generated config file by default. Bye boo 👋 Closes #632
- `non-iterator-returned` / `E0301` can now sniff out classes with iterator-metaclasses ✨ Closes #679
- New error just dropped: `unsupported-membership-test` / `E1135` — fires when the thing on the right of the `in` operator doesn't support the membership test protocol (i.e. it doesn't define `__contains__`/`__iter__`/`__getitem__`) 😭
- Two new errors, besties: `not-an-iterable` / `E1133`, emitted when a non-iterable value gets used in an iterating context (starargs, for-statement, comprehensions, etc), and `not-a-mapping` / `E1134`, emitted when a non-mapping value is used in a mapping context 🫶 Closes #563
- `no-self-use` / `R6301` is being so chill now — it won't emit a warning if there's a `super()` call inside the method 💅 Closes #667
- New checker to spot multiple imports on one line 👀 Closes #598
- Fixed a `unused-argument` / `W0613` false positive when the `+=` operator is used. We love a fix 🎀 Closes #518
- `import-error` / `E0401` is a no-show for ignored modules now. PyLint will not emit import errors for any import which is, or is a subpackage of, a module in the `ignored-modules` list 🙈 Closes #223
- Fixed `unused-import` / `W0611` false positive when the import is used in a class assignment 💖 Closes #475
- New error, `not-context-manager` / `E1129` — emitted when something that doesn't implement `__enter__` and `__exit__` gets used in a `with` statement 🚫
- New warning, `confusing-with-statement` / `W0124`, emitted by the base checker when an ambiguous looking `with` statement shows up. Like `with open() as first, second` — looks like a tuple assignment but it's actually 2 context managers 💀
- New warning, `duplicate-except` / `W0705` — emitted when there's an exception handler handling an exception type that was already handled before 👯♀️ Closes #485
- A couple of warnings got **promoted to errors**, since they could uncover potential bugs in the code. The glow-up squad: `assignment-from-none` / `E1128`, `unbalanced-tuple-unpacking` / `W0632`, `unpacking-non-sequence` / `E0633`, `non-iterator-returned` / `E0301` 👑 Closes #388
- Pragma controls can end with a semicolon now, so you can add a whole reason for why you used it, like `# pylint: disable=old-style-class;reason=...` 📝 Closes #449
- `--jobs` can vibe with `--load-plugins` now 🤝 Closes #456
- `--jobs` performance is improved when you're dealing only with a package name ⚡ Closes #479
- No more `unused-wildcard-import` / `W0614` when the imported name comes from another module and it is in fact a `__future__` name 🌈
- The colorized reporter now works on Windows 🪟✨ Closes #96.
- `pointless-except` warning? Gone. It was previously disabled by default and it wasn't very useful 🗑️ Closes #506.
- Fixed a crash on Python 3 related to the string checker — it crashed when it encountered a bytes string with a `.format` method called 😱
- No more `no-self-use` / `R6301` warnings for builtin properties 🏠
- Fixed a false positive for `bad-reversed-sequence` / `E0111` when a subclass of a `dict` provides a `__reversed__` method 🔄
- The default `no-docstring-rgx` changed so `missing-docstring` / `C0111` isn't emitted for private functions 🤫
- No `redefined-outer-name` / `W0621` for `__future__` directives 🙅♀️ Closes #520.
- `bad-builtin` / `W0141` now comes with some hints 💡 Closes #522.
- When checking for invalid arguments to a callable, in `typecheck.py`, it looks up the `__init__` in case the found `__new__` comes from builtins.
Since the `__new__` comes from builtins, it will not have attached any information regarding what parameters it expects, so the check will be useless. Retrieving `__init__` in that case will at least detect a couple of false negatives 🔍 Closes #429.
- No `no-member` / `E1101` for classes with unknown bases. Since we don't know what those bases might add, we simply ignore the error in this case 🤷♀️
- It looks in the implicit metaclass when checking for `no-member` / `E1101`, if the class in question has an implicit metaclass, which is `True` for new style classes 🧠 Closes #438.
- Two new warnings: `duplicate-bases` / `E0241` and `inconsistent-mro` / `E0240`.
`duplicate-bases` / `E0241` is emitted when a class has the same bases listed more than once in its bases definition, while `inconsistent-mro` / `E0240` is emitted when no sane mro hierarchy can be determined 🌳 Closes #526.
- `interface-not-implemented` warning removed 🚮 Closes #532.
- The rest of the interface checks are gone too: `interface-is-not-class`, `missing-interface-method`, `unresolved-interface`. The reason is that it's better to start recommending ABCs instead of the old Zope era of interfaces. One side effect of this change is that `ignore-iface-methods` becomes a noop, it's deprecated and it will be removed at some time ⚠️
- A proper deprecation warning is now emitted for `reporters.BaseReporter.add_message`.
The alternative way is to use `handle_message`. `add_message` will be removed in Pylint 1.6 🫡
- New module `extensions` for optional checkers, with the test directory `test/extensions` and documentation file `doc/extensions.rst` 📦
- New checker `extensions.check_docs` that verifies parameter documentation in Sphinx, Google, and Numpy style 📚
- It now catches undefined variable cases where the "definition" of an undefined variable was in a `del` statement. Instead of emitting `used-before-assignment` / `E0601`, which is totally misleading, it now emits `undefined-variable` / `E0602` 🎯 Closes #528.
- No `attribute-defined-outside-init` / `W0201` and `access-member-before-definition` / `E0203` for mixin classes. Actual errors can occur in mixin classes, but this is controlled by the `ignore-mixin-members` option 🧩 Closes #412.
- Better detection of undefined variables and variables used before assignment for variables used as default arguments to functions, where the variable was first defined in the class scope 🕵️♀️ Closes #342 and issue #404.
- New warning, `unexpected-special-method-signature` / `E0302` — emitted when a special method (dunder method) doesn't have the expected signature, which can lead to actual errors in the application code 🚨 Closes #253.
- `bad-context-manager` / `E0235` removed, thanks to the inclusion of `unexpected-special-method-signature` / `E0302` 🔥
- No `no-name-in-module` / `E0611` if the import is guarded by an `ImportError`, `Exception` or a bare `except` clause 🛡️
- No `no-member` / `E1101` if the attribute access node is protected by an except handler, which handles `AttributeError`, `Exception` or it is a bare `except` 🛡️
- No `import-error` / `E0401` if the import is guarded by an `ImportError`, `Exception` or a bare `except` clause 🛡️
- No `undefined-variable` / `E0602` if the node is guarded by a `NameError`, `Exception` or bare `except` clause 🛡️
- New warning, `using-constant-test` / `W0125` — emitted when a conditional statement (`If`, `IfExp`) uses a test which is always constant, such as numbers, classes, functions etc. This is most likely an error from the user's part 😬 Closes #524.
- No `raising-non-exception` / `E0710` when the exception has unknown bases. We don't know what those bases actually are and it's better to assume that the user knows what he is doing rather than emitting a message which can be considered a false positive 🤷♀️
- It looks for a `.pylintrc` configuration file in the current folder, if `pylintrc` is not found. Dotted pylintrc files will **not** be searched in the parents of the current folder, as it is done for `pylintrc` 📁
- New error, `invalid-unary-type-operand`, emitted when an unary operand is used on something which doesn't support that operation (for instance, using the unary bitwise inversion operator on an instance which doesn't implement `__invert__`) ➖
- It now takes in consideration differences between arguments of various type of functions (classmethods, staticmethods, properties) when checking for `arguments-differ` 🔬 Closes #548.
- `astroid.inspector` was moved to `pylint.pyreverse`, since it belongs there and it doesn't need to be in astroid 📦
- `astroid.utils.LocalsVisitor` was moved to `pylint.pyreverse.LocalsVisitor` 📦
- `pylint.checkers.utils.excepts_import_error` was removed. Use `pylint.checkers.utils.error_of_type` instead 🔁
- No `undefined-all-variables` for nodes which can't be inferred (YES nodes) 🙅♀️
- `yield-outside-func` is also emitted for `yield from` 🌾
- New error, `too-many-star-expressions` / `E0112` — emitted when there are more than one starred expression (`*x`) in an assignment. The warning is emitted **only on Python 3** ⭐ Closes
- New error, `invalid-star-assignment-target` / `E0113` — emitted when a starred expression (`*x`) is used as the lhs side of an assignment, as in `*x = [1, 2]`. This is not a `SyntaxError` on Python 3 though 😮
- It now detects a couple of objects which can't be base classes (`bool`, `slice`, `range` and `memoryview`, which weren't detected until now) 🚫
- New error for the Python 3 porting checker: `import-star-module-level`, used when a star import is detected in another scope than the module level, which is an error on Python 3. Using this will emit a `SyntaxWarning` on Python 2 ⚠️
- **`star-needs-assignment-target` / `E0114`** 🎯 — brand new error, and it fires on Python 3 whenever a Starred expression (`*x`) is *not* used in an assignment target. The thing is, parsing the AST on Python 3 won't catch this for you, so it had to become its own separate check. Bestie, it's doing the work the parser refuses to do.
- **`unsupported-binary-operation` / `E1131`** 💀 — new error, emitted when a binary arithmetic operation runs between two objects that literally don't support it (think a number plus a string, bestie). It's *currently disabled* because it exhibits way too many false positives, but it'll be re-enabled the second that gets sorted. So don't @ it yet.
- New imported features from `astroid` into `pyreverse` 🦋: `pyreverse.inspector.Project`, `pyreverse.inspector.project_from_files` and `pyreverse.inspector.interfaces`.
These got moved because they simply did not belong in astroid. Periodt.
- Enabled `misplaced-future` / `W0410` for Python 3 ✨.
Closes #580.
- **`nonlocal-and-global` / `E0115`** 👀 — new error, emitted when a name turns out to be *both* `nonlocal` and `global` in the same scope. Make up your mind, babe.
Closes #581.
- The `ignored-classes` option can now work with qualified names, like `ignored-classes=optparse.Values` 💅
Closes #297
- `ignored-modules` can work with qualified names as well as with Unix pattern matching for recursive ignoring 🌈
Closes #244
- Improved detection of relative imports in non-packages, plus importing missing modules with a relative import from a package. Lowkey a glow-up for import checking 🔥
- Don't emit `no-init` if not all the bases from a class are known 🙈
Closes #604.
- The `--no-space-check` option accepts `empty-line` as a possible option ✨
Closes #541.
- `--generate-rcfile` now generates, by default, human readable symbols for the `--disable` option 🎀
Closes #608.
- Improved the `not-in-loop` / `E0103` checker so it properly detects more cases 👑
- **`continue-in-finally` / `W0136`** — new error, emitted when the `continue` keyword is found inside a `finally` clause, which is a `SyntaxError`. Yeah. Do not do that. 😭
- The `--zope` flag is **deprecated** and it is slated for removal in Pylint 1.6 ⚠️
The reason behind this removal is the fact that it's a specialized flag, and there are solutions for the original problem: use `--generated-members` with the members that cause problems when using Zope, or add AST transforms tailored to the zope project.
At the same time, `--include-ids` and `--symbols` will also be removed in Pylint 1.6.
Closes #570.
- `missing-module-attribute` was removed, and so was the corresponding CLI option, `required-attributes`, which is slated for removal in Pylint 1.6 💀
- `missing-reversed-argument` was removed.
The reason behind this is that this kind of errors should be detected by the type checker for all the builtins, and not as a special case for the `reversed` builtin. This will happen shortly in the future. iykyk 🦋
- The `--comment` flag is **obsolete** and it will be removed in Pylint 1.6 ⚠️
- The `--profile` flag is **obsolete** and it will be removed in Pylint 1.6 ⚠️
- **`misplaced-bare-raise` / `E0704`** — new error 🙈
The error is used when a bare `raise` is not used inside an `except` clause. This can generate a `RuntimeError` in Python if there are no active exceptions to be reraised. While it works in Python 2 due to the fact that the exception leaks outside of the `except` block, it's nevertheless a behaviour that a user *shouldn't* depend upon, since it's not obvious to the reader of the code what exception will be raised — and it will not be compatible with Python 3 anyhow.
Closes #633.
- Brought logilab-common's `ureports` into `pylint.reporters` 🧚
With this change, we moved away from depending on logilab-common, having in Pylint all the components that were used from logilab-common. The API should be considered an implementation detail and can change at some point in the future.
Closes #621.
- `reimported` is emitted for reimported objects on the same line 👀
Closes #639.
- Abbreviations of command line options are **not supported anymore** 💅
Using abbreviations for CLI options was never considered to be a feature of pylint — that fact was only a side effect of using optparse. As this was the case, using `--load-plugin` or other abbreviation for `--load-plugins` never actually worked, while it also didn't raise an error. So yeah, that era is over.
Closes #424.
- **`nonlocal-without-binding` / `E0117`** — new error 🎯
The error is emitted on Python 3 when a `nonlocal` name is not bound to any variable in the parents scopes.
Closes #582.
- `deprecated-module` / `W4901` can be shown for modules which aren't available 😭
Closes #362.
- Don't consider a class abstract if its members can't be properly inferred 🦋
This fixes a false positive related to `abstract-class-instantiated` / `E0110`.
Closes #648.
- Added a new checker for the `async` features added by PEP 492 ✨
- **`yield-inside-async-function` / `E1700`** — new error, emitted on Python 3.5 and upwards when the `yield` statement is found inside a new coroutine function (PEP 492) 🌸
- **`not-async-context-manager` / `E1701`** — new error, emitted when an `async` context manager block is used with an object which doesn't support this protocol (PEP 492) 🌈
- **`singleton-comparison` / `C0121`** — new convention warning, emitted when a comparison to `True`, `False` or `None` is found 👑
- Don't emit `assigning-non-slot` / `E0237` for descriptors 🙈
Closes #652.
- **`repeated-keyword` / `E1132`** — new error, raised when a keyword argument is passed multiple times into a function call 💀
This is similar to `redundant-keyword-arg` / `E1124`, but it's mildly different, so it needs to be a separate error.
- `--enable=all` can now be used 🎀
Closes #142.
- **`misplaced-comparison-constant` / `C2201`** — new convention message, emitted when a constant is placed on the left hand side of a comparison, as in `'5 == func()'`. This is also called a Yoda condition, since the flow of code reminds of the Star Wars green character — conditions usually encountered in languages with variabile assignments in conditional statements. mmm, yes, iconic 🌟
- **`consider-using-enumerate` / `C0200`** — new convention message, emitted when code that uses `range` and `len` for iterating is encountered 🦋
Closes #684.
- Added two new refactoring messages, `no-classmethod-decorator` / `R0202` and `no-staticmethod-decorator` / `R0203`, which are emitted when a static method or a class method is declared without using decorator syntax 👑
Closes #675.
---
## ✨ What's New In Pylint 1.4 ✨
### 🌸 What's New in Pylint 1.4.3? 🌸
Release date: 2015-03-14
- Removed three warnings: `star-args`, `abstract-class-little-used`, `abstract-class-not-used`. These warnings don't add any real value and they don't imply errors or problems in the code. bye besties 💅
- Added a new option for controlling the peephole optimizer in astroid 🎯 The option `--optimize-ast` will control the peephole optimizer, which is used to optimize a couple of AST subtrees. The current problem solved by the peephole optimizer is when multiple joined strings, with the addition operator, are encountered. If the number of such strings is high enough, Pylint will then fail with a maximum recursion depth exceeded error, due to its visitor architecture. The peephole just transforms such calls, if it can, into the final resulting string — and this exhibits a problem, because the `visit_binop` method stops being called (in the optimized AST it will be a `Const` node). 😭
### 🦋 What's New in Pylint 1.4.2? 🦋
Release date: 2015-03-11
- Don't require a docstring for empty modules 🙈
Closes #261
- Fixed a false positive with the `too-few-format-args` string warning, emitted when the string format contained a normal positional argument (`'{0}'`), mixed with a positional argument which did an attribute access (`'{0.__class__}'`). 💀
Closes #463
- Take in account all the methods from the ancestors when checking for `too-few-public-methods` / `R0903` 👑
Closes #471
- Catch enchant errors and emit `invalid-characters-in-docstring` / `C0403` when checking for spelling errors ✨
Closes #469
- Use all the inferred statements for the `super-init-not-called` / `W0231` check 🌸
Closes #389
- Added a new warning, `'unichr-builtin'`, emitted by the Python 3 porting checker when the `unichr` builtin is found 🌈
Closes #472
- Added a new warning, `'intern-builtin'`, emitted by the Python 3 porting checker when the `intern` builtin is found 🌈
Closes #473
- Added support for editable installations 🎀
- The HTML output accepts the `--msg-template` option. Patch by Dan Goldsmith ✨
- Added `'map-builtin-not-iterating'` (replacing `'implicit-map-evaluation'`), `'zip-builtin-not-iterating'`, `'range-builtin-not-iterating'`, and `'filter-builtin-not-iterating'`, which are emitted by `--py3k` when the appropriate built-in is not used in an iterating context (semantics taken from `2to3`) 💅
- Added a new warning, `unidiomatic-typecheck` / `C0123`, emitted when an explicit typecheck uses `type()` instead of `isinstance()`. For example, `type(x) == Y` instead of `isinstance(x, Y)`. Patch by Chris Rebert 👑
Closes #299
- Added support for combining the Python 3 checker mode with the `--jobs` flag (`--py3k` and `--jobs`) 🔥
Closes #467
- Added a new warning for the Python 3 porting checker, `'using-cmp-argument'`, emitted when the `cmp` argument for the `list.sort` or `sorted` builtin is encountered ✨
- Made the `--py3k` flag commutative with the `-E` flag. Also, this patch fixes the leaks of error messages from the Python 3 checker when the errors mode was activated 🦋
Closes #437
### 💜 What's New in Pylint 1.4.1? 💜
Release date: 2015-01-16
- Look only in the current function's scope for `bad-super-call` / `E1003` 🎯
Closes #403
- Check the return of properties when checking for `not-callable` / `E1102` 👀
Closes #406
- Warn about using the `input()` or `round()` built-ins for Python 3 ⚠️
Closes #411
- Proper abstract method lookup while checking for `abstract-class-instantiated` / `E0110`.
Closes #401
- Use a mro traversal for finding abstract methods.
Closes #415
- Fix a false positive with `catching-non-exception` / `E0712` and tuples of exceptions.
- Fix a false negative with `raising-non-exception` / `E0710`, when the raise used an uninferrable exception context.
- Fix a false positive on Python 2 for `raising-bad-type` / `E0702`, when raising tuples in the form `raise (ZeroDivisionError, None)`.
- Fix a false positive with `invalid-slots-objects`, where the slot entry was a unicode string on Python 2.
Closes #421
- Add a new warning, `redundant-unittest-assert` / `W1503`, emitted when using unittest's methods `assertTrue` and `assertFalse` with constant value as argument. Patch by Vlad Temian.
- Add a new JSON reporter, usable through `-f` flag.
- Add the method names for the `signature-differs` / `W0222` and `'argument-differs'` warnings.
Closes #433
- Don't compile test files when installing.
- Fix a crash which occurred when using multiple jobs and the files given as argument didn't exist at all.
## ✨ What's New in Pylint 1.4.0? ✨
Release date: 2014-11-23
- Added new options for controlling the loading of C extensions. By default, only C extensions from the stdlib will be loaded into the active Python interpreter for inspection, because they can run arbitrary code on import. The option `--extension-pkg-whitelist` can be used to specify modules or packages that are safe to load.
- Change default `max-line-length` to 100 rather than 80
- Drop `BaseRawChecker` class which were only there for backward compatibility for a while now
- Don't try to analyze string formatting with objects coming from function arguments.
Closes #373
- Port source code to be Python 2/3 compatible. This drops the need for `2to3`, but does drop support for Python 2.5.
- Each message now comes with a confidence level attached, and can be filtered base on this level. This allows to filter out all messages that were emitted even though an inference failure happened during checking.
- Improved presenting `unused-import` / `W0611` message.
Closes #293
- Add new checker for finding spelling errors. New messages: `wrong-spelling-in-comment` / `C0401`, `wrong-spelling-in-docstring` / `C0402`. New options: `spelling-dict`, `spelling-ignore-words`.
- Add new `'-j'` option for running checks in sub-processes.
- Added new checks for line endings if they are mixed (LF vs CRLF) or if they are not as expected. New messages: `mixed-line-endings` / `C0327`, `unexpected-line-ending-format` / `C0328`. New option: `expected-line-ending-format`.
- `dangerous-default-value` / `W0102` no longer evaluates the value of the arguments, which could result in long error messages or sensitive data being leaked.
Closes #282
- Fix a false positive with string formatting checker, when encountering a string which uses only position-based arguments.
Closes #285
- Fix a false positive with string formatting checker, when using keyword argument packing.
Closes #288
- Proper handle class level scope for lambdas.
- Handle `too-few-format-args` / `E1306` or `too-many-format-args` / `E1305` for format strings with both named and positional fields.
Closes #286
- Analyze only strings by the string format checker.
Closes #287
- Properly handle nested format string fields.
Closes #294
- Don't emit `attribute-defined-outside-init` / `W0201` if the attribute was set by a function call in a defining method.
Closes #192
- Properly handle unicode format strings for Python 2.
Closes #296
- Don't emit `import-error` / `E0401` if an import was protected by a try-except, which excepted `ImportError`.
- Fix an `unused-import` / `W0611` false positive, when the error was emitted for all the members imported with `'from import'` form.
Closes #304
- Don't emit `invalid-name` / `C0103` when assigning a name in an `ImportError` handler.
Closes #302
- Don't count branches from nested functions.
- Fix a false positive with `too-few-format-args` / `E1306`, when the format strings contains duplicate manual position arguments.
Closes #310
- fixme regex handles comments without spaces after the hash.
Closes #311
- Don't emit `unused-import` / `W0611` when a special object is imported (`__all__`, `__doc__` etc.).
Closes #309
- Look in the metaclass, if defined, for members not found in the current class.
Closes #306
- Don't emit `protected-access` / `W0212` if the attribute is accessed using a property defined at the class level.
- Detect calls of the parent's `__init__`, through a binded `super()` call.
- Check that a class has an explicitly defined metaclass before emitting `'old-style-class'` for Python 2.
- Emit `catching-non-exception` / `E0712` for non-class nodes.
Closes #303
- Order of reporting is consistent.
- Add a new warning, `boolean-datetime` / `W1502`, emitted when an instance of `'datetime.time'` is used in a boolean context.
Closes #239
- Fix a crash which occurred while checking for `method-hidden` / `E0202`, when the parent frame was something different than a function.
- Generate html output for missing files.
Closes #320
- Fix a false positive with `too-many-format-args` / `E1305`, when the format string contains mixed attribute access arguments and manual fields.
Closes #322
- Extend the cases where `undefined-variable` / `E0602` and `used-before-assignment` / `E0601` can be detected.
Closes #291
- Add support for customising callback identifiers, by adding a new `'--callbacks'` command line option.
Closes #326
- Add a new warning, `logging-format-interpolation` / `W1202`, emitted when `.format()` string interpolation is used within logging function calls.
- Don't emit `unbalanced-tuple-unpacking` / `W0632` when the rhs of the assignment is a variable length argument.
Closes #329
- Add a new warning, `inherit-non-class` / `E0239`, emitted when a class inherits from something which is not a class.
Closes #331
- Fix another false positives with `undefined-variable` / `E0602`, where the variable can be found as a class assignment and used in a function annotation.
Closes #342
- Handle assignment of the string format method to a variable.
Closes #351
- Support wheel packaging format for PyPi.
Closes #334
- Check that various built-ins that do not exist in Python 3 are not used: `apply`, `basestring`, `buffer`, `cmp`, `coerce`, `execfile`, `file`, `long raw_input`, `reduce`, `StandardError`, `unicode`, `reload` and `xrange`.
- Warn for magic methods which are not used in any way in Python 3: `__coerce__`, `__delslice__`, `__getslice__`, `__setslice__`, `__cmp__`, `__oct__`, `__nonzero__` and `__hex__`.
- Don't emit `assigning-non-slot` / `E0237` when the assignment is for a property.
Closes #359
- Fix for regression: `'{path}'` was no longer accepted in `'--msg-template'`.
- Report the percentage of all messages, not just for errors and warnings.
Closes #319
- `too-many-public-methods` / `R0904` is reported only for methods defined in a class, not in its ancestors.
Closes #248
- `too-many-lines` / `C0302` disable pragma can be located on any line, not only the first.
Closes #321
- Warn in Python 2 when an import statement is found without a corresponding `from __future__ import absolute_import`.
- Warn in Python 2 when a non-floor division operation is found without a corresponding `from __future__ import division`.
- Add a new option, `'exclude-protected'`, for excluding members from the `protected-access` / `W0212` warning.
Closes #48
- Warn in Python 2 when using `dict.iter*()`, `dict.view*()`; none of these methods are available in Python 3.
- Warn in Python 2 when calling an object's `next()` method; Python 3 uses `__next__()` instead.
- Warn when assigning to `__metaclass__` at a class scope; in Python 3 a metaclass is specified as an argument to the `'class'` statement.
- Warn when performing parameter tuple unpacking; it is not supported in Python 3.
- `abstract-class-instantiated` / `E0110` is also emitted for Python 2. It was previously disabled.
- Add `'long-suffix'` error, emitted when encountering the long suffix on numbers.
- Add support for disabling a checker, by specifying an `'enabled'` attribute on the checker class.
- Add a new CLI option, `--py3k`, for enabling Python 3 porting mode. This mode will disable all other checkers and will emit warnings and errors for constructs which are invalid or removed in Python 3.
- Add `'old-octal-literal'` to Python 3 porting checker, emitted when encountering octals with the old syntax.
- Add `'implicit-map-evaluation'` to Python 3 porting checker, emitted when encountering the use of `map` builtin, without explicit evaluation.
## 🌸 What's New In Pylint 1.3 🌸
Release date: 2014-07-26
- Allow hanging continued indentation for implicitly concatenated strings.
Closes #232.
- Pylint works under Python 2.5 again, and its test suite passes.
- Fix some false positives for the `cellvar-from-loop` warnings.
Closes #233.
- Return new astroid class nodes when the inferencer can detect that that result of a function invocation on a type (like `type` or `abc.ABCMeta`) is requested.
Closes #205.
- Emit `undefined-variable` / `E0602` for undefined names when using the Python 3 `metaclass=` argument.
- Checkers respect priority now.
Closes #229
- **`W0511`** is fixed — no more false positive there. Closes #149. 💅
- Fixed the `unused-import` / `W0611` false positive that showed up with Python 3 metaclasses. Closes #143 🦋
- Please don't warn with `bad-format-character` / `E1300` anymore when you hit the `'a'` format on Python 3. ✨
- Added a whole squad of checks for PEP 3101 advanced string formatting: `bad-format-string` / `W1302`, `missing-format-argument-key` / `W1303`, `unused-format-string-argument` / `W1304`, `format-combined-specification` / `W1305`, `missing-format-attribute` / `W1306` and `invalid-format-index` / `W1307`. Bestie, the format string is getting a full vibe check. 🎀
- Now `broad-except` / `W0703` and `bare-except` / `W0702` get issued even if the number of except handlers is different than 1. Closes #113 🔥
- `attribute-defined-outside-init` / `W0201` now gets issued for **all** cases, not just for the last assignment. Closes #262 👑
- Emits `not-callable` / `E1102` when you call properties. Closes #268. 😭
- Fixed a false positive with unbalanced iterable unpacking when it runs into starred nodes. Closes #273. ⭐
- New checks, `invalid-slice-index` / `E1127` and `invalid-sequence-index` / `E1126`, for invalid sequence and slice indices. ✨
- New warning `assigning-non-slot` / `E0237`, which catches assignments to attributes not defined in slots. 🫶
- Does **not** emit `no-name-in-module` / `E0611` for ignored modules anymore. Closes #223. 💜
- Fixed an `unused-variable` / `W0612` false positive where the variable is assigned through an import. Closes #196. 🙈
- Definition order is considered for classes, function arguments and annotations. Closes #257. 📚
- Won't emit `unused-variable` / `W0612` when you're assigning to a `nonlocal`. Closes #275. ✨
- The `ImportError` is no longer allowed to propagate from the import checker, which was straight-up crashing things in some namespace package related cases. Closes #203. 💀
- Won't emit `pointless-string-statement` / `W0105` for attribute docstrings anymore. Closes #193. 🌸
- Uses the proper mode for `pickle` when opening and writing the stats file. Closes #148. 🍓
- Doesn't emit the hidden-method message when the attribute has been monkey-patched — honestly you're on your own when you do that. 🧚
- Only emits `attribute-defined-outside-init` / `W0201` for definition within the same module as the offended class, which avoids mangling the output in some cases. 💅
- Won't emit `unnecessary-lambda` / `W0108` if the body of the lambda call contains call chaining. Closes #243. 🔗
- Won't emit `missing-docstring` / `C0111` when the actual docstring uses `.format`. Closes #281. 🎀
---
## ✨ What's New In Pylint 1.2 ✨
Release date: 2014-04-30 🎂
- Restored the ability to specify the `init-hook` option via the configuration file, which was accidentally broken in 1.2.0. Not me breaking `init-hook`… 😭
- Added a new warning `[bad-continuation]` for badly indented continued lines. 🦋
- Emits `assignment-from-none` / `E1128` when the function contains bare returns. Closes BitBucket #191 ⭐
- Added a new warning for closing over variables that are defined in loops. Closes BitBucket #176 🌀
- Does **not** warn about `u` escapes in string literals when Unicode literals are used for Python 2.*. Closes BitBucket #151 🌈
- Extended the checking for `unbalanced-tuple-unpacking` / `W0632` and `unpacking-non-sequence` / `E0633` to instance attribute unpacking as well. 💜
- Fixed explicit checking of python script (a 1.2 regression). Closes #219 💀
- Restored `--init-hook`, which got renamed accidentally into `--init-hooks` in 1.2.0. Closes #211 🙈
- Added the `'indexing-exception'` warning, which detects that indexing an exception occurs in Python 2 (behaviour removed in Python 3). 👑
---
## ✨ What's New in Pylint 1.2.0? ✨
Release date: 2014-04-18 🎉
- Passes the current python paths to the pylint process when invoked via `epylint`. Closes BitBucket #133. 🫶
- Added `-i` / `--include-ids` and `-s` / `--symbols` back as **completely ignored** options. Closes BitBucket #180. They're giving ghost icon energy, no cap. 💀
- Extended the number of cases in which logging calls are detected. Closes BitBucket #182. 🔥
- Improved pragma handling so it doesn't detect `pylint:*` strings in non-comments. Closes BitBucket #79. 🌸
- No more crashing with `UnknownMessage` if an unknown message ID/name shows up in `disable` or `enable` in the configuration. Patch by Cole Robinson. Closes BitBucket #170 🎀
- Added new warning `eval-used` / `W0123`, checking that the builtin function `eval` was used. 🚨
- Made it possible to show a naming hint for an invalid name by setting `include-naming-hint`. Also made the naming hints configurable. Closes BitBucket #138 ✨
- Added support for enforcing multiple, but consistent name styles for different name types inside a single module; based on a patch written by `<morbo@google.com>`. 👑
- Also warns about empty docstrings on overridden methods; contributed by `<sebastianu@google.com>`. 🌷
- Also inspects arguments to constructor calls, and emits relevant warnings; contributed by `<sebastianu@google.com>`. 🔍
- Added a new configuration option `logging-modules` to make the list of module names that can be checked for `logging-not-lazy` / `W1201` et. al. configurable; contributed by `<morbo@google.com>`. 🧚
- Ensures `init-hooks` is evaluated before other options, notably `load-plugins`. Closes #166 ⏰
- Python 2.5 support restored: fixed the small issues preventing pylint to run on python 2.5. Closes BitBucket #50, Closes BitBucket #62 🐍
- pylint doesn't crash when looking for `used-before-assignment` / `E0601` in context manager assignments. Closes BitBucket #128 💅
- Added new warning `bad-reversed-sequence` / `E0111`, for checking that the `reversed()` builtin receives a sequence (implements `__getitem__` and `__len__`, without being a dict or a dict subclass) or an instance which implements `__reversed__`. 🔄
- Marks file as a bad function when using python2. Closes #8 💀
- Added new warning `bad-exception-context` / `E0703`, checking that `raise ... from ...` uses a proper exception context (`None` or an exception). 🎀
- Enhanced the check for `used-before-assignment` / `E0601` to look for `'nonlocal'` uses. 🦋
- Emits `undefined-all-variable` / `E0603` if a package's `__all__` variable contains a missing submodule. Closes #126 🌟
- Added a new warning `abstract-class-instantiated` / `E0110` for checking that abstract classes created with the `abc` module and with abstract methods are instantiated. 🚫
- Does **not** warn about `return-arg-in-generator` / `E0106` in Python 3.3+. ✅
- Does **not** warn about `abstract-method` / `W0223` when the abstract method is implemented through assignment. Closes #155 🧩
- Improved cyclic import detection in the case of packages, patch by Buck Golemon. 🌀
- Added new warnings for checking proper class `__slots__`: `invalid-slots-object` and `invalid-slots`. 💅
- Searches for the rc file in `~/.config/pylintrc` if `~/.pylintrc` doesn't exist. Closes #121 🔎
- Doesn't register the new style checker w/ python >= 3. 🙅
- Fixed `unused-import` / `W0611` false positive w/ augment assignment. Closes #78 ✨
- Fixed `access-member-before-definition` / `E0203` false negative wrt aug assign. Closes #164 😭
- Doesn't attempt to analyze non python files, e.g. `.so` files. Closes #122 🛑
---
## ✨ What's New In Pylint 1.1 ✨
Release date: 2013-12-22 🎄
- Added new check for use of `deprecated-pragma` / `I0022` directives `"pylint:disable-msg"` or `"pylint:enable-msg"`, which was previously emitted as a regular `warn()`. 💅
- Avoids the false `used-before-assignment` / `E0601` for except handler defined identifier used on the same line. Closes #111 ✨
- Combined `'no-space-after-operator'`, `'no-space-after-comma'` and `'no-space-before-operator'` into a new warning `'bad-whitespace'`. 🎀
- Added a new warning `superfluous-parens` / `C0325` for unnecessary parentheses after certain keywords. 🦋
- Fixed a potential crash in the `redefine-in-handler` warning if the redefined name is a nested `getattr` node. 💀
- Added a new option for the multi-statement warning to allow single-line `if` statements. 👑
- Added `bad-context-manager` / `E0235` error, checking that the `'__exit__'` special method accepts the right number of arguments. 🚪
- Runs pylint as a python module: `'python -m pylint'` (Anatoly Techtonik). 🐍
- Checks for non-exception classes inside an `except` clause. 🚨
- `epylint` supports options to give to pylint after the file to analyze and has basic input validation, patches provided by felipeochoa and Brian Lane. Closes BitBucket #53, Closes BitBucket #54 🫶
- Added a new warning, `non-iterator-returned` / `E0301`, for non-iterators returned by `'__iter__'`. 🔁
- Added new checks for unpacking non-sequences in assignments (`unpacking-non-sequence` / `E0633`) as well as unbalanced tuple unpacking (`unbalanced-tuple-unpacking` / `W0632`). 📦
- `useless-else-on-loop` / `W0120` is not emitted if there is a `break` in the `else` clause of the inner loop. Closes #117 🛑
- Doesn't mark `input` as a bad function when using python3. Closes #110 ✅
- `badly-implemented-container` caused several problems in its current implementation. Deactivated it until we have something better. Refs #112 💀
- Uses attribute regexp for properties in python3, as in python2. 🌈
- Creates the `PYLINTHOME` directory when needed — it might fail and lead to spurious warnings on import of `pylint.config`. 📁
- Fixed `setup.py` so that pylint properly installs on Windows when using python3. 🪟
- Various documentation fixes and enhancements. 📚
- Fixed a false-positive `trailing-whitespace` / `C0303` on Windows. Closes #55 🎀
---
## ✨ What's New In Pylint 1.0 ✨
Release date: 2013-08-06 🎆
- Added check for the use of the `'exec'` function. 🚨
- **`--msg-template`** is new, bestie — it controls the output ✨. It deprecates the `"msvc"` and `"parseable"` output formats, and it straight-up kills the `--include-ids` and `--symbols` options 💀
- No more `[fixme]` spam on every single line when the config value `'notes'` is empty but `[fixme]` is enabled 🙈
- It *will* still warn you about lines that go past the column limit when those lines are chilling inside multiline docstrings 🎀
- Parameter names no longer get double-checked against the regex for parameters *and* inline variables — not me catching that twice 💅
- New warning dropped: `missing-final-newline` / `C0304 <# missing-final-newline>` for files that are missing the final newline 😭
- Methods decorated as properties are now treated as *attributes* for name-checking purposes — glow-up fr fr 👑
- Names of derived instance class members are not checked anymore, we're leaving them alone 🦋
- Names in `global` statements are now checked against the regular expression for constants ⭐
- For toplevel name assignment, the **class name regex** gets used if pylint can detect that the value on the right-hand side is a class (like `collections.namedtuple()`) 🌸
- `invalid-name` / `C0103 <#invalid-name>` message got simplified 💖
- New warning `invalid-encoded-data` (`W0512`) for files that contain data that can't be decoded with the specified or default encoding 😭
- New warning `bad-open-mode` / `W1501 <#bad-open-mode>` for calls to `open` (or `file`) that specify invalid open modes — original implementation by Sasha Issayev 🫶
- New warning `old-style-class` (`C1001`) for classes that don't have any base class 💀
- New name type `'class_attribute'` for attributes defined in class scope — by default it allows **both** const and variable names ✨
- New warning `trailing-whitespace` / `C0303 <#trailing-whitespace>` that warns about trailing whitespace 🧚
- New warning `unpacking-in-except` (`W0712`) about unpacking exceptions in handlers, which is literally unsupported in Python 3 🙈
- There's a config option for `missing-docstring` / `C0111 <# missing-docstring>` so it can optionally exempt short functions/methods/classes from the check 👑
- The type of the offending node got added to `missing-docstring` / `C0111 <# missing-docstring>` and `empty-docstring` / `C0112 <# empty-docstring>` 🔥
- New utility classes for per-checker unittests in `testutils.py` 💅
- No warnings about redefinitions of variables that match the dummy regex — we don't care, it's giving dummy 💖
- Not *all* variables starting with `_` get treated as dummy variables anymore — only `_` itself, periodt ✨
- The `line-too-long` / `C0301 <#line-too-long>` warning is now configurable, because there's a new regex for lines where the length limit should not be enforced 🎀
- It won't warn about a long line if a pylint `disable` option is what brings it above the length limit — iconic behaviour honestly 👑
- Names in nested `with` statements no longer get flagged as undefined 🦋
- New warning `'old-raise-syntax'` for the deprecated syntax `raise Exception, args` 💀
- Support for PEP 3102, plus the new `missing-kwoa` / `E1125 <#missing-kwoa>` message for a missing mandatory keyword argument 🌸
Closes Logilab #107788
- Fixed the spelling of the `max-branchs` option — it's `max-branches` now 😭
- Added a new base class and interface for checkers that work on the **tokens** rather than the syntax, and it only tokenizes the input file once ⭐
- Followed the `astng` → `astroid` renaming 🦋
- Checking for unbalanced unpacking in assignments 💅
Closes BitBucket #37
- Fixed the `incomplete-protocol` false positive for read-only containers like `tuple` 🎀
Closes BitBucket #25
- Fixed the false positive `E1003` on Python 3 for argument-less `super()` ✨
Closes BitBucket #16
- Put the documentation back in the source distribution 📚
Closes BitBucket #6
- `epylint` shouldn't hang anymore when there's a large output on pylint's `stderr` 😮💨
Closes BitBucket #15
- Fixed `epylint` w/ python3 💖
Closes BitBucket #7
- Removed the `string` module from the default list of deprecated modules 🫶
Closes BitBucket #3
## 0.x
From pylint's beginning all the way through pylint 2.14.0 included, we had a changelog that contained **all** the nontrivial changes to Pylint — including the changes that weren't user facing, i.e. changes that might only interest you if you're a developer working on pylint or pylint plugins, or an open-source historian 🏛️✨
Ticket numbers are almost always internal to Logilab, and some come from bitbucket 🎀
### What's New in Pylint 0.28.0?
Release date: `2013-04-25`
- bitbucket #1: fixed the `"dictionary changed size during iteration"` crash 💀
- `#74013`: new `E1310[bad-str-strip-call]` message, warning when a call to a `{l,r,}strip` method contains duplicate characters (patch by Torsten Marek) 🎀
- `#123233`: new `E0108[duplicate-argument-name]` message reporting duplicate argument names 🙈
- `#81378`: emit `W0120[useless-else-on-loop]` for loops without `break` ✨
- `#124660`: internal dependencies should not appear in the external dependencies report 💅
- `#124662`: fixed a name error causing a crash when symbols are included in output messages 😭
- `#123285`: apply pragmas for warnings attached to lines to *physical* source code lines ⭐
- `#123259`: do not emit `E0105` for `yield` expressions inside lambdas 🦋
- `#123892`: don't crash when attempting to show a source code line that can't be encoded with the current locale settings 🌸
- Simplified the checks for dangerous default values by unifying the tests for all the different mutable compound literals 🔥
- Improved the description for `redundant-keyword-arg` / `E1124 <# redundant-keyword-arg>` 👑
### What's New in Pylint 0.27.0?
Release date: `2013-02-26`
- `#20693`: replaced `pylint.el` with Ian Eure's version (patch by J.Kotta) 🎀
- `#105327`: added support for the `--disable=all` option and deprecated the `'disable-all'` inline directive in favour of `'skip-file'` (patch by A.Fayolle) ✨
- `#110840`: added messages `I0020` and `I0021` for reporting suppressed messages and useless suppression pragmas (patch by Torsten Marek) 💅
- `#112728`: added warning `E0604` for non-string objects in `__all__` (patch by Torsten Marek) 🦋
- `#120657`: added warning `W0110/deprecated-lambda` when a `map`/`filter` of a lambda could be a comprehension (patch by Martin Pool) 🌸
- `#113231`: the logging checker now looks at instances of `Logger` classes in addition to the base `logging` module (patch by Mike Bryant) ⭐
- `#111799`: don't warn about octal escape sequences, but *do* warn about `o` which is not octal in Python (patch by Martin Pool) 💀
- `#110839`: bound <kbd>F5</kbd> to the Run button in `pylint-gui` 🫶
- `#115580`: fixed erroneous `W0212` (access to protected member) on `super` calls (patch by Martin Pool) 😭
- `#110853`: fixed a crash when an `__init__` method in a base class has been created by assignment rather than a direct function definition (patch by Torsten Marek) 🎀
- `#110838`: fixed a `pylint-gui` crash when `include-ids` is activated (patch by Omega Weapon) ✨
- `#112667`: fixed the emission of `reimport` warnings for mixed imports and extended the testcase (patch by Torsten Marek) 💅
- `#112698`: fixed a crash related to non-inferable `__all__` attributes and invalid `__all__` contents (patch by Torsten Marek) 🌸
- Python 3 related fixes:
- `#110213`: fixed the import of checkers broken with python 3.3, causing `"No such message id W0704"` breakage 💀
- `#120635`: redefined the `cmp` function used in `pylint.reporters` 🦋
- Included the full warning id for `I0020` and `I0021`, and made sure to flush warnings after each module, not at the end of the pylint run (patch by Torsten Marek) ⭐
- Changed the regular expression for inline options so that it must be preceded by a `#` (patch by Torsten Marek) 🎀
- Made the dot output for the import graph predictable and not depend on the ordering of strings in hashes (patch by Torsten Marek) ✨
- Added hooks for import path setup and moved pylint's `sys.path` modifications into them (patch by Torsten Marek) 💖
### What's New in Pylint 0.26.0?
Release date: `2012-10-05`
- `#106534`: added the `--ignore-imports` option to code similarity checking and the `'symilar'` command line tool (patch by Ry4an Brase) 🦋
- `#104571`: check for anomalous backslash escapes, introducing the new `W1401` and `W1402` messages (patch by Martin Pool) 🌸
- `#100707`: check for `boolop` being used as an exception class, introducing the new `W0711` message (patch by Tim Hatch) 💅
- `#4014`: improved the checking of metaclass methods' first args, introducing the new `C0204` message (patch by `<lothiraldan@gmail.com>` finalized by sthenault) ⭐
- `#4685`: check for consistency of a module's `__all__` variable, introducing the new `E0603` message 🎀
- `#105337`: allow a custom reporter in `output-format` (patch by Kevin Jing Qiu) ✨
- `#104420`: check for protocol completeness and avoid the false `R0903` (patch by Peter Hammond) 🫶
- `#100654`: fixed the grammatical error in the `W0332` message (using `'l'` as a long int identifier) 😭
- `#103656`: fixed the `W0231` false positive for a missing call to `object.__init__` (patch by `<lothiraldan@gmail.com>`) 🙈
- `#63424`: fixed similarity report disabling by properly renaming it to `RP0801` 💅
- `#103949`: created a `console_scripts` entry point to be used by `easy_install`, `buildout` and `pip` 🔥
- Fixed the cross-interpreter issue (non-compatible access to `__builtins__`) 👑
- Stopped including test files in the distribution — they cause crashes when installed with python3 (`#72022`, `#82417`, `#76910`) 💀
### What's New in Pylint 0.25.2?
Release date: `2012-07-17`
- `#93591`: correctly emit warnings about clobbered variable names when an `except` handler contains a tuple of names instead of a single name (patch by `<tmarek@google.com>`) 🎀
- `#7394`: `W0212` (access to protected member) is no longer emitted on assignments (patch by `<lothiraldan@gmail.com>`) ✨
- `#18772`: no prototype consistency check for mangled methods (patch by `<lothiraldan@gmail.com>`) 🦋
- `#92911`: emit `W0102` when sets are used as default arguments in functions (patch by `<tmarek@google.com>`) 🌸
- `#77982`: do not emit `E0602` for loop variables of comprehensions used as argument values inside a decorator (patch by `<tmarek@google.com>`) 💅
- `#89092`: don't emit `E0202` (attribute hiding a method) on `@property` methods ⭐
- `#92584`: fixed a `pylint-gui` crash due to an internal API change 😭
- `#87192`: fixed a crash when decorators are accessed through more than one dot (for instance `@a.b` is fine, `@a.b.c` crashes) 🙈
- `#88914`: fixed parsing of `--generated-members` options, which was leading to a crash when using a regexp value set 🫶
- Fixed potential crashes with `utils.safe_infer` raising `InferenceError` 💀
### What's New in Pylint 0.25.1?
Release date: `2011-12-08`
- `#81078`: warn if names in exception handlers clobber/overwrite existing names (patch by `<tmarek@google.com>`) 🎀
- `#81113`: fixed `W0702` messages appearing with the wrong line number (patch by `<tmarek@google.com>`) ✨
- `#50461`, `#52020`, `#51222`: do not issue warnings when using 2.6's `property.setter`/`deleter` functionality (patch by `<dneil@google.com>`) 🦋
- **`#9188`, `#4024`** — do <pink>not</pink> trigger `W0631` if a loop variable is assigned in the `else` branch of a `for` loop. ok that one's just here chilling, carry on 💅
### What's New in Pylint 0.25.0? 🎀
Release date: `2011-10-7`
- **`#74742`** — make the allowed name for the first argument of a class method configurable (patch by Google) ✨
- **`#74087`** — handle the case where inference of a module returns `YES`; this avoids some cases of `"TypeError: '_Yes' object does not support indexing"` (patch by Google)
- **`#74745`** — make "too general" exception names configurable (patch by Google)
- **`#74747`** — crash occurs when looking up a special attribute in class scope (patch by google) 💀
- **`#76920`** — crash if on e.g. `"pylint --rcfile"` (patch by Torsten Marek)
- **`#77237`** — warning for `E0202` may be very misleading 🙈
- **`#73941`** — HTML report messages table is badly rendered
---
### What's New in Pylint 0.24.0? 🦋
Release date: `2011-07-18`
- **`#69738`** — add regular expressions support for `"generated-members"`
- ids of `logging` and `string_format` checkers have been changed: `logging: 65 -> 12`, `string_format: 99 -> 13`. Also add documentation to say that ids of range `1-50` shall be reserved to pylint internal checkers, iykyk 👑
- **`#69993`** — additional string format checks for the logging module: check for missing arguments, too many arguments, or invalid string formats in the logging checker module. Contributed by Daniel Arena 🫶
- **`#69220`** — add column offset to the reports. If you've a custom reporter, this change may break it — it has now location gain a new item giving the column offset. (yes that sentence is giving chaos, but that's the deal bestie 😭)
- **`#60828`** — Fix false positive in reimport check
- **`#70495`** — absolute imports fail depending on module path (patch by Jacek Konieczny)
- **`#22273`** — Fix `--ignore` option documentation to match reality 💀
---
### What's New in Pylint 0.23.0? 🌸
Release date: `2011-01-11`
- documentation update, add manpages 📖
- several performance improvements ⚡
- finalize python3 support 🐍
- new `W0106` warning `'Expression "%s" is assigned to nothing'`
- drop `E0501` and `E0502` messages about wrong source encoding: not anymore interesting since it's a syntax error for python `>= 2.5` and we now only support this python version and above.
- don't emit `W0221` or `W0222` when methods as variable arguments (e.g. `*arg` and/or `**args`). Patch submitted by Charles Duffy.
---
### What's New in Pylint 0.22.0? 🌈
Release date: `2010-11-15`
- python versions: minimal `python3.x` support; drop `python < 2.5` support
---
### What's New in Pylint 0.21.4? 🍓
Release date: `2010-10-27`
- fix **`#48066`**: pylint crashes when redirecting output containing non-ascii characters 😭
- fix **`#19799`**: `"pylint -blah"` exit with status `2`
- update documentation
---
### What's New in Pylint 0.21.3? 🧚
Release date: `2010-09-28`
- restored python `2.3` compatibility. Along with `logilab-astng 0.21.3` and `logilab-common 0.52`, this will much probably be the latest release supporting `python < 2.5`.
---
### What's New in Pylint 0.21.2? 💜
Release date: `2010-08-26`
- fix **`#36193`**: import checker raise exception on cyclic import
- fix **`#28796`**: regression in `--generated-members` introduced pylint `0.20`
- some documentation cleanups ✨
---
### What's New in Pylint 0.21.1? ⭐
Release date: `2010-06-04`
- fix **`#28962`**: pylint crash with new options, due to missing stats data while writing the Statistics by types report 💀
- updated man page to `0.21` or greater command line usage (fix debian `#582494`)
---
### What's New in Pylint 0.21.0? 🔥
Release date: `2010-05-11`
- command line updated (closes `#9774`, `#9787`, `#9992`, `#22962`):
- all `enable-*` / `disable-*` options have been merged into `--enable` / `--disable`
- **BACKWARD INCOMPATIBLE CHANGE**: short name of `--errors-only` becomes `-E`, `-e` being affected to `--enable` ⚠️
- `pylint --help` output much simplified, with `--long-help` available to get the complete one
- revisited gui, thanks to students from Toronto university (they are great contributors to this release!) 🫶
- fix **`#21591`**: html reporter produces no output if reports is set to `'no'`
- fix **`#4581`**: not Missing docstring (`C0111`) warning if a method is overridden
- fix **`#4683`**: Non-ASCII characters count double if utf8 encode
- fix **`#9018`**: when using `defining-attr-method`, method order matters
- fix **`#4595`**: Comma not followed by a space should not occurs on trailing comma in list/tuple/dict definition
- fix **`#22585`**: `[Patch]` fix man warnings for `pyreverse.1` manpage
- fix **`#20067`**: `AttributeError: 'NoneType' object has no attribute 'name'` with with 💀
---
### What's New in Pylint 0.20.0? 🦋
Release date: `2010-03-01`
- fix **`#19498`**: fix windows batch file
- fix **`#19339`**: `pylint.el` : non existing `py-mod-map` (closes Debian Bug report logs - `#475939`)
- implement **`#18860`**, new `W0199` message on `assert (a, b)`
- implement **`#9776`**, `'W0150'` break or return statement in finally block may swallow exception.
- fix **`#9263`**, `__init__` and `__new__` are checked for unused arguments
- fix **`#20991`**, class scope definitions ignored in a genexpr
- fix **`#5975`**, Abstract intermediate class not recognized as such
- fix **`#5977`**, `yield` and `return` statement have their own counters, no more `R0911` (Too many return statements) when a function have many yield stamtements
- implement **`#5564`**, function / method arguments with leading `"_"` are ignored in arguments / local variables count.
- implement **`#9982`**, `E0711` specific error message when raising `NotImplemented`
- remove `--cache-size` option
---
### What's New in Pylint 0.19.0? 🌸
Release date: `2009-12-18`
- implement **`#18947`**, **`#5561`**: checker for function arguments
- include James Lingard string format checker
- include simple message (ids) listing by Vincent Ferotin (`#9791`)
- `--errors-only` does not hide fatal error anymore
- include james Lingard patches for `++`/`--` and duplicate key in dicts
- include James Lingard patches for function call arguments checker
- improved Flymake code and doc provided by Derek Harland
- refactor and fix the imports checker
- fix **`#18862`**: `E0601` false positive with lambda functions
- fix **`#8764`**: More than one statement on a single line false positive with `try/except/finally`
- fix **`#9215`**: false undefined variable error in lambda function
- fix for `w0108` false positive (Nathaniel)
- fix `test/fulltest.sh`
- **`#5821`** added a utility function to run pylint in another process (patch provide by Vincent Ferotin) ✨
---
### What's New in Pylint 0.18.0? 🎀
Release date: `2009-03-25`
- tests ok with python `2.4`, `2.5`, `2.6`. `2.3` not tested
- fix **`#8687`**, `W0613` false positive on inner function
- fix **`#8350`**, `C0322` false positive on multi-line string
- fix **`#8332`**: set `E0501` line no to the first line where non ascii character has been found
- avoid some `E0203` / `E0602` false negatives by detecting respectively `AttributeError` / `NameError`
- implements **`#4037`**: don't issue `W0142` (`*` or `**` magic) when they are barely passed from `/*` arguments
- complete **`#5573`**: more complete list of special methods, also skip `W0613` for python internal method
- don't show information messages by default 🙈
- integration of Yuen Ho Wong's patches on Emacs lisp files
---
### What's New in Pylint 0.17.0? 🌈
Release date: `2009-03-19`
- semicolon check : move `W0601` to `W0301`
- remove rpython : remove all rpython checker, modules and tests
- astng `0.18` compatibility: support for `_ast` module modifies interfaces
---
### What's New in Pylint 0.16.0? 💜
Release date: `2009-01-28`
- change `[en|dis]able-msg-cat` options: only accept message categories identified by their first letter (e.g. `IRCWEF`) without the need for comma as separator
- add `epylint.bat` script to fix Windows installation
- setuptools/easy_install support
- include a modified version of Maarten ter Huurne patch to avoid `W0613` warning on arguments from overridden method
- implement **`#5575`** drop dumb `W0704` message) by adding `W0704` to ignored messages by default
- new `W0108` message, checking for suspicious lambda (provided by Nathaniel Manista)
- fix `W0631`, false positive reported by Paul Hachmann
- fix **`#6951`**: false positive with `W0104`
- fix **`#6949`**
- patches by Mads Kiilerich:
- implement **`#4691`**, make pylint exits with a non zero return status if any messages other then Information are issued 🔥
- fix **`#3711`**, **`#5626`** (name resolution bug w/ decorator and class members)
- fix **`#6954`**
---
### What's New in Pylint 0.15.2? 🍓
Release date: `2008-10-13`
- fix **`#5672`**: `W0706` weirdness ( `W0706` removed )
- fix **`#5998`**: documentation points to wrong url for mailing list
- fix **`#6022`**: no error message on wrong module names
- fix **`#6040`**: `pytest` doesn't run `test/func_test.py`
---
### What's New in Pylint 0.15.1? 🧚
Release date: `2008-09-15`
- fix **`#4910`**: default values are missing in manpage
- fix **`#5991`**: missing files in `0.15.0` tarball
- fix **`#5993`**: epylint should work with python `2.3`
---
### What's New in Pylint 0.15.0? ⭐
Release date: `2008-09-10`
- include `pyreverse` package and class diagram generation 🎨
- included Stefan Rank's patch to deal with `2.4` relative import
- included Robert Kirkpatrick's tutorial and typos fixes
- fix bug in reenabling message
- fix **`#2473`**: invoking pylint on `__init__.py` (hopefully)
- typecheck: `acquired-members` option has been dropped in favor of the more generic `generated-members` option. If the `zope` option is set, the behaviour is now to add some default values to `generated-members`.
- Flymake integration: added `bin/epylint` and `elisp/pylint-flymake.el`
---
### What's New in Pylint 0.14.0? 🦋
Release date: `2008-01-14`
- fix **`#3733`**: Messages (dis)appear depending on order of file names 💀
- fix **`#4026`**: `pylint.el` should require compile
- fix a bug in colorized reporter, spotted by Dave Borowitz
- applied patch from Stefan Rank to avoid `W0410` false positive when multiple `"from __future__"` import statements
- implement **`#4012`**: flag back tick as deprecated (new `W0333` message)
- new `ignored-class` option on typecheck checker allowing to skip members checking based on class name (patch provided by Thomas W Barr)
---
### What's New in Pylint 0.13.2? 🎀
Release date: `2007-06-07`
- fix `disable-checker` option so that it won't accidentally enable the rpython checker which is disabled by default
- added note about the gedit plugin into documentation
---
### What's New in Pylint 0.13.1? 🌸
Release date: `2007-03-02`
- fix some unexplained `0.13.0` packaging issue which led to a bunch of files missing from the distribution 😭
---
### What's New in Pylint 0.13.0? 🫶
Release date: `2007-02-28`
- <pink>a whole new **RPython** (Restricted Python) checker</pink> for the PyPy besties 💜 or honestly anyone who wants a compiled version of their python program using the translator of the PyPy project. Wanna learn more about PyPy or RPython? Go visit <https://www.pypy.org> 🦋 (previously `codespeak.net/pypy/`, iykyk)
- bestie, new `E0104` and `E0105` messages dropped and they respectively warn about `"return"` and `"yield"` outside function or method 😭 like those things cannot just be floating around out there
- new `E0106` message for when `"yield"` and `"return something"` get mixed in a function or method — do NOT do both, it's giving chaos
- new `W0107` message for an unnecessary `pass` statement ✨ we see you doing nothing, and we're calling it out
- new `W0614` message so we can finally differentiate between unused `import X` and unused `from X import *` (<cyan>`#3209`</cyan>, patch submitted by Daniel Drake 🫶)
- included Daniel Drake's patch to have a different message `E1003` instead of `E1001` when a missing member is found but an inference failure has been detected 🔥
- `msvs` reporter for Visual Studio line number reporting (<cyan>`#3285`</cyan>) 👑
- allow `disable-all` option inline (<cyan>`#3218`</cyan>, patch submitted by Daniel Drake) 💅
- `--init-hook` option to call arbitrary code necessary to set environment (e.g. `sys.path`) (<cyan>`#3156`</cyan>) — yes you can run stuff, slay
- One more Daniel's patch fixing a command line option parsing problem, this'll definitely be the DDrake release :) 😭
- fix <cyan>`#3184`</cyan>: crashes on `"return"` outside function 💀
- fix <cyan>`#3205`</cyan>: `W0704` false positive 🙈
- fix <cyan>`#3123`</cyan>: `W0212` false positive on static method
- fix <cyan>`#2485`</cyan>: `W0222` false positive
- fix <cyan>`#3259`</cyan>: when a message is explicitly enabled, check the checker emitting it is enabled 🌸
---
## 🦋 What's New in Pylint 0.12.2? 🦋
Release date: `2006-11-23`
- fix <cyan>`#3143`</cyan>: `W0233` bug w/ `YES` objects 😭
- fix <cyan>`#3119`</cyan>: Off-by-one error counting lines in a file
- fix <cyan>`#3117`</cyan>: ease `sys.stdout` overriding for reporters ✨
- fix <cyan>`#2508`</cyan>: `E0601` false positive with `lambda`
- fix <cyan>`#3125`</cyan>: `E1101` false positive and a message duplication. ngl only the last part is actually fixed, since the initial false positive is due to dynamic setting of attributes on the `decimal.Context` class 🙈
- fix <cyan>`#3149`</cyan>: `E0101` false positives, plus we introduced `E0100` for generator `__init__` methods 💅
- fixed some format checker false positives 🎀
---
## 💜 What's New in Pylint 0.12.1? 💜
Release date: `2006-09-25`
- fixed python >= `2.4` format false positive with multiple lines statement
- fixed some `2.5` issues 🔥
- fixed generator expression scope bug (depends on `astng 0.16.1`) — yes it literally depends on it, no cap
- stop requiring `__revision__` 🎉 iconic behaviour honestly
---
## ✨ What's New in Pylint 0.12.0? ✨
Release date: `2006-08-10`
- usability changes, bestie, listen up 👑:
- `parseable`, `html` and `color` options are now handled by a single `output-format` option 🎀
- `enable-<checkerid>` and `disable-all` options are now handled by two (exclusive) `enable-checker` and `disable-checker` options taking a comma separated list of checker names as value 💅
- renamed `debug-mode` option to `errors-only`
- started a reference user manual 🫶 (you're literally reading the vibe-ified descendant of it rn)
- new `W0212` message for access to protected member from client code (Closes <cyan>`#14081`</cyan>)
- new `W0105` and `W0106` messages extracted from `W0104` (statement seems to have no effect), respectively when the statement is actually string (that's sometimes used instead of comments for documentation) or an empty statement generated by a useless semicolon 😭
- reclassified `W0302` to `C0302`
- fix so that global messages are not anymore connected to the last analyzed module (Closes <cyan>`#10106`</cyan>)
- fix some bugs related to local disabling of messages 🌸
- fix cr/lf pb when generating the rc file on windows platforms 💀
---
## 🌈 What's New in Pylint 0.11.0? 🌈
Release date: `2006-04-19`
- fix crash caused by the exceptions checker in some case
- fix some `E1101` false positive with abstract method or classes defining `__getattr__`
- dirty fix to avoid `"_socketobject"` has not `"connect"` member 🙈 the actual problem is that astng isn't able to understand the code used to create `socket.socket` object with `exec` — fr fr it's a whole thing
- added an option in the similarity checker to ignore docstrings, enabled by default ✨
- included patch from Benjamin Niemann to allow block level enabling/disabling of messages 🫶
---
## 🎀 What's New in Pylint 0.10.0? 🎀
Release date: `2006-03-06`
> ⚠️ **WARNING**, bestie, this release includes some configuration changes (see below), so you may have to check and update your own configuration file(s) if you use one 😭
- this release requires the `0.15` version of astng or superior (it will save you a lot of pylint crashes... seriously 💀)
- `W0705` has been reclassified to `E0701`, and is now detecting more inheriting problem, and a false positive when empty `except` clause is following an `Exception` catch has been fixed (Closes <cyan>`#10422`</cyan>) 👑
- `E0212` and `E0214` (metaclass/class method should have `mcs`/`cls` as first argument) have been reclassified to `C0202` and `C0203` since this is not as well established as `"self"` for instance method (`E0213`) 💅
- `W0224` has been reclassified into `F0220` (failed to resolve interfaces implemented by a class)
- a new typecheck checker, introducing the following checks 🔥:
- `E1101`, access to nonexistent member (implements <cyan>`#10430`</cyan>), removes the need of `E0201` and so some options has been moved from the classes checker to this one
- `E1102`, calling a non callable object
- `E1111` and `W1111` when an assignment is done on a function call but the inferred function returns `None` (implements <cyan>`#10431`</cyan>)
- change in the base checker 🦋:
- checks module level and instance attribute names (new `const-rgx` and `attr-rgx` configuration option) (implements <cyan>`#10209`</cyan> and <cyan>`#10440`</cyan>)
- list comprehension and generator expression variables have their own regular expression (the `inlinevar-rgx` option) (implements <cyan>`#9146`</cyan>)
- the `C0101` check with its `min-name-length` option has been removed (this can be specified in the regxp after all... 😌)
- `W0103` and `W0121` are now handled by the variables checker (`W0103` is now `W0603` and `W0604` has been split into different messages)
- `W0131` and `W0132` messages have been reclassified to `C0111` and `C0112` respectively
- new `W0104` message on statement without effect 💀
- regexp support for dummy-variables (`dummy-variables-rgx` option replaces `dummy-variables`) (implements <cyan>`#10027`</cyan>) ✨
- better global statement handling, see `W0602`, `W0603`, `W0604` messages (implements <cyan>`#10344`</cyan> and <cyan>`#10236`</cyan>)
- `--debug-mode` option, disabling all checkers without error message and filtering others to only display error 🔥
- fixed some `R0201` (method could be a function) false positive 🙈
---
## 🌸 What's New in Pylint 0.9.0? 🌸
Release date: `2006-01-10`
- a lot of updates to follow astng `0.14` API changes, so install `logilab-astng 0.14` or greater before using this version of pylint 😭 (no like actually, do it)
- checker number 10! `newstyle` will search for problems regarding old style / new style classes usage problems (relies on astng `0.14` new style detection feature) 👑
- new `load-plugins` options to load additional pylint plugins (usable from the command line or from a configuration file) (implements <cyan>`#10031`</cyan>) 🫶
- check if a `pylintrc` file exists in the current working directory before using the one specified in the `PYLINTRC` environment variable or the default `~/.pylintrc` or `/etc/pylintrc` ✨ (order matters bestie)
- fixed `W0706` (Identifier used to raise an exception is assigned...) false positive and reraising a caught exception instance
- fixed `E0611` (No name get in module blabla) false positive when accessing to a class' `__dict__` 🙈
- fixed some `E0203` (`"access to member before its definition"`) false positive
- fixed `E0214` (`"metaclass method first argument should be mcs"`) false positive with `staticmethod` used on a metaclass 💅
- fixed packaging which was missing the `test/regrtest_data` directory 💀
- `W0212` (method could be a function) has been reclassified in the `REFACTOR` category as `R0201`, and is no more considerer when a method overrides an abstract method from an ancestor class 🌈
- include module name in `W0401` (wildcard import), as suggested by Amaury 🫶
- when using the `--parseable`, path are written relative to the current working directory if in a sub-directory of it (<cyan>`#9789`</cyan>)
- `pylint --version` shows `logilab-astng` and `logilab-common` versions 👑
- fixed `pylint.el` to handle space in file names ✨
- misc lint style fixes 🎀
---
## ⭐ What's New in Pylint 0.8.1? ⭐
Release date: `2005-11-07`
- fix `"deprecated module"` false positive when the code imports a module whose name starts with a deprecated module's name (close <cyan>`#10061`</cyan>)
- fix `"module has no name __dict__"` false positive (Closes <cyan>`#10039`</cyan>) 🙈
- fix `"access to undefined variable __path__"` false positive (close <cyan>`#10065`</cyan>)
- fix `"explicit return in __init__"` false positive when return is actually in an inner function (Closes <cyan>`#10075`</cyan>) 😌
---
## 💖 What's New in Pylint 0.8.0? 💖
Release date: `2005-10-21`
- check names imported from a module exists in the module (`E0611`), patch contributed by Amaury Forgeot d'Arc 🫶
- print a warning (`W0212`) for methods that could be a function (implements <cyan>`#9100`</cyan>) 💅
- new `--defining-attr-methods` option on classes checker ✨
- new `--acquired-members` option on the classes checker, used when `--zope=yes` to avoid false positive on acquired attributes (listed using this new option) (Closes <cyan>`#8616`</cyan>)
- generate one `E0602` for each use of an undefined variable (previously, only one for the first use but not for the following) (implements <cyan>`#1000`</cyan>) 🔥
- make profile option saveable 👑
- fix Windows `.bat` file, patch contributed by Amaury Forgeot d'Arc 🫶
- fix one more false positive for `E0601` (access before definition) with for loop such as `"for i in range(10): print i"` (test `func_noerror_defined_and_used_on_same_line`) 😭
- fix false positive for `E0201` (undefined member) when accessing to `__name__` on a class object
- fix astng checkers traversal order 🌸
- fix bug in format checker when parsing a file from a platform using different new line characters (Closes <cyan>`#9239`</cyan>)
- fix encoding detection regexp ✨
- fix `--rcfile` handling (support for `--rcfile=file`, Closes <cyan>`#9590`</cyan>) 💅
---
## 🧚 What's New in Pylint 0.7.0? 🧚
Release date: `2005-05-27`
> ⚠️ **WARNING**: pylint is no longer a `logilab` subpackage. Users may have to manually remove the old `logilab/pylint` directory 💀 (yes, manually, we're so sorry)
- introduce a new `--additional-builtins` option to handle user defined builtins ✨
- `--reports` option has now `-r` as short alias, and `-i` for `--include-ids` 👑
- fix a bug in the variables checker which may causing some false positives when variables are defined and used within the same statement (test `func_noerror_defined_and_used_on_same_line`) 🙈
- this time, real fix of the `"disable-msg in the config file"` problem, test added to `unittest_lint` 🔥 bestie we mean it this time
- crushed that `--list-messages` + `python -OO` bug 🐛✨
- made sure that possible false positive for `W0201` stopped being a whole thing 🫶
### 🌸 What's New in Pylint 0.6.4? 🌸
Release date: `2005-04-14`
- now you can parse files without an extension when a path is given on the command line (test `noext`) 💅
- if an inline option can’t be read (like, inside a module), pylint doesn’t just fail — it produces an information message instead (test `func_i0010`) 🎀
- new message `E0103` for `break` or `continue` outside a loop (Closes `#8883`, test `func_continue_not_in_loop`) 🔥
- fixed a bug in the variables checker that was causing some actual name errors to go undetected (Closes `#8884`, test `func_nameerror_on_string_substitution`) 👀
- fixed a bug in the classes checker that made pylint crash if `"object"` is assigned in a class inheriting from it (test `func_noerror_object_as_class_attribute`) 💀
- fixed a problem with the similar checker when related options are defined in a configuration file 🧚
- new `--generate-man` option to generate pylint’s man page (requires the latest `logilab.common`, `>= 0.9.3`) 📜
- packaged (generated...) man page — iconic, honestly 👑
### 🦋 What's New in Pylint 0.6.3? 🦋
Release date: `2005-02-24`
- fixed a scope problem that could cause a false positive and a true negative on `E0602` 😭
- fixed a problem where some options like `disable-msg` caused an error when they came from the configuration file 🫠
### 🎀 What's New in Pylint 0.6.2? 🎀
Release date: `2005-02-16`
- fixed a false positive on `E0201` (`"access to undefined member"`) with metaclasses ✨
- fixed a false positive on `E0203` (`"access to member before its definition"`) when attributes are defined in a parent class 🌸
- fixed a false positive on `W0706` (`"identifier used to raise an exception assigned to..."`) 💖
- fixed interpretation of `"t"` as the value for the `indent-string` configuration variable 🧵
- fixed `--rcfile` so that `--rcfile=pylintrc` works (only `--rcfile pylintrc` was working in an earlier release) 💅
- new raw checker example in the `examples/` directory 👑
### 🌈 What's New in Pylint 0.6.1? 🌈
Release date: `2005-02-04`
- new `--rcfile` option to specify the configuration file without the `PYLINTRC` environment variable 🎀
- added an example module for a custom pylint checker (see the `example/` directory) 🦋
- some fixes to handle fixes in common `0.9.1` (it should however still work with common `0.9.0`, even if upgrading is recommended) ⭐
### 💜 What's New in Pylint 0.6.0? 💜
Release date: `2005-01-20`
- re-fixed the pylint Emacs mode 🧚
- no more traceback when you just type `pylint` 😌
- fixed a bug that could cause crashes while resolving parent classes 💀
- fixed problems with the format checker: don’t choke on files containing multiple `CR`, and avoid `C0322`, `C0323`, `C0324` false positives with triple-quoted strings that have a quote inside 🫶
- correctly detect access to a member defined later in the `__init__` method 👀
- now depends on common `0.8.1` to fix a problem with interface resolution (Closes `#8606`) 🔥
- new `--list-msgs` option describing available checkers and their messages 📜
- added Windows-specific documentation to the `README` file, contributed by Brian van den Broek 🪟
- updated `doc/features.txt` (actually this file is now generated using the `--list-msgs` option), plus more entries in the FAQ ✨
- improved tests coverage 🧪
### 🍓 What's New in Pylint 0.5.0? 🍓
Release date: `2004-10-19`
- avoid importing analyzed modules ! 🙈
- new Refactor and Convention message categories. Some Warnings have been remapped into those new categories 🎀
- added `"similar"`, a tool to find copied and pasted lines of code, both using a specific command line tool and integrated as a pylint checker 👯
- imports checker may report import dependencies as a dot graph 📈
- new checker regrouping most Refactor detection (with some new metrics) 🧠
- more command line options storable in the configuration file 💾
- fixed a bug with the total / undocumented number of methods 😭
### 🧵 What's New in Pylint 0.4.2? 🧵
Release date: `2004-07-08`
- fixed the pylint Emacs mode 💅
- fixed classes checkers to handle twisted interfaces 🌀
### 🦋 What's New in Pylint 0.4.1? 🦋
Release date: `2004-05-14`
- fixed the `setup.py` script to allow `bdist_winst` (well, the generated installer has not been tested...) with the necessary `logilab/__init__.py` file 📦
- fixed the file naming convention as suggested by Andreas Amoroso ✨
- fixed a stupid crash bug with bad method names 💀
### 💖 What's New in Pylint 0.4.0? 💖
Release date: `2004-05-10`
- fixed file path with `--parsable` 🛠️
- `--parsable` option has been renamed to `--parseable` 🏷️
- added patch from Andreas Amoroso to output messages to files instead of standard output 📝
- added `Run` to the list of correct variable names 🏃
- fixed variable names regexp and checking of local classes names 🔍
- some basic handling of metaclasses 🧠
- `no-docstring-rgx` now applies to classes too 🎀
- new option to specify a different regexp for methods than for functions 🧚
- do not display the evaluation report when no statements have been analysed 🙅
- fixed crash with a class nested in a method 💀
- fixed format checker to deal with triple-quoted strings and lines with code and comment mixed 🫶
- use `logilab.common.ureports` to layout reports 📊
### 🌸 What's New in Pylint 0.3.3? 🌸
Release date: `2004-02-17`
- added a parsable text output, used when the `--parsable` option is provided ✨
- added an Emacs mode using this output, available in the distrib’s `elisp` directory 🧚
- fixed some typos in messages 😌
- changed `include-ids` options to `yn`, and allowed it to be in the configuration file 💾
- do not choke on corrupted stats files 🙈
- fixed a bug in the format checker that may stop pylint execution 🛑
- provide scripts for Unix and Windows to wrap the minimal pylint tk gui 🪟
### 🎀 What's New in Pylint 0.3.2? 🎀
Release date: `2003-12-23`
- html-escape messages in the HTML reporter (bug reported by Juergen Hermann) 🌐
- added `"TODO"` to the list of default note tags 📝
- added `"rexec"` to the list of default deprecated modules 🚫
- fixed typos in some messages 😭
### 🦋 What's New in Pylint 0.3.1? 🦋
Release date: `2003-12-05`
- bug fix in format and classes checkers 🐛
- remove print statement from imports checkers 🖨️
- provide a simple tk gui, essentially useful for Windows users 🪟
### 🔥 What's New in Pylint 0.3.0? 🔥
Release date: `2003-11-20`
- new exceptions checker, checking for string exception and empty except clauses ✨
- imports checker checks for reimport of modules 📦
- classes checker checks for calls to an ancestor’s `__init__` and abstract method not overridden. It doesn’t complain anymore for unused import in `__init__` files, and provides a new option `ignore-interface-methods`, useful when you’re using zope `Interface` implementation in your project 🧚
- base checker checks for disallowed builtins calls (controlled by the `bad-functions` option) and for use of `*` and `**` 🚫
- format checker checks for use of `<>` and `"l"` as long int marker 👀
- major internal API changes 💥
- use the rewrite of `astng`, based on `compiler.ast` 🧠
- added unique id for messages, as suggested by Wolfgang Grafen 🆔
- added unique id for reports 🆔
- can take multiple modules or files as argument 📚
- new command line options: `--disable-msg`, `--enable-msg`, `--help-msg`, `--include-ids`, `--reports`, `--disable-report`, `--cache-size` 🛠️
- `--version` shows the version of the Python interpreter 🐍
- removed some options which are now replaced by `[en|dis]able-msg`, or `disable-report` 🗑️
- read `disable-msg` and `enable-msg` options in source files (should be in comments at the top of the file, in the form `"# pylint: disable-msg=W0402"`) 📝
- new message for modules importing themselves instead of the `"cyclic import"` message 🔁
- fixed bug with relative and cyclic imports 🌀
- fixed bug in imports checker (cycle was not always detected) 🔍
- still fixes in format checker: don’t check comment and docstring, check the first line after an indent 🎀
- allowed/prohibited names now apply to all identifiers, not only variables, so changed the configuration option from `(good|bad)-variable-names` to `(good|bad)-names` 🏷️
- added `string`, `rexec` and `Bastion` to the default list of deprecated modules 🚫
- do not print redefinition warning for function/class/method defined in mutually exclusive branches 🚦
### 💜 What's New in Pylint 0.2.1? 💜
Release date: `2003-10-10`
- added some documentation, fixed some typos 📚
- set environment variable `PYLINT_IMPORT` to `1` during pylint execution 🌱
- check that variables `"imported"` using the `global` statement exist 👀
- indentation problems are now warnings instead of errors ⚠️
- fixed `checkers.initialize` to try to load all files with a known Python extension (patch from wrobell) 🧩
- fixed a bunch of messages 😭
- fixed sample configuration file 🛠️
- fixed the `bad-construction` option 🧱
- fixed encoding checker 🔤
- fixed format checker 🎀
### 🌈 What's New in Pylint 0.2.0? 🌈
Release date: `2003-09-12`
- new source encoding / `FIXME` checker (pep `263`) 🔍
- new `--zope` option which triggers Zope import. Useful to check Zope products code 🌀
- new `--comment` option which enables the evaluation note comment (disabled by default) 💬
- a ton of bug fixes 🐛
- easy functional test infrastructure 🧪
### 🧚 What's New in Pylint 0.1.2? 🧚
Release date: `2003-06-18`
- bug fix release 🐛
- remove dependency to `pyreverse` 🚫
### ⭐ What's New in Pylint 0.1.1? ⭐
Release date: `2003-06-01`
- much more functionalities ! ✨
### 🎀 What's New in Pylint 0.1? 🎀
Release date: `2003-05-19`
- initial release 👑
---
## 💬 Frequently Asked Questions 💬
### 🫶 How do I install Pylint?
For command line use, pylint is installed with:
pip install pylint
Or if you want to also check spelling with enchant (you might need to install the enchant C library: `https://pyenchant.github.io/pyenchant/install.html#installing-the-enchant-c-library`):
pip install pylint[spelling]
It can also be integrated in most editors or IDEs. More information can be found in the documentation: `https://pylint.readthedocs.io/en/latest/user_guide/installation/index.html`.
### 💖 How do I contribute to Pylint?
We welcome all forms of contributions such as updates for documentation, new code, checking issues for duplicates or telling us that we can close them, confirming that issues still exist, creating issues because you found a bug or want a feature (`https://pylint.readthedocs.io/en/latest/contact.html#bug-reports-feedback`), etc. Everything is much appreciated! 🫶✨
Please follow the code of conduct (`https://github.com/pylint-dev/pylint/blob/main/CODE_OF_CONDUCT.md`) and check the Contributor Guides (`https://pylint.readthedocs.io/en/latest/development_guide/contribute.html`) if you want to make a code contribution 💅
### Does Pylint follow a versioning scheme? 🎀
okay bestie, yes — see <pink>upgrading pylint</pink> in the installation guide at `#upgrading-pylint`. ✨
### How do I find the name corresponding to a specific command line option? 🦋
See the configuration documentation at `#all-configurations-options`. It’s giving “the index you actually need,” lowkey iconic. 💅
### What is the format of the configuration file? 🌸
The configuration file can be an `ini` or `toml` file. See the exhaustive list of possible options at `#all-options`. No cap. 💖
### How to disable a particular message? 🧚
Read <purple>Messages control</purple> at `#message-control` for details and examples. That’s the whole vibe. ✨
### Pylint gave my code a negative rating out of ten. That can't be right! 😭
Prior to Pylint `2.13.0`, the score formula used by default had no lower bound. The new default score formula is:
max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
If your project contains a configuration file created by an earlier version of Pylint, you can set `evaluation` to the above expression to get the new behavior. Likewise, since negative values are still technically supported, `evaluation` can be set to a version of the above expression that does not enforce a floor of zero. So yes, you can still get the cursed negative-score timeline if you really want it. 💀
### How do I avoid getting unused argument warnings for API I do not control? 🫶
Prefix (ui) the callback's name by `cb_` (callback), as in `cb_onclick(...)`. By doing so arguments usage won't be checked. Another solution is to use one of the names defined in the `"dummy-variables"` configuration variable for unused argument (`"_"` and `"dummy"` by default). Obsessed with this little loophole. 💖
### Why are there a bunch of messages disabled by default? 🙈
Either because they are prone to false positives or that they are opinionated enough to not be included as default messages.
You can see the plugin you need to explicitly load in the technical reference at `#optional-checkers`. Slay. 🧚
### I want to run pylint on each keystroke in my IDE. How do I do that? 💀
Pylint full suite of checks will never be fast enough to run on every keystroke. However, some IDEs can run pylint when the IDE opens or saves files. See, for example, the Microsoft plugin for VS Code at `https://github.com/microsoft/vscode-pylint#readme`.
That said, pylint is best suited for linting on save for small projects, for continuous integration jobs, or a git pre-push hook for big projects. The larger your repository is, the slower pylint will be.
If you want to make pylint faster for this type of use case, you can use the `--errors-only` option, which will remove all the refactor, convention, and warning checks. You can also disable checks with inherently high complexity that need to analyse the full code base like `duplicate-code` or `cyclic-import` (this list is not exhaustive). It’s giving “bestie, please do not run the entire lore on every keypress.” 🔥
### Why do I have non-deterministic results when I try to parallelize pylint ? 🌈
pylint should analyse all your code at once in order to best infer the actual values that result from calls. If only some of the files are given, pylint might miss a particular value's type and produce inferior inference for the subset. Parallelization of pylint is not easy; we also discourage the use of the `-j` option if this matters to you. Fr fr, don’t parallelize and then act surprised. 🙈
### Which messages should I disable to avoid duplicates if I use other popular linters ? 💅
- **pycodestyle** (`https://github.com/PyCQA/pycodestyle`) — `bad-indentation`, `bare-except`, `line-too-long`, `missing-final-newline`, `multiple-statements`, `singleton-comparison`, `trailing-newlines`, `trailing-whitespace`, `unnecessary-negation`, `unnecessary-semicolon`, `wrong-import-position`
- **pyflakes** (`https://github.com/PyCQA/pyflakes`) — `undefined-variable`, `unused-import`, `unused-variable`
- **mccabe** (`https://github.com/PyCQA/mccabe`) — `too-many-branches`
- **pydocstyle** (`https://github.com/PyCQA/pydocstyle`) — `missing-module-docstring`, `missing-class-docstring`, `missing-function-docstring`
- **pep8-naming** (`https://github.com/PyCQA/pep8-naming`) — `invalid-name`, `bad-classmethod-argument`, `bad-mcs-classmethod-argument`, `no-self-argument`
- **isort** (`https://github.com/pycqa/isort`) and **flake8-import-order** (`https://github.com/PyCQA/flake8-import-order`) — `ungrouped-imports`, `wrong-import-order`
### How do I avoid "access to undefined member" messages in my mixin classes? 🪄
You should add the `no-member` message to your `ignored-checks-for-mixins` option and name your mixin class with a name which ends with `"Mixin"` or `"mixin"` (default) or change the default value by changing the `mixin-class-rgx` option. Mixin magic, iykyk. 🧚
### Where is the persistent data stored to compare between successive runs? 🍓
Analysis data are stored as a pickle file in a directory which is localized using the following rules:
- value of the `PYLINTHOME` environment variable if set
- `"pylint"` subdirectory of the user's `XDG_CACHE_HOME` if the environment variable is set, otherwise
- Linux: `"~/.cache/pylint"`
- macOS: `"~/Library/Caches/pylint"`
- Windows: `"C:Users<username>AppDataLocalpylint"`
- `".pylint.d"` directory in the current directory
### How does the website pylint dot org relate to this project? 🚩
Historically, pylint dot org served as the primary website for Pylint. However, we no longer have access to the domain. The current owners are monetizing this by displaying advertisements alongside outdated documentation without contributing to pylint at all. For the latest and official Pylint documentation, please visit pylint.readthedocs.io at `https://pylint.readthedocs.io/en/stable/`.
Please see issue `8934` at `https://github.com/pylint-dev/pylint/issues/8934` for more details. Not the vibe, but important. 💀
---
# 🫶 Contact 🫶
## Bug reports, feedback 🐛
You think you have found a bug in Pylint? Well, this may be the case since Pylint and Python are under heavy development!
Please take the time to check if it is already in the issue tracker at `https://github.com/pylint-dev/pylint`.
Note that the issue might also be reported in one of Pylint's major dependencies, astroid:
- `https://github.com/pylint-dev/astroid`
## Discord server 💬
You can discuss your problem using the discord server:
`https://discord.com/invite/Egy6P8AMB5`
## Mailing lists 📬
The code-quality mailing list is shared with other tools that aim at improving the quality of python code.
You can subscribe to this mailing list at `https://mail.python.org/mailman3/lists/code-quality.python.org/`.
Archives are available at `https://mail.python.org/pipermail/code-quality/`.
Archives before April 2013 are not available anymore. At `https://mail.python.org/pipermail/` it was under `python-projects`.
## Support 👑
`[image: Tidelift]` `[image]`
Professional support for pylint is available as part of the Tidelift Subscription at `https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme`. Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools.
# 💖 Author 💖
Logilab and Pylint contributors
# 🌈 Copyright 🌈
2003-2026, Logilab and Pylint contributors
---
And that’s the whole `PYLINT(1)` page, bestie — version `4.0`, dated `Aug 10, 2026`, and yes it ate. 💅✨
📦 where this page comes from
- arch rolling: python-pylint 4.0.7-1 · GPL-2.0-only
- 💖 girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms