Skip to content

Commit 428a536

Browse files
Michael0x2agvanrossum
authored andcommitted
Make TypeVars with only one constraint illegal (#2626)
At runtime, doing something like `T = TypeVar('T', str)` results in the following exception: TypeError: A single constraint is not allowed However, mypy itself seems to ignore this particular error, which feels incongruous. This commit slightly tweaks the semanal phase to emit an error message in this case to mirror the existing runtime behavior implemented in the typing module.
1 parent 122aa77 commit 428a536

File tree

5 files changed

+16
-12
lines changed

5 files changed

+16
-12
lines changed

mypy/semanal.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,7 +1852,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None:
18521852
res = self.process_typevar_parameters(call.args[1 + n_values:],
18531853
call.arg_names[1 + n_values:],
18541854
call.arg_kinds[1 + n_values:],
1855-
bool(values),
1855+
n_values,
18561856
s)
18571857
if res is None:
18581858
return
@@ -1899,8 +1899,9 @@ def get_typevar_declaration(self, s: AssignmentStmt) -> Optional[CallExpr]:
18991899
def process_typevar_parameters(self, args: List[Expression],
19001900
names: List[Optional[str]],
19011901
kinds: List[int],
1902-
has_values: bool,
1902+
num_values: int,
19031903
context: Context) -> Optional[Tuple[int, Type]]:
1904+
has_values = (num_values > 0)
19041905
covariant = False
19051906
contravariant = False
19061907
upper_bound = self.object_type() # type: Type
@@ -1950,6 +1951,9 @@ def process_typevar_parameters(self, args: List[Expression],
19501951
if covariant and contravariant:
19511952
self.fail("TypeVar cannot be both covariant and contravariant", context)
19521953
return None
1954+
elif num_values == 1:
1955+
self.fail("TypeVar cannot have only a single constraint", context)
1956+
return None
19531957
elif covariant:
19541958
variance = COVARIANT
19551959
elif contravariant:

test-data/unit/check-bound.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,12 @@ class A(A0):
142142
class B(A):
143143
def baz(self) -> None: pass
144144

145-
T = TypeVar('T', A)
145+
T = TypeVar('T', bound=A)
146146

147147
def f(x: T) -> T:
148148
x.foo()
149149
x.bar()
150-
x.baz() # E: "A" has no attribute "baz"
150+
x.baz() # E: "T" has no attribute "baz"
151151
x.a
152152
x.b
153153
return x

test-data/unit/check-classes.test

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,13 +1550,12 @@ tmp/foo.pyi:6: error: Signatures of "__radd__" of "B" and "__add__" of "X" are u
15501550

15511551
[case testUnsafeOverlappingWithLineNo]
15521552
from typing import TypeVar
1553-
T = TypeVar('T', Real)
15541553
class Real:
15551554
def __add__(self, other): ...
15561555
class Fraction(Real):
1557-
def __radd__(self, other: T) -> T: ...
1556+
def __radd__(self, other: Real) -> Real: ...
15581557
[out]
1559-
main:6: error: Signatures of "__radd__" of "Fraction" and "__add__" of "Real" are unsafely overlapping
1558+
main:5: error: Signatures of "__radd__" of "Fraction" and "__add__" of "Real" are unsafely overlapping
15601559

15611560
[case testOverlappingNormalAndInplaceOperatorMethod]
15621561
import typing

test-data/unit/check-typevar-values.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def outer(x: T) -> T:
498498
[case testClassMemberTypeVarInFunctionBody]
499499
from typing import TypeVar
500500
class C:
501-
T = TypeVar('T', int)
501+
T = TypeVar('T', bound=int)
502502
def f(self, x: T) -> T:
503503
A = C.T
504504
return x

test-data/unit/semanal-errors.test

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -976,10 +976,11 @@ a = TypeVar() # E: Too few arguments for TypeVar()
976976
b = TypeVar(x='b') # E: TypeVar() expects a string literal as first argument
977977
c = TypeVar(1) # E: TypeVar() expects a string literal as first argument
978978
d = TypeVar('D') # E: String argument 1 'D' to TypeVar(...) does not match variable name 'd'
979-
e = TypeVar('e', int, str, x=1) # E: Unexpected argument to TypeVar(): x
980-
f = TypeVar('f', (int, str)) # E: Type expected
981-
g = TypeVar('g', x=(int, str)) # E: Unexpected argument to TypeVar(): x
982-
h = TypeVar('h', bound=1) # E: TypeVar 'bound' must be a type
979+
e = TypeVar('e', int, str, x=1) # E: Unexpected argument to TypeVar(): x
980+
f = TypeVar('f', (int, str), int) # E: Type expected
981+
g = TypeVar('g', int) # E: TypeVar cannot have only a single constraint
982+
h = TypeVar('h', x=(int, str)) # E: Unexpected argument to TypeVar(): x
983+
i = TypeVar('i', bound=1) # E: TypeVar 'bound' must be a type
983984
[out]
984985

985986
[case testMoreInvalidTypevarArguments]

0 commit comments

Comments
 (0)