-
Notifications
You must be signed in to change notification settings - Fork 293
Expand file tree
/
Copy pathgenerics_scoping.py
More file actions
107 lines (77 loc) · 3 KB
/
generics_scoping.py
File metadata and controls
107 lines (77 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#scoping-rules-for-type-variables
from typing import TypeVar, Generic, Iterable, TypeAlias, assert_type, Literal
# > A type variable used in a generic function could be inferred to represent
# > different types in the same code block.
T = TypeVar('T')
def fun_1(x: T) -> T: # T here
return x
def fun_2(x: T) -> T: # and here could be different
return x
# One of these two should pass; either is acceptable:
assert_type(fun_1(1), int) # E[fun1]
assert_type(fun_1(1), Literal[1]) # E[fun1]
# One of these two should pass; either is acceptable:
assert_type(fun_2("a"), str) # E[fun2]
assert_type(fun_2("a"), Literal["a"]) # E[fun2]
# > A type variable used in a method of a generic class that coincides
# > with one of the variables that parameterize this class is always bound
# > to that variable.
class MyClass(Generic[T]):
def meth_1(self, x: T) -> T: # T here
return x
def meth_2(self, x: T) -> T: # and here are always the same
return x
a: MyClass[int] = MyClass()
a.meth_1(1) # OK
a.meth_2('a') # E
# > A type variable used in a method that does not match any of the variables
# > that parameterize the class makes this method a generic function in that
# > variable.
S = TypeVar("S")
class Foo(Generic[T]):
def method(self, x: T, y: S) -> S:
return y
x: Foo[int] = Foo()
# Either of these is acceptable; one of the two should pass:
assert_type(x.method(0, "abc"), str) # E[method-str]
assert_type(x.method(0, "abc"), Literal["abc"]) # E[method-str]
# Either of these is acceptable; one of the two should pass:
assert_type(x.method(0, b"abc"), bytes) # E[method-bytes]
assert_type(x.method(0, b"abc"), Literal[b"abc"]) # E[method-bytes]
# > Unbound type variables should not appear in the bodies of generic functions,
# > or in the class bodies apart from method definitions.
def fun_3(x: T) -> list[T]:
y: list[T] = [] # OK
z: list[S] = [] # E
return y
class Bar(Generic[T]):
an_attr: list[S] = [] # E
def do_something(self, x: S) -> S: # OK
return x
# A generic class definition that appears inside a generic function
# should not use type variables that parameterize the generic function.
def fun_4(x: T) -> list[T]:
a_list: list[T] = [] # OK
class MyGeneric(Generic[T]): # E
...
return a_list
# > A generic class nested in another generic class cannot use the same type
# > variables. The scope of the type variables of the outer class
# > doesn't cover the inner one
class Outer(Generic[T]):
class Bad(Iterable[T]): # E
...
class AlsoBad:
x: list[T] # E
def __init__(self, x: list[T]) -> None: # E?
self.x = x
class Inner(Iterable[S]): # OK
...
attr: Inner[T] # OK
alias: TypeAlias = list[T] # E
def __init__(self, attr: Inner[T]) -> None:
self.attr = attr
# Test unbound type variables at global scope
global_var1: T # E
global_var2: list[T] = [] # E
list[T]() # E