From b7483fa231823278a6b249d7d728edf14f658bca Mon Sep 17 00:00:00 2001 From: akamat10 Date: Wed, 28 Aug 2024 14:06:58 -0400 Subject: [PATCH 1/6] Fix to order of module discovery --- astroid/nodes/_base_nodes.py | 5 +++++ astroid/nodes/scoped_nodes/scoped_nodes.py | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/astroid/nodes/_base_nodes.py b/astroid/nodes/_base_nodes.py index 96c3c1c06d..b7c633da3f 100644 --- a/astroid/nodes/_base_nodes.py +++ b/astroid/nodes/_base_nodes.py @@ -164,10 +164,15 @@ def do_import_module(self, modname: str | None = None) -> nodes.Module: else: use_cache = True + context_file = mymodule.path[0] if mymodule.path else None + if context_file == '': + context_file = None + # pylint: disable-next=no-member # pylint doesn't recognize type of mymodule return mymodule.import_module( modname, level=level, + context_file=context_file, relative_only=bool(level and level >= 1), use_cache=use_cache, ) diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index af3b9d39a8..ff33d2e233 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -438,6 +438,7 @@ def absolute_import_activated(self) -> bool: def import_module( self, modname: str, + context_file: str | None = None, relative_only: bool = False, level: int | None = None, use_cache: bool = True, @@ -460,7 +461,7 @@ def import_module( try: return AstroidManager().ast_from_module_name( - absmodname, use_cache=use_cache + absmodname, context_file, use_cache=use_cache ) except AstroidBuildingError: # we only want to import a sub module or package of this module, @@ -471,7 +472,9 @@ def import_module( # like "_winapi" or "nt" on POSIX systems. if modname == absmodname: raise - return AstroidManager().ast_from_module_name(modname, use_cache=use_cache) + return AstroidManager().ast_from_module_name(modname, + context_file, + use_cache=use_cache) def relative_to_absolute_name(self, modname: str, level: int | None) -> str: """Get the absolute module name for a relative import. From d5772aad884c4a965b003718490d3b7ebe2d013d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 18:21:42 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- astroid/nodes/_base_nodes.py | 2 +- astroid/nodes/scoped_nodes/scoped_nodes.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/astroid/nodes/_base_nodes.py b/astroid/nodes/_base_nodes.py index b7c633da3f..b1de484c28 100644 --- a/astroid/nodes/_base_nodes.py +++ b/astroid/nodes/_base_nodes.py @@ -165,7 +165,7 @@ def do_import_module(self, modname: str | None = None) -> nodes.Module: use_cache = True context_file = mymodule.path[0] if mymodule.path else None - if context_file == '': + if context_file == "": context_file = None # pylint: disable-next=no-member # pylint doesn't recognize type of mymodule diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index ff33d2e233..588727e0c3 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -472,9 +472,9 @@ def import_module( # like "_winapi" or "nt" on POSIX systems. if modname == absmodname: raise - return AstroidManager().ast_from_module_name(modname, - context_file, - use_cache=use_cache) + return AstroidManager().ast_from_module_name( + modname, context_file, use_cache=use_cache + ) def relative_to_absolute_name(self, modname: str, level: int | None) -> str: """Get the absolute module name for a relative import. From 6adf3abfce0132e0bae9c2fa7b3f63941703dd09 Mon Sep 17 00:00:00 2001 From: akamat10 Date: Wed, 28 Aug 2024 16:15:37 -0400 Subject: [PATCH 3/6] Update tests to reflect current python behavior for python scripts --- astroid/nodes/_base_nodes.py | 1 + tests/test_inference.py | 11 +---------- tests/test_nodes.py | 6 +++++- tests/testdata/python3/data/absimp/string.py | 2 ++ 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/astroid/nodes/_base_nodes.py b/astroid/nodes/_base_nodes.py index b1de484c28..9db4da78d2 100644 --- a/astroid/nodes/_base_nodes.py +++ b/astroid/nodes/_base_nodes.py @@ -164,6 +164,7 @@ def do_import_module(self, modname: str | None = None) -> nodes.Module: else: use_cache = True + # pylint: disable-next=no-member # pylint doesn't recognize type of mymodule context_file = mymodule.path[0] if mymodule.path else None if context_file == "": context_file = None diff --git a/tests/test_inference.py b/tests/test_inference.py index a8b11b1614..850dc4cf4b 100644 --- a/tests/test_inference.py +++ b/tests/test_inference.py @@ -1505,22 +1505,13 @@ def test_name_repeat_inference(self) -> None: with pytest.raises(InferenceError): next(node.infer(context=context)) - def test_python25_no_relative_import(self) -> None: - ast = resources.build_file("data/package/absimport.py") - self.assertTrue(ast.absolute_import_activated(), True) - inferred = next( - test_utils.get_name_node(ast, "import_package_subpackage_module").infer() - ) - # failed to import since absolute_import is activated - self.assertIs(inferred, util.Uninferable) - def test_nonregr_absolute_import(self) -> None: ast = resources.build_file("data/absimp/string.py", "data.absimp.string") self.assertTrue(ast.absolute_import_activated(), True) inferred = next(test_utils.get_name_node(ast, "string").infer()) self.assertIsInstance(inferred, nodes.Module) self.assertEqual(inferred.name, "string") - self.assertIn("ascii_letters", inferred.locals) + self.assertNotIn("ascii_letters", inferred.locals) def test_property(self) -> None: code = """ diff --git a/tests/test_nodes.py b/tests/test_nodes.py index 64cae2f676..aa249fb83a 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -34,6 +34,7 @@ AstroidBuildingError, AstroidSyntaxError, AttributeInferenceError, + InferenceError, ParentMissingError, StatementMissing, ) @@ -571,7 +572,10 @@ def test_absolute_import(self) -> None: ctx = InferenceContext() # will fail if absolute import failed ctx.lookupname = "message" - next(module["message"].infer(ctx)) + + with self.assertRaises(InferenceError): + next(module["message"].infer(ctx)) + ctx.lookupname = "email" m = next(module["email"].infer(ctx)) self.assertFalse(m.file.startswith(os.path.join("data", "email.py"))) diff --git a/tests/testdata/python3/data/absimp/string.py b/tests/testdata/python3/data/absimp/string.py index e68e74965d..b48bdab32f 100644 --- a/tests/testdata/python3/data/absimp/string.py +++ b/tests/testdata/python3/data/absimp/string.py @@ -1,3 +1,5 @@ from __future__ import absolute_import, print_function +import sys +print(sys.path) import string print(string) From 04fbce5ab64795fcae7709df2959c297efce0a7b Mon Sep 17 00:00:00 2001 From: akamat10 Date: Wed, 28 Aug 2024 16:38:27 -0400 Subject: [PATCH 4/6] Remove some extra debug --- tests/testdata/python3/data/absimp/string.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/testdata/python3/data/absimp/string.py b/tests/testdata/python3/data/absimp/string.py index b48bdab32f..e68e74965d 100644 --- a/tests/testdata/python3/data/absimp/string.py +++ b/tests/testdata/python3/data/absimp/string.py @@ -1,5 +1,3 @@ from __future__ import absolute_import, print_function -import sys -print(sys.path) import string print(string) From 98b86e388399c1e689e6f0955f44bd28c0aac833 Mon Sep 17 00:00:00 2001 From: akamat10 Date: Fri, 30 Aug 2024 23:03:40 -0400 Subject: [PATCH 5/6] Fix to not use cache when unrelated module is in cache --- astroid/manager.py | 9 ++++++++- astroid/nodes/scoped_nodes/scoped_nodes.py | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/astroid/manager.py b/astroid/manager.py index 7206be616a..93e088b7f0 100644 --- a/astroid/manager.py +++ b/astroid/manager.py @@ -219,7 +219,14 @@ def ast_from_module_name( # noqa: C901 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 == '': + return self.astroid_cache[modname] + + module_parent_path = self.astroid_cache[modname].get_parent_path() + if context_file and os.path.dirname(context_file) == module_parent_path: + return self.astroid_cache[modname] + elif module_parent_path is None: + return self.astroid_cache[modname] if modname == "__main__": return self._build_stub_module(modname) if context_file: diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index 588727e0c3..4371e875a0 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -16,6 +16,7 @@ import warnings from collections.abc import Generator, Iterable, Iterator, Sequence from functools import cached_property, lru_cache +from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Literal, NoReturn, TypeVar from astroid import bases, protocols, util @@ -592,6 +593,15 @@ def bool_value(self, context: InferenceContext | None = None) -> bool: def get_children(self): yield from self.body + + def get_parent_path(self) -> str | None: + """Given the module, return its parent path""" + module_parts = self.name.split(".") + if self.file and self.file != "": + return str(Path(self.file).parents[len(module_parts)]) + else: + return None + def frame(self: _T, *, future: Literal[None, True] = None) -> _T: """The node's frame node. From d679f9a3f3d9b6c844df06f1991c657a856a65cc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 03:04:59 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- astroid/manager.py | 2 +- astroid/nodes/scoped_nodes/scoped_nodes.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/astroid/manager.py b/astroid/manager.py index 93e088b7f0..10796572b5 100644 --- a/astroid/manager.py +++ b/astroid/manager.py @@ -219,7 +219,7 @@ def ast_from_module_name( # noqa: C901 if modname in self.module_denylist: raise AstroidImportError(f"Skipping ignored module {modname!r}") if modname in self.astroid_cache and use_cache: - if modname == '': + if modname == "": return self.astroid_cache[modname] module_parent_path = self.astroid_cache[modname].get_parent_path() diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index 4371e875a0..e50fa1ac29 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -593,7 +593,7 @@ def bool_value(self, context: InferenceContext | None = None) -> bool: def get_children(self): yield from self.body - + def get_parent_path(self) -> str | None: """Given the module, return its parent path""" module_parts = self.name.split(".") @@ -602,7 +602,6 @@ def get_parent_path(self) -> str | None: else: return None - def frame(self: _T, *, future: Literal[None, True] = None) -> _T: """The node's frame node.