Skip to content

Commit 61f92e6

Browse files
author
Konstantin Ignatov
committed
Ensure builtin modules are from typeshed sooner
It should work now with custom-typeshed-dir. Fixes python#1876
1 parent ca6357e commit 61f92e6

File tree

6 files changed

+64
-14
lines changed

6 files changed

+64
-14
lines changed

build-requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
-r mypy-requirements.txt
2+
types-setuptools
23
types-typed-ast>=1.5.0,<1.6.0

mypy/build.py

+25-9
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,31 @@ def __init__(self, data_dir: str,
605605
self.fscache = fscache
606606
self.find_module_cache = FindModuleCache(self.search_paths, self.fscache, self.options,
607607
source_set=self.source_set)
608+
for module in CORE_BUILTIN_MODULES:
609+
if options.use_builtins_fixtures:
610+
continue
611+
if module == "_importlib_modulespec":
612+
continue
613+
path = self.find_module_cache.find_module(module)
614+
if not isinstance(path, str):
615+
raise CompileError([
616+
f"Failed to find builtin module {module}, perhaps typeshed is broken?",
617+
])
618+
if is_typeshed_file(path):
619+
continue
620+
if is_stub_package_file(path):
621+
continue
622+
if options.custom_typeshed_dir is not None:
623+
# Check if module lives under custom_typeshed_dir subtree
624+
custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir)
625+
if os.path.commonpath((path, custom_typeshed_dir)) == custom_typeshed_dir:
626+
continue
627+
628+
raise CompileError([
629+
f'mypy: "{os.path.relpath(path)}" shadows library module "{module}"',
630+
f'note: A user-defined top-level module with name "{module}" is not supported'
631+
])
632+
608633
self.metastore = create_metastore(options)
609634

610635
# a mapping from source files to their corresponding shadow files
@@ -2459,15 +2484,6 @@ def find_module_and_diagnose(manager: BuildManager,
24592484
if is_sub_path(result, dir):
24602485
# Silence errors in site-package dirs and typeshed
24612486
follow_imports = 'silent'
2462-
if (id in CORE_BUILTIN_MODULES
2463-
and not is_typeshed_file(result)
2464-
and not is_stub_package_file(result)
2465-
and not options.use_builtins_fixtures
2466-
and not options.custom_typeshed_dir):
2467-
raise CompileError([
2468-
f'mypy: "{os.path.relpath(result)}" shadows library module "{id}"',
2469-
f'note: A user-defined top-level module with name "{id}" is not supported'
2470-
])
24712487
return (result, follow_imports)
24722488
else:
24732489
# Could not find a module. Typically the reason is a

mypy/test/testgraph.py

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def test_scc(self) -> None:
3838
def _make_manager(self) -> BuildManager:
3939
errors = Errors()
4040
options = Options()
41+
options.use_builtins_fixtures = True
4142
fscache = FileSystemCache()
4243
search_paths = SearchPaths((), (), (), ())
4344
manager = BuildManager(

mypy/util.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import io
1010
import shutil
1111
import time
12+
try:
13+
from importlib import resources as import_resources # type: ignore[attr-defined]
14+
except ImportError: # <python3.7
15+
import import_resources # type: ignore[import]
1216

1317
from typing import (
1418
TypeVar, List, Tuple, Optional, Dict, Sequence, Iterable, Container, IO, Callable, Union, Sized
@@ -22,6 +26,12 @@
2226
except ImportError:
2327
CURSES_ENABLED = False
2428

29+
with import_resources.path(
30+
'mypy' if __package__ is None else __package__, # mypy-c doesn't support __package__
31+
"typeshed",
32+
) as _resource:
33+
TYPESHED_DIR: Final = str(_resource)
34+
2535
T = TypeVar('T')
2636

2737
ENCODING_RE: Final = re.compile(br"([ \t\v]*#.*(\r\n?|\n))??[ \t\v]*#.*coding[:=][ \t]*([-\w.]+)")
@@ -745,16 +755,18 @@ def format_error(
745755

746756

747757
def is_typeshed_file(file: str) -> bool:
748-
# gross, but no other clear way to tell
749-
return 'typeshed' in os.path.abspath(file).split(os.sep)
758+
try:
759+
return os.path.commonpath((TYPESHED_DIR, os.path.abspath(file))) == TYPESHED_DIR
760+
except ValueError: # Different drives on Windows
761+
return False
750762

751763

752764
def is_stub_package_file(file: str) -> bool:
753765
# Use hacky heuristics to check whether file is part of a PEP 561 stub package.
754766
if not file.endswith('.pyi'):
755767
return False
756768
return any(component.endswith('-stubs')
757-
for component in os.path.abspath(file).split(os.sep))
769+
for component in os.path.split(os.path.abspath(file)))
758770

759771

760772
def unnamed_function(name: Optional[str]) -> bool:

mypyc/build.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
try:
4949
# Import setuptools so that it monkey-patch overrides distutils
50-
import setuptools # type: ignore # noqa
50+
import setuptools # noqa
5151
except ImportError:
5252
if sys.version_info >= (3, 12):
5353
# Raise on Python 3.12, since distutils will go away forever
@@ -63,7 +63,7 @@ def get_extension() -> Type['Extension']:
6363
if not use_setuptools:
6464
from distutils.core import Extension
6565
else:
66-
from setuptools import Extension # type: ignore # noqa
66+
from setuptools import Extension # noqa
6767

6868
return Extension
6969

test-data/unit/cmdline.test

+20
Original file line numberDiff line numberDiff line change
@@ -1425,3 +1425,23 @@ b\.c \d+
14251425
# cmd: mypy --enable-incomplete-features a.py
14261426
[file a.py]
14271427
pass
1428+
1429+
[case testShadowTypingModuleEarlyLoad]
1430+
# cmd: mypy dir
1431+
[file dir/__init__.py]
1432+
from typing import Union
1433+
1434+
def foo(a: Union[int, str]) -> str:
1435+
return str
1436+
[file typing.py]
1437+
import sys
1438+
import os
1439+
del sys.modules["typing"]
1440+
path = sys.path
1441+
sys.path.remove(os.getcwd())
1442+
from typing import *
1443+
sys.path = path
1444+
[out]
1445+
mypy: "typing.py" shadows library module "typing"
1446+
note: A user-defined top-level module with name "typing" is not supported
1447+
== Return code: 2

0 commit comments

Comments
 (0)