Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix confusion between stubs for google.protobuf and google.cloud #10609

Merged
merged 1 commit into from Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 6 additions & 4 deletions mypy/build.py
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
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
@@ -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
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
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"