Skip to content

Commit b8755ba

Browse files
Prefer stdlib modules over same-named modules on sys.path
For example: `import copy` now finds `copy` instead of `copy.py`. This worked correctly before in at least some cases if there was a (more?) complete chain of __init__.py files from cwd all the way to the location of the `copy.py` module. Closes pylint-dev/pylint#6535
1 parent efb34f2 commit b8755ba

File tree

4 files changed

+34
-2
lines changed

4 files changed

+34
-2
lines changed

ChangeLog

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ Release date: TBA
1515
Closes #1780
1616
Refs #2140
1717

18+
* Prefer standard library modules over same-named modules on sys.path. For example
19+
``import copy`` now finds ``copy`` instead of ``copy.py``. Solves ``no-member`` issues.
20+
21+
Closes pylint-dev/pylint#6535
22+
1823
* Reduce file system access in ``ast_from_file()``.
1924

2025
* Reduce time to ``import astroid`` by delaying ``astroid_bootstrapping()`` until

astroid/interpreter/_import/spec.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from typing import Any, Literal, NamedTuple, Protocol
2121

2222
from astroid.const import PY310_PLUS
23-
from astroid.modutils import EXT_LIB_DIRS
23+
from astroid.modutils import EXT_LIB_DIRS, STD_LIB_DIRS
2424

2525
from . import util
2626

@@ -157,6 +157,19 @@ def find_module(
157157
location=getattr(spec.loader_state, "filename", None),
158158
type=ModuleType.PY_FROZEN,
159159
)
160+
if (
161+
spec
162+
and isinstance(spec.loader, importlib.machinery.SourceFileLoader)
163+
and any(spec.origin.startswith(std_lib) for std_lib in STD_LIB_DIRS)
164+
and not spec.origin.endswith("__init__.py")
165+
):
166+
# Return standard library modules before local modules
167+
# https://github.com/pylint-dev/pylint/issues/6535
168+
return ModuleSpec(
169+
name=modname,
170+
location=spec.origin,
171+
type=ModuleType.PY_SOURCE,
172+
)
160173
except ValueError:
161174
pass
162175
submodule_path = sys.path

tests/test_modutils.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import astroid
2222
from astroid import modutils
23-
from astroid.const import PY310_PLUS
23+
from astroid.const import PY310_PLUS, WIN32
2424
from astroid.interpreter._import import spec
2525

2626
from . import resources
@@ -268,6 +268,19 @@ def test_std_lib(self) -> None:
268268
os.path.realpath(os.path.__file__.replace(".pyc", ".py")),
269269
)
270270

271+
def test_std_lib_found_before_same_named_package_on_path(self) -> None:
272+
realpath = str(resources.RESOURCE_PATH)
273+
if WIN32:
274+
# Escape backslashes.
275+
realpath = realpath.replace("\\", "\\\\")
276+
sys.path.insert(0, realpath)
277+
self.addCleanup(sys.path.pop, 0)
278+
279+
file = modutils.file_from_modpath(["copy"])
280+
281+
self.assertNotIn("test", file) # tests/testdata/python3/data/copy.py
282+
self.assertTrue(any(stdlib in file for stdlib in modutils.STD_LIB_DIRS))
283+
271284
def test_builtin(self) -> None:
272285
self.assertIsNone(modutils.file_from_modpath(["sys"]))
273286

tests/testdata/python3/data/copy.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""fake copy module (unlike email, we need one without __init__.py)"""

0 commit comments

Comments
 (0)