Skip to content

Commit 8f51fbf

Browse files
committed
Fix confusion between stubs for google.protobuf and google.cloud (#10609)
Previously we used `google` as a package prefix for `google.protobuf`. That wasn't correct, since there are other packages in the Google namespace, such as `google.cloud`. This fixes the prefix to to be `google.protobuf`. Fixes #10601.
1 parent fee4e8c commit 8f51fbf

File tree

5 files changed

+35
-6
lines changed

5 files changed

+35
-6
lines changed

mypy/build.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from mypy.errors import Errors, CompileError, ErrorInfo, report_internal_error
3636
from mypy.util import (
3737
DecodeError, decode_python_encoding, is_sub_path, get_mypy_comments, module_prefix,
38-
read_py_file, hash_digest, is_typeshed_file, is_stub_package_file
38+
read_py_file, hash_digest, is_typeshed_file, is_stub_package_file, get_top_two_prefixes
3939
)
4040
if TYPE_CHECKING:
4141
from mypy.report import Reports # Avoid unconditional slow import
@@ -2443,13 +2443,13 @@ def find_module_and_diagnose(manager: BuildManager,
24432443
# search path or the module has not been installed.
24442444

24452445
ignore_missing_imports = options.ignore_missing_imports
2446-
top_level = file_id.partition('.')[0]
2446+
top_level, second_level = get_top_two_prefixes(file_id)
24472447
# Don't honor a global (not per-module) ignore_missing_imports
24482448
# setting for modules that used to have bundled stubs, as
24492449
# otherwise updating mypy can silently result in new false
24502450
# negatives.
24512451
global_ignore_missing_imports = manager.options.ignore_missing_imports
2452-
if (top_level in legacy_bundled_packages
2452+
if ((top_level in legacy_bundled_packages or second_level in legacy_bundled_packages)
24532453
and global_ignore_missing_imports
24542454
and not options.ignore_missing_imports_per_module):
24552455
ignore_missing_imports = False
@@ -2553,7 +2553,9 @@ def module_not_found(manager: BuildManager, line: int, caller_state: State,
25532553
msg, notes = reason.error_message_templates(daemon)
25542554
pyver = '%d.%d' % manager.options.python_version
25552555
errors.report(line, 0, msg.format(module=target, pyver=pyver), code=codes.IMPORT)
2556-
top_level = target.partition('.')[0]
2556+
top_level, second_level = get_top_two_prefixes(target)
2557+
if second_level in legacy_bundled_packages:
2558+
top_level = second_level
25572559
for note in notes:
25582560
if '{stub_dist}' in note:
25592561
note = note.format(stub_dist=legacy_bundled_packages[top_level])

mypy/modulefinder.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ def _find_module_non_stub_helper(self, components: List[str],
230230
elif not plausible_match and (self.fscache.isdir(dir_path)
231231
or self.fscache.isfile(dir_path + ".py")):
232232
plausible_match = True
233-
if components[0] in legacy_bundled_packages:
233+
if (components[0] in legacy_bundled_packages
234+
or '.'.join(components[:2]) in legacy_bundled_packages):
234235
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
235236
elif plausible_match:
236237
return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS

mypy/stubinfo.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Stubs for these third-party packages used to be shipped with mypy.
22
#
33
# Map package name to PyPI stub distribution name.
4+
#
5+
# Package name can have one or two components ('a' or 'a.b').
46
legacy_bundled_packages = {
57
'aiofiles': 'types-aiofiles',
68
'atomicwrites': 'types-atomicwrites',
@@ -36,7 +38,7 @@
3638
'frozendict': 'types-frozendict',
3739
'geoip2': 'types-geoip2',
3840
'gflags': 'types-python-gflags',
39-
'google': 'types-protobuf',
41+
'google.protobuf': 'types-protobuf',
4042
'ipaddress': 'types-ipaddress',
4143
'itsdangerous': 'types-itsdangerous',
4244
'jinja2': 'types-Jinja2',

mypy/util.py

+11
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,17 @@ def get_prefix(fullname: str) -> str:
284284
return fullname.rsplit('.', 1)[0]
285285

286286

287+
def get_top_two_prefixes(fullname: str) -> Tuple[str, str]:
288+
"""Return one and two component prefixes of a fully qualified name.
289+
290+
Given 'a.b.c.d', return ('a', 'a.b').
291+
292+
If fullname has only one component, return (fullname, fullname).
293+
"""
294+
components = fullname.split('.', 3)
295+
return components[0], '.'.join(components[:2])
296+
297+
287298
def correct_relative_import(cur_mod_id: str,
288299
relative: int,
289300
target: str,

test-data/unit/check-modules.test

+13
Original file line numberDiff line numberDiff line change
@@ -3094,3 +3094,16 @@ ignore_missing_imports = true
30943094
\[[tool.mypy.overrides]]
30953095
module = "foobar1"
30963096
ignore_missing_imports = true
3097+
3098+
[case testIgnoreErrorFromGoogleCloud]
3099+
# flags: --ignore-missing-imports
3100+
import google.cloud
3101+
from google.cloud import x
3102+
3103+
[case testErrorFromGoogleCloud]
3104+
import google.cloud
3105+
from google.cloud import x
3106+
[out]
3107+
main:1: error: Cannot find implementation or library stub for module named "google.cloud"
3108+
main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
3109+
main:1: error: Cannot find implementation or library stub for module named "google"

0 commit comments

Comments
 (0)