Skip to content
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

Allow type ignores of PEP 695 constructs #16608

Merged
merged 6 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions misc/dump-ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import sys

from mypy import defaults
from mypy.errors import CompileError
from mypy.errors import CompileError, Errors
from mypy.options import Options
from mypy.parse import parse

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

Expand Down
4 changes: 2 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
31 changes: 16 additions & 15 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand All @@ -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)
Expand All @@ -254,9 +249,6 @@ def parse(
)
tree = MypyFile([], [], False, {})

if raise_on_error and errors.is_errors():
errors.raise_error()

assert isinstance(tree, MypyFile)
return tree

Expand Down Expand Up @@ -357,8 +349,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"]] = []
Expand All @@ -367,8 +359,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]] = {}

Expand All @@ -380,6 +372,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:
Expand Down Expand Up @@ -858,8 +854,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,
Expand Down
12 changes: 10 additions & 2 deletions mypy/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@


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,
raise_on_error: bool = False,
) -> MypyFile:
"""Parse a source file, without doing any semantic analysis.

Expand All @@ -19,4 +24,7 @@ def parse(
source = options.transform_source(source)
import mypy.fastparse

return mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options)
tree = mypy.fastparse.parse(source, fnam=fnam, module=module, errors=errors, options=options)
if raise_on_error and errors.is_errors():
errors.raise_error()
return tree
16 changes: 13 additions & 3 deletions mypy/test/testparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -51,7 +51,12 @@ 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,
raise_on_error=True,
)
a = n.str_with_options(options).split("\n")
except CompileError as e:
Expand Down Expand Up @@ -82,7 +87,12 @@ 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_on_error=True,
)
raise AssertionError("No errors reported")
except CompileError as e:
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -975,11 +975,13 @@ def f(d: D, s: str) -> None:
[typing fixtures/typing-typeddict.pyi]

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

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

[case testShowErrorCodesInConfig]
Expand Down
5 changes: 5 additions & 0 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down