diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index ca21cc6a7199..836557590623 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -506,21 +506,24 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult: dir_prefix = base_dir for _ in range(len(components) - 1): dir_prefix = os.path.dirname(dir_prefix) + + # Stubs-only packages always take precedence over py.typed packages + path_stubs = f"{base_path}-stubs{sepinit}.pyi" + if fscache.isfile_case(path_stubs, dir_prefix): + if verify and not verify_module(fscache, id, path_stubs, dir_prefix): + near_misses.append((path_stubs, dir_prefix)) + else: + return path_stubs + # Prefer package over module, i.e. baz/__init__.py* over baz.py*. for extension in PYTHON_EXTENSIONS: path = base_path + sepinit + extension - path_stubs = base_path + "-stubs" + sepinit + extension if fscache.isfile_case(path, dir_prefix): has_init = True if verify and not verify_module(fscache, id, path, dir_prefix): near_misses.append((path, dir_prefix)) continue return path - elif fscache.isfile_case(path_stubs, dir_prefix): - if verify and not verify_module(fscache, id, path_stubs, dir_prefix): - near_misses.append((path_stubs, dir_prefix)) - continue - return path_stubs # In namespace mode, register a potential namespace package if self.options and self.options.namespace_packages: diff --git a/mypy/test/testmodulefinder.py b/mypy/test/testmodulefinder.py index 65d9a66c5fa0..d4ee3af041c5 100644 --- a/mypy/test/testmodulefinder.py +++ b/mypy/test/testmodulefinder.py @@ -195,6 +195,9 @@ def test__packages_with_ns(self) -> None: ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")), ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")), ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND), + # Regular package with py.typed, bundled stubs, and external stubs-only package + ("pkg_typed_w_stubs", self.path("pkg_typed_w_stubs-stubs", "__init__.pyi")), + ("pkg_typed_w_stubs.spam", self.path("pkg_typed_w_stubs-stubs", "spam.pyi")), # Regular package without py.typed ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), @@ -250,6 +253,9 @@ def test__packages_without_ns(self) -> None: ("pkg_typed.b", self.path("pkg_typed", "b", "__init__.py")), ("pkg_typed.b.c", self.path("pkg_typed", "b", "c.py")), ("pkg_typed.a.a_var", ModuleNotFoundReason.NOT_FOUND), + # Regular package with py.typed, bundled stubs, and external stubs-only package + ("pkg_typed_w_stubs", self.path("pkg_typed_w_stubs-stubs", "__init__.pyi")), + ("pkg_typed_w_stubs.spam", self.path("pkg_typed_w_stubs-stubs", "spam.pyi")), # Regular package without py.typed ("pkg_untyped", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), ("pkg_untyped.a", ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS), diff --git a/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi b/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi new file mode 100644 index 000000000000..5605b1454039 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/foo-stubs/qux.pyi @@ -0,0 +1 @@ +qux_var: int diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi new file mode 100644 index 000000000000..579a7556fdd1 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/__init__.pyi @@ -0,0 +1 @@ +pkg_typed_w_stubs_var: str = ... diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi new file mode 100644 index 000000000000..e3ef9cce5905 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs-stubs/spam.pyi @@ -0,0 +1 @@ +spam_var: str diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py new file mode 100644 index 000000000000..11fa3635a2c7 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.py @@ -0,0 +1 @@ +pkg_typed_w_stubs_var = "pkg_typed_w_stubs" diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi new file mode 100644 index 000000000000..3a03f395d014 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/__init__.pyi @@ -0,0 +1 @@ +pkg_typed_w_stubs_var: object diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/py.typed b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/py.typed new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py new file mode 100644 index 000000000000..0aff1579b57f --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.py @@ -0,0 +1 @@ +spam_var = "spam" diff --git a/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi new file mode 100644 index 000000000000..8eca196a7981 --- /dev/null +++ b/test-data/packages/modulefinder-site-packages/pkg_typed_w_stubs/spam.pyi @@ -0,0 +1 @@ +spam_var: object