Skip to content

Commit 0fa975d

Browse files
Merge branch 'python:master' into callable_pretty
2 parents 2c499d5 + 5082a22 commit 0fa975d

29 files changed

+486
-212
lines changed

docs/source/command_line.rst

Lines changed: 9 additions & 5 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
@@ -537,12 +541,12 @@ potentially problematic or redundant in some way.
537541

538542
This limitation will be removed in future releases of mypy.
539543

540-
.. option:: --report-deprecated-as-error
544+
.. option:: --report-deprecated-as-note
541545

542-
By default, mypy emits notes if your code imports or uses deprecated
543-
features. This flag converts such notes to errors, causing mypy to
544-
eventually finish with a non-zero exit code. Features are considered
545-
deprecated when decorated with ``warnings.deprecated``.
546+
If error code ``deprecated`` is enabled, mypy emits errors if your code
547+
imports or uses deprecated features. This flag converts such errors to
548+
notes, causing mypy to eventually finish with a zero exit code. Features
549+
are considered deprecated when decorated with ``warnings.deprecated``.
546550

547551
.. _miscellaneous-strictness-flags:
548552

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/error_code_list2.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,13 @@ incorrect control flow or conditional checks that are accidentally always true o
236236
Check that imported or used feature is deprecated [deprecated]
237237
--------------------------------------------------------------
238238

239-
By default, mypy generates a note if your code imports a deprecated feature explicitly with a
239+
If you use :option:`--enable-error-code deprecated <mypy --enable-error-code>`,
240+
mypy generates an error if your code imports a deprecated feature explicitly with a
240241
``from mod import depr`` statement or uses a deprecated feature imported otherwise or defined
241242
locally. Features are considered deprecated when decorated with ``warnings.deprecated``, as
242-
specified in `PEP 702 <https://peps.python.org/pep-0702>`_. You can silence single notes via
243-
``# type: ignore[deprecated]`` or turn off this check completely via ``--disable-error-code=deprecated``.
244-
Use the :option:`--report-deprecated-as-error <mypy --report-deprecated-as-error>` option for
245-
more strictness, which turns all such notes into errors.
243+
specified in `PEP 702 <https://peps.python.org/pep-0702>`_.
244+
Use the :option:`--report-deprecated-as-note <mypy --report-deprecated-as-note>` option to
245+
turn all such errors into notes.
246246

247247
.. note::
248248

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/checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7696,7 +7696,7 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None:
76967696
and ((deprecated := node.deprecated) is not None)
76977697
and not self.is_typeshed_stub
76987698
):
7699-
warn = self.msg.fail if self.options.report_deprecated_as_error else self.msg.note
7699+
warn = self.msg.note if self.options.report_deprecated_as_note else self.msg.fail
77007700
warn(deprecated, context, code=codes.DEPRECATED)
77017701

77027702

mypy/errorcodes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ def __hash__(self) -> int:
308308
"deprecated",
309309
"Warn when importing or using deprecated (overloaded) functions, methods or classes",
310310
"General",
311+
default_enabled=False,
311312
)
312313

313314
# This copy will not include any error codes defined later in the plugins.

mypy/main.py

Lines changed: 7 additions & 2 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"],
@@ -805,10 +810,10 @@ def add_invertible_flag(
805810
group=lint_group,
806811
)
807812
add_invertible_flag(
808-
"--report-deprecated-as-error",
813+
"--report-deprecated-as-note",
809814
default=False,
810815
strict_flag=False,
811-
help="Report importing or using deprecated features as errors instead of notes",
816+
help="Report importing or using deprecated features as notes instead of errors",
812817
group=lint_group,
813818
)
814819

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: 4 additions & 1 deletion
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.
@@ -174,7 +177,7 @@ def __init__(self) -> None:
174177
self.warn_return_any = False
175178

176179
# Report importing or using deprecated features as errors instead of notes.
177-
self.report_deprecated_as_error = False
180+
self.report_deprecated_as_note = False
178181

179182
# Warn about unused '# type: ignore' comments
180183
self.warn_unused_ignores = False

mypy/typeanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,7 @@ def check_and_warn_deprecated(self, info: TypeInfo, ctx: Context) -> None:
790790
if isinstance(imp, ImportFrom) and any(info.name == n[0] for n in imp.names):
791791
break
792792
else:
793-
warn = self.fail if self.options.report_deprecated_as_error else self.note
793+
warn = self.note if self.options.report_deprecated_as_note else self.fail
794794
warn(deprecated, ctx, code=codes.DEPRECATED)
795795

796796
def analyze_type_with_type_info(

0 commit comments

Comments
 (0)