Skip to content

Support recursive traversal into PEP 420 namespace packages #7219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ def add_invertible_flag(flag: str,
search_paths = SearchPaths((os.getcwd(),), tuple(mypy_path() + options.mypy_path), (), ())
targets = []
# TODO: use the same cache that the BuildManager will
cache = FindModuleCache(search_paths, fscache)
cache = FindModuleCache(search_paths, fscache, options, special_opts.packages)
for p in special_opts.packages:
if os.sep in p or os.altsep and os.altsep in p:
fail("Package name '{}' cannot have a slash in it.".format(p),
Expand Down
11 changes: 10 additions & 1 deletion mypy/modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ class FindModuleCache:
def __init__(self,
search_paths: SearchPaths,
fscache: Optional[FileSystemCache] = None,
options: Optional[Options] = None) -> None:
options: Optional[Options] = None,
ns_packages: Optional[List[str]] = None) -> None:
self.search_paths = search_paths
self.fscache = fscache or FileSystemCache()
# Cache for get_toplevel_possibilities:
Expand All @@ -74,6 +75,7 @@ def __init__(self,
self.results = {} # type: Dict[str, Optional[str]]
self.ns_ancestors = {} # type: Dict[str, str]
self.options = options
self.ns_packages = ns_packages or [] # type: List[str]

def clear(self) -> None:
self.results.clear()
Expand Down Expand Up @@ -307,6 +309,13 @@ def find_modules_recursive(self, module: str) -> List[BuildSource]:
if mod not in hits:
hits.add(mod)
result += self.find_modules_recursive(module + '.' + mod)
elif os.path.isdir(module_path) and module in self.ns_packages:
# Even more subtler: handle recursive decent into PEP 420
# namespace packages that are explicitly listed on the command
# line with -p/--packages.
for item in sorted(self.fscache.listdir(module_path)):
if os.path.isdir(os.path.join(module_path, item)):
result += self.find_modules_recursive(module + '.' + item)
return result


Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/cmdline.test
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,19 @@ p/a.py:4: error: Argument 1 to "foo" has incompatible type "str"; expected "int"
p/b/__init__.py:5: error: Argument 1 to "bar" has incompatible type "str"; expected "int"
c.py:2: error: Argument 1 to "bar" has incompatible type "str"; expected "int"

[case testSrcPEP420Packages]
# cmd: mypy -p anamespace --namespace-packages
[file mypy.ini]
[[mypy]]
mypy_path = src
[file src/setup.cfg]
[file src/anamespace/foo/__init__.py]
[file src/anamespace/foo/bar.py]
def bar(a: int, b: int) -> str:
return a + b
[out]
src/anamespace/foo/bar.py:2: error: Incompatible return value type (got "int", expected "str")

[case testFollowImportStubs1]
# cmd: mypy main.py
[file mypy.ini]
Expand Down