Skip to content

Commit

Permalink
Fix confusion between stubs for google.protobuf and google.cloud (#10609
Browse files Browse the repository at this point in the history
)

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.
  • Loading branch information
JukkaL committed Jun 10, 2021
1 parent fee4e8c commit 8f51fbf
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 6 deletions.
10 changes: 6 additions & 4 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from mypy.errors import Errors, CompileError, ErrorInfo, report_internal_error
from mypy.util import (
DecodeError, decode_python_encoding, is_sub_path, get_mypy_comments, module_prefix,
read_py_file, hash_digest, is_typeshed_file, is_stub_package_file
read_py_file, hash_digest, is_typeshed_file, is_stub_package_file, get_top_two_prefixes
)
if TYPE_CHECKING:
from mypy.report import Reports # Avoid unconditional slow import
Expand Down Expand Up @@ -2443,13 +2443,13 @@ def find_module_and_diagnose(manager: BuildManager,
# search path or the module has not been installed.

ignore_missing_imports = options.ignore_missing_imports
top_level = file_id.partition('.')[0]
top_level, second_level = get_top_two_prefixes(file_id)
# Don't honor a global (not per-module) ignore_missing_imports
# setting for modules that used to have bundled stubs, as
# otherwise updating mypy can silently result in new false
# negatives.
global_ignore_missing_imports = manager.options.ignore_missing_imports
if (top_level in legacy_bundled_packages
if ((top_level in legacy_bundled_packages or second_level in legacy_bundled_packages)
and global_ignore_missing_imports
and not options.ignore_missing_imports_per_module):
ignore_missing_imports = False
Expand Down Expand Up @@ -2553,7 +2553,9 @@ def module_not_found(manager: BuildManager, line: int, caller_state: State,
msg, notes = reason.error_message_templates(daemon)
pyver = '%d.%d' % manager.options.python_version
errors.report(line, 0, msg.format(module=target, pyver=pyver), code=codes.IMPORT)
top_level = target.partition('.')[0]
top_level, second_level = get_top_two_prefixes(target)
if second_level in legacy_bundled_packages:
top_level = second_level
for note in notes:
if '{stub_dist}' in note:
note = note.format(stub_dist=legacy_bundled_packages[top_level])
Expand Down
3 changes: 2 additions & 1 deletion mypy/modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ def _find_module_non_stub_helper(self, components: List[str],
elif not plausible_match and (self.fscache.isdir(dir_path)
or self.fscache.isfile(dir_path + ".py")):
plausible_match = True
if components[0] in legacy_bundled_packages:
if (components[0] in legacy_bundled_packages
or '.'.join(components[:2]) in legacy_bundled_packages):
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
elif plausible_match:
return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS
Expand Down
4 changes: 3 additions & 1 deletion mypy/stubinfo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Stubs for these third-party packages used to be shipped with mypy.
#
# Map package name to PyPI stub distribution name.
#
# Package name can have one or two components ('a' or 'a.b').
legacy_bundled_packages = {
'aiofiles': 'types-aiofiles',
'atomicwrites': 'types-atomicwrites',
Expand Down Expand Up @@ -36,7 +38,7 @@
'frozendict': 'types-frozendict',
'geoip2': 'types-geoip2',
'gflags': 'types-python-gflags',
'google': 'types-protobuf',
'google.protobuf': 'types-protobuf',
'ipaddress': 'types-ipaddress',
'itsdangerous': 'types-itsdangerous',
'jinja2': 'types-Jinja2',
Expand Down
11 changes: 11 additions & 0 deletions mypy/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,17 @@ def get_prefix(fullname: str) -> str:
return fullname.rsplit('.', 1)[0]


def get_top_two_prefixes(fullname: str) -> Tuple[str, str]:
"""Return one and two component prefixes of a fully qualified name.
Given 'a.b.c.d', return ('a', 'a.b').
If fullname has only one component, return (fullname, fullname).
"""
components = fullname.split('.', 3)
return components[0], '.'.join(components[:2])


def correct_relative_import(cur_mod_id: str,
relative: int,
target: str,
Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/check-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -3094,3 +3094,16 @@ ignore_missing_imports = true
\[[tool.mypy.overrides]]
module = "foobar1"
ignore_missing_imports = true

[case testIgnoreErrorFromGoogleCloud]
# flags: --ignore-missing-imports
import google.cloud
from google.cloud import x

[case testErrorFromGoogleCloud]
import google.cloud
from google.cloud import x
[out]
main:1: error: Cannot find implementation or library stub for module named "google.cloud"
main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
main:1: error: Cannot find implementation or library stub for module named "google"

0 comments on commit 8f51fbf

Please sign in to comment.