pypi astroid 4.3.0
v4.3.0

5 hours ago

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 namedtuple whose field name changes under NFKC
    normalization, such as namedtuple("mu", ["µ"]). Python normalizes
    identifiers, so the parsed class stores the field under "μ" while the
    brain looked it up as written, raising KeyError on a definition that
    namedtuple itself accepts.

    Closes pylint-dev/pylint#8746

  • Fix inference of a method's first argument (self or cls) when the
    method's return value is stored on an unrelated object. This avoids inferring
    cls as the caller's class when indexing a tuple returned by a
    classmethod such as A.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 a typing.Self return value survives a chain of super() calls.

    Closes #2852
    Closes pylint-dev/pylint#10807

  • typing.overload stubs 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 Enum or namedtuple
    call are bytes, as in Enum("", b""). Such a definition is invalid, so
    inference now falls back to its default instead of raising TypeError.

    Closes #3189

  • Fix a crash when the body of a typing.NamedTuple subclass contains an
    assignment whose target is not a single name, such as cat.color = "black",
    basket[0] = "apple" or apple, 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 in class C(metaclass=sum) followed by C | None. The type of
    such a class is a function, which has no bases, so has_known_bases() now
    returns False for 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, Position and the BadOperationMessage classes
    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 for Decorators
    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 a BoolOp, 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. The EvaluatedObject, Interpolation,
    NamedExpr and TemplateStr nodes were missing from the list of nodes
    and are listed now.

  • Add __required_keys__ and __optional_keys__ to the inferred
    TypedDict base class, so subclasses no longer raise a no-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, like if statement conditions already did:
    [x for x in lst if x is not None] no longer infers None for x
    in the element expression.

    Refs #3094

  • Fix isinstance() constraints only filtering the first inferred value of
    a variable: checking a value made the isinstance classinfo uninferable
    for the checks of the following values.

  • Remove Python 2 era dead code and outdated comments.
    Notably: the internal TreeRebuilder constructor no longer takes a
    parser_module argument.

    Refs #3154

  • Fix inference of a starred target that is not last in a for loop, 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
    b was truncated whenever the iterable was longer than the target.

  • Add qname() method to the Slice node class, returning
    "builtins.slice", so that the node can be used in contexts
    that require a qualified name (e.g. pylint's basic_checker).

    Closes #3115

  • Add brain tip for numpy.fromfile so that calls to
    np.fromfile(...) are correctly inferred as returning
    numpy.ndarray instead of Uninferable.

    Closes #600

  • Add brain for decimal that gets applied if _decimal isn't available.

  • Removed the **kwargs argument from all infer functions.
    Users upgrading their astroid versions should ensure they do no pass values other than
    context to infer.

  • 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 stops dict.fromkeys from
    materializing one Const node 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" % 1 made
    _infer_old_style_string_formatting eagerly build a multi-gigabyte
    Const while inferring otherwise untrusted source. The width and
    precision are now read out of the conversion specifiers (including *
    fields) and the interpolation infers as Uninferable past 1e8,
    mirroring the repetition, concatenation and str.format caps. 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
    multiprocessing brain when a local package shadows the stdlib
    multiprocessing module.

    Closes pylint-dev/pylint#10014

  • Bound the number of nodes built when inferring list/set/tuple/
    frozenset and dict.fromkeys from a str/bytes constant. These
    built one Const node 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.

Refs #3127

  • Bound string/bytes multiplication ("x" * n) and integer left shifts
    (1 << n) in const_infer_binary_op the same way list/tuple
    multiplication and ** already are. Inferring a constant such as
    "A" * 10 ** 10 previously materialized the multi-gigabyte result
    eagerly; these operations now infer as Uninferable when the result
    would be oversized.

    Refs #3107

  • Bound the field width and precision when inferring str.format calls.
    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
    Uninferable when the field size would be oversized.

    Refs #3131

  • Remove the asname keyword argument from Import._infer and
    ImportFrom._infer. The alias-to-real-name translation that
    asname=True performed is now hoisted into ImportNode._infer_name,
    which runs in _infer_stmts before _infer is dispatched. Direct
    callers of Import.infer / ImportFrom.infer (previously the
    asname=False path) now consistently resolve the lookup name as-is.

    The two flows used to collapse into a single inference cache entry
    because the asname kwarg 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 different lookupname values 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") or f"{1.5:.2000000000f}".
    _infer_str_format_call and FormattedValue._infer now yield
    Uninferable when a format spec asks for a width or precision over 1e8,
    mirroring the sequence/repetition caps in astroid.protocols.

Refs #3108

  • Fix uncaught IndentationError when parsing code whose lines end in
    \r: the slice of source tokenized to compute a class or function
    position is split on \n only and could be misaligned with the AST
    line numbers. Such nodes now simply have no position information, as
    already done when tokenize raises TokenError.

    Closes #3091

  • Support PEP 810 lazy imports (new in Python 3.15). Import and
    ImportFrom nodes gain an is_lazy integer attribute, mirroring
    the ast field 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 catching TypeError from
    getattr(obj, alias) in InspectBuilder.object_build. PyPy 7.3.22
    raises TypeError instead of AttributeError for unset getset
    descriptors like types.FunctionType.__text_signature__, which made
    _astroid_bootstrapping() blow up on any call into astroid.

    Refs pylint-dev/pylint#10999

  • Shorten import astroid by deferring imports only needed on cold paths.
    logging (used by modutils and raw_building solely to report
    stderr/stdout captured while importing a module) and pprint (used only
    to format debug __repr__ / repr_tree output) are now imported
    lazily, and astroid.exceptions imports astroid.typing under
    TYPE_CHECKING.

  • Fix AttributeError crash in starred_assigned_stmts when a starred
    unpacking target is an attribute (e.g. for *o.attr, x in ...) rather
    than a simple name.

    Closes #2646

  • Fix AttributeError crash in the Arguments assigned_stmts
    protocol when called without an inference context (the public
    assigned_stmts API defaults context to None). Resolving a
    function's first parameter dereferenced context.boundnode while the
    matching context and ... guard a few lines below was missing; it now
    degrades to Uninferable.

  • Fix AttributeError crash in ClassDef.infer_call_result when called
    through the public API without a context (it defaults to None) for a
    class whose metaclass defines __call__: the callee was assigned to
    context.callcontext.callee without checking that a call context exists.
    It is now guarded, matching the surrounding code.

  • Wrap assignment expressions (:=) in parentheses when emitting
    as_string output 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 (and ValueError) when validating
    type comments with ast.parse. Pathological type comments produced by
    fuzzers (e.g. # type: i{{{{{{{...) previously crashed parsing with a
    MemoryError or RecursionError depending 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__ in InferenceContext.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 TypeError in brain_random when random.sample is called with a
    sequence containing nodes whose __init__ does not accept lineno
    (e.g. Module). The clone helper now filters init params to those the
    class actually accepts.

    Closes #3043

  • Fix RecursionError in _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_range to consider else its own block, allowing pylint to apply
    disables to just the block.

    References pylint-dev/pylint#872

  • Fix uncaught TokenError when building a class or function whose source
    slice is malformed. tokenize.generate_tokens may 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 to Const values that stringify to the same string, infer_str
    returns that string; otherwise it keeps falling back to Const("").

    Closes #2994

  • Fix AttributeError crash 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
    AttributeInferenceError instead of crashing.

    Closes #3063

  • Fix AttributeError crash in ClassDef.getitem when __class_getitem__
    resolves to a non-callable node (e.g. an AssignName). getitem now
    raises AstroidTypeError in that case, consistent with its documented
    behaviour.

    Closes #3064

  • Fix DuplicateBasesError crash in the enum brain when inferring an enum
    class with duplicate bases (e.g. class C(enum.Enum, enum.Enum)).
    infer_enum_class now catches MroError and leaves such a malformed
    class untransformed.

    Closes #3065

  • Fix InferenceError crash in ClassDef.slots() when __slots__ is
    declared as an annotation without a value (e.g. __slots__: None). Such a
    __slots__ cannot be inferred, so slots() now returns None.

    Closes #3067

  • Fix TypeError crash in the enum brain when a functional Enum call
    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_FACTORY sentinel into a module's locals when
    the generated dataclass __init__ actually references it. Parsing a module
    containing a dataclass without any field(default_factory=...) no longer
    exposes an unexpected _HAS_DEFAULT_FACTORY name.

    Closes #2808

    • Fix a crash when inferring __func__ on a bound method that proxies its function directly,
      such as A.method.__func__ for a classmethod, or a lambda assigned to a class attribute
      such as A().lam.__func__.

    Closes pylint-dev/pylint#11198

  • Add qname() and pytype() to the TypeVar, ParamSpec,
    TypeVarTuple and TypeAlias node 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 an AttributeError, as FunctionDef.decoratornames()
    did for @T inside class 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 instance exc.__traceback__.tb_frame or gen.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

Don't miss a new astroid release

NewReleases is sending notifications on new releases.