Skip to content

Commit

Permalink
Ensure builtin modules are from typeshed sooner
Browse files Browse the repository at this point in the history
It should work now with custom-typeshed-dir.

Fixes python#1876
  • Loading branch information
Konstantin Ignatov committed Jul 17, 2022
1 parent ca6357e commit 3d2f8a2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 12 deletions.
34 changes: 25 additions & 9 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,31 @@ def __init__(self, data_dir: str,
self.fscache = fscache
self.find_module_cache = FindModuleCache(self.search_paths, self.fscache, self.options,
source_set=self.source_set)
for module in CORE_BUILTIN_MODULES:
if options.use_builtins_fixtures:
continue
if module == "_importlib_modulespec":
continue
path = self.find_module_cache.find_module(module)
if not isinstance(path, str):
raise CompileError([
f"Failed to find builtin module {module}, perhaps typeshed is broken?",
])
if is_typeshed_file(path):
continue
if is_stub_package_file(path):
continue
if options.custom_typeshed_dir is not None:
# Check if module lives under custom_typeshed_dir subtree
custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir)
if os.path.commonpath((path, custom_typeshed_dir)) == custom_typeshed_dir:
continue

raise CompileError([
f'mypy: "{os.path.relpath(path)}" shadows library module "{module}"',
f'note: A user-defined top-level module with name "{module}" is not supported'
])

self.metastore = create_metastore(options)

# a mapping from source files to their corresponding shadow files
Expand Down Expand Up @@ -2459,15 +2484,6 @@ def find_module_and_diagnose(manager: BuildManager,
if is_sub_path(result, dir):
# Silence errors in site-package dirs and typeshed
follow_imports = 'silent'
if (id in CORE_BUILTIN_MODULES
and not is_typeshed_file(result)
and not is_stub_package_file(result)
and not options.use_builtins_fixtures
and not options.custom_typeshed_dir):
raise CompileError([
f'mypy: "{os.path.relpath(result)}" shadows library module "{id}"',
f'note: A user-defined top-level module with name "{id}" is not supported'
])
return (result, follow_imports)
else:
# Could not find a module. Typically the reason is a
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def test_scc(self) -> None:
def _make_manager(self) -> BuildManager:
errors = Errors()
options = Options()
options.use_builtins_fixtures = True
fscache = FileSystemCache()
search_paths = SearchPaths((), (), (), ())
manager = BuildManager(
Expand Down
7 changes: 4 additions & 3 deletions mypy/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
"__init__", "__new__", "__call__", "__init_subclass__", "__class_getitem__",
))

TYPESHED_DIR: Final = os.path.join(os.path.dirname(__file__), 'typeshed')


def is_dunder(name: str, exclude_special: bool = False) -> bool:
"""Returns whether name is a dunder name.
Expand Down Expand Up @@ -745,16 +747,15 @@ def format_error(


def is_typeshed_file(file: str) -> bool:
# gross, but no other clear way to tell
return 'typeshed' in os.path.abspath(file).split(os.sep)
return os.path.commonpath((TYPESHED_DIR, os.path.abspath(file))) == TYPESHED_DIR


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


def unnamed_function(name: Optional[str]) -> bool:
Expand Down
19 changes: 19 additions & 0 deletions test-data/unit/cmdline.test
Original file line number Diff line number Diff line change
Expand Up @@ -1425,3 +1425,22 @@ b\.c \d+
# cmd: mypy --enable-incomplete-features a.py
[file a.py]
pass

[case testShadowTypingModuleEarlyLoad]
# cmd: mypy .
from typing import Union

def foo(a: Union[int, str]) -> str:
return str
[file typing.py]
import sys
import os
del sys.modules["typing"]
path = sys.path
sys.path.remove(os.getcwd())
from typing import *
sys.path = path
[out]
mypy: "tmp/typing.py" shadows library module "typing"
note: A user-defined top-level module with name "typing" is not supported
== Return code: 2

0 comments on commit 3d2f8a2

Please sign in to comment.