Skip to content

Commit 67f2165

Browse files
Fix dmypy not respecting @no_type_check after change in dependency
1 parent 74ecdd8 commit 67f2165

4 files changed

Lines changed: 34 additions & 0 deletions

File tree

mypy/checker.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,11 @@ def visit_func_def(self, defn: FuncDef) -> None:
13321332
self.visit_func_def_impl(defn)
13331333

13341334
def visit_func_def_impl(self, defn: FuncDef) -> None:
1335+
if defn.is_no_type_check:
1336+
# @typing.no_type_check: the body must be skipped. The decorator
1337+
# dispatch (visit_decorator) handles this at the top level, but a
1338+
# fine-grained reprocess can re-check the bare FuncDef target.
1339+
return
13351340
with self.tscope.function_scope(defn), self.set_recurse_into_functions():
13361341
self.check_func_item(defn, name=defn.name)
13371342
if not self.can_skip_diagnostics:

mypy/nodes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,7 @@ def is_dynamic(self) -> bool:
10831083
"is_trivial_body",
10841084
"is_trivial_self",
10851085
"is_mypy_only",
1086+
"is_no_type_check",
10861087
]
10871088

10881089
# Abstract status of a function
@@ -1109,6 +1110,7 @@ class FuncDef(FuncItem, SymbolNode, Statement):
11091110
"is_trivial_self",
11101111
"is_invalid_redefinition",
11111112
"is_mypy_only",
1113+
"is_no_type_check",
11121114
# Present only when a function is decorated with @typing.dataclass_transform or similar
11131115
"dataclass_transform_spec",
11141116
"docstring",
@@ -1139,6 +1141,10 @@ def __init__(
11391141
self.original_def: None | FuncDef | Var | Decorator = None
11401142
# Definitions that appear in if TYPE_CHECKING are marked with this flag.
11411143
self.is_mypy_only = False
1144+
# Decorated with @typing.no_type_check: the body must never be type-checked.
1145+
# Stored as a durable flag so it survives fine-grained reprocessing of the
1146+
# FuncDef target (which bypasses the decorator dispatch in the checker).
1147+
self.is_no_type_check = False
11421148
self.dataclass_transform_spec: DataclassTransformSpec | None = None
11431149
self.docstring: str | None = None
11441150
self.deprecated: str | None = None

mypy/semanal.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,7 @@ def visit_decorator(self, dec: Decorator) -> None:
18101810
if (not dec.is_overload or dec.var.is_property) and self.type:
18111811
dec.var.info = self.type
18121812
dec.var.is_initialized_in_class = True
1813+
dec.func.is_no_type_check = no_type_check
18131814
if no_type_check:
18141815
erase_func_annotations(dec.func)
18151816
if not no_type_check and (self.recurse_into_functions or dec.func.def_or_infer_vars):

test-data/unit/fine-grained.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11839,3 +11839,25 @@ def bar() -> str: return "a"
1183911839
main:2: note: Revealed type is "None | builtins.int"
1184011840
==
1184111841
main:2: note: Revealed type is "None | builtins.str"
11842+
11843+
[case testNoTypeCheckReprocessedViaDependency]
11844+
# A @no_type_check function reprocessed via a dependency change (without editing
11845+
# its own module) must still have its body skipped. Regression test: the daemon
11846+
# used to re-check the bare FuncDef target, bypassing the @no_type_check guard.
11847+
# flags: --check-untyped-defs
11848+
import a
11849+
[file a.py]
11850+
from typing import no_type_check
11851+
from b import SCALE
11852+
11853+
@no_type_check
11854+
def f() -> None:
11855+
factor: int = SCALE
11856+
bad: int = "not an int"
11857+
[file b.py]
11858+
SCALE: int = 3
11859+
[file b.py.2]
11860+
SCALE: str = ""
11861+
[typing fixtures/typing-medium.pyi]
11862+
[out]
11863+
==

0 commit comments

Comments
 (0)