diff --git a/mypy/main.py b/mypy/main.py index f177bb1c2062..79ac530dcd94 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -572,6 +572,11 @@ def add_invertible_flag( action="store_true", help="Silently ignore imports of missing modules", ) + imports_group.add_argument( + "--enable-installed-packages", + action="store_true", + help="Typecheck modules without stubs or py.typed marker", + ) imports_group.add_argument( "--follow-imports", choices=["normal", "silent", "skip", "error"], diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 452cfef20f4c..f3ed955dcc5a 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -13,7 +13,7 @@ import subprocess import sys from enum import Enum, unique -from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union +from typing import Dict, Final, List, NamedTuple, Optional, Tuple, Union, cast from typing_extensions import TypeAlias as _TypeAlias from mypy import pyinfo @@ -334,6 +334,19 @@ def _find_module_non_stub_helper( if approved_stub_package_exists(".".join(components[:i])): return ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED if plausible_match: + if self.options: + enable_installed_packages = self.options.enable_installed_packages + try: + enable_installed_packages = cast( + bool, + self.options.per_module_options[components[0]][ + "enable_installed_packages" + ], + ) + except KeyError: + pass + if enable_installed_packages: + return os.path.join(pkg_dir, *components[:-1]), False return ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS else: return ModuleNotFoundReason.NOT_FOUND diff --git a/mypy/options.py b/mypy/options.py index 5e64d5e40035..0a1676ccc7ae 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -39,6 +39,7 @@ class BuildType: "disallow_untyped_defs", "enable_error_code", "enabled_error_codes", + "enable_installed_packages", "extra_checks", "follow_imports_for_stubs", "follow_imports", @@ -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.enable_installed_packages = 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. diff --git a/test-data/unit/pep561.test b/test-data/unit/pep561.test index 9969c2894c36..acde01d0e891 100644 --- a/test-data/unit/pep561.test +++ b/test-data/unit/pep561.test @@ -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 --enable-installed-packages +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