What's New in Pylint 4.1¶
:Release:4.1 :Date: TBA
Summary -- Release highlights¶
The duplicate-code checker and symilar received optimizations that
result in considerable performance improvements and memory use reduction
on larger codebases. For example, pandas analysis went from 20 min to
55 s and pylint does not get OOM-killed when analyzing cpython anymore.
The required astroid version is now 4.1.1. See the
astroid changelog
for additional fixes, features, and performance improvements applicable to pylint.
What's new in Pylint 4.0.0-dev0?¶
Release date: TBA
Breaking Changes¶
The
confidenceparameter is no longer nullable on any APIs except foris_message_enabled(whereconfidence=Nonemeans "don't filter by confidence"). The default becameinterfaces.UNDEFINED(an immutable value); the behavior is unchanged unless you were passing an explicitNone. This avoids a runtime check in multiple function to set the default value conditionally.The
constants.MSG_STATE_*integer were replaced by aMessageDisableReasonenum. The old names remain as deprecated aliases pointing at the enum members.MessageDisableReasonis anIntEnumso existing code comparing the return of_get_message_state_scopeto the literal0/1/2keeps working.Refs #11018 (#11018)
New Features¶
Add support for
ignore-pattern-in-long-linesto allow ignoring specific parts of a line when checking line length.Refs #3352 (#3352)
The dict-init-mutate message now includes a suggested dictionary literal showing how to combine the initialization and subsequent mutations into a single statement.
Closes #7819 (#7819)
pyreverse: add
--no-signaturesto show method names without parameter lists or return type annotations in class diagrams.Closes #10772 (#10772)
Add support for --known-first-party similar to --known-third-party.
Refs #10803 (#10803)
False Positives Fixed¶
Fix a false positive for
too-many-argumentsin (non-static) methods and classmethods.Closes #8675 (#8675)
Fix a false positive for
inconsistent-return-statementswhen an instance method annotated withNoReturn(orNever) is called via the class rather than an instance (e.g.MyClass.raise_method(obj)). The unbound method form is now recognised as never returning, matching the existing behaviour for the bound-method form.Closes #9692 (#9692)
Fix a false positive for
no-memberwhen a value is inferred to several possible types and at least one of them defines a dynamic__getattr__.Closes #9833 (#9833)
Fix
used-before-assignmentfalse positive for names bound in only some arms of anif/elif/elsechain.Closes #9879 (#9879)
Fix a false positive for
function-redefined(E0102) when reusing names that matchdummy-variables-rgx(such as_), which is common forpytest-bddstep definitions. This restores the behavior from before pylint 4.0.0; as a consequence the false negative fixed in #9894 is reintroduced for functions whose name matchesdummy-variables-rgx.Closes #10665 (#10665)
Fix a false positive for
unexpected-keyword-argfor dataclasses using generic type aliases (PEP 695).Closes #10703 (#10703)
Fix possibly-used-before-assignment false positive when using self.fail() in tests.
Closes #10743 (#10743)
Fixed false positive for
logging-unsupported-formatwhen 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 (#10752)
Fix false positive
unreachablewhen calling a function with@overloadwhere one signature returnsNoReturn.Closes #10785 (#10785)
Fix a false positive for
too-many-function-argsfor dataclasses using generic type aliases (PEP 695).Closes #10788 (#10788)
Fix a false positive for
invalid-namewhere a dataclass field typed withFinalwas evaluated against theclass_constregex instead of theclass_attributeregex.Closes #10790 (#10790)
Avoid emitting unspecified-encoding (W1514) when py-version is 3.15+.
Refs #10791 (#10791)
Fix a false positive
relative-beyond-top-levelerror when linting specific files in namespace packages in parallel mode by augmentingsys.pathbefore loading plugins and expanding files consistently for parallel workers.Closes #10794 (#10794)
Fix
undefined-variablefalse positive when a name used as ametaclassargument in a nested class is referenced again later in the module.Closes #10823 (#10823)
Fix a false positive for
unused-variablewhere global variables matchingdummy-variables-rgxwere still reported as unused whenallow-global-unused-variableswas disabled.Closes #10890 (#10890)
Fix
# pylint: enableinside atryblock leaking into theexcepthandler. For example in the following code,no-memberis no longer incorrectly re-enabled in theexceptblock:class Basket: # pylint: disable=no-member def pick(self): try: # pylint: enable=no-member print(self.apple) # no-member emitted here except KeyError: print(self.banana) # no-member NOT emitted here (correct)
Requires astroid 4.2.
Refs #10933 (#10933)
Fix a false positive for
bad-dunder-namewhen there is a user-defined__suppress_context__attribute on exception subclasses.Closes #10960 (#10960)
Fix
used-before-assignmentfalse positive for names bound byfrom X import *in every branch of anif/elif/elsechain.Refs #10980 (#10980)
Fix
access-member-before-definitionfalse positive for bare type annotations (self.x: Type) that don't assign a value.Refs #11015 (#11015)
Check for metaclass __call__ signature when evaluating arguments of a class call.
Closes #11032 (#11032)
Fix false positive
method-hiddenwhencached_propertyis imported directly withfrom functools import cached_property.Refs #11037 (#11037)
False Negatives Fixed¶
missing-param-docandmissing-type-docno longer false-negative on NumPy-style parameters whose type line includes a default value, e.g.number : int, default 0. Any text after the colon on the type line is now accepted as the type, matching the NumPy style guide.Closes #6211 (#6211)
Fix a false negative for
abstract-methodwhere a concrete subclass inheriting from an abstract class (without redeclaringabc.ABCorABCMeta) was treated as abstract and silently exempted from the check. A class is now only considered abstract when it opts in explicitly, via directabc.ABCinheritance,metaclass=ABCMeta, an@abstractmethoddefined on the class, or being aProtocol.Closes #7950 (#7950)
Other Bug Fixes¶
dangerous-default-valuenow detects mutable default values intyping.NamedTuplefield definitions.Closes #3716 (#3716)
Fix enabling checks from extensions which are disabled by default if multiple jobs are used.
Closes #10037 (#10037)
Fix a crash in
consider-using-enumeratewhen theforloop target is an attribute (e.g.for self.idx in range(len(x))) rather than a simple variable name.Closes #10099 (#10099)
wrong-import-positionnow exemptstry,if,with, andmatchblocks from marking the import boundary. Fixedasync defnot being detected as an import boundary. Pragma on non-import lines now suppresses following imports until the next non-import.Closes #10589 (#10589)
Fix duplicate messages for extension checks if multiple jobs are used.
Refs #10642 (#10642)
Fix --known_third_party config being ignored.
Closes #10801 (#10801)
Fixed dynamic color mapping for "fail-on" messages when using multiple reporter/output formats.
Closes #10825 (#10825)
dependency on isort is now set to <9, permitting to use isort 8.
Closes #10857 (#10857)
Fix crash when checking
attribute-defined-outside-initon classes that inherit from a base class pylint cannot fully analyze.Closes #10892 (#10892)
Fix an issue where discovery can miss a similarly named directory if a shorter named directory is processed first.
Closes #10969 (#10969)
Fixed inflated message occurrence counts in the final
Messagesreport when running pylint in parallel mode with--jobsgreater than 1.Closes #10996 (#10996)
Fix
add_messagesilently overwriting an explicitcol_offset=0(or any other zero-valuedline/end_lineno/end_col_offset) with the AST node's value. The internal_add_one_messagehelper used a falsey check (if not col_offset:) to detect an omitted argument, which incorrectly treated a legitimate0the same asNone. It now uses an identity check againstNone.Refs #11020 (#11020)
Fix a crash in the name checker when a non-constant value is passed as the
covariantorcontravariantargument of aTypeVar.Closes #11022 (#11022)
Fix a crash in the variable checker when a name resolves to a dataclass-synthesized
__init__, which has no line number.Closes #11023 (#11023)
Fix a crash in the variables checker when
NotImplementedis used as the test of anifstatement, which raised aTypeErrorin a boolean context on Python 3.14.Closes #11025 (#11025)
Avoided a crash from the implicit booleaness checker for
len()calls without arguments.Closes #11028 (#11028)
Fix a crash in the variables checker when a class declares a metaclass whose attribute-access chain does not bottom out at a name (e.g.
class C(metaclass=None._)). Originally reported aspylint-dev/astroid#3066.Refs #11031 (#11031)
Fix a crash in the name checker when a chained assignment of a
TypeAliasvalue has a non-name target such as aSubscript(for examplea[0] = b = TypeAlias).Closes #11056 (#11056)
Fix a crash in the deprecated checker when
__import__is called with a non-string constant argument (for example__import__(1)).Closes #11059 (#11059)
Prevent a crash in
unexpected-keyword-arganalysis wheninfer_call_result()raisesInferenceErrorwhile inspecting decorator return signatures.Closes #11070 (#11070)
Fix a crash in the typecheck checker when a class uses a non-class object (for example a function) as its
metaclass=argument.Closes #11071 (#11071)
Other Changes¶
You can now set the
filesoption in configuration files and on the command line. Passing files without the--filesflag is still supported. This allows to setfilestofiles = my_source_directoryand invokingpylintwith only thepylintcommand similar to how other CLI tools allow to do so. The help message can always be invoked withpylint -horpylint --help.Closes #5701 (#5701)
Document that the
wrong-import-order(C0411) classification of imports as third-party vs first-party depends on the current working directory and recommendknown-first-partyas the deterministic workaround.Closes #8801 (#8801)
Internal Changes¶
Add
assertDoesNotAddMessagestoCheckerTestCaseto assert that specific messages are not emitted, while allowing other messages to be present. This complementsassertNoMessageswhich asserts that no messages at all are emitted.Refs #9598 (#9598)
Performance Improvements¶
Lazily import
isort,dill,multiprocessing/concurrent.futures, andtomlkitso they are only loaded when actually needed. This reduces startup time by ~25% (e.g.--version: 91 => 67 ms,--help: 176 => 133 ms, single-file lint: 272 => 226 ms).Closes #2866 (#2866)
The duplicate-code checker no longer runs when its message (R0801) is disabled, even if
reports=yesis set. Previously, the checker's report (RP0801) would cause the expensive similarity computation to run regardless.Closes #3443 (#3443)
Sped up the
duplicate-codechecker. When run inside pylint the checker now reuses the already-parsed AST instead of re-parsing every file like it has to do when launched viasymilar, and it uses a rolling hash window with caching across file pairs. Additionally, a quadratic blow-up in the hash-matching phase is avoided by switching algorithm at a threshold, which previously caused the checker to hang on files with many repeated lines.Speedup scales with codebase size from 1.5x on small projects (~10k lines), to 20x on large ones (500k+ lines). Memory usage also drops 12-27%. Codebases that previously hung or were OOM-killed could now complete.
Refs #10881 (#10881)
Skip isort classification in the import checker when no import-ordering message is enabled, and cache the isort configuration so it is built once instead of once per import statement. Skipping the isort processing become a negligible improvement once the caching is applied. pylint became ~17% faster on ansible (~=4500 imports) even with isort enabled.
Refs #10886, #2866, #10637 (#10886)