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 over eager types-google-cloud-ndb suggestion #15347

Merged
merged 5 commits into from
Aug 10, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
DecodeError,
decode_python_encoding,
get_mypy_comments,
get_top_two_prefixes,
hash_digest,
is_stub_package_file,
is_sub_path,
Expand Down Expand Up @@ -91,12 +90,7 @@
from mypy.plugins.default import DefaultPlugin
from mypy.renaming import LimitedVariableRenameVisitor, VariableRenameVisitor
from mypy.stats import dump_type_stats
from mypy.stubinfo import (
is_legacy_bundled_package,
legacy_bundled_packages,
non_bundled_packages,
stub_package_name,
)
from mypy.stubinfo import legacy_bundled_packages, non_bundled_packages, stub_distribution_name
from mypy.types import Type
from mypy.typestate import reset_global_state, type_state
from mypy.version import __version__
Expand Down Expand Up @@ -2665,14 +2659,18 @@ def find_module_and_diagnose(
# search path or the module has not been installed.

ignore_missing_imports = options.ignore_missing_imports
top_level, second_level = get_top_two_prefixes(id)

id_components = id.split(".")
# 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. (Unless there are stubs but they are incomplete.)
global_ignore_missing_imports = manager.options.ignore_missing_imports
if (
(is_legacy_bundled_package(top_level) or is_legacy_bundled_package(second_level))
any(
".".join(id_components[:i]) in legacy_bundled_packages
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered if the iteration could be wrapped in a func but can't think of a name that's clearer than the literal code

for i in range(len(id_components), 0, -1)
)
and global_ignore_missing_imports
and not options.ignore_missing_imports_per_module
and result is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
Expand Down Expand Up @@ -2790,15 +2788,19 @@ def module_not_found(
else:
code = codes.IMPORT
errors.report(line, 0, msg.format(module=target), code=code)
top_level, second_level = get_top_two_prefixes(target)
if second_level in legacy_bundled_packages or second_level in non_bundled_packages:
top_level = second_level

components = target.split(".")
for i in range(len(components), 0, -1):
module = ".".join(components[:i])
if module in legacy_bundled_packages or module in non_bundled_packages:
break

for note in notes:
if "{stub_dist}" in note:
note = note.format(stub_dist=stub_package_name(top_level))
note = note.format(stub_dist=stub_distribution_name(module))
errors.report(line, 0, note, severity="note", only_once=True, code=codes.IMPORT)
if reason is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED:
manager.missing_stub_packages.add(stub_package_name(top_level))
manager.missing_stub_packages.add(stub_distribution_name(module))
errors.set_import_context(save_import_context)


Expand Down
9 changes: 2 additions & 7 deletions mypy/modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,14 +337,9 @@ def _find_module_non_stub_helper(
# If this is not a directory then we can't traverse further into it
if not self.fscache.isdir(dir_path):
break
if approved_stub_package_exists(components[0]):
if len(components) == 1 or (
self.find_module(components[0])
is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
):
for i in range(len(components), 0, -1):
if approved_stub_package_exists(".".join(components[:i])):
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
if approved_stub_package_exists(".".join(components[:2])):
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
if plausible_match:
return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS
else:
Expand Down
6 changes: 2 additions & 4 deletions mypy/stubinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ def approved_stub_package_exists(prefix: str) -> bool:
return is_legacy_bundled_package(prefix) or prefix in non_bundled_packages


def stub_package_name(prefix: str) -> str:
def stub_distribution_name(prefix: str) -> str:
return legacy_bundled_packages.get(prefix) or non_bundled_packages[prefix]


# 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",
"bleach": "types-bleach",
Expand Down Expand Up @@ -116,7 +114,7 @@ def stub_package_name(prefix: str) -> str:
"flask_sqlalchemy": "types-Flask-SQLAlchemy",
"fpdf": "types-fpdf2",
"gdb": "types-gdb",
"google.cloud": "types-google-cloud-ndb",
"google.cloud.ndb": "types-google-cloud-ndb",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to update the comment above re: a.b.

"hdbcli": "types-hdbcli",
"html5lib": "types-html5lib",
"httplib2": "types-httplib2",
Expand Down
11 changes: 0 additions & 11 deletions mypy/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,17 +308,6 @@ 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, is_cur_package_init_file: bool
) -> tuple[str, bool]:
Expand Down
24 changes: 13 additions & 11 deletions test-data/unit/check-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -3121,26 +3121,28 @@ import google.cloud
from google.cloud import x

[case testErrorFromGoogleCloud]
import google.cloud
import google.cloud # E: Cannot find implementation or library stub for module named "google.cloud" \
# E: Cannot find implementation or library stub for module named "google"
from google.cloud import x
import google.non_existent
import google.non_existent # E: Cannot find implementation or library stub for module named "google.non_existent"
from google.non_existent import x
[out]
main:1: error: Library stubs not installed for "google.cloud"
main:1: note: Hint: "python3 -m pip install types-google-cloud-ndb"
main:1: note: (or run "mypy --install-types" to install all missing stub packages)
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"
main:3: error: Cannot find implementation or library stub for module named "google.non_existent"

import google.cloud.ndb # E: Library stubs not installed for "google.cloud.ndb" \
# N: Hint: "python3 -m pip install types-google-cloud-ndb" \
# N: (or run "mypy --install-types" to install all missing stub packages) \
# N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
from google.cloud import ndb

[case testMissingSubmoduleOfInstalledStubPackage]
import bleach.xyz
from bleach.abc import fgh
[file bleach/__init__.pyi]
[out]
main:1: error: Cannot find implementation or library stub for module named "bleach.xyz"
main:1: error: Library stubs not installed for "bleach.xyz"
main:1: note: Hint: "python3 -m pip install types-bleach"
main:1: note: (or run "mypy --install-types" to install all missing stub packages)
main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
main:2: error: Cannot find implementation or library stub for module named "bleach.abc"
main:2: error: Library stubs not installed for "bleach.abc"

[case testMissingSubmoduleOfInstalledStubPackageIgnored]
# flags: --ignore-missing-imports
Expand Down