diff --git a/ChangeLog b/ChangeLog index 9dae0f4356..7c81884ff1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,11 @@ What's New in astroid 2.12.5? ============================= Release date: TBA + +* Prevent first-party imports from being resolved to `site-packages`. + + Refs PyCQA/pylint#7365 + * Fix ``astroid.interpreter._import.util.is_namespace()`` incorrectly returning ``True`` for frozen stdlib modules on PyPy. diff --git a/astroid/interpreter/_import/util.py b/astroid/interpreter/_import/util.py index 0c29d670ef..8cdb8ea19d 100644 --- a/astroid/interpreter/_import/util.py +++ b/astroid/interpreter/_import/util.py @@ -15,6 +15,13 @@ @lru_cache(maxsize=4096) def is_namespace(modname: str) -> bool: + from astroid.modutils import ( # pylint: disable=import-outside-toplevel + EXT_LIB_DIRS, + STD_LIB_DIRS, + ) + + STD_AND_EXT_LIB_DIRS = STD_LIB_DIRS.union(EXT_LIB_DIRS) + if modname in sys.builtin_module_names: return False @@ -72,8 +79,15 @@ def is_namespace(modname: str) -> bool: last_submodule_search_locations.append(str(assumed_location)) continue - # Update last_submodule_search_locations + # Update last_submodule_search_locations for next iteration if found_spec and found_spec.submodule_search_locations: + # But immediately return False if we can detect we are in stdlib + # or external lib (e.g site-packages) + if any( + any(location.startswith(lib_dir) for lib_dir in STD_AND_EXT_LIB_DIRS) + for location in found_spec.submodule_search_locations + ): + return False last_submodule_search_locations = found_spec.submodule_search_locations return ( diff --git a/tests/unittest_manager.py b/tests/unittest_manager.py index 79f21db138..6c13da787d 100644 --- a/tests/unittest_manager.py +++ b/tests/unittest_manager.py @@ -17,7 +17,7 @@ from astroid.const import IS_JYTHON, IS_PYPY from astroid.exceptions import AstroidBuildingError, AstroidImportError from astroid.interpreter._import import util -from astroid.modutils import is_standard_module +from astroid.modutils import EXT_LIB_DIRS, is_standard_module from astroid.nodes import Const from astroid.nodes.scoped_nodes import ClassDef @@ -130,6 +130,9 @@ def test_submodule_homonym_with_non_module(self) -> None: def test_module_is_not_namespace(self) -> None: self.assertFalse(util.is_namespace("tests.testdata.python3.data.all")) self.assertFalse(util.is_namespace("__main__")) + self.assertFalse( + util.is_namespace(list(EXT_LIB_DIRS)[0].rsplit("/", maxsplit=1)[-1]), + ) self.assertFalse(util.is_namespace("importlib._bootstrap")) def test_module_unexpectedly_missing_spec(self) -> None: