Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/interpreter/_import/util.py: 19%
47 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
5from __future__ import annotations
7import pathlib
8import sys
9from functools import lru_cache
10from importlib._bootstrap_external import _NamespacePath
11from importlib.util import _find_spec_from_path # type: ignore[attr-defined]
13from astroid.const import IS_PYPY
16@lru_cache(maxsize=4096)
17def is_namespace(modname: str) -> bool:
18 from astroid.modutils import ( # pylint: disable=import-outside-toplevel
19 EXT_LIB_DIRS,
20 STD_LIB_DIRS,
21 )
23 STD_AND_EXT_LIB_DIRS = STD_LIB_DIRS.union(EXT_LIB_DIRS)
25 if modname in sys.builtin_module_names:
26 return False
28 found_spec = None
30 # find_spec() attempts to import parent packages when given dotted paths.
31 # That's unacceptable here, so we fallback to _find_spec_from_path(), which does
32 # not, but requires instead that each single parent ('astroid', 'nodes', etc.)
33 # be specced from left to right.
34 processed_components = []
35 last_submodule_search_locations: _NamespacePath | None = None
36 for component in modname.split("."):
37 processed_components.append(component)
38 working_modname = ".".join(processed_components)
39 try:
40 # Both the modname and the path are built iteratively, with the
41 # path (e.g. ['a', 'a/b', 'a/b/c']) lagging the modname by one
42 found_spec = _find_spec_from_path(
43 working_modname, path=last_submodule_search_locations
44 )
45 except AttributeError:
46 return False
47 except ValueError:
48 if modname == "__main__":
49 return False
50 try:
51 # .pth files will be on sys.modules
52 # __spec__ is set inconsistently on PyPy so we can't really on the heuristic here
53 # See: https://foss.heptapod.net/pypy/pypy/-/issues/3736
54 # Check first fragment of modname, e.g. "astroid", not "astroid.interpreter"
55 # because of cffi's behavior
56 # See: https://github.com/pylint-dev/astroid/issues/1776
57 mod = sys.modules[processed_components[0]]
58 return (
59 mod.__spec__ is None
60 and getattr(mod, "__file__", None) is None
61 and hasattr(mod, "__path__")
62 and not IS_PYPY
63 )
64 except KeyError:
65 return False
66 except AttributeError:
67 # Workaround for "py" module
68 # https://github.com/pytest-dev/apipkg/issues/13
69 return False
70 except KeyError:
71 # Intermediate steps might raise KeyErrors
72 # https://github.com/python/cpython/issues/93334
73 # TODO: update if fixed in importlib
74 # For tree a > b > c.py
75 # >>> from importlib.machinery import PathFinder
76 # >>> PathFinder.find_spec('a.b', ['a'])
77 # KeyError: 'a'
79 # Repair last_submodule_search_locations
80 if last_submodule_search_locations:
81 # TODO: py38: remove except
82 try:
83 # pylint: disable=unsubscriptable-object
84 last_item = last_submodule_search_locations[-1]
85 except TypeError:
86 last_item = last_submodule_search_locations._recalculate()[-1]
87 # e.g. for failure example above, add 'a/b' and keep going
88 # so that find_spec('a.b.c', path=['a', 'a/b']) succeeds
89 assumed_location = pathlib.Path(last_item) / component
90 last_submodule_search_locations.append(str(assumed_location))
91 continue
93 # Update last_submodule_search_locations for next iteration
94 if found_spec and found_spec.submodule_search_locations:
95 # But immediately return False if we can detect we are in stdlib
96 # or external lib (e.g site-packages)
97 if any(
98 any(location.startswith(lib_dir) for lib_dir in STD_AND_EXT_LIB_DIRS)
99 for location in found_spec.submodule_search_locations
100 ):
101 return False
102 last_submodule_search_locations = found_spec.submodule_search_locations
104 return (
105 found_spec is not None
106 and found_spec.submodule_search_locations is not None
107 and found_spec.origin is None
108 )