Skip to content

Commit f53f422

Browse files
hauntsaninjawesleywright
authored andcommitted
Allow type ignores of PEP 695 constructs (#16608)
This is basically a pre-existing bug and affects other errors that ASTConverter might raise, like merging overloads. It could vaguely be nice to move all the set_file_ignored_lines into fastparse, instead of BuildManager.parse_file. Could also clean up the ignore_errors logic a little bit more. Fixes #16607
1 parent 7c33e7c commit f53f422

File tree

7 files changed

+52
-26
lines changed

7 files changed

+52
-26
lines changed

misc/dump-ast.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import sys
1010

1111
from mypy import defaults
12-
from mypy.errors import CompileError
12+
from mypy.errors import CompileError, Errors
1313
from mypy.options import Options
1414
from mypy.parse import parse
1515

@@ -19,7 +19,7 @@ def dump(fname: str, python_version: tuple[int, int], quiet: bool = False) -> No
1919
options.python_version = python_version
2020
with open(fname, "rb") as f:
2121
s = f.read()
22-
tree = parse(s, fname, None, errors=None, options=options)
22+
tree = parse(s, fname, None, errors=Errors(options), options=options)
2323
if not quiet:
2424
print(tree)
2525

mypy/build.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2174,8 +2174,8 @@ def parse_file(self, *, temporary: bool = False) -> None:
21742174
self.id,
21752175
self.xpath,
21762176
source,
2177-
self.ignore_all or self.options.ignore_errors,
2178-
self.options,
2177+
ignore_errors=self.ignore_all or self.options.ignore_errors,
2178+
options=self.options,
21792179
)
21802180

21812181
else:

mypy/fastparse.py

+16-15
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def parse(
190190
source: str | bytes,
191191
fnam: str,
192192
module: str | None,
193-
errors: Errors | None = None,
193+
errors: Errors,
194194
options: Options | None = None,
195195
) -> MypyFile:
196196
"""Parse a source file, without doing any semantic analysis.
@@ -199,16 +199,13 @@ def parse(
199199
on failure. Otherwise, use the errors object to report parse errors.
200200
"""
201201
ignore_errors = (options is not None and options.ignore_errors) or (
202-
errors is not None and fnam in errors.ignored_files
202+
fnam in errors.ignored_files
203203
)
204204
# If errors are ignored, we can drop many function bodies to speed up type checking.
205205
strip_function_bodies = ignore_errors and (options is None or not options.preserve_asts)
206-
raise_on_error = False
206+
207207
if options is None:
208208
options = Options()
209-
if errors is None:
210-
errors = Errors(options)
211-
raise_on_error = True
212209
errors.set_file(fnam, module, options=options)
213210
is_stub_file = fnam.endswith(".pyi")
214211
if is_stub_file:
@@ -228,11 +225,9 @@ def parse(
228225
options=options,
229226
is_stub=is_stub_file,
230227
errors=errors,
231-
ignore_errors=ignore_errors,
232228
strip_function_bodies=strip_function_bodies,
229+
path=fnam,
233230
).visit(ast)
234-
tree.path = fnam
235-
tree.is_stub = is_stub_file
236231
except SyntaxError as e:
237232
# alias to please mypyc
238233
is_py38_or_earlier = sys.version_info < (3, 9)
@@ -254,9 +249,6 @@ def parse(
254249
)
255250
tree = MypyFile([], [], False, {})
256251

257-
if raise_on_error and errors.is_errors():
258-
errors.raise_error()
259-
260252
assert isinstance(tree, MypyFile)
261253
return tree
262254

@@ -357,8 +349,8 @@ def __init__(
357349
is_stub: bool,
358350
errors: Errors,
359351
*,
360-
ignore_errors: bool,
361352
strip_function_bodies: bool,
353+
path: str,
362354
) -> None:
363355
# 'C' for class, 'D' for function signature, 'F' for function, 'L' for lambda
364356
self.class_and_function_stack: list[Literal["C", "D", "F", "L"]] = []
@@ -367,8 +359,8 @@ def __init__(
367359
self.options = options
368360
self.is_stub = is_stub
369361
self.errors = errors
370-
self.ignore_errors = ignore_errors
371362
self.strip_function_bodies = strip_function_bodies
363+
self.path = path
372364

373365
self.type_ignores: dict[int, list[str]] = {}
374366

@@ -380,6 +372,10 @@ def note(self, msg: str, line: int, column: int) -> None:
380372

381373
def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None:
382374
if blocker or not self.options.ignore_errors:
375+
# Make sure self.errors reflects any type ignores that we have parsed
376+
self.errors.set_file_ignored_lines(
377+
self.path, self.type_ignores, self.options.ignore_errors
378+
)
383379
self.errors.report(line, column, msg.value, blocker=blocker, code=msg.code)
384380

385381
def fail_merge_overload(self, node: IfStmt) -> None:
@@ -858,8 +854,13 @@ def visit_Module(self, mod: ast3.Module) -> MypyFile:
858854
self.type_ignores[ti.lineno] = parsed
859855
else:
860856
self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1, blocker=False)
857+
861858
body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True))
862-
return MypyFile(body, self.imports, False, self.type_ignores)
859+
860+
ret = MypyFile(body, self.imports, False, ignored_lines=self.type_ignores)
861+
ret.is_stub = self.is_stub
862+
ret.path = self.path
863+
return ret
863864

864865
# --- stmt ---
865866
# FunctionDef(identifier name, arguments args,

mypy/parse.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66

77

88
def parse(
9-
source: str | bytes, fnam: str, module: str | None, errors: Errors | None, options: Options
9+
source: str | bytes,
10+
fnam: str,
11+
module: str | None,
12+
errors: Errors,
13+
options: Options,
14+
raise_on_error: bool = False,
1015
) -> MypyFile:
1116
"""Parse a source file, without doing any semantic analysis.
1217
@@ -19,4 +24,7 @@ def parse(
1924
source = options.transform_source(source)
2025
import mypy.fastparse
2126

22-
return mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options)
27+
tree = mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options)
28+
if raise_on_error and errors.is_errors():
29+
errors.raise_error()
30+
return tree

mypy/test/testparse.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from mypy import defaults
1010
from mypy.config_parser import parse_mypy_comments
11-
from mypy.errors import CompileError
11+
from mypy.errors import CompileError, Errors
1212
from mypy.options import Options
1313
from mypy.parse import parse
1414
from mypy.test.data import DataDrivenTestCase, DataSuite
@@ -51,7 +51,12 @@ def test_parser(testcase: DataDrivenTestCase) -> None:
5151

5252
try:
5353
n = parse(
54-
bytes(source, "ascii"), fnam="main", module="__main__", errors=None, options=options
54+
bytes(source, "ascii"),
55+
fnam="main",
56+
module="__main__",
57+
errors=Errors(options),
58+
options=options,
59+
raise_on_error=True,
5560
)
5661
a = n.str_with_options(options).split("\n")
5762
except CompileError as e:
@@ -82,7 +87,12 @@ def test_parse_error(testcase: DataDrivenTestCase) -> None:
8287
skip()
8388
# Compile temporary file. The test file contains non-ASCII characters.
8489
parse(
85-
bytes("\n".join(testcase.input), "utf-8"), INPUT_FILE_NAME, "__main__", None, options
90+
bytes("\n".join(testcase.input), "utf-8"),
91+
INPUT_FILE_NAME,
92+
"__main__",
93+
errors=Errors(options),
94+
options=options,
95+
raise_on_error=True,
8696
)
8797
raise AssertionError("No errors reported")
8898
except CompileError as e:

test-data/unit/check-errorcodes.test

+4-2
Original file line numberDiff line numberDiff line change
@@ -975,11 +975,13 @@ def f(d: D, s: str) -> None:
975975
[typing fixtures/typing-typeddict.pyi]
976976

977977
[case testRecommendErrorCode]
978-
# type: ignore[whatever] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever"` [syntax]
978+
# type: ignore[whatever] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever"` [syntax] \
979+
# N: Error code "syntax" not covered by "type: ignore" comment
979980
1 + "asdf"
980981

981982
[case testRecommendErrorCode2]
982-
# type: ignore[whatever, other] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever, other"` [syntax]
983+
# type: ignore[whatever, other] # E: type ignore with error code is not supported for modules; use `# mypy: disable-error-code="whatever, other"` [syntax] \
984+
# N: Error code "syntax" not covered by "type: ignore" comment
983985
1 + "asdf"
984986

985987
[case testShowErrorCodesInConfig]

test-data/unit/check-python312.test

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ def g(x: MyList[int]) -> MyList[int]: # E: Variable "__main__.MyList" is not va
1111
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
1212
return reveal_type(x) # N: Revealed type is "MyList?[builtins.int]"
1313

14+
type MyInt2 = int # type: ignore[valid-type]
15+
16+
def h(x: MyInt2) -> MyInt2:
17+
return reveal_type(x) # N: Revealed type is "builtins.int"
18+
1419
[case test695Class]
1520
class MyGen[T]: # E: PEP 695 generics are not yet supported
1621
def __init__(self, x: T) -> None: # E: Name "T" is not defined

0 commit comments

Comments
 (0)