Skip to content

Commit ed5b36c

Browse files
The final patch to wrap everything up
1 parent 826cc86 commit ed5b36c

11 files changed

+74
-81
lines changed

mypy/checker.py

+3-12
Original file line numberDiff line numberDiff line change
@@ -1897,12 +1897,7 @@ def check_init_subclass(self, defn: ClassDef) -> None:
18971897
def check_final_enum(self, defn: ClassDef, base: TypeInfo) -> None:
18981898
for sym in base.names.values():
18991899
if self.is_final_enum_value(sym):
1900-
self.fail(
1901-
'Cannot extend enum with existing members: "{}"'.format(
1902-
base.name,
1903-
),
1904-
defn,
1905-
)
1900+
self.fail(message_registry.ENUM_EXTEND_EXISTING_MEMBERS.format(base.name), defn)
19061901
break
19071902

19081903
def is_final_enum_value(self, sym: SymbolTableNode) -> bool:
@@ -5342,13 +5337,9 @@ def temp_node(self, t: Type, context: Optional[Context] = None) -> TempNode:
53425337
"""Create a temporary node with the given, fixed type."""
53435338
return TempNode(t, context=context)
53445339

5345-
def fail(self, msg: Union[str, ErrorMessage], context: Context, *,
5346-
code: Optional[ErrorCode] = None) -> None:
5340+
def fail(self, msg: ErrorMessage, context: Context) -> None:
53475341
"""Produce an error message."""
5348-
if isinstance(msg, ErrorMessage):
5349-
self.msg.fail(msg.value, context, code=msg.code)
5350-
return
5351-
self.msg.fail(msg, context, code=code)
5342+
self.msg.fail(msg, context)
53525343

53535344
def note(self,
53545345
msg: str,

mypy/checkexpr.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4094,7 +4094,7 @@ def visit_await_expr(self, e: AwaitExpr) -> Type:
40944094
return self.check_awaitable_expr(actual_type, e,
40954095
message_registry.INCOMPATIBLE_TYPES_IN_AWAIT)
40964096

4097-
def check_awaitable_expr(self, t: Type, ctx: Context, msg: Union[str, ErrorMessage]) -> Type:
4097+
def check_awaitable_expr(self, t: Type, ctx: Context, msg: ErrorMessage) -> Type:
40984098
"""Check the argument to `await` and extract the type of value.
40994099
41004100
Also used by `async for` and `async with`.

mypy/checkmember.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ def analyze_class_attribute_access(itype: Instance,
714714
if is_method:
715715
mx.msg.cant_assign_to_method(mx.context)
716716
if isinstance(node.node, TypeInfo):
717-
mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE.value, mx.context)
717+
mx.msg.fail(message_registry.CANNOT_ASSIGN_TO_TYPE, mx.context)
718718

719719
# If a final attribute was declared on `self` in `__init__`, then it
720720
# can't be accessed on the class object.

mypy/message_registry.py

+44-29
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class ErrorMessage(NamedTuple):
1919
def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
2020
return ErrorMessage(self.value.format(*args, **kwargs), code=self.code)
2121

22+
# Strings
23+
INCOMPATIBLE_TYPES: Final = "Incompatible types"
24+
INCOMPATIBLE_TYPES_IN_ASSIGNMENT: Final = "Incompatible types in assignment"
2225

2326
# Invalid types
2427
INVALID_TYPE_RAW_ENUM_VALUE: Final = ErrorMessage(
@@ -51,17 +54,15 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
5154
" its third type parameter in Python 2"
5255
)
5356
YIELD_VALUE_EXPECTED: Final = ErrorMessage("Yield value expected")
54-
INCOMPATIBLE_TYPES: Final = "Incompatible types"
55-
INCOMPATIBLE_TYPES_IN_ASSIGNMENT: Final = "Incompatible types in assignment"
5657
INCOMPATIBLE_TYPES_IN_AWAIT: Final = ErrorMessage('Incompatible types in "await"')
5758
INCOMPATIBLE_REDEFINITION: Final = ErrorMessage("Incompatible redefinition")
58-
INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AENTER: Final = (
59+
INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AENTER: Final = ErrorMessage(
5960
'Incompatible types in "async with" for "__aenter__"'
6061
)
61-
INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AEXIT: Final = (
62+
INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AEXIT: Final = ErrorMessage(
6263
'Incompatible types in "async with" for "__aexit__"'
6364
)
64-
INCOMPATIBLE_TYPES_IN_ASYNC_FOR: Final = 'Incompatible types in "async for"'
65+
INCOMPATIBLE_TYPES_IN_ASYNC_FOR: Final = ErrorMessage('Incompatible types in "async for"')
6566

6667
INCOMPATIBLE_TYPES_IN_YIELD: Final = ErrorMessage('Incompatible types in "yield"')
6768
INCOMPATIBLE_TYPES_IN_YIELD_FROM: Final = ErrorMessage('Incompatible types in "yield from"')
@@ -72,7 +73,7 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
7273
TUPLE_INDEX_OUT_OF_RANGE: Final = ErrorMessage("Tuple index out of range")
7374
INVALID_SLICE_INDEX: Final = ErrorMessage("Slice index must be an integer or None")
7475
CANNOT_INFER_LAMBDA_TYPE: Final = ErrorMessage("Cannot infer type of lambda")
75-
CANNOT_ACCESS_INIT: Final = 'Cannot access "__init__" directly'
76+
CANNOT_ACCESS_INIT: Final = ErrorMessage('Cannot access "__init__" directly')
7677
NON_INSTANCE_NEW_TYPE: Final = ErrorMessage('"__new__" must return a class instance (got {})')
7778
INVALID_NEW_TYPE: Final = ErrorMessage('Incompatible return type for "__new__"')
7879
BAD_CONSTRUCTOR_TYPE: Final = ErrorMessage("Unsupported decorated constructor type")
@@ -118,7 +119,7 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
118119
MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?")
119120
DUPLICATE_TYPE_SIGNATURES: Final = ErrorMessage("Function has duplicate type signatures")
120121
DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable")
121-
DESCRIPTOR_GET_NOT_CALLABLE: Final = "{}.__get__ is not callable"
122+
DESCRIPTOR_GET_NOT_CALLABLE: Final = ErrorMessage("{}.__get__ is not callable")
122123
MODULE_LEVEL_GETATTRIBUTE: Final = ErrorMessage(
123124
"__getattribute__ is not valid at the module level"
124125
)
@@ -140,29 +141,43 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
140141
code=codes.TRUTHY_BOOL,
141142
)
142143
NOT_CALLABLE: Final = '{} not callable'
143-
PYTHON2_PRINT_FILE_TYPE: Final = (
144+
PYTHON2_PRINT_FILE_TYPE: Final = ErrorMessage(
144145
'Argument "file" to "print" has incompatible type "{}"; expected "{}"'
145146
)
147+
ENUM_EXTEND_EXISTING_MEMBERS: Final = ErrorMessage(
148+
'Cannot extend enum with existing members: "{}"'
149+
)
146150

147151
# Generic
148-
GENERIC_INSTANCE_VAR_CLASS_ACCESS: Final = (
152+
GENERIC_INSTANCE_VAR_CLASS_ACCESS: Final = ErrorMessage(
149153
"Access to generic instance variables via class is ambiguous"
150154
)
151-
GENERIC_CLASS_VAR_ACCESS: Final = "Access to generic class variables is ambiguous"
155+
GENERIC_CLASS_VAR_ACCESS: Final = ErrorMessage("Access to generic class variables is ambiguous")
152156
BARE_GENERIC: Final = ErrorMessage("Missing type parameters for generic type {}", codes.TYPE_ARG)
153157
IMPLICIT_GENERIC_ANY_BUILTIN: Final = ErrorMessage(
154158
'Implicit generic "Any". Use "{}" and specify generic parameters', codes.TYPE_ARG
155159
)
156160

157161
# TypeVar
158-
INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}'
159-
CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = 'Type variable "{}.{}" cannot be used as an expression'
160-
INVALID_TYPEVAR_AS_TYPEARG: Final = 'Type variable "{}" not valid as type argument value for "{}"'
161-
INVALID_TYPEVAR_ARG_BOUND: Final = 'Type argument {} of "{}" must be a subtype of {}'
162-
INVALID_TYPEVAR_ARG_VALUE: Final = 'Invalid type argument value for "{}"'
162+
INCOMPATIBLE_TYPEVAR_VALUE: Final = ErrorMessage(
163+
'Value of type variable "{}" of {} cannot be {}', codes.TYPE_VAR
164+
)
165+
CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = ErrorMessage(
166+
'Type variable "{}.{}" cannot be used as an expression'
167+
)
168+
INVALID_TYPEVAR_AS_TYPEARG: Final = ErrorMessage(
169+
'Type variable "{}" not valid as type argument value for "{}"', codes.TYPE_VAR
170+
)
171+
INVALID_TYPEVAR_ARG_BOUND: Final = ErrorMessage(
172+
'Type argument {} of "{}" must be a subtype of {}', codes.TYPE_VAR
173+
)
174+
INVALID_TYPEVAR_ARG_VALUE: Final = ErrorMessage(
175+
'Invalid type argument value for "{}"', codes.TYPE_VAR
176+
)
163177
TYPEVAR_VARIANCE_DEF: Final = ErrorMessage('TypeVar "{}" may only be a literal bool')
164178
TYPEVAR_BOUND_MUST_BE_TYPE: Final = ErrorMessage('TypeVar "bound" must be a type')
165179
TYPEVAR_UNEXPECTED_ARGUMENT: Final = ErrorMessage('Unexpected argument to "TypeVar()"')
180+
PARAMSPEC_INVALID_LOCATION: Final = ErrorMessage('Invalid location for ParamSpec "{}"')
166181

167182
# FastParse
168183
TYPE_COMMENT_SYNTAX_ERROR_VALUE: Final = ErrorMessage(
@@ -1031,7 +1046,7 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
10311046
DEPENDENT_FINAL_IN_CLASS_BODY: Final = ErrorMessage(
10321047
"Final name declared in class body cannot depend on type variables"
10331048
)
1034-
CANNOT_ACCESS_FINAL_INSTANCE_ATTR: Final = (
1049+
CANNOT_ACCESS_FINAL_INSTANCE_ATTR: Final = ErrorMessage(
10351050
'Cannot access final instance attribute "{}" on class object'
10361051
)
10371052
CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final")
@@ -1062,19 +1077,23 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
10621077
TYPE_GUARD_POS_ARG_REQUIRED: Final = ErrorMessage("Type guard requires positional argument")
10631078

10641079
# Match Statement
1065-
MISSING_MATCH_ARGS: Final = 'Class "{}" doesn\'t define "__match_args__"'
1066-
OR_PATTERN_ALTERNATIVE_NAMES: Final = "Alternative patterns bind different names"
1067-
CLASS_PATTERN_GENERIC_TYPE_ALIAS: Final = (
1080+
MISSING_MATCH_ARGS: Final = ErrorMessage('Class "{}" doesn\'t define "__match_args__"')
1081+
OR_PATTERN_ALTERNATIVE_NAMES: Final = ErrorMessage("Alternative patterns bind different names")
1082+
CLASS_PATTERN_GENERIC_TYPE_ALIAS: Final = ErrorMessage(
10681083
"Class pattern class must not be a type alias with type parameters"
10691084
)
1070-
CLASS_PATTERN_TYPE_REQUIRED: Final = 'Expected type in class pattern; found "{}"'
1071-
CLASS_PATTERN_TOO_MANY_POSITIONAL_ARGS: Final = "Too many positional patterns for class pattern"
1072-
CLASS_PATTERN_KEYWORD_MATCHES_POSITIONAL: Final = (
1085+
CLASS_PATTERN_TYPE_REQUIRED: Final = ErrorMessage('Expected type in class pattern; found "{}"')
1086+
CLASS_PATTERN_TOO_MANY_POSITIONAL_ARGS: Final = ErrorMessage(
1087+
"Too many positional patterns for class pattern"
1088+
)
1089+
CLASS_PATTERN_KEYWORD_MATCHES_POSITIONAL: Final = ErrorMessage(
10731090
'Keyword "{}" already matches a positional pattern'
10741091
)
1075-
CLASS_PATTERN_DUPLICATE_KEYWORD_PATTERN: Final = 'Duplicate keyword pattern "{}"'
1076-
CLASS_PATTERN_UNKNOWN_KEYWORD: Final = 'Class "{}" has no attribute "{}"'
1077-
MULTIPLE_ASSIGNMENTS_IN_PATTERN: Final = 'Multiple assignments to name "{}" in pattern'
1092+
CLASS_PATTERN_DUPLICATE_KEYWORD_PATTERN: Final = ErrorMessage('Duplicate keyword pattern "{}"')
1093+
CLASS_PATTERN_UNKNOWN_KEYWORD: Final = ErrorMessage('Class "{}" has no attribute "{}"')
1094+
MULTIPLE_ASSIGNMENTS_IN_PATTERN: Final = ErrorMessage(
1095+
'Multiple assignments to name "{}" in pattern'
1096+
)
10781097

10791098
# Plugin: attrs
10801099
CANNOT_DETERMINE_INIT_TYPE: Final = ErrorMessage("Cannot determine __init__ type from converter")
@@ -1149,7 +1168,3 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
11491168
"Never apply isinstance() to unexpanded types; use mypy.types.get_proper_type() first"
11501169
)
11511170
REDUNDANT_GET_PROPER_TYPE: Final = ErrorMessage("Redundant call to get_proper_type()")
1152-
1153-
1154-
# test-data/type_anal_hook.py
1155-
INVALID_SIGNAL_TYPE: Final = ErrorMessage('Invalid "Signal" type (expected "Signal[[t, ...]]")')

mypy/messages.py

+4-11
Original file line numberDiff line numberDiff line change
@@ -190,21 +190,14 @@ def report(self,
190190
end_line=end_line, code=code, allow_dups=allow_dups)
191191

192192
def fail(self,
193-
msg: Union[str, ErrorMessage],
193+
msg: ErrorMessage,
194194
context: Optional[Context],
195195
*,
196-
code: Optional[ErrorCode] = None,
197196
file: Optional[str] = None,
198197
origin: Optional[Context] = None,
199198
allow_dups: bool = False) -> None:
200199
"""Report an error message (unless disabled)."""
201-
# TODO(tushar): Remove `str` support after full migration
202-
if isinstance(msg, ErrorMessage):
203-
self.report(msg.value, context, 'error', code=msg.code, file=file,
204-
origin=origin, allow_dups=allow_dups)
205-
return
206-
207-
self.report(msg, context, 'error', code=code, file=file,
200+
self.report(msg.value, context, 'error', code=msg.code, file=file,
208201
origin=origin, allow_dups=allow_dups)
209202

210203
def note(self,
@@ -365,7 +358,7 @@ def has_no_attr(self,
365358
self.fail(
366359
message_registry.TYPEVAR_UPPER_BOUND_HAS_NO_ATTRIBUTE.format(
367360
typ_fmt, bound_fmt, original_type_fmt, member, extra),
368-
context, code=codes.UNION_ATTR)
361+
context)
369362
return AnyType(TypeOfAny.from_error)
370363

371364
def unsupported_operand_types(self,
@@ -1059,7 +1052,7 @@ def incompatible_typevar_value(self,
10591052
context: Context) -> None:
10601053
self.fail(message_registry.INCOMPATIBLE_TYPEVAR_VALUE
10611054
.format(typevar_name, callable_name(callee) or 'function', format_type(typ)),
1062-
context, code=codes.TYPE_VAR) # TODO: migrate with TypeVar messages
1055+
context)
10631056

10641057
def dangerous_comparison(self, left: Type, right: Type, kind: str, ctx: Context) -> None:
10651058
left_str = 'element' if kind == 'container' else 'left operand'

mypy/semanal.py

+3-8
Original file line numberDiff line numberDiff line change
@@ -5118,23 +5118,18 @@ def in_checked_function(self) -> bool:
51185118
# no regular functions.
51195119
return True
51205120

5121-
# TODO(tushar): remove `str` type and `code` property from here
51225121
def fail(self,
5123-
msg: Union[str, ErrorMessage],
5122+
msg: ErrorMessage,
51245123
ctx: Context,
51255124
serious: bool = False,
51265125
*,
5127-
code: Optional[ErrorCode] = None,
51285126
blocker: bool = False) -> None:
51295127
if not serious and not self.in_checked_function():
51305128
return
51315129
# In case it's a bug and we don't really have context
51325130
assert ctx is not None, msg
5133-
if isinstance(msg, ErrorMessage):
5134-
self.errors.report(ctx.get_line(), ctx.get_column(), msg.value, code=msg.code,
5135-
blocker=blocker)
5136-
return
5137-
self.errors.report(ctx.get_line(), ctx.get_column(), msg, blocker=blocker, code=code)
5131+
self.errors.report(ctx.get_line(), ctx.get_column(), msg.value, code=msg.code,
5132+
blocker=blocker)
51385133

51395134
def note(self, msg: str, ctx: Context, code: Optional[ErrorCode] = None) -> None:
51405135
if not self.in_checked_function():

mypy/semanal_shared.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ def lookup_fully_qualified(self, name: str) -> SymbolTableNode:
4545
def lookup_fully_qualified_or_none(self, name: str) -> Optional[SymbolTableNode]:
4646
raise NotImplementedError
4747

48-
# TODO(tushar): remove `str` type and `code` property from here
4948
@abstractmethod
50-
def fail(self, msg: Union[str, ErrorMessage], ctx: Context, serious: bool = False, *,
51-
blocker: bool = False, code: Optional[ErrorCode] = None) -> None:
49+
def fail(self, msg: ErrorMessage, ctx: Context, serious: bool = False, *,
50+
blocker: bool = False) -> None:
5251
raise NotImplementedError
5352

5453
@abstractmethod

mypy/semanal_typeargs.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@
1818
from mypy.errors import Errors
1919
from mypy.scope import Scope
2020
from mypy.options import Options
21-
from mypy.errorcodes import ErrorCode
2221
from mypy import message_registry, errorcodes as codes
2322
from mypy.messages import format_type
24-
23+
from mypy.message_registry import ErrorMessage
2524

2625
class TypeArgumentAnalyzer(MixedTraverserVisitor):
2726
def __init__(self, errors: Errors, options: Options, is_typeshed_file: bool) -> None:
@@ -74,7 +73,7 @@ def visit_instance(self, t: Instance) -> None:
7473
if isinstance(tvar, TypeVarType):
7574
if isinstance(arg, ParamSpecType):
7675
# TODO: Better message
77-
self.fail(f'Invalid location for ParamSpec "{arg.name}"', t)
76+
self.fail(message_registry.PARAMSPEC_INVALID_LOCATION.format(arg.name), t)
7877
continue
7978
if tvar.values:
8079
if isinstance(arg, TypeVarType):
@@ -83,7 +82,7 @@ def visit_instance(self, t: Instance) -> None:
8382
self.fail(
8483
message_registry.INVALID_TYPEVAR_AS_TYPEARG.format(
8584
arg.name, info.name),
86-
t, code=codes.TYPE_VAR)
85+
t)
8786
continue
8887
else:
8988
arg_values = [arg]
@@ -92,7 +91,7 @@ def visit_instance(self, t: Instance) -> None:
9291
self.fail(
9392
message_registry.INVALID_TYPEVAR_ARG_BOUND.format(
9493
format_type(arg), info.name, format_type(tvar.upper_bound)),
95-
t, code=codes.TYPE_VAR)
94+
t)
9695
super().visit_instance(t)
9796

9897
def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: str,
@@ -104,15 +103,14 @@ def check_type_var_values(self, type: TypeInfo, actuals: List[Type], arg_name: s
104103
if len(actuals) > 1 or not isinstance(actual, Instance):
105104
self.fail(
106105
message_registry.INVALID_TYPEVAR_ARG_VALUE.format(type.name),
107-
context, code=codes.TYPE_VAR)
106+
context)
108107
else:
109108
class_name = '"{}"'.format(type.name)
110109
actual_type_name = '"{}"'.format(actual.type.name)
111110
self.fail(
112111
message_registry.INCOMPATIBLE_TYPEVAR_VALUE.format(
113112
arg_name, class_name, actual_type_name),
114-
context,
115-
code=codes.TYPE_VAR)
113+
context)
116114

117-
def fail(self, msg: str, context: Context, *, code: Optional[ErrorCode] = None) -> None:
118-
self.errors.report(context.get_line(), context.get_column(), msg, code=code)
115+
def fail(self, msg: ErrorMessage, context: Context) -> None:
116+
self.errors.report(context.get_line(), context.get_column(), msg.value, code=msg.code)

mypy/typeanal.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -927,8 +927,7 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> Optional[L
927927
def analyze_type(self, t: Type) -> Type:
928928
return t.accept(self)
929929

930-
# TODO(tushar): remove `str` type and `code` property from here
931-
def fail(self, msg: Union[str, ErrorMessage], ctx: Context) -> None:
930+
def fail(self, msg: ErrorMessage, ctx: Context) -> None:
932931
self.fail_func(msg, ctx)
933932

934933
def note(self, msg: str, ctx: Context, *, code: Optional[ErrorCode] = None) -> None:

test-data/unit/plugins/arg_kinds.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Optional, Callable
22
from mypy.plugin import Plugin, MethodContext, FunctionContext
33
from mypy.types import Type
4-
4+
from mypy.message_registry import ErrorMessage
55

66
class ArgKindsPlugin(Plugin):
77
def get_function_hook(self, fullname: str
@@ -18,12 +18,14 @@ def get_method_hook(self, fullname: str
1818

1919

2020
def extract_arg_kinds_from_function(ctx: FunctionContext) -> Type:
21-
ctx.api.fail(str([[x.value for x in y] for y in ctx.arg_kinds]), ctx.context)
21+
error_message = ErrorMessage(str([[x.value for x in y] for y in ctx.arg_kinds]))
22+
ctx.api.fail(error_message, ctx.context)
2223
return ctx.default_return_type
2324

2425

2526
def extract_arg_kinds_from_method(ctx: MethodContext) -> Type:
26-
ctx.api.fail(str([[x.value for x in y] for y in ctx.arg_kinds]), ctx.context)
27+
error_message = ErrorMessage(str([[x.value for x in y] for y in ctx.arg_kinds]))
28+
ctx.api.fail(error_message, ctx.context)
2729
return ctx.default_return_type
2830

2931

test-data/unit/plugins/attrhook2.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Optional, Callable
22

3+
from mypy.message_registry import ErrorMessage
34
from mypy.plugin import Plugin, AttributeContext
45
from mypy.types import Type, AnyType, TypeOfAny
56

@@ -18,7 +19,7 @@ def magic_field_callback(ctx: AttributeContext) -> Type:
1819

1920

2021
def nonexistent_field_callback(ctx: AttributeContext) -> Type:
21-
ctx.api.fail("Field does not exist", ctx.context)
22+
ctx.api.fail(ErrorMessage("Field does not exist"), ctx.context)
2223
return AnyType(TypeOfAny.from_error)
2324

2425

0 commit comments

Comments
 (0)