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-03 15:29:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-astroid (Old) and /work/SRC/openSUSE:Factory/.python-astroid.new.29461 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-astroid" Tue Mar 3 15:29:38 2026 rev:65 rq:1335676 version:4.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-astroid/python-astroid.changes 2026-03-01 22:13:46.043355808 +0100 +++ /work/SRC/openSUSE:Factory/.python-astroid.new.29461/python-astroid.changes 2026-03-03 15:29:42.390014440 +0100 @@ -1,0 +2,9 @@ +Mon Mar 2 01:36:06 UTC 2026 - Steve Kowalik <[email protected]> + +- Actually update to 4.1.1: + * Let `UnboundMethodModel` inherit from `FunctionModel` to improve + inference of dunder methods for unbound methods. + * Filter ``Unknown`` from ``UnboundMethod`` and ``Super`` special attribute + lookup to prevent placeholder nodes from leaking during inference. + +------------------------------------------------------------------- Old: ---- astroid-4.1.0-gh.tar.gz New: ---- astroid-4.1.1-gh.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-astroid.spec ++++++ --- /var/tmp/diff_new_pack.Cx4uky/_old 2026-03-03 15:29:43.666067047 +0100 +++ /var/tmp/diff_new_pack.Cx4uky/_new 2026-03-03 15:29:43.710068861 +0100 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-astroid -Version: 4.1.0 +Version: 4.1.1 Release: 0 Summary: Representation of Python source as an AST for pylint License: LGPL-2.1-or-later @@ -29,8 +29,6 @@ BuildRequires: %{python_module pip} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} -# typing-extensions for python310 required for tests only: gh#PyCQA/astroid#1585 -BuildRequires: %{python_module typing-extensions >= 3.10} BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros ++++++ astroid-4.1.0-gh.tar.gz -> astroid-4.1.1-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/.pre-commit-config.yaml new/astroid-4.1.1/.pre-commit-config.yaml --- old/astroid-4.1.0/.pre-commit-config.yaml 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/.pre-commit-config.yaml 2026-02-23 03:34:23.000000000 +0100 @@ -10,7 +10,7 @@ - id: end-of-file-fixer exclude: tests/testdata - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.14.14" + rev: "v0.15.0" hooks: - id: ruff-check args: ["--fix"] @@ -81,6 +81,6 @@ - id: prettier args: [--prose-wrap=always, --print-width=88] - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.12.1" + rev: "v2.15.0" hooks: - id: pyproject-fmt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/ChangeLog new/astroid-4.1.1/ChangeLog --- old/astroid-4.1.0/ChangeLog 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/ChangeLog 2026-02-23 03:34:23.000000000 +0100 @@ -9,12 +9,27 @@ -What's New in astroid 4.1.1? +What's New in astroid 4.1.2? ============================ Release date: TBA +What's New in astroid 4.1.1? +============================ +Release date: 2026-02-22 + +* Let `UnboundMethodModel` inherit from `FunctionModel` to improve inference of + dunder methods for unbound methods. + + Refs #2741 + +* Filter ``Unknown`` from ``UnboundMethod`` and ``Super`` special attribute + lookup to prevent placeholder nodes from leaking during inference. + + Refs #2741 + + What's New in astroid 4.1.0? ============================ Release date: 2026-02-08 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/astroid/__pkginfo__.py new/astroid-4.1.1/astroid/__pkginfo__.py --- old/astroid-4.1.0/astroid/__pkginfo__.py 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/astroid/__pkginfo__.py 2026-02-23 03:34:23.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.0" +__version__ = "4.1.1" version = __version__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/astroid/bases.py new/astroid-4.1.1/astroid/bases.py --- old/astroid-4.1.0/astroid/bases.py 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/astroid/bases.py 2026-02-23 03:34:23.000000000 +0100 @@ -462,14 +462,18 @@ def getattr(self, name: str, context: InferenceContext | None = None): if name in self.special_attributes: - return [self.special_attributes.lookup(name)] + special_attr = self.special_attributes.lookup(name) + if not isinstance(special_attr, nodes.Unknown): + return [special_attr] return self._proxied.getattr(name, context) def igetattr( self, name: str, context: InferenceContext | None = None ) -> Iterator[InferenceResult]: if name in self.special_attributes: - return iter((self.special_attributes.lookup(name),)) + special_attr = self.special_attributes.lookup(name) + if not isinstance(special_attr, nodes.Unknown): + return iter((special_attr,)) return self._proxied.igetattr(name, context) def infer_call_result( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/astroid/interpreter/objectmodel.py new/astroid-4.1.1/astroid/interpreter/objectmodel.py --- old/astroid-4.1.0/astroid/interpreter/objectmodel.py 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/astroid/interpreter/objectmodel.py 2026-02-23 03:34:23.000000000 +0100 @@ -412,8 +412,12 @@ "Invalid class inferred", target=self, context=context ) - # For some reason func is a Node that the below - # code is not expecting + # The `func` can already be a Unbound or BoundMethod. If the former, make sure to + # wrap as a BoundMethod like we do below when constructing the function from scratch. + if isinstance(func, bases.UnboundMethod): + yield bases.BoundMethod(proxy=func, bound=cls) + return + if isinstance(func, bases.BoundMethod): yield func return @@ -629,7 +633,7 @@ return self._instance._proxied -class UnboundMethodModel(ObjectModel): +class UnboundMethodModel(FunctionModel): @property def attr___class__(self): # pylint: disable=import-outside-toplevel; circular import diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/astroid/objects.py new/astroid-4.1.1/astroid/objects.py --- old/astroid-4.1.0/astroid/objects.py 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/astroid/objects.py 2026-02-23 03:34:23.000000000 +0100 @@ -222,8 +222,10 @@ # Only if we haven't found any explicit overwrites for the # attribute we look it up in the special attributes if not found and name in self.special_attributes: - yield self.special_attributes.lookup(name) - return + special_attr = self.special_attributes.lookup(name) + if not isinstance(special_attr, node_classes.Unknown): + yield special_attr + return if not found: raise AttributeInferenceError(target=self, attribute=name, context=context) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/pyproject.toml new/astroid-4.1.1/pyproject.toml --- old/astroid-4.1.0/pyproject.toml 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/pyproject.toml 2026-02-23 03:34:23.000000000 +0100 @@ -1,6 +1,5 @@ [build-system] build-backend = "setuptools.build_meta" - requires = [ "setuptools>=77" ] [project] @@ -10,7 +9,6 @@ keywords = [ "abstract syntax tree", "python", "static code analysis" ] license = "LGPL-2.1-OR-later" license-files = [ "LICENSE", "CONTRIBUTORS.txt" ] - requires-python = ">=3.10.0" classifiers = [ "Development Status :: 6 - Mature", @@ -31,7 +29,6 @@ "Topic :: Software Development :: Testing", ] dynamic = [ "version" ] - dependencies = [ "typing-extensions>=4; python_version<'3.11'", ] @@ -40,24 +37,17 @@ urls."Docs" = "https://pylint.readthedocs.io/projects/astroid/en/latest/" urls."Source Code" = "https://github.com/pylint-dev/astroid" -[tool.setuptools.dynamic] -version = { attr = "astroid.__pkginfo__.__version__" } - -[tool.setuptools.package-dir] -"" = "." - -[tool.setuptools.packages.find] -include = [ "astroid*" ] +[tool.setuptools] +dynamic.version = { attr = "astroid.__pkginfo__.__version__" } +package-dir."" = "." +packages.find.include = [ "astroid*" ] [tool.ruff] target-version = "py310" - # ruff is less lenient than pylint and does not make any exceptions # (for docstrings, strings and comments in particular). line-length = 110 - extend-exclude = [ "tests/testdata/" ] - lint.select = [ "B", # bugbear "E", # pycodestyle @@ -86,11 +76,11 @@ [tool.pyproject-fmt] max_supported_python = "3.14" -[tool.pytest.ini_options] -addopts = '-m "not acceptance"' -python_files = [ "*test_*.py" ] -testpaths = [ "tests" ] -filterwarnings = "error" +[tool.pytest] +ini_options.addopts = '-m "not acceptance"' +ini_options.python_files = [ "*test_*.py" ] +ini_options.testpaths = [ "tests" ] +ini_options.filterwarnings = "error" [tool.mypy] python_version = "3.10" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/requirements_full.txt new/astroid-4.1.1/requirements_full.txt --- old/astroid-4.1.0/requirements_full.txt 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/requirements_full.txt 2026-02-23 03:34:23.000000000 +0100 @@ -7,7 +7,8 @@ python-dateutil PyQt6 regex -setuptools; python_version>="3.12" +# Pinned as 82.0.0 removes `pkg_resources` which we require for a test. +setuptools<82; python_version>="3.12" six urllib3>1,<2 typing_extensions>=4.4.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/tbump.toml new/astroid-4.1.1/tbump.toml --- old/astroid-4.1.0/tbump.toml 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/tbump.toml 2026-02-23 03:34:23.000000000 +0100 @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/astroid" [version] -current = "4.1.0" +current = "4.1.1" regex = ''' ^(?P<major>0|[1-9]\d*) \. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-4.1.0/tests/test_object_model.py new/astroid-4.1.1/tests/test_object_model.py --- old/astroid-4.1.0/tests/test_object_model.py 2026-02-09 03:17:33.000000000 +0100 +++ new/astroid-4.1.1/tests/test_object_model.py 2026-02-23 03:34:23.000000000 +0100 @@ -101,6 +101,7 @@ t.im_class #@ t.im_func #@ t.im_self #@ + t.__doc__ #@ """) assert isinstance(ast_nodes, list) cls = next(ast_nodes[0].infer()) @@ -121,6 +122,10 @@ self.assertEqual(func, next(ast_nodes[4].infer())) self.assertIsNone(next(ast_nodes[5].infer()).value) + doc = next(ast_nodes[6].infer()) + self.assertIsInstance(doc, nodes.Const) + self.assertIsNone(doc.value) + class ClassModelTest(unittest.TestCase): def test_priority_to_local_defined_values(self) -> None: @@ -943,3 +948,53 @@ hash_attr = cls.getattr("__hash__")[0] assert isinstance(hash_attr, nodes.Const) assert hash_attr.value is None + + +def test_unbound_method_object_dunders_return_uninferable() -> None: + """Test that ObjectModel-only dunders on unbound methods return Uninferable.""" + node = builder.extract_node(""" + class A: + def test(self): pass + A.test #@ + """) + unbound = next(node.infer()) + assert isinstance(unbound, bases.UnboundMethod) + + for dunder in ( + "__eq__", + "__ne__", + "__lt__", + "__le__", + "__gt__", + "__ge__", + "__repr__", + "__str__", + "__hash__", + ): + inferred = next(unbound.igetattr(dunder)) + assert inferred is util.Uninferable, f"{dunder} should be Uninferable" + + attrs = unbound.getattr(dunder) + assert attrs, f"{dunder} getattr should return results" + + +def test_super_special_attributes_fallback() -> None: + """Test that super() special attributes use the fallback path correctly.""" + doc_node = builder.extract_node(""" + class Base: + def method(self): + return super().__doc__ + Base().method() #@ + """) + doc_inferred = next(doc_node.infer()) + assert doc_inferred is util.Uninferable + + thisclass_node = builder.extract_node(""" + class Base: + def method(self): + return super().__thisclass__ + Base().method() #@ + """) + thisclass_inferred = next(thisclass_node.infer()) + assert isinstance(thisclass_inferred, nodes.ClassDef) + assert thisclass_inferred.name == "Base"
