Skip to content

Commit 9ce3470

Browse files
Migrate fastparse to use ErrorMessage class (python#14753)
Use the `ErrorMessage` class from `message_registry.py` to move the error messages from `fastparse` module into the message registry. This is a retry of python#10947 and python#12004, but much more granular this time.
1 parent a2b0f18 commit 9ce3470

File tree

3 files changed

+72
-45
lines changed

3 files changed

+72
-45
lines changed

mypy/errorcodes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def __str__(self) -> str:
224224

225225

226226
# Syntax errors are often blocking.
227-
SYNTAX: Final = ErrorCode("syntax", "Report syntax errors", "General")
227+
SYNTAX: Final[ErrorCode] = ErrorCode("syntax", "Report syntax errors", "General")
228228

229229
# This is an internal marker code for a whole-file ignore. It is not intended to
230230
# be user-visible.

mypy/fastparse.py

+31-43
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from mypy import defaults, errorcodes as codes, message_registry
1111
from mypy.errors import Errors
12+
from mypy.message_registry import ErrorMessage
1213
from mypy.nodes import (
1314
ARG_NAMED,
1415
ARG_NAMED_OPT,
@@ -242,10 +243,6 @@ def ast3_parse(
242243
MISSING_FALLBACK: Final = FakeInfo("fallback can't be filled out until semanal")
243244
_dummy_fallback: Final = Instance(MISSING_FALLBACK, [], -1)
244245

245-
TYPE_COMMENT_SYNTAX_ERROR: Final = "syntax error in type comment"
246-
247-
INVALID_TYPE_IGNORE: Final = 'Invalid "type: ignore" comment'
248-
249246
TYPE_IGNORE_PATTERN: Final = re.compile(r"[^#]*#\s*type:\s*ignore\s*(.*)")
250247

251248

@@ -354,8 +351,8 @@ def parse_type_comment(
354351
except SyntaxError:
355352
if errors is not None:
356353
stripped_type = type_comment.split("#", 2)[0].strip()
357-
err_msg = f'{TYPE_COMMENT_SYNTAX_ERROR} "{stripped_type}"'
358-
errors.report(line, column, err_msg, blocker=True, code=codes.SYNTAX)
354+
err_msg = message_registry.TYPE_COMMENT_SYNTAX_ERROR_VALUE.format(stripped_type)
355+
errors.report(line, column, err_msg.value, blocker=True, code=err_msg.code)
359356
return None, None
360357
else:
361358
raise
@@ -366,7 +363,9 @@ def parse_type_comment(
366363
ignored: list[str] | None = parse_type_ignore_tag(tag)
367364
if ignored is None:
368365
if errors is not None:
369-
errors.report(line, column, INVALID_TYPE_IGNORE, code=codes.SYNTAX)
366+
errors.report(
367+
line, column, message_registry.INVALID_TYPE_IGNORE.value, code=codes.SYNTAX
368+
)
370369
else:
371370
raise SyntaxError
372371
else:
@@ -439,24 +438,16 @@ def __init__(
439438
def note(self, msg: str, line: int, column: int) -> None:
440439
self.errors.report(line, column, msg, severity="note", code=codes.SYNTAX)
441440

442-
def fail(
443-
self,
444-
msg: str,
445-
line: int,
446-
column: int,
447-
blocker: bool = True,
448-
code: codes.ErrorCode = codes.SYNTAX,
449-
) -> None:
441+
def fail(self, msg: ErrorMessage, line: int, column: int, blocker: bool = True) -> None:
450442
if blocker or not self.options.ignore_errors:
451-
self.errors.report(line, column, msg, blocker=blocker, code=code)
443+
self.errors.report(line, column, msg.value, blocker=blocker, code=msg.code)
452444

453445
def fail_merge_overload(self, node: IfStmt) -> None:
454446
self.fail(
455-
"Condition can't be inferred, unable to merge overloads",
447+
message_registry.FAILED_TO_MERGE_OVERLOADS,
456448
line=node.line,
457449
column=node.column,
458450
blocker=False,
459-
code=codes.MISC,
460451
)
461452

462453
def visit(self, node: AST | None) -> Any:
@@ -516,10 +507,7 @@ def translate_stmt_list(
516507
if ignores:
517508
joined_ignores = ", ".join(ignores)
518509
self.fail(
519-
(
520-
"type ignore with error code is not supported for modules; "
521-
f'use `# mypy: disable-error-code="{joined_ignores}"`'
522-
),
510+
message_registry.TYPE_IGNORE_WITH_ERRCODE_ON_MODULE.format(joined_ignores),
523511
line=min(self.type_ignores),
524512
column=0,
525513
blocker=False,
@@ -912,7 +900,7 @@ def visit_Module(self, mod: ast3.Module) -> MypyFile:
912900
if parsed is not None:
913901
self.type_ignores[ti.lineno] = parsed
914902
else:
915-
self.fail(INVALID_TYPE_IGNORE, ti.lineno, -1, blocker=False)
903+
self.fail(message_registry.INVALID_TYPE_IGNORE, ti.lineno, -1, blocker=False)
916904
body = self.fix_function_overloads(self.translate_stmt_list(mod.body, ismodule=True))
917905
return MypyFile(body, self.imports, False, self.type_ignores)
918906

@@ -985,7 +973,7 @@ def do_func_def(
985973
arg_types.insert(0, AnyType(TypeOfAny.special_form))
986974
except SyntaxError:
987975
stripped_type = n.type_comment.split("#", 2)[0].strip()
988-
err_msg = f'{TYPE_COMMENT_SYNTAX_ERROR} "{stripped_type}"'
976+
err_msg = message_registry.TYPE_COMMENT_SYNTAX_ERROR_VALUE.format(stripped_type)
989977
self.fail(err_msg, lineno, n.col_offset)
990978
if n.type_comment and n.type_comment[0] not in ["(", "#"]:
991979
self.note(
@@ -1005,18 +993,20 @@ def do_func_def(
1005993
func_type = None
1006994
if any(arg_types) or return_type:
1007995
if len(arg_types) != 1 and any(isinstance(t, EllipsisType) for t in arg_types):
996+
self.fail(message_registry.ELLIPSIS_WITH_OTHER_TYPEARGS, lineno, n.col_offset)
997+
elif len(arg_types) > len(arg_kinds):
1008998
self.fail(
1009-
"Ellipses cannot accompany other argument types in function type signature",
999+
message_registry.TYPE_SIGNATURE_TOO_MANY_ARGS,
10101000
lineno,
10111001
n.col_offset,
1012-
)
1013-
elif len(arg_types) > len(arg_kinds):
1014-
self.fail(
1015-
"Type signature has too many arguments", lineno, n.col_offset, blocker=False
1002+
blocker=False,
10161003
)
10171004
elif len(arg_types) < len(arg_kinds):
10181005
self.fail(
1019-
"Type signature has too few arguments", lineno, n.col_offset, blocker=False
1006+
message_registry.TYPE_SIGNATURE_TOO_FEW_ARGS,
1007+
lineno,
1008+
n.col_offset,
1009+
blocker=False,
10201010
)
10211011
else:
10221012
func_type = CallableType(
@@ -1162,7 +1152,7 @@ def make_argument(
11621152
return argument
11631153

11641154
def fail_arg(self, msg: str, arg: ast3.arg) -> None:
1165-
self.fail(msg, arg.lineno, arg.col_offset)
1155+
self.fail(ErrorMessage(msg), arg.lineno, arg.col_offset)
11661156

11671157
# ClassDef(identifier name,
11681158
# expr* bases,
@@ -1889,9 +1879,9 @@ def parent(self) -> AST | None:
18891879
return None
18901880
return self.node_stack[-2]
18911881

1892-
def fail(self, msg: str, line: int, column: int) -> None:
1882+
def fail(self, msg: ErrorMessage, line: int, column: int) -> None:
18931883
if self.errors:
1894-
self.errors.report(line, column, msg, blocker=True, code=codes.SYNTAX)
1884+
self.errors.report(line, column, msg.value, blocker=True, code=msg.code)
18951885

18961886
def note(self, msg: str, line: int, column: int) -> None:
18971887
if self.errors:
@@ -1911,7 +1901,7 @@ def visit_Call(self, e: Call) -> Type:
19111901
note = "Suggestion: use {0}[...] instead of {0}(...)".format(constructor)
19121902
return self.invalid_type(e, note=note)
19131903
if not constructor:
1914-
self.fail("Expected arg constructor name", e.lineno, e.col_offset)
1904+
self.fail(message_registry.ARG_CONSTRUCTOR_NAME_EXPECTED, e.lineno, e.col_offset)
19151905

19161906
name: str | None = None
19171907
default_type = AnyType(TypeOfAny.special_form)
@@ -1924,25 +1914,21 @@ def visit_Call(self, e: Call) -> Type:
19241914
elif i == 1:
19251915
name = self._extract_argument_name(arg)
19261916
else:
1927-
self.fail("Too many arguments for argument constructor", f.lineno, f.col_offset)
1917+
self.fail(message_registry.ARG_CONSTRUCTOR_TOO_MANY_ARGS, f.lineno, f.col_offset)
19281918
for k in e.keywords:
19291919
value = k.value
19301920
if k.arg == "name":
19311921
if name is not None:
19321922
self.fail(
1933-
'"{}" gets multiple values for keyword argument "name"'.format(
1934-
constructor
1935-
),
1923+
message_registry.MULTIPLE_VALUES_FOR_NAME_KWARG.format(constructor),
19361924
f.lineno,
19371925
f.col_offset,
19381926
)
19391927
name = self._extract_argument_name(value)
19401928
elif k.arg == "type":
19411929
if typ is not default_type:
19421930
self.fail(
1943-
'"{}" gets multiple values for keyword argument "type"'.format(
1944-
constructor
1945-
),
1931+
message_registry.MULTIPLE_VALUES_FOR_TYPE_KWARG.format(constructor),
19461932
f.lineno,
19471933
f.col_offset,
19481934
)
@@ -1951,7 +1937,7 @@ def visit_Call(self, e: Call) -> Type:
19511937
typ = converted
19521938
else:
19531939
self.fail(
1954-
f'Unexpected argument "{k.arg}" for argument constructor',
1940+
message_registry.ARG_CONSTRUCTOR_UNEXPECTED_ARG.format(k.arg),
19551941
value.lineno,
19561942
value.col_offset,
19571943
)
@@ -1966,7 +1952,9 @@ def _extract_argument_name(self, n: ast3.expr) -> str | None:
19661952
elif isinstance(n, NameConstant) and str(n.value) == "None":
19671953
return None
19681954
self.fail(
1969-
f"Expected string literal for argument name, got {type(n).__name__}", self.line, 0
1955+
message_registry.ARG_NAME_EXPECTED_STRING_LITERAL.format(type(n).__name__),
1956+
self.line,
1957+
0,
19701958
)
19711959
return None
19721960

mypy/message_registry.py

+40-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
131131
"Expected TypedDict key to be string literal"
132132
)
133133
MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?")
134-
DUPLICATE_TYPE_SIGNATURES: Final = "Function has duplicate type signatures"
134+
DUPLICATE_TYPE_SIGNATURES: Final = ErrorMessage("Function has duplicate type signatures")
135135
DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable")
136136
DESCRIPTOR_GET_NOT_CALLABLE: Final = "{}.__get__ is not callable"
137137
MODULE_LEVEL_GETATTRIBUTE: Final = ErrorMessage(
@@ -274,3 +274,42 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
274274
DATACLASS_FIELD_ALIAS_MUST_BE_LITERAL: Final = (
275275
'"alias" argument to dataclass field must be a string literal'
276276
)
277+
278+
# fastparse
279+
FAILED_TO_MERGE_OVERLOADS: Final = ErrorMessage(
280+
"Condition can't be inferred, unable to merge overloads"
281+
)
282+
TYPE_IGNORE_WITH_ERRCODE_ON_MODULE: Final = ErrorMessage(
283+
"type ignore with error code is not supported for modules; "
284+
'use `# mypy: disable-error-code="{}"`',
285+
codes.SYNTAX,
286+
)
287+
INVALID_TYPE_IGNORE: Final = ErrorMessage('Invalid "type: ignore" comment', codes.SYNTAX)
288+
TYPE_COMMENT_SYNTAX_ERROR_VALUE: Final = ErrorMessage(
289+
'syntax error in type comment "{}"', codes.SYNTAX
290+
)
291+
ELLIPSIS_WITH_OTHER_TYPEARGS: Final = ErrorMessage(
292+
"Ellipses cannot accompany other argument types in function type signature", codes.SYNTAX
293+
)
294+
TYPE_SIGNATURE_TOO_MANY_ARGS: Final = ErrorMessage(
295+
"Type signature has too many arguments", codes.SYNTAX
296+
)
297+
TYPE_SIGNATURE_TOO_FEW_ARGS: Final = ErrorMessage(
298+
"Type signature has too few arguments", codes.SYNTAX
299+
)
300+
ARG_CONSTRUCTOR_NAME_EXPECTED: Final = ErrorMessage("Expected arg constructor name", codes.SYNTAX)
301+
ARG_CONSTRUCTOR_TOO_MANY_ARGS: Final = ErrorMessage(
302+
"Too many arguments for argument constructor", codes.SYNTAX
303+
)
304+
MULTIPLE_VALUES_FOR_NAME_KWARG: Final = ErrorMessage(
305+
'"{}" gets multiple values for keyword argument "name"', codes.SYNTAX
306+
)
307+
MULTIPLE_VALUES_FOR_TYPE_KWARG: Final = ErrorMessage(
308+
'"{}" gets multiple values for keyword argument "type"', codes.SYNTAX
309+
)
310+
ARG_CONSTRUCTOR_UNEXPECTED_ARG: Final = ErrorMessage(
311+
'Unexpected argument "{}" for argument constructor', codes.SYNTAX
312+
)
313+
ARG_NAME_EXPECTED_STRING_LITERAL: Final = ErrorMessage(
314+
"Expected string literal for argument name, got {}", codes.SYNTAX
315+
)

0 commit comments

Comments
 (0)