← Back to index

generics_scoping.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 12
FP: 0
FN: 2
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, Literal
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
14# One of these two should pass; either is acceptable:
15assert_type(fun_1(1), int) # E[fun1]
Tag 'fun1' [type-assertion-failure] Type `Literal[1]` does not match asserted type `int`
16assert_type(fun_1(1), Literal[1]) # E[fun1]
18# One of these two should pass; either is acceptable:
19assert_type(fun_2("a"), str) # E[fun2]
Tag 'fun2' [type-assertion-failure] Type `Literal["a"]` does not match asserted type `str`
20assert_type(fun_2("a"), Literal["a"]) # E[fun2]
22# > A type variable used in a method of a generic class that coincides
23# > with one of the variables that parameterize this class is always bound
24# > to that variable.
26class MyClass(Generic[T]):
27 def meth_1(self, x: T) -> T: # T here
28 return x
29 def meth_2(self, x: T) -> T: # and here are always the same
30 return x
32a: MyClass[int] = MyClass()
33a.meth_1(1) # OK
34a.meth_2('a') # E
[invalid-argument-type] Argument to bound method `meth_2` is incorrect: Expected `int`, found `Literal["a"]`
36# > A type variable used in a method that does not match any of the variables
37# > that parameterize the class makes this method a generic function in that
38# > variable.
40S = TypeVar("S")
42class Foo(Generic[T]):
43 def method(self, x: T, y: S) -> S:
44 return y
46x: Foo[int] = Foo()
48# Either of these is acceptable; one of the two should pass:
49assert_type(x.method(0, "abc"), str) # E[method-str]
Tag 'method-str' [type-assertion-failure] Type `Literal["abc"]` does not match asserted type `str`
50assert_type(x.method(0, "abc"), Literal["abc"]) # E[method-str]
52# Either of these is acceptable; one of the two should pass:
53assert_type(x.method(0, b"abc"), bytes) # E[method-bytes]
Tag 'method-bytes' [type-assertion-failure] Type `Literal[b"abc"]` does not match asserted type `bytes`
54assert_type(x.method(0, b"abc"), Literal[b"abc"]) # E[method-bytes]
56# > Unbound type variables should not appear in the bodies of generic functions,
57# > or in the class bodies apart from method definitions.
59def fun_3(x: T) -> list[T]:
60 y: list[T] = [] # OK
61 z: list[S] = [] # E
[unbound-type-variable] Type variable `S` is not bound to any outer generic context
62 return y
64class Bar(Generic[T]):
65 an_attr: list[S] = [] # E
[unbound-type-variable] Type variable `S` is not bound to any outer generic context
67 def do_something(self, x: S) -> S: # OK
68 return x
70# A generic class definition that appears inside a generic function
71# should not use type variables that parameterize the generic function.
73def fun_4(x: T) -> list[T]:
74 a_list: list[T] = [] # OK
76 class MyGeneric(Generic[T]): # E
[shadowed-type-variable] Generic class `MyGeneric` uses type variable `T` already bound by an enclosing scope [shadowed-type-variable] Generic class `MyGeneric` uses type variable `T` already bound by an enclosing scope
77 ...
79 return a_list
81# > A generic class nested in another generic class cannot use the same type
82# > variables. The scope of the type variables of the outer class
83# > doesn't cover the inner one
85class Outer(Generic[T]):
86 class Bad(Iterable[T]): # E
[shadowed-type-variable] Generic class `Bad` uses type variable `T` already bound by an enclosing scope
87 ...
88 class AlsoBad:
89 x: list[T] # E
[unbound-type-variable] Type variable `T` is not bound to any outer generic context
91 def __init__(self, x: list[T]) -> None: # E?
92 self.x = x
94 class Inner(Iterable[S]): # OK
95 ...
96 attr: Inner[T] # OK
98 alias: TypeAlias = list[T] # E
Expected a ty diagnostic for this line
100 def __init__(self, attr: Inner[T]) -> None:
101 self.attr = attr
104# Test unbound type variables at global scope
105global_var1: T # E
[unbound-type-variable] Type variable `T` is not bound to any outer generic context
106global_var2: list[T] = [] # E
[unbound-type-variable] Type variable `T` is not bound to any outer generic context
107list[T]() # E
Expected a ty diagnostic for this line