8000 Improves `TypeVar` error messages (#11448) · python/mypy@7f0ad94 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7f0ad94

Browse files
authored
Improves TypeVar error messages (#11448)
1 parent 7a5c6f0 commit 7f0ad94

File tree

4 files changed

+53
-21
lines changed

4 files changed

+53
-21
lines changed

mypy/message_registry.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":
152152
INVALID_TYPEVAR_AS_TYPEARG: Final = 'Type variable "{}" not valid as type argument value for "{}"'
153153
INVALID_TYPEVAR_ARG_BOUND: Final = 'Type argument {} of "{}" must be a subtype of {}'
154154
INVALID_TYPEVAR_ARG_VALUE: Final = 'Invalid type argument value for "{}"'
155+
TYPEVAR_VARIANCE_DEF: Final = 'TypeVar "{}" may only be a literal bool'
156+
TYPEVAR_BOUND_MUST_BE_TYPE: Final = 'TypeVar "bound" must be a type'
157+
TYPEVAR_UNEXPECTED_ARGUMENT: Final = 'Unexpected argument to "TypeVar()"'
155158

156159
# Super
157160
TOO_MANY_ARGS_FOR_SUPER: Final = 'Too many arguments for "super"'

mypy/semanal.py

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3172,27 +3172,23 @@ def process_typevar_parameters(self, args: List[Expression],
31723172
upper_bound: Type = self.object_type()
31733173
for param_value, param_name, param_kind in zip(args, names, kinds):
31743174
if not param_kind.is_named():
3175-
self.fail("Unexpected argument to TypeVar()", context)
3175+
self.fail(message_registry.TYPEVAR_UNEXPECTED_ARGUMENT, context)
31763176
return None
31773177
if param_name == 'covariant':
3178-
if isinstance(param_value, NameExpr):
3179-
if param_value.name == 'True':
3180-
covariant = True
3181-
else:
3182-
self.fail("TypeVar 'covariant' may only be 'True'", context)
3183-
return None
3178+
if (isinstance(param_value, NameExpr)
3179+
and param_value.name in ('True', 'False')):
3180+
covariant = param_value.name == 'True'
31843181
else:
3185-
self.fail("TypeVar 'covariant' may only be 'True'", context)
3182+
self.fail(message_registry.TYPEVAR_VARIANCE_DEF.format(
3183+
'covariant'), context)
31863184
return None
31873185
elif param_name == 'contravariant':
3188-
if isinstance(param_value, NameExpr):
3189-
if param_value.name == 'True':
3190-
contravariant = True
3191-
else:
3192-
self.fail("TypeVar 'contravariant' may only be 'True'", context)
3193-
return None
3186+
if (isinstance(param_value, NameExpr)
3187+
and param_value.name in ('True', 'False')):
3188+
contravariant = param_value.name == 'True'
31943189
else:
3195-
self.fail("TypeVar 'contravariant' may only be 'True'", context)
3190+
self.fail(message_registry.TYPEVAR_VARIANCE_DEF.format(
3191+
'contravariant'), context)
31963192
return None
31973193
elif param_name == 'bound':
31983194
if has_values:
@@ -3214,11 +3210,11 @@ def process_typevar_parameters(self, args: List[Expression],
32143210
analyzed = PlaceholderType(None, [], context.line)
32153211
upper_bound = get_proper_type(analyzed)
32163212
if isinstance(upper_bound, AnyType) and upper_bound.is_from_error:
3217-
self.fail('TypeVar "bound" must be a type', param_value)
3213+
self.fail(message_registry.TYPEVAR_BOUND_MUST_BE_TYPE, param_value)
32183214
# Note: we do not return 'None' here -- we want to continue
32193215
# using the AnyType as the upper bound.
32203216
except TypeTranslationError:
3221-
self.fail('TypeVar "bound" must be a type', param_value)
3217+
self.fail(message_registry.TYPEVAR_BOUND_MUST_BE_TYPE, param_value)
32223218
return None
32233219
elif param_name == 'values':
32243220
# Probably using obsolete syntax with values=(...). Explain the current syntax.
@@ -3227,7 +3223,9 @@ def process_typevar_parameters(self, args: List[Expression],
32273223
context)
32283224
return None
32293225
else:
3230-
self.fail('Unexpected argument to TypeVar(): "{}"'.format(param_name), context)
3226+
self.fail('{}: "{}"'.format(
3227+
message_registry.TYPEVAR_UNEXPECTED_ARGUMENT, param_name,
3228+
), context)
32313229
return None
32323230

32333231
if covariant and contravariant:

test-data/unit/semanal-errors.test

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,11 +1030,13 @@ a = TypeVar() # E: Too few arguments for TypeVar()
10301030
b = TypeVar(x='b') # E: TypeVar() expects a string literal as first argument
10311031
c = TypeVar(1) # E: TypeVar() expects a string literal as first argument
10321032
d = TypeVar('D') # E: String argument 1 "D" to TypeVar(...) does not match variable name "d"
1033-
e = TypeVar('e', int, str, x=1) # E: Unexpected argument to TypeVar(): "x"
1033+
e = TypeVar('e', int, str, x=1) # E: Unexpected argument to "TypeVar()": "x"
10341034
f = TypeVar('f', (int, str), int) # E: Type expected
10351035
g = TypeVar('g', int) # E: TypeVar cannot have only a single constraint
1036-
h = TypeVar('h', x=(int, str)) # E: Unexpected argument to TypeVar(): "x"
1036+
h = TypeVar('h', x=(int, str)) # E: Unexpected argument to "TypeVar()": "x"
10371037
i = TypeVar('i', bound=1) # E: TypeVar "bound" must be a type
1038+
j = TypeVar('j', covariant=None) # E: TypeVar "covariant" may only be a literal bool
1039+
k = TypeVar('k', contravariant=1) # E: TypeVar "contravariant" may only be a literal bool
10381040
[out]
10391041

10401042
[case testMoreInvalidTypevarArguments]
@@ -1046,7 +1048,7 @@ S = TypeVar('S', covariant=True, contravariant=True) \
10461048

10471049
[case testInvalidTypevarValues]
10481050
from typing import TypeVar
1049-
b = TypeVar('b', *[int]) # E: Unexpected argument to TypeVar()
1051+
b = TypeVar('b', *[int]) # E: Unexpected argument to "TypeVar()"
10501052
c = TypeVar('c', int, 2) # E: Invalid type: try using Literal[2] instead?
10511053
[out]
10521054

test-data/unit/semanal-types.test

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,35 @@ MypyFile:1(
12841284
builtins.int
12851285
builtins.str))))
12861286

1287+
[case testTypevarWithFalseVariance]
1288+
from typing import TypeVar
1289+
T1 = TypeVar('T1', covariant=False)
1290+
T2 = TypeVar('T2', covariant=False, contravariant=False)
1291+
T3 = TypeVar('T3', contravariant=False)
1292+
T4 = TypeVar('T4', covariant=True, contravariant=False)
1293+
T5 = TypeVar('T5', covariant=False, contravariant=True)
1294+
[builtins fixtures/bool.pyi]
1295+
[out]
1296+
MypyFile:1(
1297+
ImportFrom:1(typing, [TypeVar])
1298+
AssignmentStmt:2(
1299+
NameExpr(T1* [__main__.T1])
1300+
TypeVarExpr:2())
1301+
AssignmentStmt:3(
1302+
NameExpr(T2* [__main__.T2])
1303+
TypeVarExpr:3())
1304+
AssignmentStmt:4(
1305+
NameExpr(T3* [__main__.T3])
1306+
TypeVarExpr:4())
1307+
AssignmentStmt:5(
1308+
NameExpr(T4* [__main__.T4])
1309+
TypeVarExpr:5(
1310+
Variance(COVARIANT)))
1311+
AssignmentStmt:6(
1312+
NameExpr(T5* [__main__.T5])
1313+
TypeVarExpr:6(
1314+
Variance(CONTRAVARIANT))))
1315+
12871316
[case testTypevarWithBound]
12881317
from typing import TypeVar
12891318
T = TypeVar('T', bound=int)

0 commit comments

Comments
 (0)
0