Skip to content

Commit f0d3402

Browse files
committed
Support recursive traversal into PEP 420 namespace packages
E.g. with this file layout: src/ anamespace/ foo/ __init__.py bar.py $ MYPYPATH=src mypy -p foo --namespace-packages will traverse into src/anamespace/foo
1 parent 0b59a10 commit f0d3402

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

mypy/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ def add_invertible_flag(flag: str,
763763
search_paths = SearchPaths((os.getcwd(),), tuple(mypy_path() + options.mypy_path), (), ())
764764
targets = []
765765
# TODO: use the same cache that the BuildManager will
766-
cache = FindModuleCache(search_paths, fscache)
766+
cache = FindModuleCache(search_paths, fscache, options, special_opts.packages)
767767
for p in special_opts.packages:
768768
if os.sep in p or os.altsep and os.altsep in p:
769769
fail("Package name '{}' cannot have a slash in it.".format(p),

mypy/modulefinder.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ class FindModuleCache:
6464
def __init__(self,
6565
search_paths: SearchPaths,
6666
fscache: Optional[FileSystemCache] = None,
67-
options: Optional[Options] = None) -> None:
67+
options: Optional[Options] = None,
68+
ns_packages: Optional[List[str]] = None) -> None:
6869
self.search_paths = search_paths
6970
self.fscache = fscache or FileSystemCache()
7071
# Cache for get_toplevel_possibilities:
@@ -74,6 +75,7 @@ def __init__(self,
7475
self.results = {} # type: Dict[str, Optional[str]]
7576
self.ns_ancestors = {} # type: Dict[str, str]
7677
self.options = options
78+
self.ns_packages = ns_packages or [] # type: List[str]
7779

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

312321

test-data/unit/cmdline.test

+13
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,19 @@ p/a.py:4: error: Argument 1 to "foo" has incompatible type "str"; expected "int"
11241124
p/b/__init__.py:5: error: Argument 1 to "bar" has incompatible type "str"; expected "int"
11251125
c.py:2: error: Argument 1 to "bar" has incompatible type "str"; expected "int"
11261126

1127+
[case testSrcPEP420Packages]
1128+
# cmd: mypy -p anamespace --namespace-packages
1129+
[file mypy.ini]
1130+
[[mypy]]
1131+
mypy_path = src
1132+
[file src/setup.cfg]
1133+
[file src/anamespace/foo/__init__.py]
1134+
[file src/anamespace/foo/bar.py]
1135+
def bar(a: int, b: int) -> str:
1136+
return a + b
1137+
[out]
1138+
src/anamespace/foo/bar.py:2: error: Incompatible return value type (got "int", expected "str")
1139+
11271140
[case testFollowImportStubs1]
11281141
# cmd: mypy main.py
11291142
[file mypy.ini]

0 commit comments

Comments
 (0)