What's New in astroid 4.3.0?
Release date: 2026-08-07
Version 4.2.0 was skipped: a v4.2.0 tag was created by mistake during an
aborted release attempt, so the changes from the 4.2.0 betas are released
as 4.3.0.
-
Fix a crash on a functional
namedtuplewhose field name changes under NFKC
normalization, such asnamedtuple("mu", ["µ"]). Python normalizes
identifiers, so the parsed class stores the field under"μ"while the
brain looked it up as written, raisingKeyErroron a definition that
namedtupleitself accepts.Closes pylint-dev/pylint#8746
-
Fix inference of a method's first argument (
selforcls) when the
method's return value is stored on an unrelated object. This avoids inferring
clsas the caller's class when indexing a tuple returned by a
classmethod such asA.c()[0].Closes pylint-dev/pylint#11101
-
super()with no argument now resolves against the class of the object the
method was called on, instead of the class the method is written in. One consequence
is that atyping.Selfreturn value survives a chain ofsuper()calls.Closes #2852
Closes pylint-dev/pylint#10807 -
typing.overloadstubs no longer shadow the implementation when a special
method is looked up in the MRO.Closes #2448
-
Fix a crash when the field names of a functional
Enumornamedtuple
call are bytes, as inEnum("", b""). Such a definition is invalid, so
inference now falls back to its default instead of raisingTypeError.Closes #3189
-
Fix a crash when the body of a
typing.NamedTuplesubclass contains an
assignment whose target is not a single name, such ascat.color = "black",
basket[0] = "apple"orapple, banana = "red", "yellow". The names bound
by unpacking are now copied to the inferred class as well.Closes #3190
-
Fix a crash when a binary operation involves a class whose metaclass is not a
class, as inclass C(metaclass=sum)followed byC | None. The type of
such a class is a function, which has no bases, sohas_known_bases()now
returnsFalsefor anything that is not a class.Closes #3191
-
Fix a crash when a dataclass inherits from a dataclass that annotates
__init__as a field, as in__init__: int. Such a base binds
__init__to a name instead of a function, so it no longer contributes
arguments to the generated__init__.Closes #3200
-
The documentation build now fails on any Sphinx warning, so a cross-reference
that does not resolve is caught by CI instead of quietly rendering as plain
text.Uninferable,Positionand theBadOperationMessageclasses
are documented as part of the public API, and a number of docstrings that
Sphinx could not parse were repaired. -
The
>>>examples in the documentation are run when the documentation is
built, so one that stops being true is noticed. Most of them could not run
before: they printed node addresses recorded in 2018, and the multi-line ones
were missing the...continuation prompt. The example forDecorators
reported the wrong line numbers. -
Fix the example in the docstring of
BoolOp, which showed the node it was
copied from:astroid.extract_node("a and b")gives aBoolOp, not a
BinOp. -
Names written between single backticks in the ChangeLog render as code again,
rather than as italics. -
Prevent crashes while processing extension-module classes whose bases cannot
be resolved when applying enum inference transformations.Closes pylint-dev/pylint#11179
-
The API documentation now covers the proxies, the objects with no node of
their own, the inference context, the manager, the object models and the
extension call signatures. TheEvaluatedObject,Interpolation,
NamedExprandTemplateStrnodes were missing from the list of nodes
and are listed now. -
Add
__required_keys__and__optional_keys__to the inferred
TypedDictbase class, so subclasses no longer raise ano-member
false positive in pylint when accessing those attributes at runtime.Closes pylint-dev/pylint#10158
-
Comprehension conditions now constrain the inference of names used in the
comprehension, likeifstatement conditions already did:
[x for x in lst if x is not None]no longer infersNoneforx
in the element expression.Refs #3094
-
Fix
isinstance()constraints only filtering the first inferred value of
a variable: checking a value made theisinstanceclassinfo uninferable
for the checks of the following values. -
Remove Python 2 era dead code and outdated comments.
Notably: the internalTreeRebuilderconstructor no longer takes a
parser_moduleargument.Refs #3154
-
Fix inference of a starred target that is not last in a
forloop, such as
for a, *b, c in .... The elements following the starred one were counted
from the target's own arity rather than from the end of the iterable, so
bwas truncated whenever the iterable was longer than the target. -
Add
qname()method to theSlicenode class, returning
"builtins.slice", so that the node can be used in contexts
that require a qualified name (e.g. pylint'sbasic_checker).Closes #3115
-
Add brain tip for
numpy.fromfileso that calls to
np.fromfile(...)are correctly inferred as returning
numpy.ndarrayinstead ofUninferable.Closes #600
-
Add brain for
decimalthat gets applied if_decimalisn't available. -
Removed the
**kwargsargument from allinferfunctions.
Users upgrading theirastroidversions should ensure they do no pass values other than
contexttoinfer. -
Deduplicate keys when inferring
dict.fromkeys.dict.fromkeys("aab")
now infers a dict with keys"a","b"instead of"a","a",
"b", matching the runtime. This also stopsdict.fromkeysfrom
materializing oneConstnode per character for a repeated string such as
dict.fromkeys("x" * 10 ** 8), which is a single-key dict. -
Bound str/bytes and list/tuple concatenation (
a + b) during inference. -
Bound oversized old-style (
%) string/bytes formatting during inference.
A tiny literal such as"%1000000000d" % 1made
_infer_old_style_string_formattingeagerly build a multi-gigabyte
Constwhile inferring otherwise untrusted source. The width and
precision are now read out of the conversion specifiers (including*
fields) and the interpolation infers asUninferablepast1e8,
mirroring the repetition, concatenation andstr.formatcaps. Bytes
%formatting is routed through the same handler so it is bounded too;
small format strings keep inferring their exact value. -
Fix crash (
TypeError: 'UninferableBase' object is not iterable) in the
multiprocessingbrain when a local package shadows the stdlib
multiprocessingmodule.Closes pylint-dev/pylint#10014
-
Bound the number of nodes built when inferring
list/set/tuple/
frozensetanddict.fromkeysfrom astr/bytesconstant. These
built oneConstnode per character with no cap, so ``list(("a" * 10 ** 8)- "b")
(concatenation is not size-bounded) materialized hundreds of millions of nodes. They now fall back to the default inference past1e8`` characters,
matching the sequence-repetition guard.
- "b")
Refs #3127
-
Bound string/bytes multiplication (
"x" * n) and integer left shifts
(1 << n) inconst_infer_binary_opthe same way list/tuple
multiplication and**already are. Inferring a constant such as
"A" * 10 ** 10previously materialized the multi-gigabyte result
eagerly; these operations now infer asUninferablewhen the result
would be oversized.Refs #3107
-
Bound the field width and precision when inferring
str.formatcalls.
A template such as"{:>2000000000}".format("x")(or a width/precision
supplied through a nested{}field) previously built the padded
multi-gigabyte string eagerly during inference; such calls now infer as
Uninferablewhen the field size would be oversized.Refs #3131
-
Remove the
asnamekeyword argument fromImport._inferand
ImportFrom._infer. The alias-to-real-name translation that
asname=Trueperformed is now hoisted intoImportNode._infer_name,
which runs in_infer_stmtsbefore_inferis dispatched. Direct
callers ofImport.infer/ImportFrom.infer(previously the
asname=Falsepath) now consistently resolve the lookup name as-is.The two flows used to collapse into a single inference cache entry
because theasnamekwarg was not part of the cache key, so the
second call returned the cached result of the first regardless of
which one ran. With the translation hoisted upstream, the two flows
set differentlookupnamevalues and therefore land on distinct
cache keys, eliminating the collision.Refs pylint-dev/pylint#10193
Closes #3007 -
Avoid materializing a multi-gigabyte string while inferring a small literal
such as"{:>2000000000}".format("x")orf"{1.5:.2000000000f}".
_infer_str_format_callandFormattedValue._infernow yield
Uninferablewhen a format spec asks for a width or precision over 1e8,
mirroring the sequence/repetition caps inastroid.protocols.
Refs #3108
-
Fix uncaught
IndentationErrorwhen parsing code whose lines end in
\r: the slice of source tokenized to compute a class or function
positionis split on\nonly and could be misaligned with the AST
line numbers. Such nodes now simply have no position information, as
already done whentokenizeraisesTokenError.Closes #3091
-
Support PEP 810 lazy imports (new in Python 3.15).
Importand
ImportFromnodes gain anis_lazyinteger attribute, mirroring
theastfield of the same name. -
Support PEP 798 comprehension unpacking (
{**d for d in dicts},
new in Python 3.15). -
Fix astroid bootstrap crash on PyPy 7.3.22 (
TypeError: expected str, got getset_descriptor object) by also catchingTypeErrorfrom
getattr(obj, alias)inInspectBuilder.object_build. PyPy 7.3.22
raisesTypeErrorinstead ofAttributeErrorfor unset getset
descriptors liketypes.FunctionType.__text_signature__, which made
_astroid_bootstrapping()blow up on any call into astroid. -
Shorten
import astroidby deferring imports only needed on cold paths.
logging(used bymodutilsandraw_buildingsolely to report
stderr/stdout captured while importing a module) andpprint(used only
to format debug__repr__/repr_treeoutput) are now imported
lazily, andastroid.exceptionsimportsastroid.typingunder
TYPE_CHECKING. -
Fix
AttributeErrorcrash instarred_assigned_stmtswhen a starred
unpacking target is an attribute (e.g.for *o.attr, x in ...) rather
than a simple name.Closes #2646
-
Fix
AttributeErrorcrash in theArgumentsassigned_stmts
protocol when called without an inference context (the public
assigned_stmtsAPI defaultscontexttoNone). Resolving a
function's first parameter dereferencedcontext.boundnodewhile the
matchingcontext and ...guard a few lines below was missing; it now
degrades to Uninferable. -
Fix
AttributeErrorcrash inClassDef.infer_call_resultwhen called
through the public API without a context (it defaults toNone) for a
class whose metaclass defines__call__: the callee was assigned to
context.callcontext.calleewithout checking that a call context exists.
It is now guarded, matching the surrounding code. -
Wrap assignment expressions (
:=) in parentheses when emitting
as_stringoutput so the rendered code remains syntactically valid in
contexts such as comparisons, where Python requires the walrus expression
to be parenthesized.Closes #2668
-
Catch
MemoryError/RecursionError(andValueError) when validating
type comments withast.parse. Pathological type comments produced by
fuzzers (e.g.# type: i{{{{{{{...) previously crashed parsing with a
MemoryErrororRecursionErrordepending on the runtime; astroid now
treats them as invalid type comments and skips them, mirroring the f-string
fix from #2762.Closes #2993
-
Bypass
__init__inInferenceContext.clone()and write the slots
directly.clone()is called ~85k times per pandas/frame.py pylint run;
skipping the conditional defaults shaves measurable time off the hottest
constructor in inference.Refs #1115
-
Fix
TypeErrorinbrain_randomwhenrandom.sampleis called with a
sequence containing nodes whose__init__does not acceptlineno
(e.g.Module). The clone helper now filters init params to those the
class actually accepts.Closes #3043
-
Fix
RecursionErrorin_compute_mro()when circular class hierarchies
are created through runtime name rebinding. Circular bases are now resolved
to the original class instead of recursing.Closes #3023
Closes pylint-dev/pylint#10821 -
Changed
block_rangeto considerelseits own block, allowingpylintto apply
disables to just the block.References pylint-dev/pylint#872
-
Fix uncaught
TokenErrorwhen building a class or function whose source
slice is malformed.tokenize.generate_tokensmay raise (e.g. on an
unterminated bracket on Python < 3.12); position computation now treats such
a node as having no position information instead of crashing.Closes #2527
-
str()of a constant argument now infers the actual string value instead
of always inferring"". When every inference path of the argument
resolves toConstvalues that stringify to the same string,infer_str
returns that string; otherwise it keeps falling back toConst("").Closes #2994
-
Fix
AttributeErrorcrash when looking up a special method on a class
whose explicit metaclass infers to a non-class node (e.g. a function). Such
a metaclass has no MRO, so the dunder lookup now raises
AttributeInferenceErrorinstead of crashing.Closes #3063
-
Fix
AttributeErrorcrash inClassDef.getitemwhen__class_getitem__
resolves to a non-callable node (e.g. anAssignName).getitemnow
raisesAstroidTypeErrorin that case, consistent with its documented
behaviour.Closes #3064
-
Fix
DuplicateBasesErrorcrash in the enum brain when inferring an enum
class with duplicate bases (e.g.class C(enum.Enum, enum.Enum)).
infer_enum_classnow catchesMroErrorand leaves such a malformed
class untransformed.Closes #3065
-
Fix
InferenceErrorcrash inClassDef.slots()when__slots__is
declared as an annotation without a value (e.g.__slots__: None). Such a
__slots__cannot be inferred, soslots()now returnsNone.Closes #3067
-
Fix
TypeErrorcrash in the enum brain when a functionalEnumcall
has a non-string member name (e.g.Enum("e", (1,))). Such a definition
is invalid, so inference now falls back to the default instead of crashing.Closes #3068
-
Fix detecting static/class methods and inspecting IntFlag types in
GObject-based libraries (GLib, Gtk etc) -
Only inject the
_HAS_DEFAULT_FACTORYsentinel into a module's locals when
the generated dataclass__init__actually references it. Parsing a module
containing a dataclass without anyfield(default_factory=...)no longer
exposes an unexpected_HAS_DEFAULT_FACTORYname.Closes #2808
- Fix a crash when inferring
__func__on a bound method that proxies its function directly,
such asA.method.__func__for a classmethod, or a lambda assigned to a class attribute
such asA().lam.__func__.
Closes pylint-dev/pylint#11198
- Fix a crash when inferring
-
Add
qname()andpytype()to theTypeVar,ParamSpec,
TypeVarTupleandTypeAliasnode classes, returning
"typing.TypeVar","typing.ParamSpec","typing.TypeVarTuple"
and"typing.TypeAliasType". PEP 695 type parameters and type aliases
are inferred as themselves, so callers that ask an inferred value for its
type crashed with anAttributeError, asFunctionDef.decoratornames()
did for@Tinsideclass Basket[T].Refs #3115
-
Infer the value of a data descriptor of a C-implemented class as unknown
instead of as a class named after the attribute. Reading such an attribute,
for instanceexc.__traceback__.tb_frameorgen.gi_frame, inferred a
class, so the attributes of the value the descriptor returns were reported as
missing.raw_building.object_build_datadescriptor()now returns an
EmptyNode.Closes pylint-dev/pylint#11218