Skip to content

Commit c73c95c

Browse files
brandtbucherGuido van Rossum
authored and
Guido van Rossum
committed
Ignoring a file with a single # type: ignore comment. (#6830)
Fixes #626 (There are potential further features as discussed in the PR, but these are somewhat controversial and may even be excised from PEP 484.)
1 parent 35e81fc commit c73c95c

File tree

4 files changed

+115
-29
lines changed

4 files changed

+115
-29
lines changed

docs/source/common_issues.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,20 @@ generates spurious errors. Mypy will only look at the stub file
149149
and ignore the implementation, since stub files take precedence
150150
over ``.py`` files.
151151

152+
Ignoring a whole file
153+
---------------------
154+
155+
A ``# type: ignore`` comment at the top of a module (before any statements,
156+
including imports or docstrings) has the effect of ignoring the *entire* module.
157+
158+
.. code-block:: python
159+
160+
# type: ignore
161+
162+
import foo
163+
164+
foo.bar()
165+
152166
Unexpected errors about 'None' and/or 'Optional' types
153167
------------------------------------------------------
154168

mypy/fastparse.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33

44
from typing import (
5-
Tuple, Union, TypeVar, Callable, Sequence, Optional, Any, Dict, cast, List, overload
5+
Tuple, Union, TypeVar, Callable, Sequence, Optional, Any, Dict, cast, List, overload, Set
66
)
77
MYPY = False
88
if MYPY:
@@ -258,7 +258,7 @@ def __init__(self,
258258
self.is_stub = is_stub
259259
self.errors = errors
260260

261-
self.extra_type_ignores = [] # type: List[int]
261+
self.type_ignores = set() # type: Set[int]
262262

263263
# Cache of visit_X methods keyed by type of visited object
264264
self.visitor_cache = {} # type: Dict[type, Callable[[Optional[AST]], Any]]
@@ -294,11 +294,29 @@ def translate_expr_list(self, l: Sequence[AST]) -> List[Expression]:
294294
res.append(exp)
295295
return res
296296

297-
def translate_stmt_list(self, l: Sequence[AST]) -> List[Statement]:
297+
def get_lineno(self, node: Union[ast3.expr, ast3.stmt]) -> int:
298+
if (isinstance(node, (ast3.AsyncFunctionDef, ast3.ClassDef, ast3.FunctionDef))
299+
and node.decorator_list):
300+
return node.decorator_list[0].lineno
301+
return node.lineno
302+
303+
def translate_stmt_list(self,
304+
stmts: Sequence[ast3.stmt],
305+
ismodule: bool = False) -> List[Statement]:
306+
# A "# type: ignore" comment before the first statement of a module
307+
# ignores the whole module:
308+
if (ismodule and stmts and self.type_ignores
309+
and min(self.type_ignores) < self.get_lineno(stmts[0])):
310+
self.errors.used_ignored_lines[self.errors.file].add(min(self.type_ignores))
311+
block = Block(self.fix_function_overloads(self.translate_stmt_list(stmts)))
312+
block.is_unreachable = True
313+
return [block]
314+
298315
res = [] # type: List[Statement]
299-
for e in l:
300-
stmt = self.visit(e)
301-
res.append(stmt)
316+
for stmt in stmts:
317+
node = self.visit(stmt)
318+
res.append(node)
319+
302320
return res
303321

304322
op_map = {
@@ -403,13 +421,12 @@ def translate_module_id(self, id: str) -> str:
403421
return id
404422

405423
def visit_Module(self, mod: ast3.Module) -> MypyFile:
406-
body = self.fix_function_overloads(self.translate_stmt_list(mod.body))
407-
ignores = [ti.lineno for ti in mod.type_ignores]
408-
ignores.extend(self.extra_type_ignores)
424+
self.type_ignores = {ti.lineno for ti in mod.type_ignores}
425+
body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True))
409426
return MypyFile(body,
410427
self.imports,
411428
False,
412-
set(ignores),
429+
self.type_ignores,
413430
)
414431

415432
# --- stmt ---
@@ -615,7 +632,7 @@ def make_argument(self, arg: ast3.arg, default: Optional[ast3.expr], kind: int,
615632
elif type_comment is not None:
616633
extra_ignore, arg_type = parse_type_comment(type_comment, arg.lineno, self.errors)
617634
if extra_ignore:
618-
self.extra_type_ignores.append(arg.lineno)
635+
self.type_ignores.add(arg.lineno)
619636

620637
return Argument(Var(arg.arg), arg_type, self.visit(default), kind)
621638

@@ -673,7 +690,7 @@ def visit_Assign(self, n: ast3.Assign) -> AssignmentStmt:
673690
if n.type_comment is not None:
674691
extra_ignore, typ = parse_type_comment(n.type_comment, n.lineno, self.errors)
675692
if extra_ignore:
676-
self.extra_type_ignores.append(n.lineno)
693+
self.type_ignores.add(n.lineno)
677694
else:
678695
typ = None
679696
s = AssignmentStmt(lvalues, rvalue, type=typ, new_syntax=False)
@@ -707,7 +724,7 @@ def visit_For(self, n: ast3.For) -> ForStmt:
707724
if n.type_comment is not None:
708725
extra_ignore, target_type = parse_type_comment(n.type_comment, n.lineno, self.errors)
709726
if extra_ignore:
710-
self.extra_type_ignores.append(n.lineno)
727+
self.type_ignores.add(n.lineno)
711728
else:
712729
target_type = None
713730
node = ForStmt(self.visit(n.target),
@@ -722,7 +739,7 @@ def visit_AsyncFor(self, n: ast3.AsyncFor) -> ForStmt:
722739
if n.type_comment is not None:
723740
extra_ignore, target_type = parse_type_comment(n.type_comment, n.lineno, self.errors)
724741
if extra_ignore:
725-
self.extra_type_ignores.append(n.lineno)
742+
self.type_ignores.add(n.lineno)
726743
else:
727744
target_type = None
728745
node = ForStmt(self.visit(n.target),
@@ -753,7 +770,7 @@ def visit_With(self, n: ast3.With) -> WithStmt:
753770
if n.type_comment is not None:
754771
extra_ignore, target_type = parse_type_comment(n.type_comment, n.lineno, self.errors)
755772
if extra_ignore:
756-
self.extra_type_ignores.append(n.lineno)
773+
self.type_ignores.add(n.lineno)
757774
else:
758775
target_type = None
759776
node = WithStmt([self.visit(i.context_expr) for i in n.items],
@@ -767,7 +784,7 @@ def visit_AsyncWith(self, n: ast3.AsyncWith) -> WithStmt:
767784
if n.type_comment is not None:
768785
extra_ignore, target_type = parse_type_comment(n.type_comment, n.lineno, self.errors)
769786
if extra_ignore:
770-
self.extra_type_ignores.append(n.lineno)
787+
self.type_ignores.add(n.lineno)
771788
else:
772789
target_type = None
773790
s = WithStmt([self.visit(i.context_expr) for i in n.items],

mypy/fastparse2.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"""
1717
import sys
1818

19-
from typing import Tuple, Union, TypeVar, Callable, Sequence, Optional, Any, Dict, cast, List
19+
from typing import Tuple, Union, TypeVar, Callable, Sequence, Optional, Any, Dict, cast, List, Set
2020
MYPY = False
2121
if MYPY:
2222
import typing # for typing.Type, which conflicts with types.Type
@@ -163,7 +163,7 @@ def __init__(self,
163163
# Cache of visit_X methods keyed by type of visited object
164164
self.visitor_cache = {} # type: Dict[type, Callable[[Optional[AST]], Any]]
165165

166-
self.extra_type_ignores = [] # type: List[int]
166+
self.type_ignores = set() # type: Set[int]
167167

168168
def fail(self, msg: str, line: int, column: int, blocker: bool = True) -> None:
169169
if blocker or not self.options.ignore_errors:
@@ -193,12 +193,28 @@ def translate_expr_list(self, l: Sequence[AST]) -> List[Expression]:
193193
res.append(exp)
194194
return res
195195

196-
def translate_stmt_list(self, l: Sequence[AST]) -> List[Statement]:
196+
def get_lineno(self, node: Union[ast27.expr, ast27.stmt]) -> int:
197+
if isinstance(node, (ast27.ClassDef, ast27.FunctionDef)) and node.decorator_list:
198+
return node.decorator_list[0].lineno
199+
return node.lineno
200+
201+
def translate_stmt_list(self,
202+
stmts: Sequence[ast27.stmt],
203+
module: bool = False) -> List[Statement]:
204+
# A "# type: ignore" comment before the first statement of a module
205+
# ignores the whole module:
206+
if (module and stmts and self.type_ignores
207+
and min(self.type_ignores) < self.get_lineno(stmts[0])):
208+
self.errors.used_ignored_lines[self.errors.file].add(min(self.type_ignores))
209+
block = Block(self.fix_function_overloads(self.translate_stmt_list(stmts)))
210+
block.is_unreachable = True
211+
return [block]
212+
197213
res = [] # type: List[Statement]
198-
for e in l:
199-
stmt = self.visit(e)
200-
assert isinstance(stmt, Statement)
201-
res.append(stmt)
214+
for stmt in stmts:
215+
node = self.visit(stmt)
216+
assert isinstance(node, Statement)
217+
res.append(node)
202218
return res
203219

204220
op_map = {
@@ -304,13 +320,12 @@ def translate_module_id(self, id: str) -> str:
304320
return id
305321

306322
def visit_Module(self, mod: ast27.Module) -> MypyFile:
323+
self.type_ignores = {ti.lineno for ti in mod.type_ignores}
307324
body = self.fix_function_overloads(self.translate_stmt_list(mod.body))
308-
ignores = [ti.lineno for ti in mod.type_ignores]
309-
ignores.extend(self.extra_type_ignores)
310325
return MypyFile(body,
311326
self.imports,
312327
False,
313-
set(ignores),
328+
self.type_ignores,
314329
)
315330

316331
# --- stmt ---
@@ -558,7 +573,7 @@ def visit_Assign(self, n: ast27.Assign) -> AssignmentStmt:
558573
extra_ignore, typ = parse_type_comment(n.type_comment, n.lineno, self.errors,
559574
assume_str_is_unicode=self.unicode_literals)
560575
if extra_ignore:
561-
self.extra_type_ignores.append(n.lineno)
576+
self.type_ignores.add(n.lineno)
562577

563578
stmt = AssignmentStmt(self.translate_expr_list(n.targets),
564579
self.visit(n.value),
@@ -578,7 +593,7 @@ def visit_For(self, n: ast27.For) -> ForStmt:
578593
extra_ignore, typ = parse_type_comment(n.type_comment, n.lineno, self.errors,
579594
assume_str_is_unicode=self.unicode_literals)
580595
if extra_ignore:
581-
self.extra_type_ignores.append(n.lineno)
596+
self.type_ignores.add(n.lineno)
582597
else:
583598
typ = None
584599
stmt = ForStmt(self.visit(n.target),
@@ -608,7 +623,7 @@ def visit_With(self, n: ast27.With) -> WithStmt:
608623
extra_ignore, typ = parse_type_comment(n.type_comment, n.lineno, self.errors,
609624
assume_str_is_unicode=self.unicode_literals)
610625
if extra_ignore:
611-
self.extra_type_ignores.append(n.lineno)
626+
self.type_ignores.add(n.lineno)
612627
else:
613628
typ = None
614629
stmt = WithStmt([self.visit(n.context_expr)],

test-data/unit/check-ignore.test

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,43 @@ def f() -> None: pass
218218

219219
[case testCannotIgnoreBlockingError]
220220
yield # type: ignore # E: 'yield' outside function
221+
222+
[case testIgnoreWholeModule1]
223+
# flags: --warn-unused-ignores
224+
# type: ignore
225+
IGNORE # type: ignore # E: unused 'type: ignore' comment
226+
227+
[case testIgnoreWholeModule2]
228+
# type: ignore
229+
if True:
230+
IGNORE
231+
232+
[case testIgnoreWholeModule3]
233+
# type: ignore
234+
@d
235+
class C: ...
236+
IGNORE
237+
238+
[case testIgnoreWholeModule4]
239+
# type: ignore
240+
@d
241+
242+
def f(): ...
243+
IGNORE
244+
245+
[case testDontIgnoreWholeModule1]
246+
if True:
247+
# type: ignore
248+
ERROR # E: Name 'ERROR' is not defined
249+
ERROR # E: Name 'ERROR' is not defined
250+
251+
[case testDontIgnoreWholeModule2]
252+
@d # type: ignore
253+
class C: ...
254+
ERROR # E: Name 'ERROR' is not defined
255+
256+
[case testDontIgnoreWholeModule3]
257+
@d # type: ignore
258+
259+
def f(): ...
260+
ERROR # E: Name 'ERROR' is not defined

0 commit comments

Comments
 (0)