computation-graph 54__tar.gz → 56__tar.gz
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.
- {computation-graph-54 → computation_graph-56}/PKG-INFO +1 -1
- {computation-graph-54 → computation_graph-56}/computation_graph/base_types.py +5 -1
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/duplication.py +2 -1
- {computation-graph-54 → computation_graph-56}/computation_graph/graph.py +1 -1
- {computation-graph-54 → computation_graph-56}/computation_graph/graph_test.py +49 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/run.py +269 -87
- {computation-graph-54 → computation_graph-56}/computation_graph.egg-info/PKG-INFO +1 -1
- {computation-graph-54 → computation_graph-56}/computation_graph.egg-info/top_level.txt +1 -0
- {computation-graph-54 → computation_graph-56}/setup.py +1 -1
- {computation-graph-54 → computation_graph-56}/LICENSE.md +0 -0
- {computation-graph-54 → computation_graph-56}/README.md +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/__init__.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/__init__.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/composers_test.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/condition.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/condition_test.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/debug.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/lift.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/logic.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/memory.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/composers/memory_test.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/graph_runners.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/legacy.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/signature.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/trace/__init__.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/trace/ascii.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/trace/graphviz.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/trace/graphviz_test.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/trace/mermaid.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph/trace/trace_utils.py +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph.egg-info/SOURCES.txt +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph.egg-info/dependency_links.txt +0 -0
- {computation-graph-54 → computation_graph-56}/computation_graph.egg-info/requires.txt +0 -0
- {computation-graph-54 → computation_graph-56}/pyproject.toml +0 -0
- {computation-graph-54 → computation_graph-56}/setup.cfg +0 -0
|
@@ -96,9 +96,13 @@ class ComputationNode:
|
|
|
96
96
|
func: Callable
|
|
97
97
|
signature: NodeSignature
|
|
98
98
|
is_terminal: bool
|
|
99
|
+
computed_hash: int = dataclasses.field(init=False)
|
|
100
|
+
|
|
101
|
+
def __post_init__(self):
|
|
102
|
+
object.__setattr__(self, "computed_hash", hash(self.func))
|
|
99
103
|
|
|
100
104
|
def __hash__(self):
|
|
101
|
-
return
|
|
105
|
+
return self.computed_hash
|
|
102
106
|
|
|
103
107
|
def __repr__(self):
|
|
104
108
|
return self.name
|
|
@@ -44,6 +44,7 @@ _duplicate_node = gamla.compose_left(
|
|
|
44
44
|
graph.make_computation_node,
|
|
45
45
|
)
|
|
46
46
|
|
|
47
|
+
duplicate_node = _duplicate_node
|
|
47
48
|
|
|
48
49
|
_node_to_duplicated_node = gamla.compose_left(
|
|
49
50
|
gamla.remove(base_types.node_is_terminal),
|
|
@@ -104,4 +105,4 @@ def safe_replace_sources(
|
|
|
104
105
|
g = graph.replace_source(source, get_node_replacement(source), g)
|
|
105
106
|
return g
|
|
106
107
|
|
|
107
|
-
return
|
|
108
|
+
return base_types.merge_graphs(*(update_edge(e) for e in cg))
|
|
@@ -162,7 +162,7 @@ traverse_forward: Callable[
|
|
|
162
162
|
),
|
|
163
163
|
gamla.groupby_many_reduce(
|
|
164
164
|
opt_gamla.compose_left(gamla.head, gamla.wrap_tuple),
|
|
165
|
-
lambda destinations, e:
|
|
165
|
+
lambda destinations, e: {*(destinations if destinations else ()), e[1]},
|
|
166
166
|
),
|
|
167
167
|
gamla.dict_to_getter_with_default(()),
|
|
168
168
|
gamla.before(make_computation_node),
|
|
@@ -63,6 +63,55 @@ def test_simple():
|
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
async def test_async_run_as_soon_as_possible(capsys):
|
|
67
|
+
# This test is to make sure that the async node runs as soon as possible.
|
|
68
|
+
# nodes in different topological layers should run concurrently if not dependent on each other.
|
|
69
|
+
async def concurrent1(x):
|
|
70
|
+
print("start concurrent1") # noqa
|
|
71
|
+
await asyncio.sleep(0.1)
|
|
72
|
+
print("end concurrent1") # noqa
|
|
73
|
+
return "concurrent1"
|
|
74
|
+
|
|
75
|
+
async def concurrent2(x):
|
|
76
|
+
print("start concurrent2") # noqa
|
|
77
|
+
await asyncio.sleep(0.1)
|
|
78
|
+
print("end concurrent2") # noqa
|
|
79
|
+
return "concurrent2"
|
|
80
|
+
|
|
81
|
+
def sink(x, y):
|
|
82
|
+
return f"x={x}, y={y}"
|
|
83
|
+
|
|
84
|
+
g = base_types.merge_graphs(
|
|
85
|
+
composers.compose_unary(concurrent1, lambda: "x"),
|
|
86
|
+
composers.compose_unary(concurrent2, lambda y: y, lambda: "y"),
|
|
87
|
+
composers.compose_left(concurrent1, sink, key="x"),
|
|
88
|
+
composers.compose_left(concurrent2, sink, key="y"),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
await graph_runners.nullary(g, sink)
|
|
92
|
+
|
|
93
|
+
# Capture the output
|
|
94
|
+
captured = capsys.readouterr()
|
|
95
|
+
|
|
96
|
+
# Assertions
|
|
97
|
+
assert "start concurrent1" in captured.out
|
|
98
|
+
assert "start concurrent2" in captured.out
|
|
99
|
+
assert "end concurrent1" in captured.out
|
|
100
|
+
assert "end concurrent2" in captured.out
|
|
101
|
+
assert captured.out.index("start concurrent1") < captured.out.index(
|
|
102
|
+
"end concurrent1"
|
|
103
|
+
)
|
|
104
|
+
assert captured.out.index("start concurrent2") < captured.out.index(
|
|
105
|
+
"end concurrent2"
|
|
106
|
+
)
|
|
107
|
+
assert captured.out.index("start concurrent1") < captured.out.index(
|
|
108
|
+
"end concurrent2"
|
|
109
|
+
)
|
|
110
|
+
assert captured.out.index("start concurrent2") < captured.out.index(
|
|
111
|
+
"end concurrent1"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
66
115
|
async def test_simple_async():
|
|
67
116
|
assert (
|
|
68
117
|
await graph_runners.unary(
|
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import dataclasses
|
|
3
3
|
import functools
|
|
4
|
+
import inspect
|
|
4
5
|
import itertools
|
|
5
6
|
import logging
|
|
6
7
|
import os
|
|
7
8
|
import time
|
|
8
9
|
import typing
|
|
9
|
-
from typing import
|
|
10
|
+
from typing import (
|
|
11
|
+
Any,
|
|
12
|
+
Awaitable,
|
|
13
|
+
Callable,
|
|
14
|
+
Dict,
|
|
15
|
+
FrozenSet,
|
|
16
|
+
Iterable,
|
|
17
|
+
Mapping,
|
|
18
|
+
Optional,
|
|
19
|
+
Set,
|
|
20
|
+
Tuple,
|
|
21
|
+
Type,
|
|
22
|
+
)
|
|
10
23
|
|
|
11
24
|
import gamla
|
|
12
25
|
import immutables
|
|
13
26
|
import termcolor
|
|
14
27
|
import toposort
|
|
15
28
|
import typeguard
|
|
16
|
-
from gamla.optimized import async_functions as opt_async_gamla
|
|
17
29
|
from gamla.optimized import sync as opt_gamla
|
|
18
30
|
|
|
19
31
|
from computation_graph import base_types, composers, graph, signature
|
|
20
32
|
from computation_graph.composers import debug
|
|
21
33
|
|
|
34
|
+
CG_NO_RESULT = "CG_NO_RESULT"
|
|
35
|
+
|
|
22
36
|
|
|
23
37
|
class _DepNotFoundError(Exception):
|
|
24
38
|
pass
|
|
@@ -27,7 +41,6 @@ class _DepNotFoundError(Exception):
|
|
|
27
41
|
_NodeToResults = Dict[base_types.ComputationNode, base_types.Result]
|
|
28
42
|
_ComputationInput = Tuple[Tuple[base_types.Result, ...], Dict[str, base_types.Result]]
|
|
29
43
|
_SingleNodeSideEffect = Callable[[base_types.ComputationNode, Any], None]
|
|
30
|
-
_NodeToComputationInput = Callable[[base_types.ComputationNode], _ComputationInput]
|
|
31
44
|
_ComputationInputSpec = Tuple[
|
|
32
45
|
Tuple[base_types.ComputationNode, ...], Dict[str, base_types.ComputationNode]
|
|
33
46
|
]
|
|
@@ -74,34 +87,7 @@ def _profile(node, time_started: float):
|
|
|
74
87
|
)
|
|
75
88
|
|
|
76
89
|
|
|
77
|
-
|
|
78
|
-
is_async: bool, side_effect: _SingleNodeSideEffect
|
|
79
|
-
) -> Callable[..., base_types.Result]:
|
|
80
|
-
if is_async:
|
|
81
|
-
|
|
82
|
-
@opt_async_gamla.star
|
|
83
|
-
async def run_node(
|
|
84
|
-
node_to_input: _NodeToComputationInput, node: base_types.ComputationNode
|
|
85
|
-
) -> base_types.Result:
|
|
86
|
-
args, kwargs = node_to_input(node)
|
|
87
|
-
before = time.perf_counter()
|
|
88
|
-
result = await gamla.to_awaitable(node.func(*args, **kwargs))
|
|
89
|
-
side_effect(node, result)
|
|
90
|
-
_profile(node, before)
|
|
91
|
-
return node, result
|
|
92
|
-
|
|
93
|
-
else:
|
|
94
|
-
|
|
95
|
-
@opt_gamla.star
|
|
96
|
-
def run_node(node_to_input: _NodeToComputationInput, node: base_types.ComputationNode) -> base_types.Result: # type: ignore
|
|
97
|
-
args, kwargs = node_to_input(node)
|
|
98
|
-
before = time.perf_counter()
|
|
99
|
-
result = node.func(*args, **kwargs)
|
|
100
|
-
side_effect(node, result)
|
|
101
|
-
_profile(node, before)
|
|
102
|
-
return node, result
|
|
103
|
-
|
|
104
|
-
return run_node
|
|
90
|
+
_group_by_is_async_result = opt_gamla.groupby(lambda k_v: inspect.isawaitable(k_v[1]))
|
|
105
91
|
|
|
106
92
|
|
|
107
93
|
_is_graph_async = opt_gamla.compose_left(
|
|
@@ -205,29 +191,35 @@ def _to_callable_with_side_effect_for_single_and_multiple(
|
|
|
205
191
|
placeholder_to_future_source = opt_gamla.pipe(
|
|
206
192
|
future_source_to_placeholder, gamla.itemmap(lambda k_v: (k_v[1], k_v[0]))
|
|
207
193
|
)
|
|
194
|
+
get_node_executor = _make_get_node_executor(
|
|
195
|
+
edges, handled_exceptions, single_node_side_effect
|
|
196
|
+
)
|
|
197
|
+
|
|
208
198
|
topological_layers = opt_gamla.pipe(
|
|
209
199
|
edges,
|
|
210
200
|
_toposort_nodes,
|
|
211
201
|
gamla.map_filter_empty(
|
|
212
202
|
gamla.compose_left(
|
|
213
|
-
gamla.remove(gamla.contains(placeholder_to_future_source)),
|
|
203
|
+
gamla.remove(gamla.contains(placeholder_to_future_source)),
|
|
204
|
+
opt_gamla.map(opt_gamla.pair_right(get_node_executor)),
|
|
205
|
+
frozenset,
|
|
214
206
|
)
|
|
215
207
|
),
|
|
216
208
|
tuple,
|
|
217
209
|
)
|
|
210
|
+
|
|
218
211
|
translate_source_to_placeholder = opt_gamla.compose_left(
|
|
219
212
|
opt_gamla.keyfilter(gamla.contains(future_sources)),
|
|
220
213
|
opt_gamla.keymap(future_source_to_placeholder.__getitem__),
|
|
221
214
|
)
|
|
222
|
-
all_node_side_effects_on_edges = all_nodes_side_effect(edges)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
)
|
|
215
|
+
all_node_side_effects_on_edges = gamla.side_effect(all_nodes_side_effect(edges))
|
|
216
|
+
|
|
217
|
+
reduce_layers = _make_reduce_layers(handled_exceptions)
|
|
226
218
|
|
|
227
219
|
if is_async:
|
|
228
220
|
|
|
229
221
|
async def final_runner(sources_to_values):
|
|
230
|
-
|
|
222
|
+
d = gamla.pipe(
|
|
231
223
|
topological_layers,
|
|
232
224
|
reduce_layers(
|
|
233
225
|
immutables.Map(translate_source_to_placeholder(sources_to_values))
|
|
@@ -235,6 +227,26 @@ def _to_callable_with_side_effect_for_single_and_multiple(
|
|
|
235
227
|
dict,
|
|
236
228
|
gamla.side_effect(all_node_side_effects_on_edges),
|
|
237
229
|
)
|
|
230
|
+
results_by_is_async = _group_by_is_async_result(d.items())
|
|
231
|
+
async_results = tuple(zip(*results_by_is_async.get(True, ())))
|
|
232
|
+
sync_results = dict(results_by_is_async.get(False, ()))
|
|
233
|
+
return all_node_side_effects_on_edges(
|
|
234
|
+
sync_results
|
|
235
|
+
| (
|
|
236
|
+
{
|
|
237
|
+
k: v
|
|
238
|
+
for (k, v) in zip(
|
|
239
|
+
async_results[0],
|
|
240
|
+
await asyncio.gather(
|
|
241
|
+
*async_results[1], return_exceptions=True
|
|
242
|
+
),
|
|
243
|
+
)
|
|
244
|
+
if not isinstance(v, Exception)
|
|
245
|
+
}
|
|
246
|
+
if async_results
|
|
247
|
+
else {}
|
|
248
|
+
)
|
|
249
|
+
)
|
|
238
250
|
|
|
239
251
|
else:
|
|
240
252
|
|
|
@@ -245,7 +257,7 @@ def _to_callable_with_side_effect_for_single_and_multiple(
|
|
|
245
257
|
immutables.Map(translate_source_to_placeholder(sources_to_values))
|
|
246
258
|
),
|
|
247
259
|
dict,
|
|
248
|
-
|
|
260
|
+
all_node_side_effects_on_edges,
|
|
249
261
|
)
|
|
250
262
|
|
|
251
263
|
return (_async_graph_reducer if is_async else _graph_reducer)(final_runner)
|
|
@@ -280,10 +292,18 @@ def _node_incoming_edges_to_input_spec(
|
|
|
280
292
|
)
|
|
281
293
|
|
|
282
294
|
|
|
283
|
-
def
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
295
|
+
def _to_awaitable(v) -> Awaitable:
|
|
296
|
+
if inspect.isawaitable(v):
|
|
297
|
+
return v
|
|
298
|
+
f = asyncio.get_event_loop().create_future()
|
|
299
|
+
f.set_result(v)
|
|
300
|
+
return f
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _make_get_node_executor(
|
|
304
|
+
edges, handled_exceptions, single_node_side_effect: _SingleNodeSideEffect
|
|
305
|
+
):
|
|
306
|
+
node_to_incoming_edges = functools.cache(graph.get_incoming_edges_for_node(edges))
|
|
287
307
|
node_to_computation_input_spec_options: Callable[
|
|
288
308
|
[base_types.ComputationNode], Tuple[_ComputationInputSpec]
|
|
289
309
|
] = functools.cache(
|
|
@@ -296,67 +316,229 @@ def _edges_to_accumulated_results_to_node_to_first_possible_input(
|
|
|
296
316
|
opt_gamla.maptuple(_node_incoming_edges_to_input_spec),
|
|
297
317
|
)
|
|
298
318
|
)
|
|
299
|
-
# TODO(eli): Add sync.translate_exception/except that avoids inspect.iscoroutinefunction so this can be inlined (ENG-5212)
|
|
300
|
-
head_or_dep_not_found = gamla.translate_exception(
|
|
301
|
-
gamla.head, StopIteration, _DepNotFoundError
|
|
302
|
-
)
|
|
303
319
|
|
|
304
|
-
def
|
|
305
|
-
|
|
306
|
-
) ->
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
lambda _: None,
|
|
313
|
-
opt_gamla.packstack(
|
|
314
|
-
opt_gamla.maptuple(accumulated_results.__getitem__),
|
|
315
|
-
opt_gamla.valmap(accumulated_results.__getitem__),
|
|
316
|
-
),
|
|
320
|
+
async def gather(
|
|
321
|
+
args: Tuple[Awaitable, ...], kwargs: Mapping[str, Awaitable]
|
|
322
|
+
) -> tuple[tuple[Any, ...], dict[str, Any]]:
|
|
323
|
+
if args or kwargs:
|
|
324
|
+
try:
|
|
325
|
+
gathered = await asyncio.gather(*args, *kwargs.values())
|
|
326
|
+
return gathered[: len(args)], dict(
|
|
327
|
+
zip(kwargs.keys(), gathered[len(args) :])
|
|
317
328
|
)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
329
|
+
except (
|
|
330
|
+
_DepNotFoundError,
|
|
331
|
+
base_types.SkipComputationError,
|
|
332
|
+
*handled_exceptions,
|
|
333
|
+
):
|
|
334
|
+
# We delete the references to the upstream tasks to avoid circular reference (task->exception->traceback->task) and improve memory performance
|
|
335
|
+
del args, kwargs
|
|
336
|
+
raise _DepNotFoundError() from None
|
|
337
|
+
return (), {}
|
|
323
338
|
|
|
324
|
-
|
|
339
|
+
def node_to_input_sync(
|
|
340
|
+
accumulated_results: Mapping[base_types.ComputationNode, base_types.Result],
|
|
341
|
+
input_options: Iterable[_ComputationInputSpec],
|
|
342
|
+
) -> Optional[_ComputationInput]:
|
|
343
|
+
for input_spec in input_options:
|
|
344
|
+
args, kwargs = input_spec
|
|
345
|
+
try:
|
|
346
|
+
return tuple(accumulated_results[arg] for arg in args), {
|
|
347
|
+
k: accumulated_results[v] for k, v in kwargs.items()
|
|
348
|
+
}
|
|
349
|
+
except KeyError:
|
|
350
|
+
...
|
|
351
|
+
return None
|
|
352
|
+
|
|
353
|
+
async def node_to_input_async(
|
|
354
|
+
accumulated_results: Mapping[
|
|
355
|
+
base_types.ComputationNode, base_types.Result | Awaitable[base_types.Result]
|
|
356
|
+
],
|
|
357
|
+
input_options: Iterable[_ComputationInputSpec],
|
|
358
|
+
) -> Optional[_ComputationInput]:
|
|
359
|
+
for input_spec in input_options:
|
|
360
|
+
args_spec, kwargs_spec = input_spec
|
|
361
|
+
if all(
|
|
362
|
+
accumulated_results.get(arg, CG_NO_RESULT) is not CG_NO_RESULT
|
|
363
|
+
for arg in args_spec
|
|
364
|
+
) and all(
|
|
365
|
+
accumulated_results.get(kwarg, CG_NO_RESULT) is not CG_NO_RESULT
|
|
366
|
+
for kwarg in kwargs_spec.values()
|
|
367
|
+
):
|
|
368
|
+
try:
|
|
369
|
+
return await gather(
|
|
370
|
+
tuple(_to_awaitable(accumulated_results[a]) for a in args_spec),
|
|
371
|
+
{
|
|
372
|
+
k: _to_awaitable(accumulated_results[v])
|
|
373
|
+
for k, v in kwargs_spec.items()
|
|
374
|
+
},
|
|
375
|
+
)
|
|
376
|
+
except (
|
|
377
|
+
_DepNotFoundError,
|
|
378
|
+
base_types.SkipComputationError,
|
|
379
|
+
*handled_exceptions,
|
|
380
|
+
):
|
|
381
|
+
...
|
|
382
|
+
return None
|
|
383
|
+
|
|
384
|
+
@opt_gamla.after(asyncio.create_task)
|
|
385
|
+
async def await_deps_and_apply(
|
|
386
|
+
accumulated_results: Mapping[
|
|
387
|
+
base_types.ComputationNode, base_types.Result | Awaitable[base_types.Result]
|
|
388
|
+
],
|
|
389
|
+
node: base_types.ComputationNode,
|
|
390
|
+
) -> base_types.Result:
|
|
391
|
+
args_kwargs = await node_to_input_async(
|
|
392
|
+
accumulated_results, node_to_computation_input_spec_options(node)
|
|
393
|
+
)
|
|
394
|
+
# We delete the references to the upstream tasks to avoid circular reference (task->exception->traceback->task) and improve memory performance
|
|
395
|
+
del accumulated_results
|
|
396
|
+
if args_kwargs is None:
|
|
397
|
+
raise _DepNotFoundError()
|
|
398
|
+
|
|
399
|
+
args, kwargs = args_kwargs
|
|
400
|
+
before = time.perf_counter()
|
|
401
|
+
result = node.func(*args, **kwargs)
|
|
402
|
+
single_node_side_effect(node, result)
|
|
403
|
+
if inspect.isawaitable(result):
|
|
404
|
+
raise Exception(
|
|
405
|
+
f"{node} returned an awaitable result but is not an async function"
|
|
406
|
+
)
|
|
407
|
+
_profile(node, before)
|
|
408
|
+
return result
|
|
409
|
+
|
|
410
|
+
@opt_gamla.after(asyncio.create_task)
|
|
411
|
+
async def await_deps_and_await(
|
|
412
|
+
accumulated_results: Mapping[
|
|
413
|
+
base_types.ComputationNode, base_types.Result | Awaitable[base_types.Result]
|
|
414
|
+
],
|
|
415
|
+
node: base_types.ComputationNode,
|
|
416
|
+
) -> base_types.Result:
|
|
417
|
+
args_kwargs = await node_to_input_async(
|
|
418
|
+
accumulated_results, node_to_computation_input_spec_options(node)
|
|
419
|
+
)
|
|
420
|
+
# We delete the references to the upstream tasks to avoid circular reference (task->exception->traceback->task) and improve memory performance
|
|
421
|
+
del accumulated_results
|
|
422
|
+
if args_kwargs is None:
|
|
423
|
+
raise _DepNotFoundError()
|
|
424
|
+
|
|
425
|
+
args, kwargs = args_kwargs
|
|
426
|
+
before = time.perf_counter()
|
|
427
|
+
result = await node.func(*args, **kwargs)
|
|
428
|
+
single_node_side_effect(node, result)
|
|
429
|
+
_profile(node, before)
|
|
430
|
+
return result
|
|
431
|
+
|
|
432
|
+
@opt_gamla.after(asyncio.create_task)
|
|
433
|
+
async def get_deps_and_await(
|
|
434
|
+
accumulated_results: Mapping[base_types.ComputationNode, base_types.Result],
|
|
435
|
+
node: base_types.ComputationNode,
|
|
436
|
+
) -> base_types.Result:
|
|
437
|
+
args_kwargs = node_to_input_sync(
|
|
438
|
+
accumulated_results, node_to_computation_input_spec_options(node)
|
|
439
|
+
)
|
|
440
|
+
# We delete the references to the upstream tasks to avoid circular reference (task->exception->traceback->task) and improve memory performance
|
|
441
|
+
del accumulated_results
|
|
442
|
+
if args_kwargs is None:
|
|
443
|
+
raise _DepNotFoundError()
|
|
444
|
+
|
|
445
|
+
args, kwargs = args_kwargs
|
|
446
|
+
before = time.perf_counter()
|
|
447
|
+
result = await node.func(*args, **kwargs)
|
|
448
|
+
single_node_side_effect(node, result)
|
|
449
|
+
_profile(node, before)
|
|
450
|
+
return result
|
|
451
|
+
|
|
452
|
+
def get_deps_and_apply(
|
|
453
|
+
accumulated_results: Mapping[base_types.ComputationNode, base_types.Result],
|
|
454
|
+
node: base_types.ComputationNode,
|
|
455
|
+
) -> base_types.Result:
|
|
456
|
+
args_kwargs = node_to_input_sync(
|
|
457
|
+
accumulated_results, node_to_computation_input_spec_options(node)
|
|
458
|
+
)
|
|
459
|
+
# We delete the references to the upstream tasks to avoid circular reference (task->exception->traceback->task) and improve memory performance
|
|
460
|
+
del accumulated_results
|
|
461
|
+
if args_kwargs is None:
|
|
462
|
+
raise _DepNotFoundError()
|
|
463
|
+
|
|
464
|
+
args, kwargs = args_kwargs
|
|
465
|
+
before = time.perf_counter()
|
|
466
|
+
result = node.func(*args, **kwargs)
|
|
467
|
+
single_node_side_effect(node, result)
|
|
468
|
+
if inspect.isawaitable(result):
|
|
469
|
+
raise Exception(
|
|
470
|
+
f"{node} returned an awaitable result but is not an async function"
|
|
471
|
+
)
|
|
472
|
+
_profile(node, before)
|
|
473
|
+
return result
|
|
474
|
+
|
|
475
|
+
all_nodes = graph.get_all_nodes(edges)
|
|
476
|
+
async_nodes = {n for n in all_nodes if asyncio.iscoroutinefunction(n.func)}
|
|
477
|
+
sync = all_nodes - async_nodes
|
|
478
|
+
tf = graph.traverse_forward(edges)
|
|
479
|
+
downstream_from_async = set(gamla.graph_traverse_many(async_nodes, tf))
|
|
480
|
+
|
|
481
|
+
async_and_downstream = async_nodes & downstream_from_async
|
|
482
|
+
async_not_downstream = async_nodes - downstream_from_async
|
|
483
|
+
sync_and_downstream = sync & downstream_from_async
|
|
484
|
+
sync_not_downstream = sync - downstream_from_async
|
|
485
|
+
|
|
486
|
+
def get_executor(
|
|
487
|
+
node: base_types.ComputationNode,
|
|
488
|
+
) -> Callable[
|
|
489
|
+
[
|
|
490
|
+
Mapping[
|
|
491
|
+
base_types.ComputationNode,
|
|
492
|
+
base_types.Result | Awaitable[base_types.Result],
|
|
493
|
+
],
|
|
494
|
+
base_types.ComputationNode,
|
|
495
|
+
],
|
|
496
|
+
base_types.Result,
|
|
497
|
+
]:
|
|
498
|
+
if node in async_and_downstream:
|
|
499
|
+
return await_deps_and_await
|
|
500
|
+
if node in async_not_downstream:
|
|
501
|
+
return get_deps_and_await
|
|
502
|
+
if node in sync_and_downstream:
|
|
503
|
+
return await_deps_and_apply
|
|
504
|
+
if node in sync_not_downstream:
|
|
505
|
+
# This is fully sync so it only uses sync results from the mapping, its typing says the whole mapping is sync.
|
|
506
|
+
return get_deps_and_apply # type: ignore
|
|
507
|
+
raise Exception("no executor found")
|
|
508
|
+
|
|
509
|
+
return get_executor
|
|
325
510
|
|
|
326
511
|
|
|
327
512
|
def _make_reduce_layers(
|
|
328
|
-
|
|
329
|
-
handled_exceptions: Tuple[Type[Exception], ...],
|
|
330
|
-
is_async: bool,
|
|
331
|
-
single_node_side_effect: _SingleNodeSideEffect,
|
|
513
|
+
handled_exceptions: Tuple[Type[Exception], ...]
|
|
332
514
|
) -> Callable[[immutables.Map], Callable[[Tuple[FrozenSet, ...]], immutables.Map]]:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
515
|
+
def apply_executor(accumulated_results, node, executor):
|
|
516
|
+
try:
|
|
517
|
+
return node, executor(accumulated_results, node)
|
|
518
|
+
except (
|
|
519
|
+
_DepNotFoundError,
|
|
520
|
+
base_types.SkipComputationError,
|
|
521
|
+
*handled_exceptions,
|
|
522
|
+
):
|
|
523
|
+
# We delete the references to the upstream tasks to avoid circular reference (task->exception->traceback->task) and improve memory performance
|
|
524
|
+
del accumulated_results
|
|
525
|
+
return None
|
|
526
|
+
|
|
339
527
|
single_layer_reducer = debug.name_callable(
|
|
340
528
|
gamla.compose_left(
|
|
341
529
|
gamla.pack,
|
|
342
530
|
gamla.juxt(
|
|
343
531
|
gamla.head,
|
|
344
532
|
gamla.compose_left(
|
|
345
|
-
opt_gamla.packstack(
|
|
346
|
-
accumulated_results_to_node_to_input, gamla.identity
|
|
347
|
-
),
|
|
348
533
|
gamla.explode(1),
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
*handled_exceptions,
|
|
355
|
-
base_types.SkipComputationError,
|
|
356
|
-
_DepNotFoundError,
|
|
357
|
-
),
|
|
534
|
+
opt_gamla.map(
|
|
535
|
+
lambda results__node_executor: apply_executor(
|
|
536
|
+
results__node_executor[0],
|
|
537
|
+
results__node_executor[1][0],
|
|
538
|
+
results__node_executor[1][1],
|
|
358
539
|
)
|
|
359
540
|
),
|
|
541
|
+
opt_gamla.filter(bool),
|
|
360
542
|
tuple,
|
|
361
543
|
),
|
|
362
544
|
),
|
|
@@ -7,7 +7,7 @@ with open("README.md", "r") as fh:
|
|
|
7
7
|
setuptools.setup(
|
|
8
8
|
name="computation-graph",
|
|
9
9
|
python_requires=">=3",
|
|
10
|
-
version="
|
|
10
|
+
version="56",
|
|
11
11
|
long_description=_LONG_DESCRIPTION,
|
|
12
12
|
long_description_content_type="text/markdown",
|
|
13
13
|
packages=setuptools.find_namespace_packages(),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{computation-graph-54 → computation_graph-56}/computation_graph.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|