8000 Fix approved stub ignore, remove normpath · python/mypy@146a600 · GitHub
[go: up one dir, main page]

Skip to content

Commit 146a600

Browse files
committed
Fix approved stub ignore, remove normpath
1 parent 715b768 commit 146a600

File tree

7 files changed

+69
-74
lines changed

7 files changed

+69
-74
lines changed

mypy/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2861,7 +2861,7 @@ def log_configuration(manager: BuildManager, sources: list[BuildSource]) -> None
28612861
manager.log(f"{'Found source:':24}{source}")
28622862

28632863
# Complete list of searched paths can get very long, put them under TRACE
2864-
for path_type, paths in manager.search_paths._asdict().items():
2864+
for path_type, paths in manager.search_paths.asdict().items():
28652865
if not paths:
28662866
manager.trace(f"No {path_type}")
28672867
continue

mypy/modulefinder.py

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,43 @@
1313
import subprocess
1414
import sys
1515
from enum import Enum, unique
16-
from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union
16+
from typing import Dict, Final, List, Optional, Tuple, Union
1717
from typing_extensions import TypeAlias as _TypeAlias
1818

1919
from mypy import pyinfo
2020
from mypy.errors import CompileError
2121
from mypy.fscache import FileSystemCache
2222
from mypy.nodes import MypyFile
2323
from mypy.options import Options
24-
from mypy.stubinfo import approved_stub_package_exists
24+
from mypy.stubinfo import stub_distribution_name
2525
from mypy.util import os_path_join
2626

2727

2828
# Paths to be searched in find_module().
29-
class SearchPaths(NamedTuple):
30-
python_path: tuple[str, ...] # where user code is found
31-
mypy_path: tuple[str, ...] # from $MYPYPATH or config variable
32-
package_path: tuple[str, ...] # from get_site_packages_dirs()
33-
typeshed_path: tuple[str, ...] # paths in typeshed
29+
class SearchPaths:
30+
def __init__(
31+
self,
32+
python_path: tuple[str, ...],
33+
mypy_path: tuple[str, ...],
34+
package_path: tuple[str, ...],
35+
typeshed_path: tuple[str, ...],
36+
) -> None:
37+
# where user code is found
38+
self.python_path = tuple(map(os.path.abspath, python_path))
39+
# from $MYPYPATH or config variable
40+
self.mypy_path = tuple(map(os.path.abspath, mypy_path))
41+
# from get_site_packages_dirs()
42+
self.package_path = tuple(map(os.path.abspath, package_path))
43+
# paths in typeshed
44+
self.typeshed_path = tuple(map(os.path.abspath, typeshed_path))
45+
46+
def asdict(self) -> dict[str, tuple[str, ...]]:
47+
return {
48+
"python_path": self.python_path,
49+
"mypy_path": self.mypy_path,
50+
"package_path": self.package_path,
51+
"typeshed_path": self.typeshed_path,
52+
}
3453

3554

3655
# Package dirs are a two-tuple of path to search and whether to verify the module
@@ -240,17 +259,17 @@ def find_module_via_source_set(self, id: str) -> ModuleSearchResult | None:
240259
return None
241260

242261
def find_lib_path_dirs(self, id: str, lib_path: tuple[str, ...]) -> PackageDirs:
243-
"""Find which elements of a lib_path have the directory a module needs to exist.
244-
245-
This is run for the python_path, mypy_path, and typeshed_path search paths.
246-
"""
262+
"""Find which elements of a lib_path have the directory a module needs to exist."""
247263
components = id.split(".")
248264
dir_chain = os.sep.join(components[:-1]) # e.g., 'foo/bar'
249265

250266
dirs = []
251267
for pathitem in self.get_toplevel_possibilities(lib_path, components[0]):
252268
# e.g., '/usr/lib/python3.4/foo/bar'
253-
dir = os.path.normpath(os_path_join(pathitem, dir_chain))
269+
if dir_chain:
270+
dir = os_path_join(pathitem, dir_chain)
271+
else:
272+
dir = pathitem
254273
if self.fscache.isdir(dir):
255274
dirs.append((dir, True))
256275
return dirs
@@ -419,7 +438,6 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
419438
for package_dir in self.find_lib_path_dirs(component, self.search_paths.package_path)
420439
}
421440
for pkg_dir in self.search_paths.package_path:
422-
pkg_dir = os.path.normpath(pkg_dir)
423441
if pkg_dir not in candidate_package_dirs:
424442
continue
425443
stub_name = components[0] + "-stubs"
@@ -552,8 +570,22 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
552570
if ancestor is not None:
553571
return ancestor
554572

555-
if approved_stub_package_exists(id):
556-
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
573+
approved_dist_name = stub_distribution_name(id)
574+
if approved_dist_name:
575+
if len(components) == 1:
576+
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
577+
# If we're a missing submodule of an already installed approved stubs, we don't want to
578+
# error with APPROVED_STUBS_NOT_INSTALLED, but rather want to return NOT_FOUND.
579+
for i in range(1, len(components)):
580+
parent_id = ".".join(components[:i])
581+
if stub_distribution_name(parent_id) == approved_dist_name:
582+
break
583+
else:
584+
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
585+
if self.find_module(parent_id) is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED:
586+
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
587+
return ModuleNotFoundReason.NOT_FOUND
588+
557589
if found_possible_third_party_missing_type_hints:
558590
return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS
559591
return ModuleNotFoundReason.NOT_FOUND
@@ -836,7 +868,6 @@ def compute_search_paths(
836868
return SearchPaths(
837869
python_path=tuple(reversed(python_path)),
838870
mypy_path=tuple(mypypath),
839-
# package_path and typeshed_path must be normalised and absolute via os.path.abspath
840871
package_path=tuple(sys_path + site_packages),
841872
typeshed_path=tuple(lib_path),
842873
)

mypy/strconv.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def visit_mypy_file(self, o: mypy.nodes.MypyFile) -> str:
112112
if o.path != "main":
113113
# Insert path. Normalize directory separators to / to unify test
114114
# case# output in all platforms.
115-
a.insert(0, o.path.replace(os.sep, "/"))
115+
a.insert(0, o.path.replace(os.getcwd() + os.sep, "").replace(os.sep, "/"))
116116
if o.ignored_lines:
117117
a.append("IgnoredLines(%s)" % ", ".join(str(line) for line in sorted(o.ignored_lines)))
118118
return self.dump(a, o)

mypy/stubinfo.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,6 @@ def is_module_from_legacy_bundled_package(module: str) -> bool:
66
return top_level in legacy_bundled_packages
77

88

9-
def approved_stub_package_exists(module: str) -> bool:
10-
top_level = module.split(".", 1)[0]
11-
if top_level in legacy_bundled_packages:
12-
return True
13-
if top_level in non_bundled_packages_flat:
14-
return True
15-
if top_level in non_bundled_packages_namespace:
16-
namespace = non_bundled_packages_namespace[top_level]
17-
components = module.split(".")
18-
for i in range(len(components), 0, -1):
19-
module = ".".join(components[:i])
20-
if module in namespace:
21-
return True
22-
return False
23-
24-
259
def stub_distribution_name(module: str) -> str | None:
2610
top_level = module.split(".", 1)[0]
2711

mypy/test/testmodulefinder.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ def test__no_namespace_packages__find_a_in_pkg1(self) -> None:
5353
Find find pkg1/a.py for "a" with namespace_packages False.
5454
"""
5555
found_module = self.fmc_nons.find_module("a")
56-
expected = os.path.join(data_path, "pkg1", "a.py")
56+
expected = os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))
5757
assert_equal(expected, found_module)
5858

5959
def test__no_namespace_packages__find_b_in_pkg2(self) -> None:
6060
found_module = self.fmc_ns.find_module("b")
61-
expected = os.path.join(data_path, "pkg2", "b", "__init__.py")
61+
expected = os.path.abspath(os.path.join(data_path, "pkg2", "b", "__init__.py"))
6262
assert_equal(expected, found_module)
6363

6464
def test__find_nsx_as_namespace_pkg_in_pkg1(self) -> None:
@@ -67,39 +67,39 @@ def test__find_nsx_as_namespace_pkg_in_pkg1(self) -> None:
6767
the path to the first one found in mypypath.
6868
"""
6969
found_module = self.fmc_ns.find_module("nsx")
70-
expected = os.path.join(data_path, "nsx-pkg1", "nsx")
70+
expected = os.path.abspath(os.path.join(data_path, "nsx-pkg1", "nsx"))
7171
assert_equal(expected, found_module)
7272

7373
def test__find_nsx_a_init_in_pkg1(self) -> None:
7474
"""
7575
Find nsx-pkg1/nsx/a/__init__.py for "nsx.a" in namespace mode.
7676
"""
7777
found_module = self.fmc_ns.find_module("nsx.a")
78-
expected = os.path.join(data_path, "nsx-pkg1", "nsx", "a", "__init__.py")
78+
expected = os.path.abspath(os.path.join(data_path, "nsx-pkg1", "nsx", "a", "__init__.py"))
7979
assert_equal(expected, found_module)
8080

8181
def test__find_nsx_b_init_in_pkg2(self) -> None:
8282
"""
8383
Find nsx-pkg2/nsx/b/__init__.py for "nsx.b" in namespace mode.
8484
"""
8585
found_module = self.fmc_ns.find_module("nsx.b")
86-
expected = os.path.join(data_path, "nsx-pkg2", "nsx", "b", "__init__.py")
86+
expected = os.path.abspath(os.path.join(data_path, "nsx-pkg2", "nsx", "b", "__init__.py"))
8787
assert_equal(expected, found_module)
8888

8989
def test__find_nsx_c_c_in_pkg3(self) -> None:
9090
"""
9191
Find nsx-pkg3/nsx/c/c.py for "nsx.c.c" in namespace mode.
9292
"""
9393
found_module = self.fmc_ns.find_module("nsx.c.c")
94-
expected = os.path.join(data_path, "nsx-pkg3", "nsx", "c", "c.py")
94+
expected = os.path.abspath(os.path.join(data_path, "nsx-pkg3", "nsx", "c", "c.py"))
9595
assert_equal(expected, found_module)
9696

9797
def test__find_nsy_a__init_pyi(self) -> None:
9898
"""
9999
Prefer nsy-pkg1/a/__init__.pyi file over __init__.py.
100100
"""
101101
found_module = self.fmc_ns.find_module("nsy.a")
102-
expected = os.path.join(data_path, "nsy-pkg1", "nsy", "a", "__init__.pyi")
102+
expected = os.path.abspath(os.path.join(data_path, "nsy-pkg1", "nsy", "a", "__init__.pyi"))
103103
assert_equal(expected, found_module)
104104

105105
def test__find_nsy_b__init_py(self) -> None:
@@ -109,7 +109,7 @@ def test__find_nsy_b__init_py(self) -> None:
109109
a package is preferred over a module.
110110
"""
111111
found_module = self.fmc_ns.find_module("nsy.b")
112-
expected = os.path.join(data_path, "nsy-pkg2", "nsy", "b", "__init__.py")
112+
expected = os.path.abspath(os.path.join(data_path, "nsy-pkg2", "nsy", "b", "__init__.py"))
113113
assert_equal(expected, found_module)
114114

115115
def test__find_nsy_c_pyi(self) -> None:
@@ -119,17 +119,17 @@ def test__find_nsy_c_pyi(self) -> None:
119119
.pyi is preferred over .py.
120120
"""
121121
found_module = self.fmc_ns.find_module("nsy.c")
122-
expected = os.path.join(data_path, "nsy-pkg2", "nsy", "c.pyi")
122+
expected = os.path.abspath(os.path.join(data_path, "nsy-pkg2", "nsy", "c.pyi"))
123123
assert_equal(expected, found_module)
124124

125125
def test__find_a_in_pkg1(self) -> None:
126126
found_module = self.fmc_ns.find_module("a")
127-
expected = os.path.join(data_path, "pkg1", "a.py")
127+
expected = os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))
128128
assert_equal(expected, found_module)
129129

130130
def test__find_b_init_in_pkg2(self) -> None:
131131
found_module = self.fmc_ns.find_module("b")
132-
expected = os.path.join(data_path, "pkg2", "b", "__init__.py")
132+
expected = os.path.abspath(os.path.join(data_path, "pkg2", "b", "__init__.py"))
133133
assert_equal(expected, found_module)
134134

135135
def test__find_d_nowhere(self) -> None:
@@ -165,7 +165,7 @@ def setUp(self) -> None:
165165
self.fmc_nons = FindModuleCache(self.search_paths, fscache=None, options=options)
166166

167167
def path(self, *parts: str) -> str:
168-
return os.path.normpath(os.path.join(self.package_dir, *parts))
168+
return os.path.abspath(os.path.join(self.package_dir, *parts))
169169

170170
def test__packages_with_ns(self) -> None:
171171
cases = [
@@ -214,7 +214,7 @@ def test__packages_with_ns(self) -> None:
214214
# A regular package with an installed set of stubs
215215
("foo.bar", self.path("foo-stubs", "bar.pyi")),
216216
# A regular, non-site-packages module
217-
("a", os.path.join(data_path, "pkg1", "a.py")),
217+
("a", os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))),
218218
]
219219
for module, expected in cases:
220220
template = "Find(" + module + ") got {}; expected {}"
@@ -269,7 +269,7 @@ def test__packages_without_ns(self) -> None:
269269
# A regular package with an installed set of stubs
270270
("foo.bar", self.path("foo-stubs", "bar.pyi")),
271271
# A regular, non-site-packages module
272-
("a", os.path.join(data_path, "pkg1", "a.py")),
272+
("a", os.path.abspath(os.path.join(data_path, "pkg1", "a.py"))),
273273
]
274274
for module, expected in cases:
275275
template = "Find(" + module + ") got {}; expected {}"

mypy/test/teststubinfo.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import unittest
44

55
from mypy.stubinfo import (
6-
approved_stub_package_exists,
76
is_module_from_legacy_bundled_package,
87
legacy_bundled_packages,
98
non_bundled_packages_flat,
@@ -18,17 +17,6 @@ def test_is_legacy_bundled_packages(self) -> None:
1817
assert is_module_from_legacy_bundled_package("pycurl")
1918
assert is_module_from_legacy_bundled_package("dataclasses")
2019

21-
def test_approved_stub_package_exists(self) -> None:
22-
assert not approved_stub_package_exists("foobar_asdf")
23-
assert approved_stub_package_exists("pycurl")
24-
assert approved_stub_package_exists("babel")
25-
assert approved_stub_package_exists("google.cloud.ndb")
26-
assert approved_stub_package_exists("google.cloud.ndb.submodule")
27-
assert not approved_stub_package_exists("google.cloud.unknown")
28-
assert approved_stub_package_exists("google.protobuf")
29-
assert approved_stub_package_exists("google.protobuf.submodule")
30-
assert not approved_stub_package_exists("google")
31-
3220
def test_stub_distribution_name(self) -> None:
3321
assert stub_distribution_name("foobar_asdf") is None
3422
assert stub_distribution_name("pycurl") == "types-pycurl"

test-data/unit/check-modules.test

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,23 +3136,15 @@ import google.cloud.ndb # E: Library stubs not installed for "google.cloud.ndb"
31363136
from google.cloud import ndb
31373137

31383138
[case testMissingSubmoduleOfInstalledStubPackage]
3139-
import bleach.xyz
3140-
from bleach.abc import fgh
3139+
import bleach.exists
3140+
import bleach.xyz # E: Cannot find implementation or library stub for module named "bleach.xyz" \
3141+
# N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
3142+
from bleach.abc import fgh # E: Cannot find implementation or library stub for module named "bleach.abc"
31413143
[file bleach/__init__.pyi]
3142-
[out]
3143-
main:1: error: Library stubs not installed for "bleach.xyz"
3144-
main:1: note: Hint: "python3 -m pip install types-bleach"
3145-
main:1: note: (or run "mypy --install-types" to install all missing stub packages)
3146-
main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
3147-
main:2: error: Library stubs not installed for "bleach.abc"
3144+
[file bleach/exists.pyi]
31483145

3149-
[case testMissingSubmoduleOfInstalledStubPackageIgnored-xfail]
3146+
[case testMissingSubmoduleOfInstalledStubPackageIgnored]
31503147
# flags: --ignore-missing-imports
3151-
3152-
# TODO: testMissingSubmoduleOfInstalledStubPackageIgnored was regressed in
3153-
# https://github.com/python/mypy/pull/15347 but didn't cause failures because we don't have a
3154-
# package path in this unit test
3155-
31563148
import bleach.xyz
31573149
from bleach.abc import fgh
31583150
[file bleach/__init__.pyi]

0 commit comments

Comments
 (0)
0