diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 087471ba79..bd6728aa4c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,7 +10,7 @@ on: env: CACHE_VERSION: 3 KEY_PREFIX: venv - DEFAULT_PYTHON: "3.12" + DEFAULT_PYTHON: "3.13" PRE_COMMIT_CACHE: ~/.cache/pre-commit concurrency: @@ -24,7 +24,7 @@ jobs: timeout-minutes: 20 steps: - name: Check out code from GitHub - uses: actions/checkout@v4.2.0 + uses: actions/checkout@v4.2.1 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.2.0 @@ -39,7 +39,7 @@ jobs: 'requirements_full.txt', 'requirements_minimal.txt') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.1.0 with: path: venv key: >- @@ -59,7 +59,7 @@ jobs: hashFiles('.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT - name: Restore pre-commit environment id: cache-precommit - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.1.0 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- @@ -81,12 +81,12 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.9, "3.10", "3.11", "3.12", "3.13-dev"] + python-version: [3.9, "3.10", "3.11", "3.12", "3.13"] outputs: python-key: ${{ steps.generate-python-key.outputs.key }} steps: - name: Check out code from GitHub - uses: actions/checkout@v4.2.0 + uses: actions/checkout@v4.2.1 - name: Set up Python ${{ matrix.python-version }} id: python uses: actions/setup-python@v5.2.0 @@ -106,7 +106,7 @@ jobs: 'requirements_full.txt', 'requirements_minimal.txt') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.1.0 with: path: venv key: >- @@ -125,7 +125,7 @@ jobs: . venv/bin/activate pytest --cov - name: Upload coverage artifact - uses: actions/upload-artifact@v4.4.0 + uses: actions/upload-artifact@v4.4.1 with: name: coverage-linux-${{ matrix.python-version }} path: .coverage @@ -139,14 +139,14 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.9, "3.10", "3.11", "3.12", "3.13-dev"] + python-version: [3.9, "3.10", "3.11", "3.12", "3.13"] steps: - name: Set temp directory run: echo "TEMP=$env:USERPROFILE\AppData\Local\Temp" >> $env:GITHUB_ENV # 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.2.0 + uses: actions/checkout@v4.2.1 - name: Set up Python ${{ matrix.python-version }} id: python uses: actions/setup-python@v5.2.0 @@ -161,7 +161,7 @@ jobs: 'requirements_full.txt', 'requirements_minimal.txt') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.1.0 with: path: venv key: >- @@ -180,7 +180,7 @@ jobs: . venv\\Scripts\\activate pytest --cov - name: Upload coverage artifact - uses: actions/upload-artifact@v4.4.0 + uses: actions/upload-artifact@v4.4.1 with: name: coverage-windows-${{ matrix.python-version }} path: .coverage @@ -197,7 +197,7 @@ jobs: python-version: ["pypy3.9", "pypy3.10"] steps: - name: Check out code from GitHub - uses: actions/checkout@v4.2.0 + uses: actions/checkout@v4.2.1 - name: Set up Python ${{ matrix.python-version }} id: python uses: actions/setup-python@v5.2.0 @@ -212,7 +212,7 @@ jobs: }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.1.0 with: path: venv key: >- @@ -231,7 +231,7 @@ jobs: . venv/bin/activate pytest --cov - name: Upload coverage artifact - uses: actions/upload-artifact@v4.4.0 + uses: actions/upload-artifact@v4.4.1 with: name: coverage-pypy-${{ matrix.python-version }} path: .coverage @@ -244,12 +244,12 @@ jobs: needs: ["tests-linux", "tests-windows", "tests-pypy"] steps: - name: Check out code from GitHub - uses: actions/checkout@v4.2.0 - - name: Set up Python 3.12 + uses: actions/checkout@v4.2.1 + - name: Set up Python 3.13 id: python uses: actions/setup-python@v5.2.0 with: - python-version: "3.12" + python-version: "3.13" check-latest: true - name: Install dependencies run: pip install -U -r requirements_minimal.txt diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8402c938a9..331166e2fd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4.2.0 + uses: actions/checkout@v4.2.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/release-tests.yml b/.github/workflows/release-tests.yml index 7538d6039e..9dd4e78919 100644 --- a/.github/workflows/release-tests.yml +++ b/.github/workflows/release-tests.yml @@ -13,7 +13,7 @@ jobs: timeout-minutes: 5 steps: - name: Check out code from GitHub - uses: actions/checkout@v4.2.0 + uses: actions/checkout@v4.2.1 - name: Set up Python 3.9 id: python uses: actions/setup-python@v5.2.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 862436c781..3300397bee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: - published env: - DEFAULT_PYTHON: "3.12" + DEFAULT_PYTHON: "3.13" permissions: contents: read @@ -20,7 +20,7 @@ jobs: url: https://pypi.org/project/astroid/ steps: - name: Check out code from Github - uses: actions/checkout@v4.2.0 + uses: actions/checkout@v4.2.1 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.2.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6d939814aa..e1d8292092 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,14 +3,14 @@ ci: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.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.6.8" + rev: "v0.6.9" hooks: - id: ruff args: ["--fix"] @@ -33,7 +33,7 @@ repos: - id: black-disable-checker exclude: tests/test_nodes_lineno.py - repo: https://github.com/psf/black - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black args: [--safe, --quiet] diff --git a/ChangeLog b/ChangeLog index 5e7a698d71..2d9266ee85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,15 +7,20 @@ What's New in astroid 4.0.0? ============================ Release date: TBA +* Removed internal functions ``infer_numpy_member``, ``name_looks_like_numpy_member``, and + ``attribute_looks_like_numpy_member`` from ``astroid.brain.brain_numpy_utils``. + +* Fix crashes with large positive and negative list multipliers. + + Closes #2521 + Closes #2523 What's New in astroid 3.3.6? ============================ Release date: TBA -* Control setting local nodes outside of the supposed local's constructor. - - Closes pylint-dev/astroid/issues/1490 +* Fix precedence of `path` arg in `modpath_from_file_with_callback` to be higher than `sys.path` What's New in astroid 3.3.5? diff --git a/astroid/brain/brain_argparse.py b/astroid/brain/brain_argparse.py index 2e6b742c38..f7ba5f92e7 100644 --- a/astroid/brain/brain_argparse.py +++ b/astroid/brain/brain_argparse.py @@ -21,7 +21,7 @@ def infer_namespace(node, context: InferenceContext | None = None): "Namespace", lineno=node.lineno, col_offset=node.col_offset, - parent=AstroidManager().adhoc_module, # this class is not real + parent=AstroidManager().synthetic_root, # this class is not real end_lineno=node.end_lineno, end_col_offset=node.end_col_offset, ) diff --git a/astroid/brain/brain_builtin_inference.py b/astroid/brain/brain_builtin_inference.py index 821261933b..7d11f8815a 100644 --- a/astroid/brain/brain_builtin_inference.py +++ b/astroid/brain/brain_builtin_inference.py @@ -165,7 +165,7 @@ def _extend_builtins(class_transforms): def on_bootstrap(): """Called by astroid_bootstrapping().""" - AstroidManager().cache_module(build_module("__astroid_adhoc")) + AstroidManager().cache_module(build_module("__astroid_synthetic")) _extend_builtins( { @@ -653,7 +653,7 @@ def infer_property( # node.frame. It's somewhere in the builtins module, but we are special # casing it for each "property()" call, so we are making up the # definition on the spot, ad-hoc. - parent=AstroidManager().adhoc_module, + parent=AstroidManager().synthetic_root, ) prop_func.postinit( body=[], diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py index b39d177b10..34f0501bc3 100644 --- a/astroid/brain/brain_namedtuple_enum.py +++ b/astroid/brain/brain_namedtuple_enum.py @@ -74,7 +74,9 @@ def _extract_namedtuple_arg_or_keyword( # pylint: disable=inconsistent-return-s def infer_func_form( node: nodes.Call, - base_type: list[nodes.NodeNG], + base_type: nodes.NodeNG, + *, + parent: nodes.NodeNG, context: InferenceContext | None = None, enum: bool = False, ) -> tuple[nodes.ClassDef, str, list[str]]: @@ -147,15 +149,10 @@ def infer_func_form( col_offset=node.col_offset, end_lineno=node.end_lineno, end_col_offset=node.end_col_offset, - parent=nodes.Unknown(), + parent=parent, ) - # A typical ClassDef automatically adds its name to the parent scope, - # but doing so causes problems, so defer setting parent until after init - # see: https://github.com/pylint-dev/pylint/issues/5982 - class_node.parent = node.parent class_node.postinit( - # set base class=tuple - bases=base_type, + bases=[base_type], body=[], decorators=None, ) @@ -195,25 +192,16 @@ def infer_named_tuple( node: nodes.Call, context: InferenceContext | None = None ) -> Iterator[nodes.ClassDef]: """Specific inference function for namedtuple Call node.""" - tuple_base_name: list[nodes.NodeNG] = [ - nodes.Name( - name="tuple", - parent=node.root(), - lineno=0, - col_offset=0, - end_lineno=None, - end_col_offset=None, - ) - ] + tuple_base: nodes.Name = _extract_single_node("tuple") class_node, name, attributes = infer_func_form( - node, tuple_base_name, context=context + node, tuple_base, parent=AstroidManager().synthetic_root, context=context ) + call_site = arguments.CallSite.from_call(node, context=context) - node = extract_node("import collections; collections.namedtuple") - try: - func = next(node.infer()) - except StopIteration as e: - raise InferenceError(node=node) from e + func = util.safe_infer( + _extract_single_node("import collections; collections.namedtuple") + ) + assert isinstance(func, nodes.NodeNG) try: rename = next( call_site.infer_argument(func, "rename", context or InferenceContext()) @@ -365,7 +353,17 @@ def value(self): __members__ = [''] """ ) - class_node = infer_func_form(node, [enum_meta], context=context, enum=True)[0] + + # FIXME arguably, the base here shouldn't be the EnumMeta class definition + # itself, but a reference (Name) to it. Otherwise, the invariant that all + # children of a node have that node as their parent is broken. + class_node = infer_func_form( + node, + enum_meta, + parent=AstroidManager().synthetic_root, + context=context, + enum=True, + )[0] return iter([class_node.instantiate_class()]) diff --git a/astroid/brain/brain_numpy_utils.py b/astroid/brain/brain_numpy_utils.py index 8ce7144372..a3d4ed5324 100644 --- a/astroid/brain/brain_numpy_utils.py +++ b/astroid/brain/brain_numpy_utils.py @@ -48,12 +48,6 @@ def infer_numpy_attribute( return extracted_node.infer(context=context) -# TODO: Deprecate and remove this function -def infer_numpy_member(src, node, context: InferenceContext | None = None): - node = extract_node(src) - return node.infer(context=context) - - def _is_a_numpy_module(node: Name) -> bool: """ Returns True if the node is a representation of a numpy module. @@ -96,25 +90,3 @@ def attribute_name_looks_like_numpy_member( and isinstance(node.expr, Name) and _is_a_numpy_module(node.expr) ) - - -# TODO: Deprecate and remove this function -def name_looks_like_numpy_member(member_name: str, node: Name) -> bool: - """ - Returns True if the Name is a member of numpy whose - name is member_name. - """ - return node.name == member_name and node.root().name.startswith("numpy") - - -# TODO: Deprecate and remove this function -def attribute_looks_like_numpy_member(member_name: str, node: Attribute) -> bool: - """ - Returns True if the Attribute is a member of numpy whose - name is member_name. - """ - return ( - node.attrname == member_name - and isinstance(node.expr, Name) - and _is_a_numpy_module(node.expr) - ) diff --git a/astroid/manager.py b/astroid/manager.py index 3ed9ca39c4..aed586c04b 100644 --- a/astroid/manager.py +++ b/astroid/manager.py @@ -116,8 +116,8 @@ def builtins_module(self) -> nodes.Module: return self.astroid_cache["builtins"] @property - def adhoc_module(self) -> nodes.Module: - return self.astroid_cache["__astroid_adhoc"] + def synthetic_root(self) -> nodes.Module: + return self.astroid_cache["__astroid_synthetic"] @property def prefer_stubs(self) -> bool: diff --git a/astroid/modutils.py b/astroid/modutils.py index bf84b3b933..957be61cbe 100644 --- a/astroid/modutils.py +++ b/astroid/modutils.py @@ -278,7 +278,7 @@ def modpath_from_file_with_callback( filename = os.path.expanduser(_path_from_filename(filename)) paths_to_check = sys.path.copy() if path: - paths_to_check += path + paths_to_check = path + paths_to_check for pathname in itertools.chain( paths_to_check, map(_cache_normalize_path, paths_to_check) ): diff --git a/astroid/nodes/node_ng.py b/astroid/nodes/node_ng.py index 99120bb65f..c6a044f5a4 100644 --- a/astroid/nodes/node_ng.py +++ b/astroid/nodes/node_ng.py @@ -21,7 +21,7 @@ overload, ) -from astroid import util +from astroid import nodes, util from astroid.context import InferenceContext from astroid.exceptions import ( AstroidError, @@ -43,7 +43,6 @@ if TYPE_CHECKING: - from astroid import nodes from astroid.nodes import _base_nodes @@ -332,11 +331,13 @@ def root(self) -> nodes.Module: :returns: The root node. """ if not (parent := self.parent): - return self # type: ignore[return-value] # Only 'Module' does not have a parent node. + assert isinstance(self, nodes.Module) + return self while parent.parent: parent = parent.parent - return parent # type: ignore[return-value] # Only 'Module' does not have a parent node. + assert isinstance(parent, nodes.Module) + return parent def child_sequence(self, child): """Search for the sequence that contains this child. diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index 176cb8dbd4..0a80eb8aac 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -44,7 +44,6 @@ Arguments, Const, NodeNG, - Unknown, _base_nodes, const_factory, node_classes, @@ -1603,7 +1602,7 @@ def infer_call_result( col_offset=0, end_lineno=0, end_col_offset=0, - parent=AstroidManager().adhoc_module, + parent=AstroidManager().synthetic_root, ) new_class.hide = True new_class.postinit( @@ -2037,7 +2036,7 @@ def _infer_type_call(self, caller, context): col_offset=0, end_lineno=0, end_col_offset=0, - parent=Unknown(), + parent=caller.parent, ) # Get the bases of the class. @@ -2071,7 +2070,6 @@ def _infer_type_call(self, caller, context): if isinstance(attr, node_classes.Const) and isinstance(attr.value, str): result.locals[attr.value] = [value] - result.parent = caller.parent return result def infer_call_result( diff --git a/astroid/objects.py b/astroid/objects.py index 4a6b58d749..829cbf1900 100644 --- a/astroid/objects.py +++ b/astroid/objects.py @@ -279,7 +279,7 @@ def __init__(self, call, name=None, lineno=None, col_offset=None, parent=None): name, lineno=lineno, col_offset=col_offset, - parent=AstroidManager().adhoc_module, + parent=AstroidManager().synthetic_root, end_col_offset=0, end_lineno=0, ) diff --git a/astroid/protocols.py b/astroid/protocols.py index 0bac32a29a..8a837b86b7 100644 --- a/astroid/protocols.py +++ b/astroid/protocols.py @@ -142,7 +142,10 @@ def _multiply_seq_by_int( context: InferenceContext, ) -> _TupleListNodeT: node = self.__class__(parent=opnode) - if value > 1e8: + if value <= 0 or not self.elts: + node.elts = [] + return node + if len(self.elts) * value > 1e8: node.elts = [util.Uninferable] return node filtered_elts = ( diff --git a/astroid/raw_building.py b/astroid/raw_building.py index b4e812f386..c1f7ec158d 100644 --- a/astroid/raw_building.py +++ b/astroid/raw_building.py @@ -136,6 +136,7 @@ def build_class( def build_function( name: str, + parent: nodes.NodeNG, args: list[str] | None = None, posonlyargs: list[str] | None = None, defaults: list[Any] | None = None, @@ -149,7 +150,7 @@ def build_function( name, lineno=0, col_offset=0, - parent=node_classes.Unknown(), + parent=parent, end_col_offset=0, end_lineno=0, ) @@ -321,6 +322,7 @@ def object_build_function( return build_function( getattr(member, "__name__", ""), + node, args, posonlyargs, defaults, @@ -344,7 +346,7 @@ def object_build_methoddescriptor( """create astroid for a living method descriptor object""" # FIXME get arguments ? name = getattr(member, "__name__", "") - func = build_function(name, doc=member.__doc__) + func = build_function(name, node, doc=member.__doc__) _add_dunder_class(func, node, member) return func @@ -668,9 +670,8 @@ def _astroid_bootstrapping() -> None: col_offset=0, end_lineno=0, end_col_offset=0, - parent=nodes.Unknown(), + parent=astroid_builtin, ) - _UnionTypeType.parent = astroid_builtin union_type_doc_node = ( nodes.Const(value=types.UnionType.__doc__) if types.UnionType.__doc__ @@ -705,9 +706,8 @@ def _astroid_bootstrapping() -> None: col_offset=0, end_lineno=0, end_col_offset=0, - parent=nodes.Unknown(), + parent=astroid_builtin, ) - klass.parent = astroid_builtin doc = _type.__doc__ if isinstance(_type.__doc__, str) else None klass.postinit( bases=[], diff --git a/astroid/util.py b/astroid/util.py index 510b81cc13..3ddbc09040 100644 --- a/astroid/util.py +++ b/astroid/util.py @@ -5,7 +5,10 @@ from __future__ import annotations +import contextlib +import sys import warnings +from collections.abc import Iterator, Sequence from typing import TYPE_CHECKING, Any, Final, Literal from astroid.exceptions import InferenceError @@ -157,3 +160,26 @@ def safe_infer( return None # there is some kind of ambiguity except StopIteration: return value + + +def _augment_sys_path(additional_paths: Sequence[str]) -> list[str]: + original = list(sys.path) + changes = [] + seen = set() + for additional_path in additional_paths: + if additional_path not in seen: + changes.append(additional_path) + seen.add(additional_path) + + sys.path[:] = changes + sys.path + return original + + +@contextlib.contextmanager +def augmented_sys_path(additional_paths: Sequence[str]) -> Iterator[None]: + """Augment 'sys.path' by adding entries from additional_paths.""" + original = _augment_sys_path(additional_paths) + try: + yield + finally: + sys.path[:] = original diff --git a/tests/test_modutils.py b/tests/test_modutils.py index 85452b0f77..6b815d986c 100644 --- a/tests/test_modutils.py +++ b/tests/test_modutils.py @@ -22,6 +22,7 @@ from astroid import modutils from astroid.const import PY310_PLUS from astroid.interpreter._import import spec +from astroid.util import augmented_sys_path from . import resources @@ -175,6 +176,37 @@ def test_import_symlink_with_source_outside_of_path(self) -> None: finally: os.remove(linked_file_name) + def test_modpath_from_file_path_order(self) -> None: + """Test for ordering of paths. + The test does the following: + 1. Add a tmp directory to beginning of sys.path via augmented_sys_path + 2. Create a module file in sub directory of tmp directory + 3. If the sub directory is passed as additional directory, module name + should be relative to the subdirectory since additional directory has + higher precedence.""" + with tempfile.TemporaryDirectory() as tmp_dir: + with augmented_sys_path([tmp_dir]): + mod_name = "module" + sub_dirname = "subdir" + sub_dir = tmp_dir + "/" + sub_dirname + os.mkdir(sub_dir) + module_file = f"{sub_dir}/{mod_name}.py" + + with open(module_file, "w+", encoding="utf-8"): + pass + + # Without additional directory, return relative to tmp_dir + self.assertEqual( + modutils.modpath_from_file(module_file), [sub_dirname, mod_name] + ) + + # With sub directory as additional directory, return relative to + # sub directory + self.assertEqual( + modutils.modpath_from_file(f"{sub_dir}/{mod_name}.py", [sub_dir]), + [mod_name], + ) + def test_import_symlink_both_outside_of_path(self) -> None: with tempfile.NamedTemporaryFile() as tmpfile: linked_file_name = os.path.join(tempfile.gettempdir(), "symlinked_file.py") diff --git a/tests/test_nodes.py b/tests/test_nodes.py index 644ceb1500..deb3b7fef5 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -37,6 +37,7 @@ ParentMissingError, StatementMissing, ) +from astroid.manager import AstroidManager from astroid.nodes.node_classes import ( AssignAttr, AssignName, @@ -1960,7 +1961,9 @@ def test_str_repr_no_warnings(node): if name == "self": continue - if "int" in param_type.annotation: + if name == "parent" and "NodeNG" in param_type.annotation: + args[name] = AstroidManager().synthetic_root + elif "int" in param_type.annotation: args[name] = random.randint(0, 50) elif ( "NodeNG" in param_type.annotation diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 72b91a1156..4a9f1f6022 100644 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -286,6 +286,28 @@ def test_uninferable_list_multiplication() -> None: element = parsed.inferred()[0].elts[0] assert element.value is Uninferable + @staticmethod + def test_uninferable_list_multiplication_with_multiple_operands() -> None: + """Attempting to calculate the result is prohibitively expensive.""" + parsed = extract_node("[0] * 825 * 16547118") + element = parsed.inferred()[0].elts[0] + assert element.value is Uninferable + + @staticmethod + def test_list_multiplication_with_empty_list_and_overflowing_multiplier() -> None: + parsed = extract_node("[] * 1163845194457646539560") + assert parsed.inferred()[0].elts == [] + + @staticmethod + def test_list_multiplication_with_zero_multiplier() -> None: + parsed = extract_node("[0] * 0") + assert parsed.inferred()[0].elts == [] + + @staticmethod + def test_list_multiplication_with_negative_overflowing_multiplier() -> None: + parsed = extract_node("[0] * -9223372036854775809") + assert parsed.inferred()[0].elts == [] + def test_named_expr_inference() -> None: code = """ diff --git a/tests/test_raw_building.py b/tests/test_raw_building.py index 1325dbc3ee..b5f3a62623 100644 --- a/tests/test_raw_building.py +++ b/tests/test_raw_building.py @@ -55,28 +55,28 @@ def test_build_class(self) -> None: self.assertEqual(node.doc_node, None) def test_build_function(self) -> None: - node = build_function("MyFunction") + node = build_function("MyFunction", DUMMY_MOD) self.assertEqual(node.name, "MyFunction") self.assertEqual(node.doc_node, None) def test_build_function_args(self) -> None: args = ["myArgs1", "myArgs2"] - node = build_function("MyFunction", args) + node = build_function("MyFunction", DUMMY_MOD, args) self.assertEqual("myArgs1", node.args.args[0].name) self.assertEqual("myArgs2", node.args.args[1].name) self.assertEqual(2, len(node.args.args)) def test_build_function_defaults(self) -> None: defaults = ["defaults1", "defaults2"] - node = build_function(name="MyFunction", args=None, defaults=defaults) + node = build_function("MyFunction", DUMMY_MOD, args=None, defaults=defaults) self.assertEqual(2, len(node.args.defaults)) def test_build_function_posonlyargs(self) -> None: - node = build_function(name="MyFunction", posonlyargs=["a", "b"]) + node = build_function("MyFunction", DUMMY_MOD, posonlyargs=["a", "b"]) self.assertEqual(2, len(node.args.posonlyargs)) def test_build_function_kwonlyargs(self) -> None: - node = build_function(name="MyFunction", kwonlyargs=["a", "b"]) + node = build_function("MyFunction", DUMMY_MOD, kwonlyargs=["a", "b"]) assert len(node.args.kwonlyargs) == 2 assert node.args.kwonlyargs[0].name == "a" assert node.args.kwonlyargs[1].name == "b"