Skip to content

Commit 242873a

Browse files
authored
Implement flag to allow typechecking of untyped modules (#17712)
Add a flag and config ini options `"follow_untyped_imports"`. Setting it to `True` instructs mypy to typecheck also modules that do not have stubs or a `py.typed` marker. Fixes #8545
1 parent 411e1f1 commit 242873a

File tree

7 files changed

+44
-2
lines changed

7 files changed

+44
-2
lines changed

docs/source/command_line.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ imports.
166166
167167
For more details, see :ref:`ignore-missing-imports`.
168168

169+
.. option:: --follow-untyped-imports
170+
171+
This flag makes mypy analyze imports without stubs or a py.typed marker.
172+
169173
.. option:: --follow-imports {normal,silent,skip,error}
170174

171175
This flag adjusts how mypy follows imported modules that were not

docs/source/config_file.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,18 @@ section of the command line docs.
315315
match the name of the *imported* module, not the module containing the
316316
import statement.
317317

318+
.. confval:: follow_untyped_imports
319+
320+
:type: boolean
321+
:default: False
322+
323+
Typechecks imports from modules that do not have stubs or a py.typed marker.
324+
325+
If this option is used in a per-module section, the module name should
326+
match the name of the *imported* module, not the module containing the
327+
import statement. Note that scanning all unannotated modules might
328+
significantly increase the runtime of your mypy calls.
329+
318330
.. confval:: follow_imports
319331

320332
:type: string

docs/source/running_mypy.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,12 @@ not catch errors in its use.
321321
recommend avoiding ``--ignore-missing-imports`` if possible: it's equivalent
322322
to adding a ``# type: ignore`` to all unresolved imports in your codebase.
323323

324+
4. To make mypy typecheck imports from modules without stubs or a py.typed
325+
marker, you can set the :option:`--follow-untyped-imports <mypy --follow-untyped-imports>`
326+
command line flag or set the :confval:`follow_untyped_imports` config file option to True,
327+
either in the global section of your mypy config file, or individually on a
328+
per-module basis.
329+
324330

325331
Library stubs not installed
326332
---------------------------

mypy/main.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,11 @@ def add_invertible_flag(
580580
action="store_true",
581581
help="Silently ignore imports of missing modules",
582582
)
583+
imports_group.add_argument(
584+
"--follow-untyped-imports",
585+
action="store_true",
586+
help="Typecheck modules without stubs or py.typed marker",
587+
)
583588
imports_group.add_argument(
584589
"--follow-imports",
585590
choices=["normal", "silent", "skip", "error"],

mypy/modulefinder.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,10 +334,11 @@ def _typeshed_has_version(self, module: str) -> bool:
334334
return version >= min_version and (max_version is None or version <= max_version)
335335

336336
def _find_module_non_stub_helper(
337-
self, components: list[str], pkg_dir: str
337+
self, id: str, pkg_dir: str
338338
) -> OnePackageDir | ModuleNotFoundReason:
339339
plausible_match = False
340340
dir_path = pkg_dir
341+
components = id.split(".")
341342
for index, component in enumerate(components):
342343
dir_path = os_path_join(dir_path, component)
343344
if self.fscache.isfile(os_path_join(dir_path, "py.typed")):
@@ -350,6 +351,10 @@ def _find_module_non_stub_helper(
350351
if not self.fscache.isdir(dir_path):
351352
break
352353
if plausible_match:
354+
if self.options:
355+
module_specific_options = self.options.clone_for_module(id)
356+
if module_specific_options.follow_untyped_imports:
357+
return os.path.join(pkg_dir, *components[:-1]), False
353358
return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS
354359
else:
355360
return ModuleNotFoundReason.NOT_FOUND
@@ -463,7 +468,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
463468
third_party_stubs_dirs.append((path, True))
464469
else:
465470
third_party_stubs_dirs.append((path, True))
466-
non_stub_match = self._find_module_non_stub_helper(components, pkg_dir)
471+
non_stub_match = self._find_module_non_stub_helper(id, pkg_dir)
467472
if isinstance(non_stub_match, ModuleNotFoundReason):
468473
if non_stub_match is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS:
469474
found_possible_third_party_missing_type_hints = True

mypy/options.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class BuildType:
4242
"extra_checks",
4343
"follow_imports_for_stubs",
4444
"follow_imports",
45+
"follow_untyped_imports",
4546
"ignore_errors",
4647
"ignore_missing_imports",
4748
"implicit_optional",
@@ -113,6 +114,8 @@ def __init__(self) -> None:
113114
self.ignore_missing_imports = False
114115
# Is ignore_missing_imports set in a per-module section
115116
self.ignore_missing_imports_per_module = False
117+
# Typecheck modules without stubs or py.typed marker
118+
self.follow_untyped_imports = False
116119
self.follow_imports = "normal" # normal|silent|skip|error
117120
# Whether to respect the follow_imports setting even for stub files.
118121
# Intended to be used for disabling specific stubs.

test-data/unit/pep561.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ b.bf(1)
187187
testNamespacePkgWStubsWithNamespacePackagesFlag.py:7: error: Argument 1 to "bf" has incompatible type "int"; expected "bool"
188188
testNamespacePkgWStubsWithNamespacePackagesFlag.py:8: error: Argument 1 to "bf" has incompatible type "int"; expected "bool"
189189

190+
[case testMissingPytypedFlag]
191+
# pkgs: typedpkg_ns_b
192+
# flags: --namespace-packages --follow-untyped-imports
193+
import typedpkg_ns.b.bbb as b
194+
b.bf("foo", "bar")
195+
[out]
196+
testMissingPytypedFlag.py:4: error: Too many arguments for "bf"
190197

191198
[case testTypedPkgNamespaceRegFromImportTwiceMissing]
192199
# pkgs: typedpkg_ns_a

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy