Skip to content

Commit a677f49

Browse files
authored
Suggest additional types-* packages from typeshed (#13698)
Now we have suggestions for almost all the currently available third-party packages in typeshed that don't have a release that includes PEP 561 type information. `sqlalchemy` is not included, since it has a few alternatives (some outside typeshed), and I don't know which we should suggest. These stubs were never bundled with mypy, so `--ignore-missing-imports` works with these, unlike the existing packages for which we have suggestions. Here's an example mypy output: ``` t.py:1: error: Library stubs not installed for "tree_sitter" t.py:1: note: Hint: "python3 -m pip install types-tree-sitter" t.py:1: note: (or run "mypy --install-types" to install all missing stub packages) t.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports ``` I manually tested that `--install-types` works for these.
1 parent 0a720ed commit a677f49

File tree

6 files changed

+128
-14
lines changed

6 files changed

+128
-14
lines changed

mypy/build.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@
9292
from mypy.plugins.default import DefaultPlugin
9393
from mypy.renaming import LimitedVariableRenameVisitor, VariableRenameVisitor
9494
from mypy.stats import dump_type_stats
95-
from mypy.stubinfo import is_legacy_bundled_package, legacy_bundled_packages
95+
from mypy.stubinfo import (
96+
is_legacy_bundled_package,
97+
legacy_bundled_packages,
98+
non_bundled_packages,
99+
stub_package_name,
100+
)
96101
from mypy.types import Type
97102
from mypy.typestate import TypeState, reset_global_state
98103
from mypy.version import __version__
@@ -2740,14 +2745,14 @@ def module_not_found(
27402745
msg, notes = reason.error_message_templates(daemon)
27412746
errors.report(line, 0, msg.format(module=target), code=codes.IMPORT)
27422747
top_level, second_level = get_top_two_prefixes(target)
2743-
if second_level in legacy_bundled_packages:
2748+
if second_level in legacy_bundled_packages or second_level in non_bundled_packages:
27442749
top_level = second_level
27452750
for note in notes:
27462751
if "{stub_dist}" in note:
2747-
note = note.format(stub_dist=legacy_bundled_packages[top_level])
2752+
note = note.format(stub_dist=stub_package_name(top_level))
27482753
errors.report(line, 0, note, severity="note", only_once=True, code=codes.IMPORT)
27492754
if reason is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED:
2750-
manager.missing_stub_packages.add(legacy_bundled_packages[top_level])
2755+
manager.missing_stub_packages.add(stub_package_name(top_level))
27512756
errors.set_import_context(save_import_context)
27522757

27532758

mypy/modulefinder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from mypy.fscache import FileSystemCache
2929
from mypy.nodes import MypyFile
3030
from mypy.options import Options
31-
from mypy.stubinfo import is_legacy_bundled_package
31+
from mypy.stubinfo import approved_stub_package_exists
3232

3333

3434
# Paths to be searched in find_module().
@@ -336,13 +336,13 @@ def _find_module_non_stub_helper(
336336
# If this is not a directory then we can't traverse further into it
337337
if not self.fscache.isdir(dir_path):
338338
break
339-
if is_legacy_bundled_package(components[0]):
339+
if approved_stub_package_exists(components[0]):
340340
if len(components) == 1 or (
341341
self.find_module(components[0])
342342
is ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
343343
):
344344
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
345-
if is_legacy_bundled_package(".".join(components[:2])):
345+
if approved_stub_package_exists(".".join(components[:2])):
346346
return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
347347
if plausible_match:
348348
return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS

mypy/stubinfo.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ def is_legacy_bundled_package(prefix: str) -> bool:
55
return prefix in legacy_bundled_packages
66

77

8+
def approved_stub_package_exists(prefix: str) -> bool:
9+
return is_legacy_bundled_package(prefix) or prefix in non_bundled_packages
10+
11+
12+
def stub_package_name(prefix: str) -> str:
13+
return legacy_bundled_packages.get(prefix) or non_bundled_packages[prefix]
14+
15+
816
# Stubs for these third-party packages used to be shipped with mypy.
917
#
1018
# Map package name to PyPI stub distribution name.
@@ -64,3 +72,98 @@ def is_legacy_bundled_package(prefix: str) -> bool:
6472
"waitress": "types-waitress",
6573
"yaml": "types-PyYAML",
6674
}
75+
76+
# Map package name to PyPI stub distribution name from typeshed.
77+
# Stubs for these packages were never bundled with mypy. Don't
78+
# include packages that have a release that includes PEP 561 type
79+
# information.
80+
#
81+
# Package name can have one or two components ('a' or 'a.b').
82+
#
83+
# Note that these packages are omitted for now:
84+
# sqlalchemy: It's unclear which stub package to suggest. There's also
85+
# a mypy plugin available.
86+
non_bundled_packages = {
87+
"MySQLdb": "types-mysqlclient",
88+
"PIL": "types-Pillow",
89+
"PyInstaller": "types-pyinstaller",
90+
"annoy": "types-annoy",
91+
"appdirs": "types-appdirs",
92+
"aws_xray_sdk": "types-aws-xray-sdk",
93+
"babel": "types-babel",
94+
"backports.ssl_match_hostname": "types-backports.ssl_match_hostname",
95+
"braintree": "types-braintree",
96+
"bs4": "types-beautifulsoup4",
97+
"bugbear": "types-flake8-bugbear",
98+
"caldav": "types-caldav",
99+
"cffi": "types-cffi",
100+
"chevron": "types-chevron",
101+
"colorama": "types-colorama",
102+
"commonmark": "types-commonmark",
103+
"cryptography": "types-cryptography",
104+
"d3dshot": "types-D3DShot",
105+
"dj_database_url": "types-dj-database-url",
106+
"docopt": "types-docopt",
107+
"editdistance": "types-editdistance",
108+
"entrypoints": "types-entrypoints",
109+
"farmhash": "types-pyfarmhash",
110+
"flake8_2020": "types-flake8-2020",
111+
"flake8_builtins": "types-flake8-builtins",
112+
"flake8_docstrings": "types-flake8-docstrings",
113+
"flake8_plugin_utils": "types-flake8-plugin-utils",
114+
"flake8_rst_docstrings": "types-flake8-rst-docstrings",
115+
"flake8_simplify": "types-flake8-simplify",
116+
"flake8_typing_imports": "types-flake8-typing-imports",
117+
"flask_cors": "types-Flask-Cors",
118+
"flask_sqlalchemy": "types-Flask-SQLAlchemy",
119+
"fpdf": "types-fpdf2",
120+
"gdb": "types-gdb",
121+
"google.cloud": "types-google-cloud-ndb",
122+
"hdbcli": "types-hdbcli",
123+
"html5lib": "types-html5lib",
124+
"httplib2": "types-httplib2",
125+
"humanfriendly": "types-humanfriendly",
126+
"invoke": "types-invoke",
127+
"jack": "types-JACK-Client",
128+
"jmespath": "types-jmespath",
129+
"jose": "types-python-jose",
130+
"jsonschema": "types-jsonschema",
131+
"keyboard": "types-keyboard",
132+
"ldap3": "types-ldap3",
133+
"nmap": "types-python-nmap",
134+
"oauthlib": "types-oauthlib",
135+
"openpyxl": "types-openpyxl",
136+
"opentracing": "types-opentracing",
137+
"parsimonious": "types-parsimonious",
138+
"passlib": "types-passlib",
139+
"passpy": "types-passpy",
140+
"pep8ext_naming": "types-pep8-naming",
141+
"playsound": "types-playsound",
142+
"prettytable": "types-prettytable",
143+
"psutil": "types-psutil",
144+
"psycopg2": "types-psycopg2",
145+
"pyaudio": "types-pyaudio",
146+
"pyautogui": "types-PyAutoGUI",
147+
"pyflakes": "types-pyflakes",
148+
"pygments": "types-Pygments",
149+
"pyi_splash": "types-pyinstaller",
150+
"pynput": "types-pynput",
151+
"pysftp": "types-pysftp",
152+
"pytest_lazyfixture": "types-pytest-lazy-fixture",
153+
"regex": "types-regex",
154+
"send2trash": "types-Send2Trash",
155+
"slumber": "types-slumber",
156+
"stdlib_list": "types-stdlib-list",
157+
"stripe": "types-stripe",
158+
"toposort": "types-toposort",
159+
"tqdm": "types-tqdm",
160+
"tree_sitter": "types-tree-sitter",
161+
"tree_sitter_languages": "types-tree-sitter-languages",
162+
"ttkthemes": "types-ttkthemes",
163+
"urllib3": "types-urllib3",
164+
"vobject": "types-vobject",
165+
"whatthepatch": "types-whatthepatch",
166+
"xmltodict": "types-xmltodict",
167+
"xxhash": "types-xxhash",
168+
"zxcvbn": "types-zxcvbn",
169+
}

test-data/unit/check-modules.test

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3111,10 +3111,15 @@ from google.cloud import x
31113111
[case testErrorFromGoogleCloud]
31123112
import google.cloud
31133113
from google.cloud import x
3114+
import google.non_existent
3115+
from google.non_existent import x
31143116
[out]
3115-
main:1: error: Cannot find implementation or library stub for module named "google.cloud"
3117+
main:1: error: Library stubs not installed for "google.cloud"
3118+
main:1: note: Hint: "python3 -m pip install types-google-cloud-ndb"
3119+
main:1: note: (or run "mypy --install-types" to install all missing stub packages)
31163120
main:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
31173121
main:1: error: Cannot find implementation or library stub for module named "google"
3122+
main:3: error: Cannot find implementation or library stub for module named "google.non_existent"
31183123

31193124
[case testMissingSubmoduleOfInstalledStubPackage]
31203125
import bleach.xyz

test-data/unit/fine-grained-modules.test

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,17 +2192,17 @@ x = 'x'
21922192
[case testLibraryStubsNotInstalled]
21932193
import a
21942194
[file a.py]
2195-
import waitress
2195+
import requests
21962196
[file a.py.2]
21972197
# nothing
21982198
[file a.py.3]
2199-
import requests
2199+
import jack
22002200
[out]
2201-
a.py:1: error: Library stubs not installed for "waitress"
2202-
a.py:1: note: Hint: "python3 -m pip install types-waitress"
2201+
a.py:1: error: Library stubs not installed for "requests"
2202+
a.py:1: note: Hint: "python3 -m pip install types-requests"
22032203
a.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
22042204
==
22052205
==
2206-
a.py:1: error: Library stubs not installed for "requests"
2207-
a.py:1: note: Hint: "python3 -m pip install types-requests"
2206+
a.py:1: error: Library stubs not installed for "jack"
2207+
a.py:1: note: Hint: "python3 -m pip install types-JACK-Client"
22082208
a.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports

test-data/unit/pythoneval.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,7 @@ import scribe # No Python 3 stubs available for scribe
15611561
from scribe import x
15621562
import maxminddb # Python 3 stubs available for maxminddb
15631563
import foobar_asdf
1564+
import jack # This has a stubs package but was never bundled with mypy, so ignoring works
15641565
[out]
15651566
_testIgnoreImportIfNoPython3StubAvailable.py:4: error: Library stubs not installed for "maxminddb"
15661567
_testIgnoreImportIfNoPython3StubAvailable.py:4: note: Hint: "python3 -m pip install types-maxminddb"

0 commit comments

Comments
 (0)