ovld 0.3.9__py3-none-any.whl → 0.4.0__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 +40 -15
- ovld/core.py +406 -585
- ovld/dependent.py +275 -0
- ovld/mro.py +169 -158
- 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.0.dist-info/METADATA +213 -0
- ovld-0.4.0.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.0.dist-info}/WHEEL +0 -0
- {ovld-0.3.9.dist-info → ovld-0.4.0.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,182 @@
|
|
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
|
-
return scf and scf()
|
28
|
+
def typeorder(t1, t2):
|
29
|
+
"""Order relation between two types.
|
42
30
|
|
31
|
+
Returns a member of the Order enum.
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
* typeorder(t1, t2) is Order.LESS if t2 is more general than t1
|
34
|
+
* typeorder(t1, t2) is Order.SAME if t1 is equal to t2
|
35
|
+
* typeorder(t1, t2) is Order.MORE if t1 is more general than t2
|
36
|
+
* typeorder(t1, t2) is Order.NONE if they cannot be compared
|
47
37
|
"""
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
result.
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
if
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
38
|
+
if t1 == t2:
|
39
|
+
return Order.SAME
|
40
|
+
|
41
|
+
t1 = getattr(t1, "__proxy_for__", t1)
|
42
|
+
t2 = getattr(t2, "__proxy_for__", t2)
|
43
|
+
|
44
|
+
if (
|
45
|
+
hasattr(t1, "__typeorder__")
|
46
|
+
and (result := t1.__typeorder__(t2)) is not NotImplemented
|
47
|
+
):
|
48
|
+
return result.order
|
49
|
+
elif (
|
50
|
+
hasattr(t2, "__typeorder__")
|
51
|
+
and (result := t2.__typeorder__(t1)) is not NotImplemented
|
52
|
+
):
|
53
|
+
return result.order.opposite()
|
54
|
+
|
55
|
+
o1 = getattr(t1, "__origin__", None)
|
56
|
+
o2 = getattr(t2, "__origin__", None)
|
57
|
+
|
58
|
+
if o2 is typing.Union:
|
59
|
+
compare = [
|
60
|
+
x for t in t2.__args__ if (x := typeorder(t1, t)) is not Order.NONE
|
61
|
+
]
|
62
|
+
if not compare:
|
63
|
+
return Order.NONE
|
64
|
+
elif any(x is Order.LESS or x is Order.SAME for x in compare):
|
65
|
+
return Order.LESS
|
66
|
+
else:
|
67
|
+
return Order.MORE
|
68
|
+
|
69
|
+
if o1 is typing.Union:
|
70
|
+
return typeorder(t2, t1).opposite()
|
71
|
+
|
72
|
+
if o2 and not o1:
|
73
|
+
return typeorder(t2, t1).opposite()
|
74
|
+
|
75
|
+
if o1:
|
76
|
+
if not o2: # or getattr(t2, "__args__", None) is None:
|
77
|
+
order = typeorder(o1, t2)
|
78
|
+
if order is order.SAME:
|
79
|
+
order = order.LESS
|
80
|
+
return order
|
81
|
+
|
82
|
+
if (order := typeorder(o1, o2)) is not Order.SAME:
|
83
|
+
return order
|
84
|
+
|
85
|
+
args1 = getattr(t1, "__args__", ())
|
86
|
+
args2 = getattr(t2, "__args__", ())
|
87
|
+
|
88
|
+
if args1 and not args2:
|
89
|
+
return Order.LESS
|
90
|
+
if args2 and not args1:
|
91
|
+
return Order.MORE
|
92
|
+
if len(args1) != len(args2):
|
93
|
+
return Order.NONE
|
94
|
+
|
95
|
+
ords = [typeorder(a1, a2) for a1, a2 in zip(args1, args2)]
|
96
|
+
if Order.MORE in ords and Order.LESS in ords:
|
97
|
+
return Order.NONE
|
98
|
+
elif Order.NONE in ords:
|
99
|
+
return Order.NONE
|
100
|
+
elif Order.MORE in ords:
|
101
|
+
return Order.MORE
|
102
|
+
elif Order.LESS in ords:
|
103
|
+
return Order.LESS
|
104
|
+
else: # pragma: no cover
|
105
|
+
# Not sure when t1 != t2 and that happens
|
106
|
+
return Order.SAME
|
107
|
+
|
108
|
+
sx = issubclass(t1, t2)
|
109
|
+
sy = issubclass(t2, t1)
|
110
|
+
if sx and sy: # pragma: no cover
|
111
|
+
# Not sure when t1 != t2 and that happens
|
112
|
+
return Order.SAME
|
113
|
+
elif sx:
|
114
|
+
return Order.LESS
|
115
|
+
elif sy:
|
116
|
+
return Order.MORE
|
91
117
|
else:
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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)
|
118
|
+
return Order.NONE
|
119
|
+
|
120
|
+
|
121
|
+
def subclasscheck(t1, t2):
|
122
|
+
"""Check whether t1 is a "subclass" of t2."""
|
123
|
+
if t1 == t2:
|
124
|
+
return True
|
135
125
|
|
136
|
-
|
137
|
-
|
138
|
-
return typ not in bases and _issubclass(cls, typ)
|
126
|
+
t1 = getattr(t1, "__proxy_for__", t1)
|
127
|
+
t2 = getattr(t2, "__proxy_for__", t2)
|
139
128
|
|
140
|
-
|
129
|
+
if (
|
130
|
+
hasattr(t2, "__typeorder__")
|
131
|
+
and (result := t2.__typeorder__(t1)) is not NotImplemented
|
132
|
+
):
|
133
|
+
return result.matches
|
141
134
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
135
|
+
o1 = getattr(t1, "__origin__", None)
|
136
|
+
o2 = getattr(t2, "__origin__", None)
|
137
|
+
|
138
|
+
if o2 is typing.Union:
|
139
|
+
return any(subclasscheck(t1, t) for t in t2.__args__)
|
140
|
+
elif o1 is typing.Union:
|
141
|
+
return all(subclasscheck(t, t2) for t in t1.__args__)
|
142
|
+
|
143
|
+
if o1 or o2:
|
144
|
+
o1 = o1 or t1
|
145
|
+
o2 = o2 or t2
|
146
|
+
if issubclass(o1, o2):
|
147
|
+
if o2 is t2: # pragma: no cover
|
147
148
|
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
|
-
|
149
|
+
else:
|
150
|
+
args1 = getattr(t1, "__args__", ())
|
151
|
+
args2 = getattr(t2, "__args__", ())
|
152
|
+
if len(args1) != len(args2):
|
153
|
+
return False
|
154
|
+
return all(
|
155
|
+
subclasscheck(a1, a2) for a1, a2 in zip(args1, args2)
|
156
|
+
)
|
157
|
+
else:
|
158
|
+
return False
|
159
|
+
else:
|
160
|
+
return issubclass(t1, t2)
|
161
|
+
|
162
|
+
|
163
|
+
def sort_types(cls, avail):
|
164
|
+
# We filter everything except subclasses and dependent types that *might* cover
|
165
|
+
# the object represented by cls.
|
166
|
+
avail = [t for t in avail if subclasscheck(cls, t)]
|
167
|
+
deps = {t: set() for t in avail}
|
168
|
+
for i, t1 in enumerate(avail):
|
169
|
+
for t2 in avail[i + 1 :]:
|
170
|
+
# NOTE: this is going to scale poorly when there's a hundred Literal in the pool
|
171
|
+
order = typeorder(t1, t2)
|
172
|
+
if order is Order.LESS:
|
173
|
+
deps[t2].add(t1)
|
174
|
+
elif order is Order.MORE:
|
175
|
+
deps[t1].add(t2)
|
176
|
+
sorter = TopologicalSorter(deps)
|
177
|
+
sorter.prepare()
|
178
|
+
while sorter.is_active():
|
179
|
+
nodes = sorter.get_ready()
|
180
|
+
yield nodes
|
181
|
+
for n in nodes:
|
182
|
+
sorter.done(n)
|