-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsubscriptable_function.py
219 lines (155 loc) · 7.01 KB
/
subscriptable_function.py
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
from __future__ import annotations
from typing import Callable, Concatenate, Tuple, Type, reveal_type
args = ()
kwargs = {}
#######################################
# functions #
#######################################
#----------------------------------#
# workarounds #
#----------------------------------#
# double parentheses to call: fn1[int, str]()()
# type arguments can be captured from self.__orig_class__.__args__
class fn1[A, B]:
def __call__(self, *args, **kwargs) -> Tuple[A, B]: ...
reveal_type(fn1[int, str]()(*args, **kwargs)) # Tuple[int, str]
# parentheses to call: fn2[int, str]()
# type arguments cannot be captured from self.__orig_class__.__args__
class fn2[A, B]:
def __new__(cls, *args, **kwargs) -> Tuple[A, B]: ...
reveal_type(fn2[int, str](*args, **kwargs)) # Tuple[int, str]
# no parentheses/call arguments
# no parentheses to call: fn3[int, str]
# type arguments can be captured from tp
class fn3:
def __class_getitem__[A, B](cls, tp: Tuple[Type[A], Type[B]]) -> Tuple[A, B]: ...
reveal_type(fn3[int, str]) # type[fn3] <-- static type inference failure
# parentheses to call: fn4[int, str]()
# type arguments can be captured from tp
class fn4:
def __class_getitem__[A, B](cls, tp: Tuple[Type[A], Type[B]]) -> Callable[[], Tuple[A, B]]:
def inner(*args, **kwargs) -> Tuple[A, B]:
...
return inner
reveal_type(fn4[int, str](*args, **kwargs)) # fn4 <-- static type inference failure
# parentheses to call: fn5[int, str]()
# type arguments can be captured from tp
class _fn5:
def __getitem__[A, B](self, tp: Tuple[Type[A], Type[B]]) -> Callable[[], Tuple[A, B]]:
def inner(*args, **kwargs) -> Tuple[A, B]:
...
return inner
fn5 = _fn5()
reveal_type(fn5[int, str](*args, **kwargs)) # Tuple[int, str]
# no parentheses/call arguments
# no parentheses to call: fn6[int, str]
# type arguments can be captured from tp
class _fn6:
def __getitem__[A, B](self, tp: Tuple[Type[A], Type[B]]) -> Tuple[A, B]:
...
fn6 = _fn6()
reveal_type(fn6[int, str]) # Tuple[int, str]
#----------------------------------#
# utils #
#----------------------------------#
class Args[*T]: pass
type TypeArgs[*T] = type[Args[*T]]
class subscriptable[*T, **P, R]:
def __init__(self, fn: Callable[Concatenate[TypeArgs[*T], P], R]) -> None:
self.fn = fn
def __getitem__(self, tp: TypeArgs[*T]) -> Callable[P, R]:
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
return self.fn(tp, *args, **kwargs)
# functools.wraps
inner.__type_params__ = (T,)
return inner
@subscriptable
def fun1[A, B](tp: TypeArgs[A, B], a: A, b: B) -> Tuple[A, B]:
...
reveal_type(fun1) # subscriptable[A@fun1, B@fun1, (a: A@fun1, b: B@fun1), Tuple[A@fun1, B@fun1]]
# Argument of type "type[Args[int, str]]" cannot be assigned to parameter "tp" of type "TypeArgs[A@fun1, B@fun1]" in function "__getitem__"
# "type[Args[int, str]]" is incompatible with "TypeArgs[A@fun1, B@fun1]"
# Type "type[Args[int, str]]" is incompatible with type "TypeArgs[A@fun1, B@fun1]"
# Type parameter "T@Args" is covariant, but "*tuple[int, str]" is not a subtype of "*tuple[A@fun1, B@fun1]"
# "*tuple[int, str]" is incompatible with "*tuple[A@fun1, B@fun1]"
# Tuple entry 1 is incorrect type
# Type "int" is incompatible with type "A@fun1"
reveal_type(fun1[Args[int, str]]) # (a: A@fun1, b: B@fun1) -> Tuple[A@fun1, B@fun1]
reveal_type(fun1[Args[int, str]]('a', 1)) # Tuple[str, int]
class subscriptable1[T, **P, R]:
def __init__(self, fn: Callable[Concatenate[Type[T], P], R]) -> None:
self.fn = fn
def __getitem__(self, tp: Type[T]) -> Callable[P, R]:
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
return self.fn(tp, *args, **kwargs)
# functools.wraps
inner.__type_params__ = (T,)
return inner
@subscriptable1
def fun2[A](tp: Type[A], a: A) -> Tuple[A]:
...
reveal_type(fun2) # subscriptable1[A@fun2, (a: A@fun2), Tuple[A@fun2]]
# Argument of type "type[int]" cannot be assigned to parameter "tp" of type "type[A@fun2]" in function "__getitem__"
# Type "type[int]" is incompatible with type "type[A@fun2]"
reveal_type(fun2[int]) # (a: A@fun2) -> Tuple[A@fun2]
reveal_type(fun2[int]('a')) # Tuple[str]
#######################################
# methods #
#######################################
#----------------------------------#
# workarounds #
#----------------------------------#
# no parentheses/call arguments
# no parentheses to call: Owner1().fn[int, str]
# type arguments can be captured from tp
class Owner1:
class _fn:
def __init__(self, owner: Owner1) -> None:
self.owner = owner
def __getitem__[A, B](self, tp: Tuple[Type[A], Type[B]]) -> Tuple[A, B]: ...
@property
def fn(self):
return Owner1._fn(self)
reveal_type(Owner1().fn[int, str]) # Tuple[int, str]
# parentheses to call: Owner2().fn[int, str]()
# type arguments can be captured from tp
class Owner2:
class _fn:
def __init__(self, owner: Owner2) -> None:
self.owner = owner
def __getitem__[A, B](self, tp: Tuple[Type[A], Type[B]]):
def inner(*args, **kwargs) -> Tuple[A, B]: ...
return inner
@property
def fn(self):
return Owner2._fn(self)
reveal_type(Owner2().fn[int, str](*args, **kwargs)) # Tuple[int, str]
#----------------------------------#
# utils #
#----------------------------------#
class subscriptablemethod[Self, *T, **P, R]:
def __init__(self, fn: Callable[Concatenate[Self, TypeArgs[*T], P], R]) -> None:
self.fn = fn
def __get__(self, instance: Self, owner: Type[Self]):
self.instance = instance
return self
def __getitem__(self, tp: TypeArgs[*T]) -> Callable[P, R]:
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
return self.fn(self.instance, tp, *args, **kwargs)
# functools.wraps
inner.__type_params__ = (T,)
return inner
class Owner3:
@subscriptablemethod
def fn[A, B](self, tp: TypeArgs[A, B], a: A, b: B) -> Tuple[A, B]:
...
reveal_type(Owner3().fn) # subscriptablemethod[Owner3, A@fn, B@fn, (a: A@fn, b: B@fn), Tuple[A@fn, B@fn]]
# Argument of type "type[Args[int, str]]" cannot be assigned to parameter "tp" of type "TypeArgs[A@fn, B@fn]" in function "__getitem__"
# "type[Args[int, str]]" is incompatible with "TypeArgs[A@fn, B@fn]"
# Type "type[Args[int, str]]" is incompatible with type "TypeArgs[A@fn, B@fn]"
# Type parameter "T@Args" is covariant, but "*tuple[int, str]" is not a subtype of "*tuple[A@fn, B@fn]"
# "*tuple[int, str]" is incompatible with "*tuple[A@fn, B@fn]"
# Tuple entry 1 is incorrect type
# Type "int" is incompatible with type "A@fn"
reveal_type(Owner3().fn[Args[int, str]]) # (a: A@fn, b: B@fn) -> Tuple[A@fn, B@fn]
reveal_type(Owner3().fn[Args[int, str]]('a', 1)) # Tuple[str, int]