Skip to content

Commit

Permalink
Implement flag to allow typechecking of untyped modules (#17712)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
DeinAlptraum authored Dec 4, 2024
1 parent 411e1f1 commit 242873a
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 2 deletions.
4 changes: 4 additions & 0 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ imports.
For more details, see :ref:`ignore-missing-imports`.

.. option:: --follow-untyped-imports

This flag makes mypy analyze imports without stubs or a py.typed marker.

.. option:: --follow-imports {normal,silent,skip,error}

This flag adjusts how mypy follows imported modules that were not
Expand Down
12 changes: 12 additions & 0 deletions docs/source/config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,18 @@ section of the command line docs.
match the name of the *imported* module, not the module containing the
import statement.

.. confval:: follow_untyped_imports

:type: boolean
:default: False

Typechecks imports from modules that do not have stubs or a py.typed marker.

If this option is used in a per-module section, the module name should
match the name of the *imported* module, not the module containing the
import statement. Note that scanning all unannotated modules might
significantly increase the runtime of your mypy calls.

.. confval:: follow_imports

:type: string
Expand Down
6 changes: 6 additions & 0 deletions docs/source/running_mypy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,12 @@ not catch errors in its use.
recommend avoiding ``--ignore-missing-imports`` if possible: it's equivalent
to adding a ``# type: ignore`` to all unresolved imports in your codebase.

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


Library stubs not installed
---------------------------
Expand Down
5 changes: 5 additions & 0 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,11 @@ def add_invertible_flag(
action="store_true",
help="Silently ignore imports of missing modules",
)
imports_group.add_argument(
"--follow-untyped-imports",
action="store_true",
help="Typecheck modules without stubs or py.typed marker",
)
imports_group.add_argument(
"--follow-imports",
choices=["normal", "silent", "skip", "error"],
Expand Down
9 changes: 7 additions & 2 deletions mypy/modulefinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,11 @@ def _typeshed_has_version(self, module: str) -> bool:
return version >= min_version and (max_version is None or version <= max_version)

def _find_module_non_stub_helper(
self, components: list[str], pkg_dir: str
self, id: str, pkg_dir: str
) -> OnePackageDir | ModuleNotFoundReason:
plausible_match = False
dir_path = pkg_dir
components = id.split(".")
for index, component in enumerate(components):
dir_path = os_path_join(dir_path, component)
if self.fscache.isfile(os_path_join(dir_path, "py.typed")):
Expand All @@ -350,6 +351,10 @@ def _find_module_non_stub_helper(
if not self.fscache.isdir(dir_path):
break
if plausible_match:
if self.options:
module_specific_options = self.options.clone_for_module(id)
if module_specific_options.follow_untyped_imports:
return os.path.join(pkg_dir, *components[:-1]), False
return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS
else:
return ModuleNotFoundReason.NOT_FOUND
Expand Down Expand Up @@ -463,7 +468,7 @@ def _find_module(self, id: str, use_typeshed: bool) -> ModuleSearchResult:
third_party_stubs_dirs.append((path, True))
else:
third_party_stubs_dirs.append((path, True))
non_stub_match = self._find_module_non_stub_helper(components, pkg_dir)
non_stub_match = self._find_module_non_stub_helper(id, pkg_dir)
if isinstance(non_stub_match, ModuleNotFoundReason):
if non_stub_match is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS:
found_possible_third_party_missing_type_hints = True
Expand Down
3 changes: 3 additions & 0 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class BuildType:
"extra_checks",
"follow_imports_for_stubs",
"follow_imports",
"follow_untyped_imports",
"ignore_errors",
"ignore_missing_imports",
"implicit_optional",
Expand Down Expand Up @@ -113,6 +114,8 @@ def __init__(self) -> None:
self.ignore_missing_imports = False
# Is ignore_missing_imports set in a per-module section
self.ignore_missing_imports_per_module = False
# Typecheck modules without stubs or py.typed marker
self.follow_untyped_imports = False
self.follow_imports = "normal" # normal|silent|skip|error
# Whether to respect the follow_imports setting even for stub files.
# Intended to be used for disabling specific stubs.
Expand Down
7 changes: 7 additions & 0 deletions test-data/unit/pep561.test
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ b.bf(1)
testNamespacePkgWStubsWithNamespacePackagesFlag.py:7: error: Argument 1 to "bf" has incompatible type "int"; expected "bool"
testNamespacePkgWStubsWithNamespacePackagesFlag.py:8: error: Argument 1 to "bf" has incompatible type "int"; expected "bool"

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

[case testTypedPkgNamespaceRegFromImportTwiceMissing]
# pkgs: typedpkg_ns_a
Expand Down

0 comments on commit 242873a

Please sign in to comment.