Skip to content

Refactor checker error messages #10959

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 15 commits into from
Nov 4, 2021
Merged
Changes from all commits
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
81 changes: 38 additions & 43 deletions mypy/checker.py
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@
is_literal_type_like,
)
from mypy import message_registry
from mypy.message_registry import ErrorMessage
from mypy.subtypes import (
is_subtype, is_equivalent, is_proper_subtype, is_more_precise,
restrict_subtype_away, is_subtype_ignoring_tvars, is_callable_compatible,
@@ -1014,10 +1015,9 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
# entirely pass/Ellipsis/raise NotImplementedError.
if isinstance(return_type, UninhabitedType):
# This is a NoReturn function
self.msg.fail(message_registry.INVALID_IMPLICIT_RETURN, defn)
self.fail(message_registry.INVALID_IMPLICIT_RETURN, defn)
else:
self.msg.fail(message_registry.MISSING_RETURN_STATEMENT, defn,
code=codes.RETURN)
self.fail(message_registry.MISSING_RETURN_STATEMENT, defn)

self.return_types.pop()

@@ -1073,31 +1073,25 @@ def is_unannotated_any(t: Type) -> bool:
if fdef.type is None and self.options.disallow_untyped_defs:
if (not fdef.arguments or (len(fdef.arguments) == 1 and
(fdef.arg_names[0] == 'self' or fdef.arg_names[0] == 'cls'))):
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef,
code=codes.NO_UNTYPED_DEF)
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
if not has_return_statement(fdef) and not fdef.is_generator:
self.note('Use "-> None" if function does not return a value', fdef,
code=codes.NO_UNTYPED_DEF)
else:
self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef,
code=codes.NO_UNTYPED_DEF)
self.fail(message_registry.FUNCTION_TYPE_EXPECTED, fdef)
elif isinstance(fdef.type, CallableType):
ret_type = get_proper_type(fdef.type.ret_type)
if is_unannotated_any(ret_type):
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef,
code=codes.NO_UNTYPED_DEF)
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
elif fdef.is_generator:
if is_unannotated_any(self.get_generator_return_type(ret_type,
fdef.is_coroutine)):
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef,
code=codes.NO_UNTYPED_DEF)
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
elif fdef.is_coroutine and isinstance(ret_type, Instance):
if is_unannotated_any(self.get_coroutine_return_type(ret_type)):
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef,
code=codes.NO_UNTYPED_DEF)
self.fail(message_registry.RETURN_TYPE_EXPECTED, fdef)
if any(is_unannotated_any(t) for t in fdef.type.arg_types):
self.fail(message_registry.ARGUMENT_TYPE_EXPECTED, fdef,
code=codes.NO_UNTYPED_DEF)
self.fail(message_registry.ARGUMENT_TYPE_EXPECTED, fdef)

def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None:
self_type = fill_typevars_with_any(fdef.info)
@@ -1372,7 +1366,7 @@ def check_getattr_method(self, typ: Type, context: Context, name: str) -> None:
if len(self.scope.stack) == 1:
# module scope
if name == '__getattribute__':
self.msg.fail(message_registry.MODULE_LEVEL_GETATTRIBUTE, context)
self.fail(message_registry.MODULE_LEVEL_GETATTRIBUTE, context)
return
# __getattr__ is fine at the module level as of Python 3.7 (PEP 562). We could
# show an error for Python < 3.7, but that would be annoying in code that supports
@@ -2593,7 +2587,7 @@ def check_assignment_to_slots(self, lvalue: Lvalue) -> None:
return

self.fail(
'Trying to assign name "{}" that is not in "__slots__" of type "{}"'.format(
message_registry.NAME_NOT_IN_SLOTS.format(
lvalue.name, inst.type.fullname,
),
lvalue,
@@ -2637,16 +2631,16 @@ def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Ex
elif self.type_is_iterable(typs) and isinstance(typs, Instance):
if (iterable_type is not None
and iterable_type != self.iterable_item_type(typs)):
self.fail("Contiguous iterable with same type expected", context)
self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context)
else:
if last_idx is None or last_idx + 1 == idx_rval:
rvalues.append(rval)
last_idx = idx_rval
iterable_type = self.iterable_item_type(typs)
else:
self.fail("Contiguous iterable with same type expected", context)
self.fail(message_registry.CONTIGUOUS_ITERABLE_EXPECTED, context)
else:
self.fail("Invalid type '{}' for *expr (iterable expected)".format(typs),
self.fail(message_registry.ITERABLE_TYPE_EXPECTED.format(typs),
context)
else:
rvalues.append(rval)
@@ -3179,8 +3173,7 @@ def check_member_assignment(self, instance_type: Type, attribute_type: Type,

dunder_set = attribute_type.type.get_method('__set__')
if dunder_set is None:
self.msg.fail(message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(attribute_type),
context)
self.fail(message_registry.DESCRIPTOR_SET_NOT_CALLABLE.format(attribute_type), context)
return AnyType(TypeOfAny.from_error), get_type, False

function = function_type(dunder_set, self.named_type('builtins.function'))
@@ -3363,8 +3356,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None:
# Functions returning a value of type None are allowed to have a None return.
if is_lambda or isinstance(typ, NoneType):
return
self.fail(message_registry.NO_RETURN_VALUE_EXPECTED, s,
code=codes.RETURN_VALUE)
self.fail(message_registry.NO_RETURN_VALUE_EXPECTED, s)
else:
self.check_subtype(
subtype_label='got',
@@ -3386,7 +3378,7 @@ def check_return_stmt(self, s: ReturnStmt) -> None:
return

if self.in_checked_function():
self.fail(message_registry.RETURN_VALUE_EXPECTED, s, code=codes.RETURN_VALUE)
self.fail(message_registry.RETURN_VALUE_EXPECTED, s)

def visit_if_stmt(self, s: IfStmt) -> None:
"""Type check an if statement."""
@@ -4236,23 +4228,16 @@ def format_expr_type() -> str:
return f'Expression has type "{t}"'

if isinstance(t, FunctionLike):
self.msg.fail(
f'Function "{t}" could always be true in boolean context', expr,
code=codes.TRUTHY_BOOL,
)
self.fail(message_registry.FUNCTION_ALWAYS_TRUE.format(t), expr)
elif isinstance(t, UnionType):
self.msg.fail(
f"{format_expr_type()} of which no members implement __bool__ or __len__ "
"so it could always be true in boolean context",
self.fail(
message_registry.TYPE_ALWAYS_TRUE_UNIONTYPE.format(format_expr_type()),
expr,
code=codes.TRUTHY_BOOL,
)
else:
self.msg.fail(
f'{format_expr_type()} which does not implement __bool__ or __len__ '
'so it could always be true in boolean context',
self.fail(
message_registry.TYPE_ALWAYS_TRUE.format(format_expr_type()),
expr,
code=codes.TRUTHY_BOOL,
)

def find_type_equals_check(self, node: ComparisonExpr, expr_indices: List[int]
@@ -4385,7 +4370,7 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM
if node.callee.type_guard is not None:
# TODO: Follow keyword args or *args, **kwargs
if node.arg_kinds[0] != nodes.ARG_POS:
self.fail("Type guard requires positional argument", node)
self.fail(message_registry.TYPE_GUARD_POS_ARG_REQUIRED, node)
return {}, {}
if literal(expr) == LITERAL_TYPE:
# Note: we wrap the target type, so that we can special case later.
@@ -4931,7 +4916,7 @@ def check_subtype(self,
subtype: Type,
supertype: Type,
context: Context,
msg: str = message_registry.INCOMPATIBLE_TYPES,
msg: Union[str, ErrorMessage] = message_registry.INCOMPATIBLE_TYPES,
subtype_label: Optional[str] = None,
supertype_label: Optional[str] = None,
*,
@@ -4941,9 +4926,14 @@ def check_subtype(self,
if is_subtype(subtype, supertype):
return True

if isinstance(msg, ErrorMessage):
msg_text = msg.value
code = msg.code
else:
msg_text = msg
subtype = get_proper_type(subtype)
supertype = get_proper_type(supertype)
if self.msg.try_report_long_tuple_assignment_error(subtype, supertype, context, msg,
if self.msg.try_report_long_tuple_assignment_error(subtype, supertype, context, msg_text,
subtype_label, supertype_label, code=code):
return False
if self.should_suppress_optional_error([subtype]):
@@ -4962,8 +4952,9 @@ def check_subtype(self,
if isinstance(subtype, Instance) and isinstance(supertype, Instance):
notes = append_invariance_notes([], subtype, supertype)
if extra_info:
msg += ' (' + ', '.join(extra_info) + ')'
self.fail(msg, context, code=code)
msg_text += ' (' + ', '.join(extra_info) + ')'

self.fail(ErrorMessage(msg_text, code=code), context)
for note in notes:
self.msg.note(note, context, code=code)
if note_msg:
@@ -5234,8 +5225,12 @@ def temp_node(self, t: Type, context: Optional[Context] = None) -> TempNode:
"""Create a temporary node with the given, fixed type."""
return TempNode(t, context=context)

def fail(self, msg: str, context: Context, *, code: Optional[ErrorCode] = None) -> None:
def fail(self, msg: Union[str, ErrorMessage], context: Context, *,
code: Optional[ErrorCode] = None) -> None:
"""Produce an error message."""
if isinstance(msg, ErrorMessage):
self.msg.fail(msg.value, context, code=msg.code)
return
self.msg.fail(msg, context, code=code)

def note(self,
18 changes: 10 additions & 8 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
@@ -44,8 +44,10 @@ def __str__(self) -> str:
OVERRIDE: Final = ErrorCode(
"override", "Check that method override is compatible with base class", "General"
)
RETURN: Final = ErrorCode("return", "Check that function always returns a value", "General")
RETURN_VALUE: Final = ErrorCode(
RETURN: Final[ErrorCode] = ErrorCode(
"return", "Check that function always returns a value", "General"
)
RETURN_VALUE: Final[ErrorCode] = ErrorCode(
"return-value", "Check that return value is compatible with signature", "General"
)
ASSIGNMENT: Final = ErrorCode(
@@ -94,11 +96,11 @@ def __str__(self) -> str:
)

# These error codes aren't enabled by default.
NO_UNTYPED_DEF: Final = ErrorCode(
NO_UNTYPED_DEF: Final[ErrorCode] = ErrorCode(
"no-untyped-def", "Check that every function has an annotation", "General"
)
NO_UNTYPED_CALL: Final = ErrorCode(
'no-untyped-call',
"no-untyped-call",
"Disallow calling functions without type annotations from annotated functions",
"General",
)
@@ -122,11 +124,11 @@ def __str__(self) -> str:
REDUNDANT_EXPR: Final = ErrorCode(
"redundant-expr", "Warn about redundant expressions", "General", default_enabled=False
)
TRUTHY_BOOL: Final = ErrorCode(
'truthy-bool',
TRUTHY_BOOL: Final[ErrorCode] = ErrorCode(
"truthy-bool",
"Warn about expressions that could always evaluate to true in boolean contexts",
'General',
default_enabled=False
"General",
default_enabled=False,
)
NAME_MATCH: Final = ErrorCode(
"name-match", "Check that type definition has consistent naming", "General"
8 changes: 7 additions & 1 deletion mypy/errors.py
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
from mypy.options import Options
from mypy.version import __version__ as mypy_version
from mypy.errorcodes import ErrorCode, IMPORT
from mypy.message_registry import ErrorMessage
from mypy import errorcodes as codes
from mypy.util import DEFAULT_SOURCE_OFFSET, is_typeshed_file

@@ -677,7 +678,12 @@ def render_messages(self,
result.append((file, -1, -1, 'note',
'In class "{}":'.format(e.type), e.allow_dups, None))

result.append((file, e.line, e.column, e.severity, e.message, e.allow_dups, e.code))
if isinstance(e.message, ErrorMessage):
result.append(
(file, e.line, e.column, e.severity, e.message.value, e.allow_dups, e.code))
else:
result.append(
(file, e.line, e.column, e.severity, e.message, e.allow_dups, e.code))

prev_import_context = e.import_ctx
prev_function_or_member = e.function_or_member
129 changes: 88 additions & 41 deletions mypy/message_registry.py
Original file line number Diff line number Diff line change
@@ -6,36 +6,52 @@
add a method to MessageBuilder and call this instead.
"""

from typing import NamedTuple, Optional
from typing_extensions import Final

from mypy import errorcodes as codes


class ErrorMessage(NamedTuple):
value: str
code: Optional[codes.ErrorCode] = None

def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
return ErrorMessage(self.value.format(*args, **kwargs), code=self.code)


# Invalid types
INVALID_TYPE_RAW_ENUM_VALUE: Final = "Invalid type: try using Literal[{}.{}] instead?"

# Type checker error message constants
NO_RETURN_VALUE_EXPECTED: Final = "No return value expected"
MISSING_RETURN_STATEMENT: Final = "Missing return statement"
INVALID_IMPLICIT_RETURN: Final = "Implicit return in function which does not return"
INCOMPATIBLE_RETURN_VALUE_TYPE: Final = "Incompatible return value type"
RETURN_VALUE_EXPECTED: Final = "Return value expected"
NO_RETURN_EXPECTED: Final = "Return statement in function which does not return"
INVALID_EXCEPTION: Final = "Exception must be derived from BaseException"
INVALID_EXCEPTION_TYPE: Final = "Exception type must be derived from BaseException"
RETURN_IN_ASYNC_GENERATOR: Final = '"return" with value in async generator is not allowed'
INVALID_RETURN_TYPE_FOR_GENERATOR: Final = (
NO_RETURN_VALUE_EXPECTED: Final = ErrorMessage("No return value expected", codes.RETURN_VALUE)
MISSING_RETURN_STATEMENT: Final = ErrorMessage("Missing return statement", codes.RETURN)
INVALID_IMPLICIT_RETURN: Final = ErrorMessage("Implicit return in function which does not return")
INCOMPATIBLE_RETURN_VALUE_TYPE: Final = ErrorMessage(
"Incompatible return value type", codes.RETURN_VALUE
)
RETURN_VALUE_EXPECTED: Final = ErrorMessage("Return value expected", codes.RETURN_VALUE)
NO_RETURN_EXPECTED: Final = ErrorMessage("Return statement in function which does not return")
INVALID_EXCEPTION: Final = ErrorMessage("Exception must be derived from BaseException")
INVALID_EXCEPTION_TYPE: Final = ErrorMessage("Exception type must be derived from BaseException")
RETURN_IN_ASYNC_GENERATOR: Final = ErrorMessage(
'"return" with value in async generator is not allowed'
)
INVALID_RETURN_TYPE_FOR_GENERATOR: Final = ErrorMessage(
'The return type of a generator function should be "Generator"' " or one of its supertypes"
)
INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR: Final = (
INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR: Final = ErrorMessage(
'The return type of an async generator function should be "AsyncGenerator" or one of its '
"supertypes"
)
INVALID_GENERATOR_RETURN_ITEM_TYPE: Final = (
INVALID_GENERATOR_RETURN_ITEM_TYPE: Final = ErrorMessage(
"The return type of a generator function must be None in"
" its third type parameter in Python 2"
)
YIELD_VALUE_EXPECTED: Final = "Yield value expected"
YIELD_VALUE_EXPECTED: Final = ErrorMessage("Yield value expected")
INCOMPATIBLE_TYPES: Final = "Incompatible types"
INCOMPATIBLE_TYPES_IN_ASSIGNMENT: Final = "Incompatible types in assignment"
INCOMPATIBLE_REDEFINITION: Final = "Incompatible redefinition"
INCOMPATIBLE_REDEFINITION: Final = ErrorMessage("Incompatible redefinition")
INCOMPATIBLE_TYPES_IN_AWAIT: Final = 'Incompatible types in "await"'
INCOMPATIBLE_TYPES_IN_ASYNC_WITH_AENTER: Final = (
'Incompatible types in "async with" for "__aenter__"'
@@ -48,49 +64,76 @@
INCOMPATIBLE_TYPES_IN_YIELD: Final = 'Incompatible types in "yield"'
INCOMPATIBLE_TYPES_IN_YIELD_FROM: Final = 'Incompatible types in "yield from"'
INCOMPATIBLE_TYPES_IN_STR_INTERPOLATION: Final = "Incompatible types in string interpolation"
MUST_HAVE_NONE_RETURN_TYPE: Final = 'The return type of "{}" must be None'
MUST_HAVE_NONE_RETURN_TYPE: Final = ErrorMessage('The return type of "{}" must be None')
INVALID_TUPLE_INDEX_TYPE: Final = "Invalid tuple index type"
TUPLE_INDEX_OUT_OF_RANGE: Final = "Tuple index out of range"
INVALID_SLICE_INDEX: Final = "Slice index must be an integer or None"
CANNOT_INFER_LAMBDA_TYPE: Final = "Cannot infer type of lambda"
CANNOT_ACCESS_INIT: Final = 'Cannot access "__init__" directly'
NON_INSTANCE_NEW_TYPE: Final = '"__new__" must return a class instance (got {})'
INVALID_NEW_TYPE: Final = 'Incompatible return type for "__new__"'
BAD_CONSTRUCTOR_TYPE: Final = "Unsupported decorated constructor type"
NON_INSTANCE_NEW_TYPE: Final = ErrorMessage('"__new__" must return a class instance (got {})')
INVALID_NEW_TYPE: Final = ErrorMessage('Incompatible return type for "__new__"')
BAD_CONSTRUCTOR_TYPE: Final = ErrorMessage("Unsupported decorated constructor type")
CANNOT_ASSIGN_TO_METHOD: Final = "Cannot assign to a method"
CANNOT_ASSIGN_TO_TYPE: Final = "Cannot assign to a type"
INCONSISTENT_ABSTRACT_OVERLOAD: Final = (
INCONSISTENT_ABSTRACT_OVERLOAD: Final = ErrorMessage(
"Overloaded method has both abstract and non-abstract variants"
)
MULTIPLE_OVERLOADS_REQUIRED: Final = "Single overload definition, multiple required"
READ_ONLY_PROPERTY_OVERRIDES_READ_WRITE: Final = (
MULTIPLE_OVERLOADS_REQUIRED: Final = ErrorMessage("Single overload definition, multiple required")
READ_ONLY_PROPERTY_OVERRIDES_READ_WRITE: Final = ErrorMessage(
"Read-only property cannot override read-write property"
)
FORMAT_REQUIRES_MAPPING: Final = "Format requires a mapping"
RETURN_TYPE_CANNOT_BE_CONTRAVARIANT: Final = (
RETURN_TYPE_CANNOT_BE_CONTRAVARIANT: Final = ErrorMessage(
"Cannot use a contravariant type variable as return type"
)
FUNCTION_PARAMETER_CANNOT_BE_COVARIANT: Final = (
FUNCTION_PARAMETER_CANNOT_BE_COVARIANT: Final = ErrorMessage(
"Cannot use a covariant type variable as a parameter"
)
INCOMPATIBLE_IMPORT_OF: Final = "Incompatible import of"
FUNCTION_TYPE_EXPECTED: Final = "Function is missing a type annotation"
ONLY_CLASS_APPLICATION: Final = "Type application is only supported for generic classes"
RETURN_TYPE_EXPECTED: Final = "Function is missing a return type annotation"
ARGUMENT_TYPE_EXPECTED: Final = "Function is missing a type annotation for one or more arguments"
FUNCTION_TYPE_EXPECTED: Final = ErrorMessage(
"Function is missing a type annotation", codes.NO_UNTYPED_DEF
)
ONLY_CLASS_APPLICATION: Final = ErrorMessage(
"Type application is only supported for generic classes"
)
RETURN_TYPE_EXPECTED: Final = ErrorMessage(
"Function is missing a return type annotation", codes.NO_UNTYPED_DEF
)
ARGUMENT_TYPE_EXPECTED: Final = ErrorMessage(
"Function is missing a type annotation for one or more arguments", codes.NO_UNTYPED_DEF
)
KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE: Final = (
'Keyword argument only valid with "str" key type in call to "dict"'
)
ALL_MUST_BE_SEQ_STR: Final = "Type of __all__ must be {}, not {}"
ALL_MUST_BE_SEQ_STR: Final = ErrorMessage("Type of __all__ must be {}, not {}")
INVALID_TYPEDDICT_ARGS: Final = (
"Expected keyword arguments, {...}, or dict(...) in TypedDict constructor"
)
TYPEDDICT_KEY_MUST_BE_STRING_LITERAL: Final = "Expected TypedDict key to be string literal"
MALFORMED_ASSERT: Final = "Assertion is always true, perhaps remove parentheses?"
MALFORMED_ASSERT: Final = ErrorMessage("Assertion is always true, perhaps remove parentheses?")
DUPLICATE_TYPE_SIGNATURES: Final = "Function has duplicate type signatures"
DESCRIPTOR_SET_NOT_CALLABLE: Final = "{}.__set__ is not callable"
DESCRIPTOR_SET_NOT_CALLABLE: Final = ErrorMessage("{}.__set__ is not callable")
DESCRIPTOR_GET_NOT_CALLABLE: Final = "{}.__get__ is not callable"
MODULE_LEVEL_GETATTRIBUTE: Final = "__getattribute__ is not valid at the module level"
MODULE_LEVEL_GETATTRIBUTE: Final = ErrorMessage(
"__getattribute__ is not valid at the module level"
)
NAME_NOT_IN_SLOTS: Final = ErrorMessage(
'Trying to assign name "{}" that is not in "__slots__" of type "{}"'
)
TYPE_ALWAYS_TRUE: Final = ErrorMessage(
"{} which does not implement __bool__ or __len__ "
"so it could always be true in boolean context",
code=codes.TRUTHY_BOOL,
)
TYPE_ALWAYS_TRUE_UNIONTYPE: Final = ErrorMessage(
"{} of which no members implement __bool__ or __len__ "
"so it could always be true in boolean context",
code=codes.TRUTHY_BOOL,
)
FUNCTION_ALWAYS_TRUE: Final = ErrorMessage(
'Function "{}" could always be true in boolean context',
code=codes.TRUTHY_BOOL,
)

# Generic
GENERIC_INSTANCE_VAR_CLASS_ACCESS: Final = (
@@ -122,38 +165,42 @@
)

# Self-type
MISSING_OR_INVALID_SELF_TYPE: Final = (
MISSING_OR_INVALID_SELF_TYPE: Final = ErrorMessage(
"Self argument missing for a non-static method (or an invalid type for self)"
)
ERASED_SELF_TYPE_NOT_SUPERTYPE: Final = (
ERASED_SELF_TYPE_NOT_SUPERTYPE: Final = ErrorMessage(
'The erased type of self "{}" is not a supertype of its class "{}"'
)
INVALID_SELF_TYPE_OR_EXTRA_ARG: Final = (
INVALID_SELF_TYPE_OR_EXTRA_ARG: Final = ErrorMessage(
"Invalid type for self, or extra argument type in function annotation"
)

# Final
CANNOT_INHERIT_FROM_FINAL: Final = 'Cannot inherit from final class "{}"'
DEPENDENT_FINAL_IN_CLASS_BODY: Final = (
CANNOT_INHERIT_FROM_FINAL: Final = ErrorMessage('Cannot inherit from final class "{}"')
DEPENDENT_FINAL_IN_CLASS_BODY: Final = ErrorMessage(
"Final name declared in class body cannot depend on type variables"
)
CANNOT_ACCESS_FINAL_INSTANCE_ATTR: Final = (
'Cannot access final instance attribute "{}" on class object'
)
CANNOT_MAKE_DELETABLE_FINAL: Final = "Deletable attribute cannot be final"
CANNOT_MAKE_DELETABLE_FINAL: Final = ErrorMessage("Deletable attribute cannot be final")

# ClassVar
CANNOT_OVERRIDE_INSTANCE_VAR: Final = (
CANNOT_OVERRIDE_INSTANCE_VAR: Final = ErrorMessage(
'Cannot override instance variable (previously declared on base class "{}") with class '
"variable"
)
CANNOT_OVERRIDE_CLASS_VAR: Final = (
CANNOT_OVERRIDE_CLASS_VAR: Final = ErrorMessage(
'Cannot override class variable (previously declared on base class "{}") with instance '
"variable"
)

# Protocol
RUNTIME_PROTOCOL_EXPECTED: Final = (
RUNTIME_PROTOCOL_EXPECTED: Final = ErrorMessage(
"Only @runtime_checkable protocols can be used with instance and class checks"
)
CANNOT_INSTANTIATE_PROTOCOL: Final = 'Cannot instantiate protocol class "{}"'
CANNOT_INSTANTIATE_PROTOCOL: Final = ErrorMessage('Cannot instantiate protocol class "{}"')

CONTIGUOUS_ITERABLE_EXPECTED: Final = ErrorMessage("Contiguous iterable with same type expected")
ITERABLE_TYPE_EXPECTED: Final = ErrorMessage("Invalid type '{}' for *expr (iterable expected)")
TYPE_GUARD_POS_ARG_REQUIRED: Final = ErrorMessage("Type guard requires positional argument")