Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-astroid for openSUSE:Factory checked in at 2026-03-24 18:48:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-astroid (Old) and /work/SRC/openSUSE:Factory/.python-astroid.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-astroid" Tue Mar 24 18:48:00 2026 rev:66 rq:1342069 version:4.1.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-astroid/python-astroid.changes 2026-03-03 15:29:42.390014440 +0100 +++ /work/SRC/openSUSE:Factory/.python-astroid.new.8177/python-astroid.changes 2026-03-24 18:48:17.441037478 +0100 @@ -1,0 +2,22 @@ +Mon Mar 23 15:57:35 UTC 2026 - Dirk Müller <[email protected]> + +- update to 4.1.2: + * Fix crash accessing property `fset` in generic classes with + type annotations. + * Fix infinite recursion caused by cyclic inference in + ``Constraint``. + * Fix ``RecursionError`` in ``_compute_mro()`` when circular + class hierarchies are created through runtime name rebinding. + * Fix ``DuplicateBasesError`` crash in dataclass transform when + a class has duplicate bases in its MRO (e.g., ``Protocol`` + appearing both directly and indirectly). Catch ``MroError`` + at ``.mro()`` call sites in ``brain_dataclasses.py``, + consistent with the existing pattern elsewhere. + * Catch ``MemoryError`` when inferring f-strings with extremely + large format widths (e.g. ``f'{0:11111111111}'``) so that + inference yields ``Uninferable`` instead of crashing. + * Fix ``ValueError`` in ``__str__``/``repr`` and error messages + when nodes have extreme values (very long identifiers or large + integers). + +------------------------------------------------------------------- Old: ---- astroid-4.1.1-gh.tar.gz New: ---- astroid-4.1.2-gh.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-astroid.spec ++++++ --- /var/tmp/diff_new_pack.RmBPix/_old 2026-03-24 18:48:18.285071693 +0100 +++ /var/tmp/diff_new_pack.RmBPix/_new 2026-03-24 18:48:18.285071693 +0100 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-astroid -Version: 4.1.1 +Version: 4.1.2 Release: 0 Summary: Representation of Python source as an AST for pylint License: LGPL-2.1-or-later ++++++ astroid-4.1.1-gh.tar.gz -> astroid-4.1.2-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/.github/workflows/ci.yaml new/astroid-4.1.2/.github/workflows/ci.yaml --- old/astroid-4.1.1/.github/workflows/ci.yaml 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/.github/workflows/ci.yaml 2026-03-22 16:06:01.000000000 +0100 @@ -4,7 +4,7 @@ push: branches: - main - - 2.* + - maintenance/** pull_request: workflow_dispatch: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/CONTRIBUTORS.txt new/astroid-4.1.2/CONTRIBUTORS.txt --- old/astroid-4.1.1/CONTRIBUTORS.txt 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/CONTRIBUTORS.txt 2026-03-22 16:06:01.000000000 +0100 @@ -109,6 +109,7 @@ - Anthony Truchet <[email protected]> - Anthony Sottile <[email protected]> - Alexander Shadchin <[email protected]> +- Fridayworks <[email protected]> - wgehalo <[email protected]> - tejaschauhan36912 <[email protected]> - rr- <[email protected]> @@ -219,6 +220,7 @@ - Alexander Scheel <[email protected]> - Alexander Presnyakov <[email protected]> - Ahmed Azzaoui <[email protected]> +- Casey Jones <[email protected]> Co-Author --------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/ChangeLog new/astroid-4.1.2/ChangeLog --- old/astroid-4.1.1/ChangeLog 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/ChangeLog 2026-03-22 16:06:01.000000000 +0100 @@ -8,11 +8,50 @@ Release date: TBA +What's New in astroid 4.1.3? +============================ +Release date: TBA + + What's New in astroid 4.1.2? ============================ -Release date: TBA +Release date: 2026-03-22 + +* Fix crash accessing property `fset` in generic classes with type annotations. + Closes #2996 + +* Fix infinite recursion caused by cyclic inference in ``Constraint``. + +* 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 #2967 + Closes pylint-dev/pylint#10821 + +* Fix ``DuplicateBasesError`` crash in dataclass transform when a class has + duplicate bases in its MRO (e.g., ``Protocol`` appearing both directly and + indirectly). Catch ``MroError`` at ``.mro()`` call sites in + ``brain_dataclasses.py``, consistent with the existing pattern elsewhere. + + Closes #2628 + +* Fix ``FunctionModel`` returning descriptor attributes for builtin functions. + + Closes #2743 + +* Catch ``MemoryError`` when inferring f-strings with extremely large format + widths (e.g. ``f'{0:11111111111}'``) so that inference yields ``Uninferable`` + instead of crashing. + + Closes #2762 + +* Fix ``ValueError`` in ``__str__``/``repr`` and error messages when nodes have + extreme values (very long identifiers or large integers). Clamp pprint width + to a minimum of 1 and truncate oversized values in error messages. + Closes #2764 What's New in astroid 4.1.1? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/__pkginfo__.py new/astroid-4.1.2/astroid/__pkginfo__.py --- old/astroid-4.1.1/astroid/__pkginfo__.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/__pkginfo__.py 2026-03-22 16:06:01.000000000 +0100 @@ -2,5 +2,5 @@ # For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE # Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt -__version__ = "4.1.1" +__version__ = "4.1.2" version = __version__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/bases.py new/astroid-4.1.2/astroid/bases.py --- old/astroid-4.1.1/astroid/bases.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/bases.py 2026-03-22 16:06:01.000000000 +0100 @@ -183,7 +183,10 @@ if not constraint_stmt.parent_of(stmt): stmt_constraints.update(potential_constraints) for inf in stmt.infer(context=context): - if all(constraint.satisfied_by(inf) for constraint in stmt_constraints): + if all( + constraint.satisfied_by(inf, context) + for constraint in stmt_constraints + ): yield inf inferred = True else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/brain/brain_dataclasses.py new/astroid-4.1.2/astroid/brain/brain_dataclasses.py --- old/astroid-4.1.1/astroid/brain/brain_dataclasses.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/brain/brain_dataclasses.py 2026-03-22 16:06:01.000000000 +0100 @@ -21,7 +21,12 @@ from astroid.brain.helpers import is_class_var from astroid.builder import parse from astroid.const import PY313_PLUS -from astroid.exceptions import AstroidSyntaxError, InferenceError, UseInferenceDefault +from astroid.exceptions import ( + AstroidSyntaxError, + InferenceError, + MroError, + UseInferenceDefault, +) from astroid.inference_tip import inference_tip from astroid.manager import AstroidManager from astroid.typing import InferenceResult @@ -174,7 +179,12 @@ # See TODO down below # all_have_defaults = True - for base in reversed(node.mro()): + try: + mro = node.mro() + except MroError: + return pos_only_store, kw_only_store + + for base in reversed(mro): if not base.is_dataclass: continue try: @@ -224,7 +234,12 @@ def _get_previous_field_default(node: nodes.ClassDef, name: str) -> nodes.NodeNG | None: """Get the default value of a previously defined field.""" - for base in reversed(node.mro()): + try: + mro = node.mro() + except MroError: + return None + + for base in reversed(mro): if not base.is_dataclass: continue if name in base.locals: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/constraint.py new/astroid-4.1.2/astroid/constraint.py --- old/astroid-4.1.1/astroid/constraint.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/constraint.py 2026-03-22 16:06:01.000000000 +0100 @@ -12,6 +12,7 @@ from typing import TYPE_CHECKING from astroid import helpers, nodes, util +from astroid.context import InferenceContext from astroid.exceptions import AstroidTypeError, InferenceError, MroError from astroid.typing import InferenceResult @@ -47,7 +48,9 @@ """ @abstractmethod - def satisfied_by(self, inferred: InferenceResult) -> bool: + def satisfied_by( + self, inferred: InferenceResult, context: InferenceContext + ) -> bool: """Return True if this constraint is satisfied by the given inferred value.""" @@ -76,7 +79,9 @@ return None - def satisfied_by(self, inferred: InferenceResult) -> bool: + def satisfied_by( + self, inferred: InferenceResult, context: InferenceContext + ) -> bool: """Return True if this constraint is satisfied by the given inferred value.""" # Assume true if uninferable if inferred is util.Uninferable: @@ -112,7 +117,9 @@ return None - def satisfied_by(self, inferred: InferenceResult) -> bool: + def satisfied_by( + self, inferred: InferenceResult, context: InferenceContext + ) -> bool: """Return True for uninferable results, or depending on negate flag: - negate=False: satisfied if boolean value is True @@ -153,7 +160,9 @@ return None - def satisfied_by(self, inferred: InferenceResult) -> bool: + def satisfied_by( + self, inferred: InferenceResult, context: InferenceContext + ) -> bool: """Return True for uninferable results, or depending on negate flag: - negate=False: satisfied when inferred is an instance of the checked types. @@ -163,8 +172,8 @@ return True try: - types = helpers.class_or_tuple_to_container(self.classinfo) - matches_checked_types = helpers.object_isinstance(inferred, types) + types = helpers.class_or_tuple_to_container(self.classinfo, context) + matches_checked_types = helpers.object_isinstance(inferred, types, context) if matches_checked_types is util.Uninferable: return True @@ -204,7 +213,9 @@ return None - def satisfied_by(self, inferred: InferenceResult) -> bool: + def satisfied_by( + self, inferred: InferenceResult, context: InferenceContext + ) -> bool: """Return True for uninferable/ambiguous results, or depending on negate flag: - negate=False: satisfied when both operands are equal. @@ -215,7 +226,7 @@ if inferred is util.Uninferable: return True - operand_inferred = util.safe_infer(self.operand) + operand_inferred = util.safe_infer(self.operand, context) if operand_inferred is util.Uninferable or operand_inferred is None: return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/interpreter/objectmodel.py new/astroid-4.1.2/astroid/interpreter/objectmodel.py --- old/astroid-4.1.1/astroid/interpreter/objectmodel.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/interpreter/objectmodel.py 2026-03-22 16:06:01.000000000 +0100 @@ -87,8 +87,11 @@ string = "%(cname)s(%(fields)s)" alignment = len(cname) + 1 for field in sorted(self.attributes()): - width = 80 - len(field) - alignment - lines = pprint.pformat(field, indent=2, width=width).splitlines(True) + width = max(80 - len(field) - alignment, 1) + try: + lines = pprint.pformat(field, indent=2, width=width).splitlines(True) + except ValueError: + lines = [f"<{type(field).__name__}>"] inner = [lines[0]] for line in lines[1:]: @@ -269,6 +272,10 @@ class FunctionModel(ObjectModel): + def _is_builtin_func(self) -> bool: + func = self._instance + return isinstance(func.parent, nodes.Module) and not func.root().pure_python + @property def attr___name__(self): return node_classes.Const(value=self._instance.name, parent=self._instance) @@ -287,6 +294,8 @@ @property def attr___defaults__(self): func = self._instance + if self._is_builtin_func(): + raise AttributeInferenceError(target=func, attribute="__defaults__") if not func.args.defaults: return node_classes.Const(value=None, parent=func) @@ -296,6 +305,10 @@ @property def attr___annotations__(self): + if self._is_builtin_func(): + raise AttributeInferenceError( + target=self._instance, attribute="__annotations__" + ) obj = node_classes.Dict( parent=self._instance, lineno=self._instance.lineno, @@ -336,6 +349,8 @@ @property def attr___dict__(self): + if self._is_builtin_func(): + raise AttributeInferenceError(target=self._instance, attribute="__dict__") return node_classes.Dict( parent=self._instance, lineno=self._instance.lineno, @@ -344,10 +359,27 @@ end_col_offset=self._instance.end_col_offset, ) - attr___globals__ = attr___dict__ + @property + def attr___globals__(self): + if self._is_builtin_func(): + raise AttributeInferenceError( + target=self._instance, attribute="__globals__" + ) + return node_classes.Dict( + parent=self._instance, + lineno=self._instance.lineno, + col_offset=self._instance.col_offset, + end_lineno=self._instance.end_lineno, + end_col_offset=self._instance.end_col_offset, + ) @property def attr___kwdefaults__(self): + if self._is_builtin_func(): + raise AttributeInferenceError( + target=self._instance, attribute="__kwdefaults__" + ) + def _default_args(args, parent): for arg in args.kwonlyargs: try: @@ -379,6 +411,9 @@ def attr___get__(self): func = self._instance + if self._is_builtin_func(): + raise AttributeInferenceError(target=func, attribute="__get__") + class DescriptorBoundMethod(bases.BoundMethod): """Bound method which knows how to understand calling descriptor binding. @@ -976,12 +1011,11 @@ :param func: property for which the setter has to be found :return: the setter function or None """ - for target in [ - t for t in func.parent.get_children() if t.name == func.function.name - ]: - for dec_name in target.decoratornames(): - if dec_name.endswith(func.function.name + ".setter"): - return target + for node in func.parent.body: + if isinstance(node, (nodes.FunctionDef, nodes.AsyncFunctionDef)): + for dec_name in node.decoratornames(): + if dec_name.endswith(func.function.name + ".setter"): + return node return None func_setter = find_setter(func) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/nodes/node_classes.py new/astroid-4.1.2/astroid/nodes/node_classes.py --- old/astroid-4.1.1/astroid/nodes/node_classes.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/nodes/node_classes.py 2026-03-22 16:06:01.000000000 +0100 @@ -2151,7 +2151,11 @@ message="Type error {error!r}", node=self, index=index, context=context ) from exc - raise AstroidTypeError(f"{self!r} (value={self.value})") + try: + value_str = str(self.value) + except ValueError: + value_str = f"<{type(self.value).__name__} (too large to display)>" + raise AstroidTypeError(f"{self!r} (value={value_str})") def has_dynamic_getattr(self) -> bool: """Check if the node has a custom __getattr__ or __getattribute__. @@ -4756,8 +4760,9 @@ end_col_offset=self.end_col_offset, ) continue - except (ValueError, TypeError): - # happens when format_spec.value is invalid + except (ValueError, TypeError, MemoryError): + # ValueError/TypeError: invalid format spec + # MemoryError: format spec with huge width (e.g. f'{0:11111111111}') yield util.Uninferable uninferable_already_generated = True continue diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/nodes/node_ng.py new/astroid-4.1.2/astroid/nodes/node_ng.py --- old/astroid-4.1.1/astroid/nodes/node_ng.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/nodes/node_ng.py 2026-03-22 16:06:01.000000000 +0100 @@ -198,8 +198,11 @@ result = [] for field in self._other_fields + self._astroid_fields: value = getattr(self, field, "Unknown") - width = 80 - len(field) - alignment - lines = pprint.pformat(value, indent=2, width=width).splitlines(True) + width = max(80 - len(field) - alignment, 1) + try: + lines = pprint.pformat(value, indent=2, width=width).splitlines(True) + except ValueError: + lines = [f"<{type(value).__name__}>"] inner = [lines[0]] for line in lines[1:]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/astroid/nodes/scoped_nodes/scoped_nodes.py new/astroid-4.1.2/astroid/nodes/scoped_nodes/scoped_nodes.py --- old/astroid-4.1.1/astroid/nodes/scoped_nodes/scoped_nodes.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/astroid/nodes/scoped_nodes/scoped_nodes.py 2026-03-22 16:06:01.000000000 +0100 @@ -2841,11 +2841,41 @@ baseobj = baseobj._proxied if not isinstance(baseobj, ClassDef): continue + if baseobj is self: + # Circular base due to name rebinding (e.g. pdb.Pdb = CustomPdb + # where CustomPdb inherits from pdb.Pdb). Fall back to the + # first non-circular inferred value from the base expression. + baseobj = self._resolve_circular_base(stmt, context) + if baseobj is None: + continue if not baseobj.hide: yield baseobj else: yield from baseobj.bases + def _resolve_circular_base( + self, + stmt: nodes.NodeNG, + context: InferenceContext | None, + ) -> ClassDef | None: + """Resolve a circular base reference by finding the original class. + + When a name is rebound to a subclass (e.g. ``pdb.Pdb = CustomPdb``), + ``_infer_last`` follows the rebinding and returns the subclass itself. + This method iterates through all inferred values to find the first + non-circular ClassDef. + """ + inf_context = copy_context(context) + try: + for inferred in stmt.infer(context=inf_context): + if isinstance(inferred, bases.Instance): + inferred = inferred._proxied + if isinstance(inferred, ClassDef) and inferred is not self: + return inferred + except InferenceError: + pass + return None + def _compute_mro(self, context: InferenceContext | None = None): if self.qname() == "builtins.object": return [self] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/requirements_minimal.txt new/astroid-4.1.2/requirements_minimal.txt --- old/astroid-4.1.1/requirements_minimal.txt 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/requirements_minimal.txt 2026-03-22 16:06:01.000000000 +0100 @@ -1,5 +1,5 @@ # Tools used when releasing -contributors-txt>=0.7.4 +contributors-txt>=1.0.1 tbump~=6.11 # Tools used to run tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/script/.contributors_aliases.json new/astroid-4.1.2/script/.contributors_aliases.json --- old/astroid-4.1.1/script/.contributors_aliases.json 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/script/.contributors_aliases.json 2026-03-22 16:06:01.000000000 +0100 @@ -37,6 +37,10 @@ "mails": ["[email protected]", "[email protected]"], "name": "James Addison" }, + "[email protected]": { + "mails": ["[email protected]"], + "name": "Casey Jones" + }, "[email protected]": { "mails": ["[email protected]", "[email protected]"], "name": "Hashem Nasarat" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/tbump.toml new/astroid-4.1.2/tbump.toml --- old/astroid-4.1.1/tbump.toml 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/tbump.toml 2026-03-22 16:06:01.000000000 +0100 @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/astroid" [version] -current = "4.1.1" +current = "4.1.2" regex = ''' ^(?P<major>0|[1-9]\d*) \. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/tests/brain/test_dataclasses.py new/astroid-4.1.2/tests/brain/test_dataclasses.py --- old/astroid-4.1.1/tests/brain/test_dataclasses.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/tests/brain/test_dataclasses.py 2026-03-22 16:06:01.000000000 +0100 @@ -1237,3 +1237,73 @@ fourth_init: bases.UnboundMethod = next(fourth.infer()) assert [a.name for a in fourth_init.args.args] == ["self", "other_attr", "attr"] assert [a.name for a in fourth_init.args.defaults] == ["Uninferable"] + + +def test_dataclass_with_duplicate_bases_no_crash(): + """Regression test for https://github.com/pylint-dev/astroid/issues/2628. + + A dataclass inheriting from a class with duplicate bases in MRO + (e.g., Protocol appearing both directly and indirectly) should not + crash with DuplicateBasesError during AST transformation. + """ + code = """ + import dataclasses + from typing import TypeVar, Protocol + + BaseT = TypeVar("BaseT") + T = TypeVar("T", bound=BaseT) + + class ConfigBase(Protocol[BaseT]): + ... + + class Config(ConfigBase[T], Protocol[T]): + ... + + @dataclasses.dataclass + class DatasetConfig(Config[T]): + name: str = "default" + + DatasetConfig.__init__ #@ + """ + node = astroid.extract_node(code) + # Should not raise DuplicateBasesError — graceful degradation instead + inferred = next(node.infer()) + assert inferred is not None + + +def test_dataclass_with_duplicate_bases_field_default(): + """Regression test for _get_previous_field_default with broken MRO. + + When a parent dataclass defines a field with a default and a child (with + duplicate bases in its MRO) re-annotates that field without a value, + _get_previous_field_default should not crash with DuplicateBasesError. + + See https://github.com/pylint-dev/astroid/issues/2628. + """ + code = """ + import dataclasses + from typing import TypeVar, Protocol + + BaseT = TypeVar("BaseT") + T = TypeVar("T", bound=BaseT) + + class ConfigBase(Protocol[BaseT]): + ... + + class Config(ConfigBase[T], Protocol[T]): + ... + + @dataclasses.dataclass + class BaseConfig(Config[T]): + name: str = "default" + + @dataclasses.dataclass + class ChildConfig(BaseConfig[T]): + name: str + + ChildConfig.__init__ #@ + """ + node = astroid.extract_node(code) + # Should not raise DuplicateBasesError in _get_previous_field_default + inferred = next(node.infer()) + assert inferred is not None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/tests/test_inference.py new/astroid-4.1.2/tests/test_inference.py --- old/astroid-4.1.1/tests/test_inference.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/tests/test_inference.py 2026-03-22 16:06:01.000000000 +0100 @@ -5262,6 +5262,28 @@ assert value_node.value == result +def test_fstring_large_width_no_memory_error() -> None: + """MemoryError should not crash inference for f-strings with huge width. + + Regression test for https://github.com/pylint-dev/astroid/issues/2762 + """ + + class OOMInt(int): + """An int whose __format__ raises MemoryError, simulating f'{0:>11111111111}'.""" + + def __format__(self, spec: str) -> str: + raise MemoryError + + node = extract_node("f'{0:>9}'") + # Replace the Const value with our OOMInt so format() raises MemoryError + # without actually allocating a huge string. + fmt_value = node.values[0] + fmt_value.value.value = OOMInt(0) + inferred = node.inferred() + assert len(inferred) == 1 + assert inferred[0] is util.Uninferable + + def test_augassign_recursion() -> None: """Make sure inference doesn't throw a RecursionError. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/tests/test_nodes.py new/astroid-4.1.2/tests/test_nodes.py --- old/astroid-4.1.1/tests/test_nodes.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/tests/test_nodes.py 2026-03-22 16:06:01.000000000 +0100 @@ -41,6 +41,7 @@ from astroid.exceptions import ( AstroidBuildingError, AstroidSyntaxError, + AstroidTypeError, AttributeInferenceError, StatementMissing, ) @@ -2253,3 +2254,42 @@ # This should not raise a DeprecationWarning # pylint: disable-next=unused-import from astroid import builtin_lookup + + +def test_str_long_name_no_crash() -> None: + """str() should not crash with ValueError on nodes with long names. + + Regression test for https://github.com/pylint-dev/astroid/issues/2764 + """ + long_name = "a" * 200 + code = f"class {long_name}:\n pass" + module = parse(code) + # This should not raise ValueError('width must be != 0') + result = str(module.body[0]) + assert long_name in result + + +def test_str_large_int_no_crash() -> None: + """str() should not crash with ValueError on nodes with large integer values. + + Regression test for https://github.com/pylint-dev/astroid/issues/2785 + """ + code = "x = 10 ** 5000" + module = parse(code) + # Infer the BinOp to get a Const with a huge int value + inferred = next(module.body[0].value.infer()) + assert isinstance(inferred.value, int) + # This should not raise ValueError about integer string conversion limit + result = str(inferred) + assert "int" in result + + +def test_str_large_int_getitem_no_crash() -> None: + """getitem error message should not crash with large integer values.""" + code = "x = 10 ** 5000" + module = parse(code) + inferred = next(module.body[0].value.infer()) + assert isinstance(inferred.value, int) + # Trigger the error path that formats the value + with pytest.raises(AstroidTypeError, match=r"too large to display|int"): + inferred.getitem(nodes.Const(0)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/tests/test_object_model.py new/astroid-4.1.2/tests/test_object_model.py --- old/astroid-4.1.1/tests/test_object_model.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/tests/test_object_model.py 2026-03-22 16:06:01.000000000 +0100 @@ -998,3 +998,49 @@ thisclass_inferred = next(thisclass_node.infer()) assert isinstance(thisclass_inferred, nodes.ClassDef) assert thisclass_inferred.name == "Base" + + [email protected]( + "attr", + [ + "__get__", + "__defaults__", + "__annotations__", + "__dict__", + "__globals__", + "__kwdefaults__", + ], +) +def test_builtin_func_no_descriptor_attrs(attr: str) -> None: + """Test builtin functions lack descriptor protocol attributes.""" + node = builder.extract_node(f"eval.{attr}") + with pytest.raises(InferenceError): + next(node.infer()) + + +def test_getattr_on_property_fset_with_annassign_does_not_crash() -> None: + """Test that getattr on property fset doesn't crash with AnnAssign.""" + node = builder.extract_node(""" + from typing import TypeVar, Generic + + T = TypeVar('T') + + class Base: + _x: object + @property + def x(self): + return self._x + @x.setter + def x(self, val): + self._x = val + + class Child(Base, Generic[T]): + _x: T + @property + def x(self) -> T: + return self._x + @x.setter + def x(self, val): + getattr(Base.x, "fset", None) #@ + """) + next(node.infer()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/tests/test_regrtest.py new/astroid-4.1.2/tests/test_regrtest.py --- old/astroid-4.1.1/tests/test_regrtest.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/tests/test_regrtest.py 2026-03-22 16:06:01.000000000 +0100 @@ -486,9 +486,8 @@ assert node.name == "c" [email protected](reason="Not fixed yet") def test_regression_eval_get_of_arg() -> None: - """Regression test for #2743""" + """Regression test for #2743.""" node = _extract_single_node("eval.__get__(1)") with pytest.raises(InferenceError): next(node.infer()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.1/tests/test_scoped_nodes.py new/astroid-4.1.2/tests/test_scoped_nodes.py --- old/astroid-4.1.1/tests/test_scoped_nodes.py 2026-02-23 03:34:23.000000000 +0100 +++ new/astroid-4.1.2/tests/test_scoped_nodes.py 2026-03-22 16:06:01.000000000 +0100 @@ -1696,6 +1696,29 @@ self.assertIsInstance(cm.exception, MroError) self.assertIsInstance(cm.exception, ResolveError) + def test_mro_circular_name_rebinding(self) -> None: + """MRO computation should handle circular name rebinding. + + When a module-level name is rebound to a subclass of itself, + _infer_last follows the rebinding and returns the subclass. + The MRO computation should resolve the cycle by falling back + to the original class. + + Regression test for https://github.com/pylint-dev/astroid/issues/2967 + """ + astroid = builder.parse(""" + import pdb + + class CustomPdb(pdb.Pdb): + pass + + pdb.Pdb = CustomPdb + """) + self.assertEqualMro( + astroid["CustomPdb"], + ["CustomPdb", "Pdb", "Bdb", "Cmd", "object"], + ) + def test_mro_with_factories(self) -> None: cls = builder.extract_node(""" def MixinFactory(cls):
