← Back to index

generics_scoping.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 3
FP: 4
FN: 7
Optional: 0 / 1
1# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#scoping-rules-for-type-variables
2
3from typing import TypeVar, Generic, Iterable, TypeAlias, assert_type
4
5# > A type variable used in a generic function could be inferred to represent
6# > different types in the same code block.
7T = TypeVar('T')
8
9def fun_1(x: T) -> T: # T here
10 return x
11def fun_2(x: T) -> T: # and here could be different
12 return x
14assert_type(fun_1(1), int)
Unexpected error [type-assertion-failure] Type `int` does not match asserted type `Literal[1]`
15assert_type(fun_2('a'), str)
Unexpected error [type-assertion-failure] Type `str` does not match asserted type `Literal["a"]`
17# > A type variable used in a method of a generic class that coincides
18# > with one of the variables that parameterize this class is always bound
19# > to that variable.
21class MyClass(Generic[T]):
22 def meth_1(self, x: T) -> T: # T here
23 return x
24 def meth_2(self, x: T) -> T: # and here are always the same
25 return x
27a: MyClass[int] = MyClass()
28a.meth_1(1) # OK
29a.meth_2('a') # E
[invalid-argument-type] Argument to bound method `meth_2` is incorrect: Expected `int`, found `Literal["a"]`
31# > A type variable used in a method that does not match any of the variables
32# > that parameterize the class makes this method a generic function in that
33# > variable.
35S = TypeVar("S")
37class Foo(Generic[T]):
38 def method(self, x: T, y: S) -> S:
39 return y
41x: Foo[int] = Foo()
42assert_type(x.method(0, "abc"), str)
Unexpected error [type-assertion-failure] Type `str` does not match asserted type `Literal["abc"]`
43assert_type(x.method(0, b"abc"), bytes)
Unexpected error [type-assertion-failure] Type `bytes` does not match asserted type `Literal[b"abc"]`
45# > Unbound type variables should not appear in the bodies of generic functions,
46# > or in the class bodies apart from method definitions.
48def fun_3(x: T) -> list[T]:
49 y: list[T] = [] # OK
50 z: list[S] = [] # E
Expected a ty diagnostic for this line
51 return y
53class Bar(Generic[T]):
54 an_attr: list[S] = [] # E
Expected a ty diagnostic for this line
56 def do_something(self, x: S) -> S: # OK
57 return x
59# A generic class definition that appears inside a generic function
60# should not use type variables that parameterize the generic function.
62def fun_4(x: T) -> list[T]:
63 a_list: list[T] = [] # OK
65 class MyGeneric(Generic[T]): # E
[invalid-generic-class] Generic class `MyGeneric` must not reference type variables bound in an enclosing scope: `T` referenced in class definition here [invalid-generic-class] Generic class `MyGeneric` must not reference type variables bound in an enclosing scope: `T` referenced in class definition here
66 ...
68 return a_list
70# > A generic class nested in another generic class cannot use the same type
71# > variables. The scope of the type variables of the outer class
72# > doesn't cover the inner one
74class Outer(Generic[T]):
75 class Bad(Iterable[T]): # E
[invalid-generic-class] Generic class `Bad` must not reference type variables bound in an enclosing scope: `T` referenced in class definition here
76 ...
77 class AlsoBad:
78 x: list[T] # E
Expected a ty diagnostic for this line
80 def __init__(self, x: list[T]) -> None: # E?
81 self.x = x
83 class Inner(Iterable[S]): # OK
84 ...
85 attr: Inner[T] # OK
87 alias: TypeAlias = list[T] # E
Expected a ty diagnostic for this line
89 def __init__(self, attr: Inner[T]) -> None:
90 self.attr = attr
93# Test unbound type variables at global scope
94global_var1: T # E
Expected a ty diagnostic for this line
95global_var2: list[T] = [] # E
Expected a ty diagnostic for this line
96list[T]() # E
Expected a ty diagnostic for this line