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 2024-07-05 19:45:10 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-astroid (Old) and /work/SRC/openSUSE:Factory/.python-astroid.new.2080 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-astroid" Fri Jul 5 19:45:10 2024 rev:51 rq:1185391 version:3.2.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-astroid/python-astroid.changes 2024-03-25 21:07:53.704515238 +0100 +++ /work/SRC/openSUSE:Factory/.python-astroid.new.2080/python-astroid.changes 2024-07-05 19:49:45.983255744 +0200 @@ -1,0 +2,21 @@ +Sun Jun 30 22:03:18 UTC 2024 - Dirk Müller <dmuel...@suse.com> + +- update to 3.2.2: + * Improve inference for generic classes using the PEP 695 + syntax (Python 3.12). + * Fix ``RecursionError`` in ``infer_call_result()`` for certain + ``__call__`` methods. + * Add ``AstroidManager.prefer_stubs`` attribute to control the + astroid 3.2.0 feature that prefers stubs. + * ``igetattr()`` returns the last same-named function in a + class (instead of the first). This avoids false positives + in pylint with ``@overload``. + * Adds ``module_denylist`` to ``AstroidManager`` for modules to + be skipped during AST generation. Modules in this list will + cause an ``AstroidImportError`` to be raised when an AST + for them is requested. + * Make ``astroid.interpreter._import.util.is_namespace`` only + consider modules using a loader set to ``NamespaceLoader`` + or ``None`` as namespaces. + +------------------------------------------------------------------- Old: ---- astroid-3.1.0-gh.tar.gz New: ---- astroid-3.2.2-gh.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-astroid.spec ++++++ --- /var/tmp/diff_new_pack.giFOJu/_old 2024-07-05 19:49:49.987402971 +0200 +++ /var/tmp/diff_new_pack.giFOJu/_new 2024-07-05 19:49:49.987402971 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-astroid -Version: 3.1.0 +Version: 3.2.2 Release: 0 Summary: Representation of Python source as an AST for pylint License: LGPL-2.1-or-later ++++++ astroid-3.1.0-gh.tar.gz -> astroid-3.2.2-gh.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/.git-blame-ignore-revs new/astroid-3.2.2/.git-blame-ignore-revs --- old/astroid-3.1.0/.git-blame-ignore-revs 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/.git-blame-ignore-revs 2024-05-20 00:21:03.000000000 +0200 @@ -1 +1,6 @@ +# Initial formatting of astroid add5f7b8eba427de9d39caae864bbc6dc37ef980 +# Apply black on doc/conf.py +2dd9027054db541871713ef1cb1ae89513d05555 +# Black's 2024 style +396f01a15d1cb0351b33654acdeedde64f537a0c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/.github/workflows/ci.yaml new/astroid-3.2.2/.github/workflows/ci.yaml --- old/astroid-3.1.0/.github/workflows/ci.yaml 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/.github/workflows/ci.yaml 2024-05-20 00:21:03.000000000 +0200 @@ -24,10 +24,10 @@ timeout-minutes: 20 steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true @@ -39,7 +39,7 @@ 'requirements_full.txt', 'requirements_minimal.txt') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.0 + uses: actions/cache@v4.0.2 with: path: venv key: >- @@ -59,7 +59,7 @@ hashFiles('.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT - name: Restore pre-commit environment id: cache-precommit - uses: actions/cache@v4.0.0 + uses: actions/cache@v4.0.2 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- @@ -86,10 +86,10 @@ python-key: ${{ steps.generate-python-key.outputs.key }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: ${{ matrix.python-version }} check-latest: true @@ -106,7 +106,7 @@ 'requirements_full.txt', 'requirements_minimal.txt') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.0 + uses: actions/cache@v4.0.2 with: path: venv key: >- @@ -125,7 +125,7 @@ . venv/bin/activate pytest --cov - name: Upload coverage artifact - uses: actions/upload-artifact@v4.3.1 + uses: actions/upload-artifact@v4.3.3 with: name: coverage-linux-${{ matrix.python-version }} path: .coverage @@ -145,10 +145,10 @@ # Workaround to set correct temp directory on Windows # https://github.com/actions/virtual-environments/issues/712 - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: ${{ matrix.python-version }} check-latest: true @@ -160,7 +160,7 @@ 'requirements_full.txt', 'requirements_minimal.txt') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.0 + uses: actions/cache@v4.0.2 with: path: venv key: >- @@ -179,7 +179,7 @@ . venv\\Scripts\\activate pytest --cov - name: Upload coverage artifact - uses: actions/upload-artifact@v4.3.1 + uses: actions/upload-artifact@v4.3.3 with: name: coverage-windows-${{ matrix.python-version }} path: .coverage @@ -195,10 +195,10 @@ python-version: ["pypy3.8", "pypy3.10"] steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 - name: Set up Python ${{ matrix.python-version }} id: python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: ${{ matrix.python-version }} check-latest: true @@ -210,7 +210,7 @@ }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.0 + uses: actions/cache@v4.0.2 with: path: venv key: >- @@ -229,7 +229,7 @@ . venv/bin/activate pytest --cov - name: Upload coverage artifact - uses: actions/upload-artifact@v4.3.1 + uses: actions/upload-artifact@v4.3.3 with: name: coverage-pypy-${{ matrix.python-version }} path: .coverage @@ -241,22 +241,22 @@ needs: ["tests-linux", "tests-windows", "tests-pypy"] steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 - name: Set up Python 3.12 id: python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: "3.12" check-latest: true - name: Install dependencies run: pip install -U -r requirements_minimal.txt - name: Download all coverage artifacts - uses: actions/download-artifact@v4.1.2 + uses: actions/download-artifact@v4.1.7 - name: Combine Linux coverage results run: | coverage combine coverage-linux*/.coverage coverage xml -o coverage-linux.xml - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true @@ -267,7 +267,7 @@ run: | coverage combine coverage-windows*/.coverage coverage xml -o coverage-windows.xml - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true @@ -278,7 +278,7 @@ run: | coverage combine coverage-pypy*/.coverage coverage xml -o coverage-pypy.xml - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/.github/workflows/codeql-analysis.yml new/astroid-3.2.2/.github/workflows/codeql-analysis.yml --- old/astroid-3.1.0/.github/workflows/codeql-analysis.yml 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/.github/workflows/codeql-analysis.yml 2024-05-20 00:21:03.000000000 +0200 @@ -46,7 +46,7 @@ steps: - name: Checkout repository - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/.github/workflows/release-tests.yml new/astroid-3.2.2/.github/workflows/release-tests.yml --- old/astroid-3.1.0/.github/workflows/release-tests.yml 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/.github/workflows/release-tests.yml 2024-05-20 00:21:03.000000000 +0200 @@ -13,10 +13,10 @@ timeout-minutes: 5 steps: - name: Check out code from GitHub - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 - name: Set up Python 3.9 id: python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: # virtualenv 15.1.0 cannot be installed on Python 3.10+ python-version: 3.9 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/.github/workflows/release.yml new/astroid-3.2.2/.github/workflows/release.yml --- old/astroid-3.1.0/.github/workflows/release.yml 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/.github/workflows/release.yml 2024-05-20 00:21:03.000000000 +0200 @@ -20,10 +20,10 @@ url: https://pypi.org/project/astroid/ steps: - name: Check out code from Github - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4.1.5 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/.pre-commit-config.yaml new/astroid-3.2.2/.pre-commit-config.yaml --- old/astroid-3.1.0/.pre-commit-config.yaml 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/.pre-commit-config.yaml 2024-05-20 00:21:03.000000000 +0200 @@ -3,14 +3,14 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: trailing-whitespace exclude: .github/|tests/testdata - id: end-of-file-fixer exclude: tests/testdata - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.2.1" + rev: "v0.4.3" hooks: - id: ruff exclude: tests/testdata @@ -23,7 +23,7 @@ exclude: tests/testdata|setup.py types: [python] - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.2 hooks: - id: pyupgrade exclude: tests/testdata @@ -34,7 +34,7 @@ - id: black-disable-checker exclude: tests/test_nodes_lineno.py - repo: https://github.com/psf/black - rev: 24.2.0 + rev: 24.4.2 hooks: - id: black args: [--safe, --quiet] @@ -54,7 +54,7 @@ ] exclude: tests/testdata|conf.py - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.10.0 hooks: - id: mypy name: mypy @@ -66,7 +66,7 @@ additional_dependencies: ["types-typed-ast"] exclude: tests/testdata| # exclude everything, we're not ready - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.1.0 + rev: v4.0.0-alpha.8 hooks: - id: prettier args: [--prose-wrap=always, --print-width=88] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/CONTRIBUTORS.txt new/astroid-3.2.2/CONTRIBUTORS.txt --- old/astroid-3.1.0/CONTRIBUTORS.txt 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/CONTRIBUTORS.txt 2024-05-20 00:21:03.000000000 +0200 @@ -204,3 +204,5 @@ - carl - alain lefroy - Mark Gius +- Jérome Perrin <perrinjer...@gmail.com> +- Jamie Scott <ja...@jami.org.uk> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/ChangeLog new/astroid-3.2.2/ChangeLog --- old/astroid-3.1.0/ChangeLog 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/ChangeLog 2024-05-20 00:21:03.000000000 +0200 @@ -3,18 +3,69 @@ =================== -What's New in astroid 3.2.0? +What's New in astroid 3.3.0? ============================ Release date: TBA -What's New in astroid 3.1.1? +What's New in astroid 3.2.3? ============================ Release date: TBA +What's New in astroid 3.2.2? +============================ +Release date: 2024-05-20 + +* Improve inference for generic classes using the PEP 695 syntax (Python 3.12). + + Closes pylint-dev/pylint#9406 + + +What's New in astroid 3.2.1? +============================ +Release date: 2024-05-16 + +* Fix ``RecursionError`` in ``infer_call_result()`` for certain ``__call__`` methods. + + Closes pylint-dev/pylint#9139 + +* Add ``AstroidManager.prefer_stubs`` attribute to control the astroid 3.2.0 feature that prefers stubs. + + Refs pylint-dev/pylint#9626 + Refs pylint-dev/pylint#9623 + + +What's New in astroid 3.2.0? +============================ +Release date: 2024-05-07 + +* ``.pyi`` stub files are now preferred over ``.py`` files when resolving imports, (except for numpy). + + Closes pylint-dev/#9185 + +* ``igetattr()`` returns the last same-named function in a class (instead of + the first). This avoids false positives in pylint with ``@overload``. + + Closes #1015 + Refs pylint-dev/pylint#4696 + +* Adds ``module_denylist`` to ``AstroidManager`` for modules to be skipped during AST + generation. Modules in this list will cause an ``AstroidImportError`` to be raised + when an AST for them is requested. + + Refs pylint-dev/pylint#9442 + +* Make ``astroid.interpreter._import.util.is_namespace`` only consider modules + using a loader set to ``NamespaceLoader`` or ``None`` as namespaces. + This fixes a problem that ``six.moves`` brain was not effective if ``six.moves`` + was already imported. + + Closes #1107 + + What's New in astroid 3.1.0? ============================ Release date: 2024-02-23 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/__pkginfo__.py new/astroid-3.2.2/astroid/__pkginfo__.py --- old/astroid-3.1.0/astroid/__pkginfo__.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/__pkginfo__.py 2024-05-20 00:21:03.000000000 +0200 @@ -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__ = "3.1.0" +__version__ = "3.2.2" version = __version__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/bases.py new/astroid-3.2.2/astroid/bases.py --- old/astroid-3.1.0/astroid/bases.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/bases.py 2024-05-20 00:21:03.000000000 +0200 @@ -326,6 +326,11 @@ for node in self._proxied.igetattr("__call__", context): if isinstance(node, UninferableBase) or not node.callable(): continue + if isinstance(node, BaseInstance) and node._proxied is self._proxied: + inferred = True + yield node + # Prevent recursion. + continue for res in node.infer_call_result(caller, context): inferred = True yield res diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/brain/brain_typing.py new/astroid-3.2.2/astroid/brain/brain_typing.py --- old/astroid-3.1.0/astroid/brain/brain_typing.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/brain/brain_typing.py 2024-05-20 00:21:03.000000000 +0200 @@ -196,6 +196,20 @@ return node.infer(context=ctx) +def _looks_like_generic_class_pep695(node: ClassDef) -> bool: + """Check if class is using type parameter. Python 3.12+.""" + return len(node.type_params) > 0 + + +def infer_typing_generic_class_pep695( + node: ClassDef, ctx: context.InferenceContext | None = None +) -> Iterator[ClassDef]: + """Add __class_getitem__ for generic classes. Python 3.12+.""" + func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE) + node.locals["__class_getitem__"] = [func_to_add] + return iter([node]) + + def _looks_like_typedDict( # pylint: disable=invalid-name node: FunctionDef | ClassDef, ) -> bool: @@ -490,3 +504,8 @@ if PY312_PLUS: register_module_extender(manager, "typing", _typing_transform) + manager.register_transform( + ClassDef, + inference_tip(infer_typing_generic_class_pep695), + _looks_like_generic_class_pep695, + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/interpreter/_import/spec.py new/astroid-3.2.2/astroid/interpreter/_import/spec.py --- old/astroid-3.1.0/astroid/interpreter/_import/spec.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/interpreter/_import/spec.py 2024-05-20 00:21:03.000000000 +0200 @@ -15,7 +15,8 @@ import types import warnings import zipimport -from collections.abc import Iterator, Sequence +from collections.abc import Iterable, Iterator, Sequence +from functools import lru_cache from pathlib import Path from typing import Any, Literal, NamedTuple, Protocol @@ -160,9 +161,10 @@ pass submodule_path = sys.path + suffixes = (".py", ".pyi", importlib.machinery.BYTECODE_SUFFIXES[0]) for entry in submodule_path: package_directory = os.path.join(entry, modname) - for suffix in (".py", ".pyi", importlib.machinery.BYTECODE_SUFFIXES[0]): + for suffix in suffixes: package_file_name = "__init__" + suffix file_path = os.path.join(package_directory, package_file_name) if os.path.isfile(file_path): @@ -423,7 +425,7 @@ raise ImportError(f"No module named {'.'.join(module_parts)}") -def find_spec(modpath: list[str], path: Sequence[str] | None = None) -> ModuleSpec: +def find_spec(modpath: Iterable[str], path: Iterable[str] | None = None) -> ModuleSpec: """Find a spec for the given module. :type modpath: list or tuple @@ -440,10 +442,15 @@ :return: A module spec, which describes how the module was found and where. """ + return _find_spec(tuple(modpath), tuple(path) if path else None) + + +@lru_cache(maxsize=1024) +def _find_spec(module_path: tuple, path: tuple) -> ModuleSpec: _path = path or sys.path # Need a copy for not mutating the argument. - modpath = modpath[:] + modpath = list(module_path) submodule_path = None module_parts = modpath[:] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/interpreter/_import/util.py new/astroid-3.2.2/astroid/interpreter/_import/util.py --- old/astroid-3.1.0/astroid/interpreter/_import/util.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/interpreter/_import/util.py 2024-05-20 00:21:03.000000000 +0200 @@ -12,6 +12,11 @@ from astroid.const import IS_PYPY +if sys.version_info >= (3, 11): + from importlib.machinery import NamespaceLoader +else: + from importlib._bootstrap_external import _NamespaceLoader as NamespaceLoader + @lru_cache(maxsize=4096) def is_namespace(modname: str) -> bool: @@ -101,4 +106,7 @@ found_spec is not None and found_spec.submodule_search_locations is not None and found_spec.origin is None + and ( + found_spec.loader is None or isinstance(found_spec.loader, NamespaceLoader) + ) ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/manager.py new/astroid-3.2.2/astroid/manager.py --- old/astroid-3.1.0/astroid/manager.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/manager.py 2024-05-20 00:21:03.000000000 +0200 @@ -59,7 +59,9 @@ "optimize_ast": False, "max_inferable_values": 100, "extension_package_whitelist": set(), + "module_denylist": set(), "_transform": TransformVisitor(), + "prefer_stubs": False, } def __init__(self) -> None: @@ -70,7 +72,9 @@ self.extension_package_whitelist = AstroidManager.brain[ "extension_package_whitelist" ] + self.module_denylist = AstroidManager.brain["module_denylist"] self._transform = AstroidManager.brain["_transform"] + self.prefer_stubs = AstroidManager.brain["prefer_stubs"] @property def always_load_extensions(self) -> bool: @@ -109,6 +113,14 @@ def builtins_module(self) -> nodes.Module: return self.astroid_cache["builtins"] + @property + def prefer_stubs(self) -> bool: + return AstroidManager.brain["prefer_stubs"] + + @prefer_stubs.setter + def prefer_stubs(self, value: bool) -> None: + AstroidManager.brain["prefer_stubs"] = value + def visit_transforms(self, node: nodes.NodeNG) -> InferenceResult: """Visit the transforms and apply them to the given *node*.""" return self._transform.visit(node) @@ -134,7 +146,9 @@ # Call get_source_file() only after a cache miss, # since it calls os.path.exists(). try: - filepath = get_source_file(filepath, include_no_ext=True) + filepath = get_source_file( + filepath, include_no_ext=True, prefer_stubs=self.prefer_stubs + ) source = True except NoSourceFile: pass @@ -200,6 +214,8 @@ # importing a module with the same name as the file that is importing # we want to fallback on the import system to make sure we get the correct # module. + if modname in self.module_denylist: + raise AstroidImportError(f"Skipping ignored module {modname!r}") if modname in self.astroid_cache and use_cache: return self.astroid_cache[modname] if modname == "__main__": @@ -305,7 +321,6 @@ modname.split("."), context_file=contextfile ) except ImportError as e: - # pylint: disable-next=redefined-variable-type value = AstroidImportError( "Failed to import module {modname} with error:\n{error}.", modname=modname, @@ -402,8 +417,7 @@ # take care, on living object __module__ is regularly wrong :( modastroid = self.ast_from_module_name(modname) if klass is obj: - for inferred in modastroid.igetattr(name, context): - yield inferred + yield from modastroid.igetattr(name, context) else: for inferred in modastroid.igetattr(name, context): yield inferred.instantiate_class() @@ -440,6 +454,7 @@ # pylint: disable=import-outside-toplevel from astroid.brain.helpers import register_all_brains from astroid.inference_tip import clear_inference_tip_cache + from astroid.interpreter._import.spec import _find_spec from astroid.interpreter.objectmodel import ObjectModel from astroid.nodes._base_nodes import LookupMixIn from astroid.nodes.scoped_nodes import ClassDef @@ -457,6 +472,7 @@ util.is_namespace, ObjectModel.attributes, ClassDef._metaclass_lookup_attribute, + _find_spec, ): lru_cache.cache_clear() # type: ignore[attr-defined] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/modutils.py new/astroid-3.2.2/astroid/modutils.py --- old/astroid-3.1.0/astroid/modutils.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/modutils.py 2024-05-20 00:21:03.000000000 +0200 @@ -45,9 +45,11 @@ if sys.platform.startswith("win"): PY_SOURCE_EXTS = ("py", "pyw", "pyi") + PY_SOURCE_EXTS_STUBS_FIRST = ("pyi", "pyw", "py") PY_COMPILED_EXTS = ("dll", "pyd") else: PY_SOURCE_EXTS = ("py", "pyi") + PY_SOURCE_EXTS_STUBS_FIRST = ("pyi", "py") PY_COMPILED_EXTS = ("so",) @@ -484,7 +486,9 @@ return files -def get_source_file(filename: str, include_no_ext: bool = False) -> str: +def get_source_file( + filename: str, include_no_ext: bool = False, prefer_stubs: bool = False +) -> str: """Given a python module's file name return the matching source file name (the filename will be returned identically if it's already an absolute path to a python source file). @@ -499,7 +503,7 @@ base, orig_ext = os.path.splitext(filename) if orig_ext == ".pyi" and os.path.exists(f"{base}{orig_ext}"): return f"{base}{orig_ext}" - for ext in PY_SOURCE_EXTS: + for ext in PY_SOURCE_EXTS_STUBS_FIRST if prefer_stubs else PY_SOURCE_EXTS: source_path = f"{base}.{ext}" if os.path.exists(source_path): return source_path diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/nodes/scoped_nodes/scoped_nodes.py new/astroid-3.2.2/astroid/nodes/scoped_nodes/scoped_nodes.py --- old/astroid-3.1.0/astroid/nodes/scoped_nodes/scoped_nodes.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/nodes/scoped_nodes/scoped_nodes.py 2024-05-20 00:21:03.000000000 +0200 @@ -2194,7 +2194,10 @@ and name in AstroidManager().builtins_module ) if ( - any(node == base or base.parent_of(node) for base in self.bases) + any( + node == base or base.parent_of(node) and not self.type_params + for base in self.bases + ) or lookup_upper_frame ): # Handle the case where we have either a name @@ -2508,12 +2511,21 @@ # to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists) if len(attributes) > 1: first_attr, attributes = attributes[0], attributes[1:] - first_scope = first_attr.scope() + first_scope = first_attr.parent.scope() attributes = [first_attr] + [ attr for attr in attributes if attr.parent and attr.parent.scope() == first_scope ] + functions = [attr for attr in attributes if isinstance(attr, FunctionDef)] + if functions: + # Prefer only the last function, unless a property is involved. + last_function = functions[-1] + attributes = [ + a + for a in attributes + if a not in functions or a is last_function or bases._is_property(a) + ] for inferred in bases._infer_stmts(attributes, context, frame=self): # yield Uninferable object instead of descriptors when necessary diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/protocols.py new/astroid-3.2.2/astroid/protocols.py --- old/astroid-3.1.0/astroid/protocols.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/protocols.py 2024-05-20 00:21:03.000000000 +0200 @@ -924,8 +924,7 @@ context: InferenceContext | None = None, assign_path: None = None, ) -> Generator[nodes.NodeNG, None, None]: - """Return empty generator (return -> raises StopIteration) so inferred value - is Uninferable. + """Hack. Return any Node so inference doesn't fail + when evaluating __class_getitem__. Revert if it's causing issues. """ - return - yield + yield nodes.Const(None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/astroid/test_utils.py new/astroid-3.2.2/astroid/test_utils.py --- old/astroid-3.1.0/astroid/test_utils.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/astroid/test_utils.py 2024-05-20 00:21:03.000000000 +0200 @@ -74,4 +74,5 @@ m._mod_file_cache = {} m._transform = transforms.TransformVisitor() m.extension_package_whitelist = set() + m.module_denylist = set() return m diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/doc/conf.py new/astroid-3.2.2/doc/conf.py --- old/astroid-3.1.0/doc/conf.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/doc/conf.py 2024-05-20 00:21:03.000000000 +0200 @@ -9,7 +9,7 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath("../../")) +sys.path.insert(0, os.path.abspath("..")) # -- General configuration ----------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/doc/requirements.txt new/astroid-3.2.2/doc/requirements.txt --- old/astroid-3.1.0/doc/requirements.txt 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/doc/requirements.txt 2024-05-20 00:21:03.000000000 +0200 @@ -1,3 +1,3 @@ -e . -sphinx~=7.2 -furo==2024.1.29 +sphinx~=7.3 +furo==2024.4.27 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/requirements_full.txt new/astroid-3.2.2/requirements_full.txt --- old/astroid-3.1.0/requirements_full.txt 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/requirements_full.txt 2024-05-20 00:21:03.000000000 +0200 @@ -4,7 +4,7 @@ # Packages used to run additional tests attrs nose -numpy>=1.17.0; python_version<"3.11" +numpy>=1.17.0; python_version<"3.12" python-dateutil PyQt6 regex diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/requirements_minimal.txt new/astroid-3.2.2/requirements_minimal.txt --- old/astroid-3.1.0/requirements_minimal.txt 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/requirements_minimal.txt 2024-05-20 00:21:03.000000000 +0200 @@ -3,6 +3,6 @@ tbump~=6.11 # Tools used to run tests -coverage~=7.3 +coverage~=7.5 pytest -pytest-cov~=4.1 +pytest-cov~=5.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tbump.toml new/astroid-3.2.2/tbump.toml --- old/astroid-3.1.0/tbump.toml 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tbump.toml 2024-05-20 00:21:03.000000000 +0200 @@ -1,7 +1,7 @@ github_url = "https://github.com/pylint-dev/astroid" [version] -current = "3.1.0" +current = "3.2.2" regex = ''' ^(?P<major>0|[1-9]\d*) \. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/brain/test_attr.py new/astroid-3.2.2/tests/brain/test_attr.py --- old/astroid-3.1.0/tests/brain/test_attr.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/brain/test_attr.py 2024-05-20 00:21:03.000000000 +0200 @@ -90,7 +90,7 @@ def test_attrs_transform(self) -> None: """Test brain for decorators of the 'attrs' package. - Package added support for 'attrs' a long side 'attr' in v21.3.0. + Package added support for 'attrs' alongside 'attr' in v21.3.0. See: https://github.com/python-attrs/attrs/releases/tag/21.3.0 """ module = astroid.parse( @@ -153,36 +153,12 @@ @frozen class Legs: d = attrs.field(default=attrs.Factory(dict)) - - m = Legs(d=1) - m.d['answer'] = 42 - - @define - class FooBar: - d = attrs.field(default=attrs.Factory(dict)) - - n = FooBar(d=1) - n.d['answer'] = 42 - - @mutable - class BarFoo: - d = attrs.field(default=attrs.Factory(dict)) - - o = BarFoo(d=1) - o.d['answer'] = 42 - - @my_mutable - class FooFoo: - d = attrs.field(default=attrs.Factory(dict)) - - p = FooFoo(d=1) - p.d['answer'] = 42 """ ) - for name in ("f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"): + for name in ("f", "g", "h", "i", "j", "k", "l"): should_be_unknown = next(module.getattr(name)[0].infer()).getattr("d")[0] - self.assertIsInstance(should_be_unknown, astroid.Unknown) + self.assertIsInstance(should_be_unknown, astroid.Unknown, name) def test_special_attributes(self) -> None: """Make sure special attrs attributes exist""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/brain/test_brain.py new/astroid-3.2.2/tests/brain/test_brain.py --- old/astroid-3.1.0/tests/brain/test_brain.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/brain/test_brain.py 2024-05-20 00:21:03.000000000 +0200 @@ -627,6 +627,24 @@ assert isinstance(inferred, nodes.ClassDef) assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef) + @test_utils.require_version(minver="3.12") + def test_typing_generic_subscriptable_pep695(self): + """Test class using type parameters is subscriptable with __class_getitem__ (added in PY312)""" + node = builder.extract_node( + """ + class Foo[T]: ... + class Bar[T](Foo[T]): ... + """ + ) + inferred = next(node.infer()) + assert isinstance(inferred, nodes.ClassDef) + assert inferred.name == "Bar" + assert isinstance(inferred.getattr("__class_getitem__")[0], nodes.FunctionDef) + ancestors = list(inferred.ancestors()) + assert len(ancestors) == 2 + assert ancestors[0].name == "Foo" + assert ancestors[1].name == "object" + @test_utils.require_version(minver="3.9") def test_typing_annotated_subscriptable(self): """Test typing.Annotated is subscriptable with __class_getitem__""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/brain/test_six.py new/astroid-3.2.2/tests/brain/test_six.py --- old/astroid-3.1.0/tests/brain/test_six.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/brain/test_six.py 2024-05-20 00:21:03.000000000 +0200 @@ -29,6 +29,8 @@ six.moves.urllib_parse #@ six.moves.urllib_error #@ six.moves.urllib.request #@ + from six.moves import StringIO + StringIO #@ """ ) assert isinstance(ast_nodes, list) @@ -64,6 +66,18 @@ self.assertIsInstance(urlretrieve, nodes.FunctionDef) self.assertEqual(urlretrieve.qname(), "urllib.request.urlretrieve") + StringIO = next(ast_nodes[4].infer()) + self.assertIsInstance(StringIO, nodes.ClassDef) + self.assertEqual(StringIO.qname(), "_io.StringIO") + self.assertTrue(StringIO.callable()) + + def test_attribute_access_with_six_moves_imported(self) -> None: + astroid.MANAGER.clear_cache() + astroid.MANAGER._mod_file_cache.clear() + import six.moves # type: ignore[import] # pylint: disable=import-outside-toplevel,unused-import,redefined-outer-name + + self.test_attribute_access() + def test_from_imports(self) -> None: ast_node = builder.extract_node( """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/resources.py new/astroid-3.2.2/tests/resources.py --- old/astroid-3.1.0/tests/resources.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/resources.py 2024-05-20 00:21:03.000000000 +0200 @@ -9,7 +9,6 @@ from pathlib import Path from astroid import builder -from astroid.manager import AstroidManager from astroid.nodes.scoped_nodes import Module DATA_DIR = Path("testdata") / "python3" @@ -34,28 +33,3 @@ for key in list(sys.path_importer_cache): if key.startswith(datadir): del sys.path_importer_cache[key] - - -class AstroidCacheSetupMixin: - """Mixin for handling test isolation issues with the astroid cache. - - When clearing the astroid cache, some tests fail due to - cache inconsistencies, where some objects had a different - builtins object referenced. - This saves the builtins module and TransformVisitor and - replaces them after the tests finish. - The builtins module is special, since some of the - transforms for a couple of its objects (str, bytes etc) - are executed only once, so astroid_bootstrapping will be - useless for retrieving the original builtins module. - """ - - @classmethod - def setup_class(cls): - cls._builtins = AstroidManager().astroid_cache.get("builtins") - cls._transforms = AstroidManager.brain["_transform"] - - @classmethod - def teardown_class(cls): - AstroidManager().astroid_cache["builtins"] = cls._builtins - AstroidManager.brain["_transform"] = cls._transforms diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/test_inference.py new/astroid-3.2.2/tests/test_inference.py --- old/astroid-3.1.0/tests/test_inference.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/test_inference.py 2024-05-20 00:21:03.000000000 +0200 @@ -30,7 +30,7 @@ ) from astroid import decorators as decoratorsmod from astroid.arguments import CallSite -from astroid.bases import BoundMethod, Instance, UnboundMethod, UnionType +from astroid.bases import BoundMethod, Generator, Instance, UnboundMethod, UnionType from astroid.builder import AstroidBuilder, _extract_single_node, extract_node, parse from astroid.const import IS_PYPY, PY39_PLUS, PY310_PLUS, PY312_PLUS from astroid.context import CallContext, InferenceContext @@ -4090,6 +4090,18 @@ inferred = next(node.infer()) self.assertRaises(InferenceError, next, inferred.infer_call_result(node)) + def test_infer_call_result_same_proxied_class(self) -> None: + node = extract_node( + """ + class A: + __call__ = A() + A() #@ + """ + ) + inferred = next(node.infer()) + fully_evaluated_inference_results = list(inferred.infer_call_result(node)) + assert fully_evaluated_inference_results[0].name == "A" + def test_infer_call_result_with_metaclass(self) -> None: node = extract_node("def with_metaclass(meta, *bases): return 42") inferred = next(node.infer_call_result(caller=node)) @@ -4321,6 +4333,53 @@ assert isinstance(inferred, nodes.Const) assert inferred.value == 123 + def test_infer_method_empty_body(self) -> None: + # https://github.com/PyCQA/astroid/issues/1015 + node = extract_node( + """ + class A: + def foo(self): ... + + A().foo() #@ + """ + ) + inferred = next(node.infer()) + assert isinstance(inferred, nodes.Const) + assert inferred.value is None + + def test_infer_method_overload(self) -> None: + # https://github.com/PyCQA/astroid/issues/1015 + node = extract_node( + """ + class A: + def foo(self): ... + + def foo(self): + yield + + A().foo() #@ + """ + ) + inferred = list(node.infer()) + assert len(inferred) == 1 + assert isinstance(inferred[0], Generator) + + def test_infer_function_under_if(self) -> None: + node = extract_node( + """ + if 1 in [1]: + def func(): + return 42 + else: + def func(): + return False + + func() #@ + """ + ) + inferred = list(node.inferred()) + assert [const.value for const in inferred] == [42, False] + def test_delayed_attributes_without_slots(self) -> None: ast_node = extract_node( """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/test_manager.py new/astroid-3.2.2/tests/test_manager.py --- old/astroid-3.1.0/tests/test_manager.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/test_manager.py 2024-05-20 00:21:03.000000000 +0200 @@ -36,12 +36,11 @@ return obj.__file__ -class AstroidManagerTest( - resources.SysPathSetup, resources.AstroidCacheSetupMixin, unittest.TestCase -): +class AstroidManagerTest(resources.SysPathSetup, unittest.TestCase): def setUp(self) -> None: super().setUp() self.manager = test_utils.brainless_manager() + self.manager.clear_cache() def test_ast_from_file(self) -> None: filepath = unittest.__file__ @@ -383,10 +382,18 @@ with pytest.raises(AstroidBuildingError): self.manager.ast_from_module_name(None) + def test_denied_modules_raise(self) -> None: + self.manager.module_denylist.add("random") + with pytest.raises(AstroidImportError, match="random"): + self.manager.ast_from_module_name("random") + # and module not in the deny list shouldn't raise + self.manager.ast_from_module_name("math") -class IsolatedAstroidManagerTest(resources.AstroidCacheSetupMixin, unittest.TestCase): + +class IsolatedAstroidManagerTest(unittest.TestCase): def test_no_user_warning(self): mgr = manager.AstroidManager() + self.addCleanup(mgr.clear_cache) with warnings.catch_warnings(): warnings.filterwarnings("error", category=UserWarning) mgr.ast_from_module_name("setuptools") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/test_modutils.py new/astroid-3.2.2/tests/test_modutils.py --- old/astroid-3.1.0/tests/test_modutils.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/test_modutils.py 2024-05-20 00:21:03.000000000 +0200 @@ -41,6 +41,7 @@ package = "mypypa" def tearDown(self) -> None: + astroid.MANAGER.clear_cache() for k in list(sys.path_importer_cache): if "MyPyPa" in k: del sys.path_importer_cache[k] @@ -290,11 +291,19 @@ def test_raise(self) -> None: self.assertRaises(modutils.NoSourceFile, modutils.get_source_file, "whatever") - def test_(self) -> None: + def test_pyi(self) -> None: package = resources.find("pyi_data") module = os.path.join(package, "__init__.pyi") self.assertEqual(modutils.get_source_file(module), os.path.normpath(module)) + def test_pyi_preferred(self) -> None: + package = resources.find("pyi_data/find_test") + module = os.path.join(package, "__init__.py") + self.assertEqual( + modutils.get_source_file(module, prefer_stubs=True), + os.path.normpath(module) + "i", + ) + class IsStandardModuleTest(resources.SysPathSetup, unittest.TestCase): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/test_protocols.py new/astroid-3.2.2/tests/test_protocols.py --- old/astroid-3.1.0/tests/test_protocols.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/test_protocols.py 2024-05-20 00:21:03.000000000 +0200 @@ -425,7 +425,10 @@ assign_stmts = extract_node("type Point[T] = tuple[float, float]") type_var: nodes.TypeVar = assign_stmts.type_params[0] assigned = next(type_var.name.assigned_stmts()) - assert assigned is Uninferable + # Hack so inference doesn't fail when evaluating __class_getitem__ + # Revert if it's causing issues. + assert isinstance(assigned, nodes.Const) + assert assigned.value is None @staticmethod def test_assigned_stmts_type_var_tuple(): @@ -433,7 +436,10 @@ assign_stmts = extract_node("type Alias[*Ts] = tuple[*Ts]") type_var_tuple: nodes.TypeVarTuple = assign_stmts.type_params[0] assigned = next(type_var_tuple.name.assigned_stmts()) - assert assigned is Uninferable + # Hack so inference doesn't fail when evaluating __class_getitem__ + # Revert if it's causing issues. + assert isinstance(assigned, nodes.Const) + assert assigned.value is None @staticmethod def test_assigned_stmts_param_spec(): @@ -441,4 +447,7 @@ assign_stmts = extract_node("type Alias[**P] = Callable[P, int]") param_spec: nodes.ParamSpec = assign_stmts.type_params[0] assigned = next(param_spec.name.assigned_stmts()) - assert assigned is Uninferable + # Hack so inference doesn't fail when evaluating __class_getitem__ + # Revert if it's causing issues. + assert isinstance(assigned, nodes.Const) + assert assigned.value is None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/astroid-3.1.0/tests/test_regrtest.py new/astroid-3.2.2/tests/test_regrtest.py --- old/astroid-3.1.0/tests/test_regrtest.py 2024-02-23 17:01:03.000000000 +0100 +++ new/astroid-3.2.2/tests/test_regrtest.py 2024-05-20 00:21:03.000000000 +0200 @@ -26,10 +26,11 @@ HAS_NUMPY = True -class NonRegressionTests(resources.AstroidCacheSetupMixin, unittest.TestCase): +class NonRegressionTests(unittest.TestCase): def setUp(self) -> None: sys.path.insert(0, resources.find("data")) MANAGER.always_load_extensions = True + self.addCleanup(MANAGER.clear_cache) def tearDown(self) -> None: MANAGER.always_load_extensions = False