Skip to content

Commit 826cc86

Browse files
Merge branch 'plugins-messages' into final
2 parents 5cc51a7 + fc6d5be commit 826cc86

File tree

10 files changed

+133
-64
lines changed

10 files changed

+133
-64
lines changed

misc/proper_plugin.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from mypy import message_registry
12
from mypy.plugin import Plugin, FunctionContext
23
from mypy.types import (
34
Type, Instance, CallableType, UnionType, get_proper_type, ProperType,
@@ -43,8 +44,7 @@ def isinstance_proper_hook(ctx: FunctionContext) -> Type:
4344
isinstance(get_proper_type(arg), AnyType) and is_dangerous_target(right)):
4445
if is_special_target(right):
4546
return ctx.default_return_type
46-
ctx.api.fail('Never apply isinstance() to unexpanded types;'
47-
' use mypy.types.get_proper_type() first', ctx.context)
47+
ctx.api.fail(message_registry.ISINSTANCE_ON_UNEXPANDED_TYPE, ctx.context)
4848
ctx.api.note('If you pass on the original type' # type: ignore[attr-defined]
4949
' after the check, always use its unexpanded version', ctx.context)
5050
return ctx.default_return_type
@@ -113,7 +113,7 @@ def proper_type_hook(ctx: FunctionContext) -> Type:
113113
# Minimize amount of spurious errors from overload machinery.
114114
# TODO: call the hook on the overload as a whole?
115115
if isinstance(arg_type, (UnionType, Instance)):
116-
ctx.api.fail('Redundant call to get_proper_type()', ctx.context)
116+
ctx.api.fail(message_registry.REDUNDANT_GET_PROPER_TYPE, ctx.context)
117117
return ctx.default_return_type
118118

119119

@@ -126,7 +126,7 @@ def proper_types_hook(ctx: FunctionContext) -> Type:
126126
item_type = UnionType.make_union([NoneTyp(), proper_type])
127127
ok_type = ctx.api.named_generic_type('typing.Iterable', [item_type])
128128
if is_proper_subtype(arg_type, ok_type):
129-
ctx.api.fail('Redundant call to get_proper_types()', ctx.context)
129+
ctx.api.fail(message_registry.REDUNDANT_GET_PROPER_TYPE, ctx.context)
130130
return ctx.default_return_type
131131

132132

mypy/message_registry.py

+78
Original file line numberDiff line numberDiff line change
@@ -1075,3 +1075,81 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
10751075
CLASS_PATTERN_DUPLICATE_KEYWORD_PATTERN: Final = 'Duplicate keyword pattern "{}"'
10761076
CLASS_PATTERN_UNKNOWN_KEYWORD: Final = 'Class "{}" has no attribute "{}"'
10771077
MULTIPLE_ASSIGNMENTS_IN_PATTERN: Final = 'Multiple assignments to name "{}" in pattern'
1078+
1079+
# Plugin: attrs
1080+
CANNOT_DETERMINE_INIT_TYPE: Final = ErrorMessage("Cannot determine __init__ type from converter")
1081+
CMP_WITH_EQ_AND_ORDER: Final = ErrorMessage('Don\'t mix "cmp" with "eq" and "order"')
1082+
EQ_TRUE_IF_ORDER_TRUE: Final = ErrorMessage("eq must be True if order is True")
1083+
ARG_MUST_BE_TRUE_OR_FALSE: Final = ErrorMessage('"{}" argument must be True or False.')
1084+
AUTO_ATTRIBS_UNSUPPORTED_PY2: Final = ErrorMessage("auto_attribs is not supported in Python 2")
1085+
ATTRS_NEWSTYLE_CLASS_ONLY: Final = ErrorMessage("attrs only works with new-style classes")
1086+
KW_ONLY_UNSUPPORTED_PY2: Final = ErrorMessage("kw_only is not supported in Python 2")
1087+
DEFAULT_WITH_FACTORY: Final = ErrorMessage('Can\'t pass both "default" and "factory".')
1088+
TYPE_INVALID_ARG: Final = ErrorMessage("Invalid argument to type")
1089+
NON_DEFAULT_ATTRS_AFTER_DEFAULT: Final = ErrorMessage(
1090+
"Non-default attributes not allowed after default attributes."
1091+
)
1092+
ATTR_TOO_MANY_NAMES: Final = ErrorMessage("Too many names for one attribute")
1093+
CONVERT_WITH_CONVERTER: Final = ErrorMessage('Can\'t pass both "convert" and "converter".')
1094+
CONVERT_DEPRECATED: Final = ErrorMessage("convert is deprecated, use converter")
1095+
UNSUPPORTED_CONVERTER: Final = ErrorMessage(
1096+
"Unsupported converter, only named functions and types are currently supported"
1097+
)
1098+
1099+
# Plugin: functools
1100+
TOTAL_ORDERING_NO_OPERATOR_DEFINED: Final = ErrorMessage(
1101+
'No ordering operation defined when using "functools.total_ordering": < > <= >='
1102+
)
1103+
1104+
# Plugin: dataclasses
1105+
DATACLASS_ORDER_METHODS_DISALLOWED: Final = ErrorMessage(
1106+
"You may not have a custom {} method when order=True"
1107+
)
1108+
DATACLASS_DEFAULT_ATTRS_BEFORE_NON_DEFAULT: Final = ErrorMessage(
1109+
"Attributes without a default cannot follow attributes with one"
1110+
)
1111+
DATACLASS_SINGLE_KW_ONLY_TYPE: Final = ErrorMessage(
1112+
"There may not be more than one field with the KW_ONLY type"
1113+
)
1114+
DATACLASS_UNPACKING_KWARGS_IN_FIELD: Final = ErrorMessage(
1115+
'Unpacking **kwargs in "field()" is not supported'
1116+
)
1117+
DATACLASS_POS_ARG_IN_FIELD: Final = ErrorMessage('"field()" does not accept positional arguments')
1118+
DATACLASS_SLOTS_ABOVE_PY310: Final = ErrorMessage(
1119+
'Keyword argument "slots" for "dataclass" is only valid in Python 3.10 and higher'
1120+
)
1121+
DATACLASS_SLOTS_CLASH: Final = ErrorMessage(
1122+
'"{}" both defines "__slots__" and is used with "slots=True"'
1123+
)
1124+
1125+
# Plugin: ctypes
1126+
CTYPES_VALUE_WITH_CHAR_OR_WCHAR_ONLY: Final = ErrorMessage(
1127+
'Array attribute "value" is only available with element type "c_char" or "c_wchar", not {}'
1128+
)
1129+
CTYPES_RAW_WITH_CHAR_ONLY: Final = ErrorMessage(
1130+
'Array attribute "raw" is only available with element type "c_char", not {}'
1131+
)
1132+
CTYPES_INCOMPATIBLE_CONSTRUCTOR_ARG: Final = ErrorMessage(
1133+
"Array constructor argument {} of type {} is not convertible to the array element type {}"
1134+
)
1135+
1136+
# Plugin: singledispatch
1137+
SINGLEDISPATCH_ATLEAST_ONE_ARG: Final = ErrorMessage(
1138+
"Singledispatch function requires at least one argument"
1139+
)
1140+
SINGLEDISPATCH_FIRST_ARG_POSITIONAL: Final = ErrorMessage(
1141+
"First argument to singledispatch function must be a positional argument"
1142+
)
1143+
DISPATCH_TYPE_FALLBACK_SUBTYPE: Final = ErrorMessage(
1144+
"Dispatch type {} must be subtype of fallback function first argument {}"
1145+
)
1146+
1147+
# misc/proper_plugin.py
1148+
ISINSTANCE_ON_UNEXPANDED_TYPE: Final = ErrorMessage(
1149+
"Never apply isinstance() to unexpanded types; use mypy.types.get_proper_type() first"
1150+
)
1151+
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/plugin.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class C: pass
120120
"""
121121

122122
from abc import abstractmethod
123-
from typing import Any, Callable, List, Tuple, Optional, NamedTuple, TypeVar, Dict, Union
123+
from typing import Any, Callable, List, Tuple, Optional, NamedTuple, TypeVar, Dict
124124
from mypy_extensions import trait, mypyc_attr
125125

126126
from mypy.nodes import (
@@ -133,7 +133,6 @@ class C: pass
133133
from mypy.messages import MessageBuilder
134134
from mypy.options import Options
135135
from mypy.lookup import lookup_fully_qualified
136-
from mypy.errorcodes import ErrorCode
137136
from mypy.message_registry import ErrorMessage
138137

139138

@@ -223,10 +222,8 @@ def type_context(self) -> List[Optional[Type]]:
223222
"""Return the type context of the plugin"""
224223
raise NotImplementedError
225224

226-
# TODO(tushar): remove `str` type and `code` property from here
227225
@abstractmethod
228-
def fail(self, msg: Union[str, ErrorMessage], ctx: Context, *,
229-
code: Optional[ErrorCode] = None) -> None:
226+
def fail(self, msg: ErrorMessage, ctx: Context) -> None:
230227
"""Emit an error message at given location."""
231228
raise NotImplementedError
232229

@@ -285,8 +282,8 @@ def parse_bool(self, expr: Expression) -> Optional[bool]:
285282
raise NotImplementedError
286283

287284
@abstractmethod
288-
def fail(self, msg: Union[str, ErrorMessage], ctx: Context, serious: bool = False, *,
289-
blocker: bool = False, code: Optional[ErrorCode] = None) -> None:
285+
def fail(self, msg: ErrorMessage, ctx: Context, serious: bool = False, *,
286+
blocker: bool = False) -> None:
290287
"""Emit an error message at given location."""
291288
raise NotImplementedError
292289

mypy/plugins/attrs.py

+16-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Plugin for supporting the attrs library (http://www.attrs.org)"""
22

3+
from mypy import message_registry
34
from mypy.backports import OrderedDict
45

56
from typing import Optional, Dict, List, cast, Tuple, Iterable
@@ -130,7 +131,7 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument:
130131
init_type = UnionType.make_union([init_type, NoneType()])
131132

132133
if not init_type:
133-
ctx.api.fail("Cannot determine __init__ type from converter", self.context)
134+
ctx.api.fail(message_registry.CANNOT_DETERMINE_INIT_TYPE, self.context)
134135
init_type = AnyType(TypeOfAny.from_error)
135136
elif self.converter.name == '':
136137
# This means we had a converter but it's not of a type we can infer.
@@ -210,7 +211,7 @@ def _determine_eq_order(ctx: 'mypy.plugin.ClassDefContext') -> bool:
210211
order = _get_decorator_optional_bool_argument(ctx, 'order')
211212

212213
if cmp is not None and any((eq is not None, order is not None)):
213-
ctx.api.fail('Don\'t mix "cmp" with "eq" and "order"', ctx.reason)
214+
ctx.api.fail(message_registry.CMP_WITH_EQ_AND_ORDER, ctx.reason)
214215

215216
# cmp takes precedence due to bw-compatibility.
216217
if cmp is not None:
@@ -224,7 +225,7 @@ def _determine_eq_order(ctx: 'mypy.plugin.ClassDefContext') -> bool:
224225
order = eq
225226

226227
if eq is False and order is True:
227-
ctx.api.fail('eq must be True if order is True', ctx.reason)
228+
ctx.api.fail(message_registry.EQ_TRUE_IF_ORDER_TRUE, ctx.reason)
228229

229230
return order
230231

@@ -248,7 +249,7 @@ def _get_decorator_optional_bool_argument(
248249
return False
249250
if attr_value.fullname == 'builtins.None':
250251
return None
251-
ctx.api.fail('"{}" argument must be True or False.'.format(name), ctx.reason)
252+
ctx.api.fail(message_registry.ARG_MUST_BE_TRUE_OR_FALSE.format(name), ctx.reason)
252253
return default
253254
return default
254255
else:
@@ -281,14 +282,14 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext',
281282

282283
if ctx.api.options.python_version[0] < 3:
283284
if auto_attribs:
284-
ctx.api.fail("auto_attribs is not supported in Python 2", ctx.reason)
285+
ctx.api.fail(message_registry.AUTO_ATTRIBS_UNSUPPORTED_PY2, ctx.reason)
285286
return
286287
if not info.defn.base_type_exprs:
287288
# Note: This will not catch subclassing old-style classes.
288-
ctx.api.fail("attrs only works with new-style classes", info.defn)
289+
ctx.api.fail(message_registry.ATTRS_NEWSTYLE_CLASS_ONLY, info.defn)
289290
return
290291
if kw_only:
291-
ctx.api.fail(KW_ONLY_PYTHON_2_UNSUPPORTED, ctx.reason)
292+
ctx.api.fail(message_registry.KW_ONLY_UNSUPPORTED_PY2, ctx.reason)
292293
return
293294

294295
attributes = _analyze_class(ctx, auto_attribs, kw_only)
@@ -407,9 +408,7 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext',
407408
context = attribute.context if i >= len(super_attrs) else ctx.cls
408409

409410
if not attribute.has_default and last_default:
410-
ctx.api.fail(
411-
"Non-default attributes not allowed after default attributes.",
412-
context)
411+
ctx.api.fail(message_registry.NON_DEFAULT_ATTRS_AFTER_DEFAULT, context)
413412
last_default |= attribute.has_default
414413

415414
return attributes
@@ -532,7 +531,7 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
532531
return None
533532

534533
if len(stmt.lvalues) > 1:
535-
ctx.api.fail("Too many names for one attribute", stmt)
534+
ctx.api.fail(message_registry.ATTR_TOO_MANY_NAMES, stmt)
536535
return None
537536

538537
# This is the type that belongs in the __init__ method for this attrib.
@@ -544,15 +543,15 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
544543
# See https://github.com/python-attrs/attrs/issues/481 for explanation.
545544
kw_only |= _get_bool_argument(ctx, rvalue, 'kw_only', False)
546545
if kw_only and ctx.api.options.python_version[0] < 3:
547-
ctx.api.fail(KW_ONLY_PYTHON_2_UNSUPPORTED, stmt)
546+
ctx.api.fail(message_registry.KW_ONLY_UNSUPPORTED_PY2, stmt)
548547
return None
549548

550549
# TODO: Check for attr.NOTHING
551550
attr_has_default = bool(_get_argument(rvalue, 'default'))
552551
attr_has_factory = bool(_get_argument(rvalue, 'factory'))
553552

554553
if attr_has_default and attr_has_factory:
555-
ctx.api.fail('Can\'t pass both "default" and "factory".', rvalue)
554+
ctx.api.fail(message_registry.DEFAULT_WITH_FACTORY, rvalue)
556555
elif attr_has_factory:
557556
attr_has_default = True
558557

@@ -562,7 +561,7 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
562561
try:
563562
un_type = expr_to_unanalyzed_type(type_arg, ctx.api.options, ctx.api.is_stub_file)
564563
except TypeTranslationError:
565-
ctx.api.fail('Invalid argument to type', type_arg)
564+
ctx.api.fail(message_registry.TYPE_INVALID_ARG, type_arg)
566565
else:
567566
init_type = ctx.api.anal_type(un_type)
568567
if init_type and isinstance(lhs.node, Var) and not lhs.node.type:
@@ -574,9 +573,9 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
574573
converter = _get_argument(rvalue, 'converter')
575574
convert = _get_argument(rvalue, 'convert')
576575
if convert and converter:
577-
ctx.api.fail('Can\'t pass both "convert" and "converter".', rvalue)
576+
ctx.api.fail(message_registry.CONVERT_WITH_CONVERTER, rvalue)
578577
elif convert:
579-
ctx.api.fail("convert is deprecated, use converter", rvalue)
578+
ctx.api.fail(message_registry.CONVERT_DEPRECATED, rvalue)
580579
converter = convert
581580
converter_info = _parse_converter(ctx, converter)
582581

@@ -613,10 +612,7 @@ def _parse_converter(ctx: 'mypy.plugin.ClassDefContext',
613612
return argument
614613

615614
# Signal that we have an unsupported converter.
616-
ctx.api.fail(
617-
"Unsupported converter, only named functions and types are currently supported",
618-
converter
619-
)
615+
ctx.api.fail(message_registry.UNSUPPORTED_CONVERTER, converter)
620616
return Converter('')
621617
return Converter(None)
622618

mypy/plugins/common.py

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

3+
from mypy import message_registry
34
from mypy.nodes import (
45
ARG_POS, MDEF, Argument, Block, CallExpr, ClassDef, Expression, SYMBOL_FUNCBASE_TYPES,
56
FuncDef, PassStmt, RefExpr, SymbolTableNode, Var, JsonDict,
@@ -39,7 +40,7 @@ def _get_bool_argument(ctx: ClassDefContext, expr: CallExpr,
3940
if attr_value:
4041
ret = ctx.api.parse_bool(attr_value)
4142
if ret is None:
42-
ctx.api.fail('"{}" argument must be True or False.'.format(name), expr)
43+
ctx.api.fail(message_registry.ARG_MUST_BE_TRUE_OR_FALSE.format(name), expr)
4344
return default
4445
return ret
4546
return default

mypy/plugins/ctypes.py

+9-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# Fully qualified instead of "from mypy.plugin import ..." to avoid circular import problems.
66
import mypy.plugin
7-
from mypy import nodes
7+
from mypy import message_registry, nodes
88
from mypy.maptype import map_instance_to_supertype
99
from mypy.messages import format_type
1010
from mypy.subtypes import is_subtype
@@ -125,17 +125,15 @@ def array_constructor_callback(ctx: 'mypy.plugin.FunctionContext') -> Type:
125125
"The stub of the ctypes.Array constructor should have a single vararg parameter"
126126
for arg_num, (arg_kind, arg_type) in enumerate(zip(ctx.arg_kinds[0], ctx.arg_types[0]), 1):
127127
if arg_kind == nodes.ARG_POS and not is_subtype(arg_type, allowed):
128-
ctx.api.msg.fail(
129-
'Array constructor argument {} of type {}'
130-
' is not convertible to the array element type {}'
128+
ctx.api.fail(
129+
message_registry.CTYPES_INCOMPATIBLE_CONSTRUCTOR_ARG
131130
.format(arg_num, format_type(arg_type), format_type(et)), ctx.context)
132131
elif arg_kind == nodes.ARG_STAR:
133132
ty = ctx.api.named_generic_type("typing.Iterable", [allowed])
134133
if not is_subtype(arg_type, ty):
135134
it = ctx.api.named_generic_type("typing.Iterable", [et])
136-
ctx.api.msg.fail(
137-
'Array constructor argument {} of type {}'
138-
' is not convertible to the array element type {}'
135+
ctx.api.fail(
136+
message_registry.CTYPES_INCOMPATIBLE_CONSTRUCTOR_ARG
139137
.format(arg_num, format_type(arg_type), format_type(it)), ctx.context)
140138

141139
return ctx.default_return_type
@@ -203,9 +201,8 @@ def array_value_callback(ctx: 'mypy.plugin.AttributeContext') -> Type:
203201
elif isinstance(tp, Instance) and tp.type.fullname == 'ctypes.c_wchar':
204202
types.append(_get_text_type(ctx.api))
205203
else:
206-
ctx.api.msg.fail(
207-
'Array attribute "value" is only available'
208-
' with element type "c_char" or "c_wchar", not {}'
204+
ctx.api.fail(
205+
message_registry.CTYPES_VALUE_WITH_CHAR_OR_WCHAR_ONLY
209206
.format(format_type(et)), ctx.context)
210207
return make_simplified_union(types)
211208
return ctx.default_attr_type
@@ -221,9 +218,8 @@ def array_raw_callback(ctx: 'mypy.plugin.AttributeContext') -> Type:
221218
or isinstance(tp, Instance) and tp.type.fullname == 'ctypes.c_char'):
222219
types.append(_get_bytes_type(ctx.api))
223220
else:
224-
ctx.api.msg.fail(
225-
'Array attribute "raw" is only available'
226-
' with element type "c_char", not {}'
221+
ctx.api.fail(
222+
message_registry.CTYPES_RAW_WITH_CHAR_ONLY
227223
.format(format_type(et)), ctx.context)
228224
return make_simplified_union(types)
229225
return ctx.default_attr_type

0 commit comments

Comments
 (0)