Skip to content

Allow type ignores of PEP 695 constructs #16608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 7, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Allow type ignores of PEP 695 constructs
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
hauntsaninja committed Dec 4, 2023
commit d10ec6e2d637873e22c0abf6737bd142e58c043c
4 changes: 2 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
@@ -2174,8 +2174,8 @@ def parse_file(self, *, temporary: bool = False) -> None:
self.id,
self.xpath,
source,
self.ignore_all or self.options.ignore_errors,
self.options,
ignore_errors=self.ignore_all or self.options.ignore_errors,
options=self.options,
)

else:
26 changes: 15 additions & 11 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
@@ -190,7 +190,7 @@ def parse(
source: str | bytes,
fnam: str,
module: str | None,
errors: Errors | None = None,
errors: Errors,
options: Options | None = None,
) -> MypyFile:
"""Parse a source file, without doing any semantic analysis.
@@ -199,16 +199,13 @@ def parse(
on failure. Otherwise, use the errors object to report parse errors.
"""
ignore_errors = (options is not None and options.ignore_errors) or (
errors is not None and fnam in errors.ignored_files
fnam in errors.ignored_files
)
# If errors are ignored, we can drop many function bodies to speed up type checking.
strip_function_bodies = ignore_errors and (options is None or not options.preserve_asts)
raise_on_error = False
if options is None:
options = Options()
if errors is None:
errors = Errors(options)
raise_on_error = True
errors.set_file(fnam, module, options=options)
is_stub_file = fnam.endswith(".pyi")
if is_stub_file:
@@ -228,11 +225,9 @@ def parse(
options=options,
is_stub=is_stub_file,
errors=errors,
ignore_errors=ignore_errors,
strip_function_bodies=strip_function_bodies,
path=fnam,
).visit(ast)
tree.path = fnam
tree.is_stub = is_stub_file
except SyntaxError as e:
# alias to please mypyc
is_py38_or_earlier = sys.version_info < (3, 9)
@@ -357,8 +352,8 @@ def __init__(
is_stub: bool,
errors: Errors,
*,
ignore_errors: bool,
strip_function_bodies: bool,
path: str,
) -> None:
# 'C' for class, 'D' for function signature, 'F' for function, 'L' for lambda
self.class_and_function_stack: list[Literal["C", "D", "F", "L"]] = []
@@ -367,8 +362,8 @@ def __init__(
self.options = options
self.is_stub = is_stub
self.errors = errors
self.ignore_errors = ignore_errors
self.strip_function_bodies = strip_function_bodies
self.path = path

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

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

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

def fail_merge_overload(self, node: IfStmt) -> None:
@@ -858,8 +857,13 @@ def visit_Module(self, mod: ast3.Module) -> MypyFile:
self.type_ignores[ti.lineno] = parsed
else:
self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1, blocker=False)

body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True))
return MypyFile(body, self.imports, False, self.type_ignores)

ret = MypyFile(body, self.imports, False, ignored_lines=self.type_ignores)
ret.is_stub = self.is_stub
ret.path = self.path
return ret

# --- stmt ---
# FunctionDef(identifier name, arguments args,
2 changes: 1 addition & 1 deletion mypy/parse.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@


def parse(
source: str | bytes, fnam: str, module: str | None, errors: Errors | None, options: Options
source: str | bytes, fnam: str, module: str | None, errors: Errors, options: Options
) -> MypyFile:
"""Parse a source file, without doing any semantic analysis.

14 changes: 11 additions & 3 deletions mypy/test/testparse.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

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

try:
n = parse(
bytes(source, "ascii"), fnam="main", module="__main__", errors=None, options=options
bytes(source, "ascii"),
fnam="main",
module="__main__",
errors=Errors(options),
options=options,
)
a = n.str_with_options(options).split("\n")
except CompileError as e:
@@ -82,7 +86,11 @@ def test_parse_error(testcase: DataDrivenTestCase) -> None:
skip()
# Compile temporary file. The test file contains non-ASCII characters.
parse(
bytes("\n".join(testcase.input), "utf-8"), INPUT_FILE_NAME, "__main__", None, options
bytes("\n".join(testcase.input), "utf-8"),
INPUT_FILE_NAME,
"__main__",
errors=Errors(options),
options=options,
)
raise AssertionError("No errors reported")
except CompileError as e:
5 changes: 5 additions & 0 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
@@ -11,6 +11,11 @@ def g(x: MyList[int]) -> MyList[int]: # E: Variable "__main__.MyList" is not va
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
return reveal_type(x) # N: Revealed type is "MyList?[builtins.int]"

type MyInt2 = int # type: ignore[valid-type]

def h(x: MyInt2) -> MyInt2:
return reveal_type(x) # N: Revealed type is "builtins.int"

[case test695Class]
class MyGen[T]: # E: PEP 695 generics are not yet supported
def __init__(self, x: T) -> None: # E: Name "T" is not defined