ovld 0.3.9__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ovld/__init__.py +41 -15
- ovld/core.py +404 -589
- ovld/dependent.py +275 -0
- ovld/mro.py +193 -161
- ovld/recode.py +518 -16
- ovld/typemap.py +383 -0
- ovld/types.py +219 -0
- ovld/utils.py +9 -110
- ovld/version.py +1 -1
- ovld-0.4.1.dist-info/METADATA +216 -0
- ovld-0.4.1.dist-info/RECORD +13 -0
- ovld-0.3.9.dist-info/METADATA +0 -305
- ovld-0.3.9.dist-info/RECORD +0 -10
- {ovld-0.3.9.dist-info → ovld-0.4.1.dist-info}/WHEEL +0 -0
- {ovld-0.3.9.dist-info → ovld-0.4.1.dist-info}/licenses/LICENSE +0 -0
ovld/dependent.py
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
import inspect
|
2
|
+
import re
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from itertools import count
|
5
|
+
from typing import TYPE_CHECKING, Any, Mapping, TypeVar, Union
|
6
|
+
|
7
|
+
from .types import (
|
8
|
+
Intersection,
|
9
|
+
Order,
|
10
|
+
TypeRelationship,
|
11
|
+
normalize_type,
|
12
|
+
subclasscheck,
|
13
|
+
typeorder,
|
14
|
+
)
|
15
|
+
|
16
|
+
_current = count()
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class CodeGen:
|
21
|
+
template: str
|
22
|
+
substitutions: dict
|
23
|
+
|
24
|
+
def mangle(self):
|
25
|
+
renamings = {
|
26
|
+
k: f"{{{k}__{next(_current)}}}" for k in self.substitutions
|
27
|
+
}
|
28
|
+
renamings["arg"] = "{arg}"
|
29
|
+
new_subs = {
|
30
|
+
newk[1:-1]: self.substitutions[k]
|
31
|
+
for k, newk in renamings.items()
|
32
|
+
if k in self.substitutions
|
33
|
+
}
|
34
|
+
return CodeGen(
|
35
|
+
template=self.template.format(**renamings),
|
36
|
+
substitutions=new_subs,
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
def combine(master_template, args):
|
41
|
+
fmts = []
|
42
|
+
subs = {}
|
43
|
+
for cg in args:
|
44
|
+
mangled = cg.mangle()
|
45
|
+
fmts.append(mangled.template)
|
46
|
+
subs.update(mangled.substitutions)
|
47
|
+
return CodeGen(master_template.format(*fmts), subs)
|
48
|
+
|
49
|
+
|
50
|
+
class DependentType:
|
51
|
+
exclusive_type = False
|
52
|
+
keyable_type = False
|
53
|
+
|
54
|
+
def __init__(self, bound):
|
55
|
+
self.bound = bound
|
56
|
+
|
57
|
+
def __class_getitem__(cls, item):
|
58
|
+
items = (item,) if not isinstance(item, tuple) else item
|
59
|
+
return cls(*items)
|
60
|
+
|
61
|
+
def with_bound(self, new_bound): # pragma: no cover
|
62
|
+
return type(self)(new_bound)
|
63
|
+
|
64
|
+
def check(self, value): # pragma: no cover
|
65
|
+
raise NotImplementedError()
|
66
|
+
|
67
|
+
def codegen(self):
|
68
|
+
return CodeGen("{this}.check({arg})", {"this": self})
|
69
|
+
|
70
|
+
def __typeorder__(self, other):
|
71
|
+
if isinstance(other, DependentType):
|
72
|
+
order = typeorder(self.bound, other.bound)
|
73
|
+
if order is Order.SAME:
|
74
|
+
# It isn't fully deterministic which of these will be called
|
75
|
+
# because of set ordering between the types we compare
|
76
|
+
if self < other: # pragma: no cover
|
77
|
+
return TypeRelationship(Order.LESS, matches=False)
|
78
|
+
elif other < self: # pragma: no cover
|
79
|
+
return TypeRelationship(Order.MORE, matches=False)
|
80
|
+
else:
|
81
|
+
return TypeRelationship(Order.NONE, matches=False)
|
82
|
+
else: # pragma: no cover
|
83
|
+
return TypeRelationship(order, matches=False)
|
84
|
+
elif (matches := subclasscheck(other, self.bound)) or subclasscheck(
|
85
|
+
self.bound, other
|
86
|
+
):
|
87
|
+
return TypeRelationship(Order.LESS, matches=matches)
|
88
|
+
else:
|
89
|
+
return TypeRelationship(Order.NONE, matches=False)
|
90
|
+
|
91
|
+
def __lt__(self, other):
|
92
|
+
return False
|
93
|
+
|
94
|
+
def __or__(self, other):
|
95
|
+
if not isinstance(other, DependentType):
|
96
|
+
return NotImplemented
|
97
|
+
return Or(self, other)
|
98
|
+
|
99
|
+
def __and__(self, other):
|
100
|
+
if not isinstance(other, DependentType):
|
101
|
+
return NotImplemented
|
102
|
+
return And(self, other)
|
103
|
+
|
104
|
+
|
105
|
+
class ParametrizedDependentType(DependentType):
|
106
|
+
def __init__(self, *parameters, bound=None):
|
107
|
+
super().__init__(
|
108
|
+
self.default_bound(*parameters) if bound is None else bound
|
109
|
+
)
|
110
|
+
self.parameters = parameters
|
111
|
+
|
112
|
+
@property
|
113
|
+
def parameter(self):
|
114
|
+
return self.parameters[0]
|
115
|
+
|
116
|
+
def default_bound(self, *parameters):
|
117
|
+
return None
|
118
|
+
|
119
|
+
def with_bound(self, new_bound):
|
120
|
+
return type(self)(*self.parameters, bound=new_bound)
|
121
|
+
|
122
|
+
def __eq__(self, other):
|
123
|
+
return (
|
124
|
+
type(self) is type(other)
|
125
|
+
and self.parameters == other.parameters
|
126
|
+
and self.bound == other.bound
|
127
|
+
)
|
128
|
+
|
129
|
+
def __hash__(self):
|
130
|
+
return hash(self.parameters) ^ hash(self.bound)
|
131
|
+
|
132
|
+
def __str__(self):
|
133
|
+
params = ", ".join(map(repr, self.parameters))
|
134
|
+
return f"{type(self).__name__}({params})"
|
135
|
+
|
136
|
+
__repr__ = __str__
|
137
|
+
|
138
|
+
|
139
|
+
class FuncDependentType(ParametrizedDependentType):
|
140
|
+
def default_bound(self, *_):
|
141
|
+
fn = type(self).func
|
142
|
+
return normalize_type(
|
143
|
+
list(inspect.signature(fn).parameters.values())[0].annotation, fn
|
144
|
+
)
|
145
|
+
|
146
|
+
def __lt__(self, other):
|
147
|
+
if len(self.parameters) != len(other.parameters):
|
148
|
+
return False
|
149
|
+
p1g = sum(
|
150
|
+
p1 is Any and p2 is not Any
|
151
|
+
for p1, p2 in zip(self.parameters, other.parameters)
|
152
|
+
)
|
153
|
+
p2g = sum(
|
154
|
+
p2 is Any and p1 is not Any
|
155
|
+
for p1, p2 in zip(self.parameters, other.parameters)
|
156
|
+
)
|
157
|
+
return p2g and not p1g
|
158
|
+
|
159
|
+
def check(self, value):
|
160
|
+
return type(self).func(value, *self.parameters)
|
161
|
+
|
162
|
+
|
163
|
+
def dependent_check(fn):
|
164
|
+
t = type(fn.__name__, (FuncDependentType,), {"func": fn})
|
165
|
+
if len(inspect.signature(fn).parameters) == 1:
|
166
|
+
t = t()
|
167
|
+
return t
|
168
|
+
|
169
|
+
|
170
|
+
class Equals(ParametrizedDependentType):
|
171
|
+
keyable_type = True
|
172
|
+
|
173
|
+
def default_bound(self, *parameters):
|
174
|
+
return type(parameters[0])
|
175
|
+
|
176
|
+
def check(self, value):
|
177
|
+
return value in self.parameters
|
178
|
+
|
179
|
+
@classmethod
|
180
|
+
def keygen(cls):
|
181
|
+
return "{arg}"
|
182
|
+
|
183
|
+
def get_keys(self):
|
184
|
+
return [self.parameter]
|
185
|
+
|
186
|
+
def codegen(self):
|
187
|
+
if len(self.parameters) == 1:
|
188
|
+
return CodeGen("({arg} == {p})", {"p": self.parameter})
|
189
|
+
else:
|
190
|
+
return CodeGen("({arg} in {ps})", {"ps": self.parameters})
|
191
|
+
|
192
|
+
|
193
|
+
@dependent_check
|
194
|
+
def HasKeys(value: Mapping, *keys):
|
195
|
+
return all(k in value for k in keys)
|
196
|
+
|
197
|
+
|
198
|
+
@dependent_check
|
199
|
+
def StartsWith(value: str, prefix):
|
200
|
+
return value.startswith(prefix)
|
201
|
+
|
202
|
+
|
203
|
+
@dependent_check
|
204
|
+
def EndsWith(value: str, suffix):
|
205
|
+
return value.endswith(suffix)
|
206
|
+
|
207
|
+
|
208
|
+
@dependent_check
|
209
|
+
def Regexp(value: str, regexp):
|
210
|
+
return bool(re.search(pattern=regexp, string=value))
|
211
|
+
|
212
|
+
|
213
|
+
class Dependent:
|
214
|
+
def __class_getitem__(cls, item):
|
215
|
+
bound, dt = item
|
216
|
+
if not isinstance(dt, DependentType):
|
217
|
+
dt = dependent_check(dt)
|
218
|
+
return dt.with_bound(bound)
|
219
|
+
|
220
|
+
|
221
|
+
class Or(DependentType):
|
222
|
+
def __init__(self, *types, bound=None):
|
223
|
+
self.types = types
|
224
|
+
super().__init__(bound or self.default_bound())
|
225
|
+
|
226
|
+
def default_bound(self):
|
227
|
+
return Union[tuple([t.bound for t in self.types])]
|
228
|
+
|
229
|
+
def codegen(self):
|
230
|
+
template = " or ".join("{}" for t in self.types)
|
231
|
+
return combine(template, [t.codegen() for t in self.types])
|
232
|
+
|
233
|
+
def check(self, value):
|
234
|
+
return any(t.check(value) for t in self.types)
|
235
|
+
|
236
|
+
|
237
|
+
class And(DependentType):
|
238
|
+
def __init__(self, *types, bound=None):
|
239
|
+
self.types = types
|
240
|
+
super().__init__(bound or self.default_bound())
|
241
|
+
|
242
|
+
def default_bound(self):
|
243
|
+
bounds = frozenset(t.bound for t in self.types)
|
244
|
+
return Intersection[tuple(bounds)]
|
245
|
+
|
246
|
+
def codegen(self):
|
247
|
+
template = " and ".join("{}" for t in self.types)
|
248
|
+
return combine(template, [t.codegen() for t in self.types])
|
249
|
+
|
250
|
+
def check(self, value):
|
251
|
+
return all(t.check(value) for t in self.types)
|
252
|
+
|
253
|
+
def __str__(self):
|
254
|
+
return " & ".join(map(repr, self.types))
|
255
|
+
|
256
|
+
__repr__ = __str__
|
257
|
+
|
258
|
+
|
259
|
+
if TYPE_CHECKING: # pragma: no cover
|
260
|
+
from typing import Annotated, TypeAlias
|
261
|
+
|
262
|
+
T = TypeVar("T")
|
263
|
+
A = TypeVar("A")
|
264
|
+
Dependent: TypeAlias = Annotated[T, A]
|
265
|
+
|
266
|
+
|
267
|
+
__all__ = [
|
268
|
+
"Dependent",
|
269
|
+
"DependentType",
|
270
|
+
"Equals",
|
271
|
+
"HasKeys",
|
272
|
+
"StartsWith",
|
273
|
+
"EndsWith",
|
274
|
+
"dependent_check",
|
275
|
+
]
|
ovld/mro.py
CHANGED
@@ -1,171 +1,203 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
else:
|
19
|
-
args1 = getattr(c1, "__args__", ())
|
20
|
-
args2 = getattr(c2, "__args__", ())
|
21
|
-
if len(args1) != len(args2):
|
22
|
-
return False
|
23
|
-
return all(_issubclass(a1, a2) for a1, a2 in zip(args1, args2))
|
1
|
+
import typing
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from enum import Enum
|
4
|
+
from graphlib import TopologicalSorter
|
5
|
+
|
6
|
+
|
7
|
+
class Order(Enum):
|
8
|
+
LESS = -1
|
9
|
+
MORE = 1
|
10
|
+
SAME = 0
|
11
|
+
NONE = None
|
12
|
+
|
13
|
+
def opposite(self):
|
14
|
+
if self is Order.LESS:
|
15
|
+
return Order.MORE
|
16
|
+
elif self is Order.MORE:
|
17
|
+
return Order.LESS
|
24
18
|
else:
|
25
|
-
return
|
26
|
-
else:
|
27
|
-
return issubclass(c1, c2)
|
19
|
+
return self
|
28
20
|
|
29
21
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
return set(cls.__origin__.__mro__)
|
35
|
-
else: # pragma: no cover
|
36
|
-
return {cls}
|
22
|
+
@dataclass
|
23
|
+
class TypeRelationship:
|
24
|
+
order: Order
|
25
|
+
matches: bool = None
|
37
26
|
|
38
27
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
def _issubclass(t1, t2):
|
29
|
+
try:
|
30
|
+
return issubclass(t1, t2)
|
31
|
+
except TypeError:
|
32
|
+
try:
|
33
|
+
return isinstance(t1, t2)
|
34
|
+
except TypeError: # pragma: no cover
|
35
|
+
return False
|
43
36
|
|
44
|
-
def _c3_merge(sequences): # pragma: no cover
|
45
|
-
"""Merges MROs in *sequences* to a single MRO using the C3 algorithm.
|
46
|
-
Adapted from http://www.python.org/download/releases/2.3/mro/.
|
47
|
-
"""
|
48
|
-
result = []
|
49
|
-
while True:
|
50
|
-
sequences = [s for s in sequences if s] # purge empty sequences
|
51
|
-
if not sequences:
|
52
|
-
return result
|
53
|
-
for s1 in sequences: # find merge candidates among seq heads
|
54
|
-
candidate = s1[0]
|
55
|
-
for s2 in sequences:
|
56
|
-
if candidate in s2[1:]:
|
57
|
-
candidate = None
|
58
|
-
break # reject the current head, it appears later
|
59
|
-
else:
|
60
|
-
break
|
61
|
-
if candidate is None:
|
62
|
-
raise RuntimeError("Inconsistent hierarchy")
|
63
|
-
result.append(candidate)
|
64
|
-
# remove the chosen candidate
|
65
|
-
for seq in sequences:
|
66
|
-
if seq[0] == candidate:
|
67
|
-
del seq[0]
|
68
|
-
|
69
|
-
|
70
|
-
def _c3_mro(cls, abcs=None, abscollect=None): # pragma: no cover
|
71
|
-
"""Computes the method resolution order using extended C3 linearization.
|
72
|
-
If no *abcs* are given, the algorithm works exactly like the built-in C3
|
73
|
-
linearization used for method resolution.
|
74
|
-
If given, *abcs* is a list of abstract base classes that should be inserted
|
75
|
-
into the resulting MRO. Unrelated ABCs are ignored and don't end up in the
|
76
|
-
result. The algorithm inserts ABCs where their functionality is introduced,
|
77
|
-
i.e. issubclass(cls, abc) returns True for the class itself but returns
|
78
|
-
False for all its direct base classes. Implicit ABCs for a given class
|
79
|
-
(either registered or inferred from the presence of a special method like
|
80
|
-
__len__) are inserted directly after the last ABC explicitly listed in the
|
81
|
-
MRO of said class. If two implicit ABCs end up next to each other in the
|
82
|
-
resulting MRO, their ordering depends on the order of types in *abcs*.
|
83
|
-
"""
|
84
|
-
bases = getattr(cls, "__bases__", ())
|
85
|
-
if hasattr(cls, "__origin__"):
|
86
|
-
bases = [cls.__origin__, *bases]
|
87
|
-
for i, base in enumerate(reversed(bases)):
|
88
|
-
if hasattr(base, "__abstractmethods__"):
|
89
|
-
boundary = len(bases) - i
|
90
|
-
break # Bases up to the last explicit ABC are considered first.
|
91
|
-
else:
|
92
|
-
boundary = 0
|
93
|
-
abcs = list(abcs) if abcs else []
|
94
|
-
explicit_bases = list(bases[:boundary])
|
95
|
-
abstract_bases = []
|
96
|
-
other_bases = list(bases[boundary:])
|
97
|
-
for base in abcs:
|
98
|
-
if _issubclass(cls, base) and not any(
|
99
|
-
_issubclass(b, base) for b in bases
|
100
|
-
):
|
101
|
-
# If *cls* is the class that introduces behaviour described by
|
102
|
-
# an ABC *base*, insert said ABC to its MRO.
|
103
|
-
abstract_bases.append(base)
|
104
|
-
for base in abstract_bases:
|
105
|
-
abcs.remove(base)
|
106
|
-
abscollect.update(abstract_bases)
|
107
|
-
explicit_c3_mros = [
|
108
|
-
_c3_mro(base, abcs=abcs, abscollect=abscollect)
|
109
|
-
for base in explicit_bases
|
110
|
-
]
|
111
|
-
abstract_c3_mros = [
|
112
|
-
_c3_mro(base, abcs=abcs, abscollect=abscollect)
|
113
|
-
for base in abstract_bases
|
114
|
-
]
|
115
|
-
other_c3_mros = [
|
116
|
-
_c3_mro(base, abcs=abcs, abscollect=abscollect) for base in other_bases
|
117
|
-
]
|
118
|
-
return _c3_merge(
|
119
|
-
[[cls]]
|
120
|
-
+ explicit_c3_mros
|
121
|
-
+ abstract_c3_mros
|
122
|
-
+ other_c3_mros
|
123
|
-
+ [explicit_bases]
|
124
|
-
+ [abstract_bases]
|
125
|
-
+ [other_bases]
|
126
|
-
)
|
127
|
-
|
128
|
-
|
129
|
-
def compose_mro(cls, types, abscollect): # pragma: no cover
|
130
|
-
"""Calculates the method resolution order for a given class *cls*.
|
131
|
-
Includes relevant abstract base classes (with their respective bases) from
|
132
|
-
the *types* iterable. Uses a modified C3 linearization algorithm.
|
133
|
-
"""
|
134
|
-
bases = _mro(cls)
|
135
37
|
|
136
|
-
|
137
|
-
|
138
|
-
return typ not in bases and _issubclass(cls, typ)
|
38
|
+
def typeorder(t1, t2):
|
39
|
+
"""Order relation between two types.
|
139
40
|
|
140
|
-
|
41
|
+
Returns a member of the Order enum.
|
141
42
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
43
|
+
* typeorder(t1, t2) is Order.LESS if t2 is more general than t1
|
44
|
+
* typeorder(t1, t2) is Order.SAME if t1 is equal to t2
|
45
|
+
* typeorder(t1, t2) is Order.MORE if t1 is more general than t2
|
46
|
+
* typeorder(t1, t2) is Order.NONE if they cannot be compared
|
47
|
+
"""
|
48
|
+
if t1 == t2:
|
49
|
+
return Order.SAME
|
50
|
+
|
51
|
+
t1 = getattr(t1, "__proxy_for__", t1)
|
52
|
+
t2 = getattr(t2, "__proxy_for__", t2)
|
53
|
+
|
54
|
+
if (
|
55
|
+
hasattr(t1, "__typeorder__")
|
56
|
+
and (result := t1.__typeorder__(t2)) is not NotImplemented
|
57
|
+
):
|
58
|
+
return result.order
|
59
|
+
elif (
|
60
|
+
hasattr(t2, "__typeorder__")
|
61
|
+
and (result := t2.__typeorder__(t1)) is not NotImplemented
|
62
|
+
):
|
63
|
+
return result.order.opposite()
|
64
|
+
|
65
|
+
o1 = getattr(t1, "__origin__", None)
|
66
|
+
o2 = getattr(t2, "__origin__", None)
|
67
|
+
|
68
|
+
if o2 is typing.Union:
|
69
|
+
if t1 is typing.Union:
|
70
|
+
return Order.MORE
|
71
|
+
compare = [
|
72
|
+
x for t in t2.__args__ if (x := typeorder(t1, t)) is not Order.NONE
|
73
|
+
]
|
74
|
+
if not compare:
|
75
|
+
return Order.NONE
|
76
|
+
elif any(x is Order.LESS or x is Order.SAME for x in compare):
|
77
|
+
return Order.LESS
|
78
|
+
else:
|
79
|
+
return Order.MORE
|
80
|
+
|
81
|
+
if o1 is typing.Union:
|
82
|
+
return typeorder(t2, t1).opposite()
|
83
|
+
|
84
|
+
if o2 and not o1:
|
85
|
+
return typeorder(t2, t1).opposite()
|
86
|
+
|
87
|
+
if o1:
|
88
|
+
if not o2: # or getattr(t2, "__args__", None) is None:
|
89
|
+
order = typeorder(o1, t2)
|
90
|
+
if order is order.SAME:
|
91
|
+
order = order.LESS
|
92
|
+
return order
|
93
|
+
|
94
|
+
if (order := typeorder(o1, o2)) is not Order.SAME:
|
95
|
+
return order
|
96
|
+
|
97
|
+
args1 = getattr(t1, "__args__", ())
|
98
|
+
args2 = getattr(t2, "__args__", ())
|
99
|
+
|
100
|
+
if args1 and not args2:
|
101
|
+
return Order.LESS
|
102
|
+
if args2 and not args1:
|
103
|
+
return Order.MORE
|
104
|
+
if len(args1) != len(args2):
|
105
|
+
return Order.NONE
|
106
|
+
|
107
|
+
ords = [typeorder(a1, a2) for a1, a2 in zip(args1, args2)]
|
108
|
+
if Order.MORE in ords and Order.LESS in ords:
|
109
|
+
return Order.NONE
|
110
|
+
elif Order.NONE in ords:
|
111
|
+
return Order.NONE
|
112
|
+
elif Order.MORE in ords:
|
113
|
+
return Order.MORE
|
114
|
+
elif Order.LESS in ords:
|
115
|
+
return Order.LESS
|
116
|
+
else: # pragma: no cover
|
117
|
+
# Not sure when t1 != t2 and that happens
|
118
|
+
return Order.SAME
|
119
|
+
|
120
|
+
sx = _issubclass(t1, t2)
|
121
|
+
sy = _issubclass(t2, t1)
|
122
|
+
if sx and sy: # pragma: no cover
|
123
|
+
# Not sure when t1 != t2 and that happens
|
124
|
+
return Order.SAME
|
125
|
+
elif sx:
|
126
|
+
return Order.LESS
|
127
|
+
elif sy:
|
128
|
+
return Order.MORE
|
129
|
+
else:
|
130
|
+
return Order.NONE
|
131
|
+
|
132
|
+
|
133
|
+
def subclasscheck(t1, t2):
|
134
|
+
"""Check whether t1 is a "subclass" of t2."""
|
135
|
+
if t1 == t2:
|
136
|
+
return True
|
137
|
+
|
138
|
+
t1 = getattr(t1, "__proxy_for__", t1)
|
139
|
+
t2 = getattr(t2, "__proxy_for__", t2)
|
140
|
+
|
141
|
+
if (
|
142
|
+
hasattr(t2, "__typeorder__")
|
143
|
+
and (result := t2.__typeorder__(t1)) is not NotImplemented
|
144
|
+
):
|
145
|
+
return result.matches
|
146
|
+
|
147
|
+
o1 = getattr(t1, "__origin__", None)
|
148
|
+
o2 = getattr(t2, "__origin__", None)
|
149
|
+
|
150
|
+
if o2 is typing.Union:
|
151
|
+
return t1 is typing.Union or any(
|
152
|
+
subclasscheck(t1, t) for t in t2.__args__
|
153
|
+
)
|
154
|
+
elif o1 is typing.Union:
|
155
|
+
return t2 is typing.Union or all(
|
156
|
+
subclasscheck(t, t2) for t in t1.__args__
|
157
|
+
)
|
158
|
+
|
159
|
+
if not isinstance(o1, type):
|
160
|
+
o1 = None
|
161
|
+
if not isinstance(o2, type):
|
162
|
+
o2 = None
|
163
|
+
|
164
|
+
if o1 or o2:
|
165
|
+
o1 = o1 or t1
|
166
|
+
o2 = o2 or t2
|
167
|
+
if _issubclass(o1, o2):
|
168
|
+
if o2 is t2: # pragma: no cover
|
147
169
|
return True
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
170
|
+
else:
|
171
|
+
args1 = getattr(t1, "__args__", ())
|
172
|
+
args2 = getattr(t2, "__args__", ())
|
173
|
+
if len(args1) != len(args2):
|
174
|
+
return False
|
175
|
+
return all(
|
176
|
+
subclasscheck(a1, a2) for a1, a2 in zip(args1, args2)
|
177
|
+
)
|
178
|
+
else:
|
179
|
+
return False
|
180
|
+
else:
|
181
|
+
return _issubclass(t1, t2)
|
182
|
+
|
183
|
+
|
184
|
+
def sort_types(cls, avail):
|
185
|
+
# We filter everything except subclasses and dependent types that *might* cover
|
186
|
+
# the object represented by cls.
|
187
|
+
avail = [t for t in avail if subclasscheck(cls, t)]
|
188
|
+
deps = {t: set() for t in avail}
|
189
|
+
for i, t1 in enumerate(avail):
|
190
|
+
for t2 in avail[i + 1 :]:
|
191
|
+
# NOTE: this is going to scale poorly when there's a hundred Literal in the pool
|
192
|
+
order = typeorder(t1, t2)
|
193
|
+
if order is Order.LESS:
|
194
|
+
deps[t2].add(t1)
|
195
|
+
elif order is Order.MORE:
|
196
|
+
deps[t1].add(t2)
|
197
|
+
sorter = TopologicalSorter(deps)
|
198
|
+
sorter.prepare()
|
199
|
+
while sorter.is_active():
|
200
|
+
nodes = sorter.get_ready()
|
201
|
+
yield nodes
|
202
|
+
for n in nodes:
|
203
|
+
sorter.done(n)
|