async-graph-data-flow 1.5.0__tar.gz → 1.5.1__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.
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/CHANGELOG.md +8 -0
- {async_graph_data_flow-1.5.0/src/async_graph_data_flow.egg-info → async_graph_data_flow-1.5.1}/PKG-INFO +2 -1
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/pyproject.toml +2 -1
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/executor.py +33 -27
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/graph.py +0 -3
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1/src/async_graph_data_flow.egg-info}/PKG-INFO +2 -1
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow.egg-info/requires.txt +1 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/LICENSE.txt +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/MANIFEST.in +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/README.md +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/setup.cfg +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/__init__.py +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/py.typed +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow.egg-info/SOURCES.txt +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow.egg-info/dependency_links.txt +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow.egg-info/not-zip-safe +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow.egg-info/top_level.txt +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/tests/test_executor.py +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/tests/test_graph.py +0 -0
- {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/tests/test_version_number.py +0 -0
|
@@ -19,6 +19,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
19
19
|
|
|
20
20
|
### Security
|
|
21
21
|
|
|
22
|
+
## [1.5.1] - 2024-11-07
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- Added static type checking using mypy. (#12)
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Fixed documentation to actually show example code. (#12)
|
|
29
|
+
|
|
22
30
|
## [1.5.0] - 2024-10-30
|
|
23
31
|
|
|
24
32
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: async-graph-data-flow
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.1
|
|
4
4
|
Summary: Asynchronous functions that pass data along a directed acyclic graph
|
|
5
5
|
Author-email: Civis Analytics <opensource@civisanalytics.com>
|
|
6
6
|
License: BSD 3-Clause
|
|
@@ -27,6 +27,7 @@ Provides-Extra: dev
|
|
|
27
27
|
Requires-Dist: black==24.10.0; extra == "dev"
|
|
28
28
|
Requires-Dist: flake8==7.1.1; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest==8.3.3; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy==1.13.0; extra == "dev"
|
|
30
31
|
Requires-Dist: build==1.2.2; extra == "dev"
|
|
31
32
|
Requires-Dist: twine==5.1.1; extra == "dev"
|
|
32
33
|
Provides-Extra: docs
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "async-graph-data-flow"
|
|
7
|
-
version = "1.5.
|
|
7
|
+
version = "1.5.1"
|
|
8
8
|
description = "Asynchronous functions that pass data along a directed acyclic graph"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">= 3.10"
|
|
@@ -43,6 +43,7 @@ dev = [
|
|
|
43
43
|
"black == 24.10.0",
|
|
44
44
|
"flake8 == 7.1.1",
|
|
45
45
|
"pytest == 8.3.3",
|
|
46
|
+
"mypy == 1.13.0",
|
|
46
47
|
|
|
47
48
|
# Managing source distributions
|
|
48
49
|
"build == 1.2.2",
|
{async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/executor.py
RENAMED
|
@@ -20,7 +20,7 @@ class AsyncExecutor:
|
|
|
20
20
|
self,
|
|
21
21
|
graph: AsyncGraph,
|
|
22
22
|
*,
|
|
23
|
-
logger: logging.Logger = None,
|
|
23
|
+
logger: logging.Logger | None = None,
|
|
24
24
|
max_exceptions: int = 1_000,
|
|
25
25
|
):
|
|
26
26
|
"""Initialize an executor.
|
|
@@ -41,23 +41,23 @@ class AsyncExecutor:
|
|
|
41
41
|
if not isinstance(self._graph, AsyncGraph):
|
|
42
42
|
raise TypeError(f"{self._graph} must be an AsyncGraph instance")
|
|
43
43
|
|
|
44
|
-
self._node_queues = {}
|
|
45
|
-
self._consumer_tasks = {}
|
|
44
|
+
self._node_queues: dict[str, asyncio.Queue] = {}
|
|
45
|
+
self._consumer_tasks: dict[str, asyncio.Task] = {}
|
|
46
46
|
self._halt_pipeline_execution = False
|
|
47
47
|
self._logger = logger if logger else _LOG
|
|
48
48
|
self._max_exceptions = max_exceptions
|
|
49
49
|
|
|
50
|
-
self._data_flow_stats = None
|
|
50
|
+
self._data_flow_stats: dict[str, dict[str, int]] | None = None
|
|
51
51
|
self._data_flow_logging_lock = asyncio.Lock()
|
|
52
52
|
self._data_flow_logging = False
|
|
53
53
|
self._data_flow_logging_node_format = _DEFAULT_DATA_FLOW_LOGGING_NODE_FORMAT
|
|
54
54
|
self._data_flow_logging_time_interval = _DEFAULT_DATA_FLOW_LOGGING_TIME_INTERVAL
|
|
55
|
-
self._data_flow_logging_node_filter = self._graph._nodes.keys()
|
|
56
|
-
self._data_flow_logging_last_timestamp = 0
|
|
55
|
+
self._data_flow_logging_node_filter: Iterable[str] = self._graph._nodes.keys()
|
|
56
|
+
self._data_flow_logging_last_timestamp = 0.0
|
|
57
57
|
|
|
58
|
-
self._start_node_args = None
|
|
58
|
+
self._start_node_args: dict[str, tuple] | None = None
|
|
59
59
|
|
|
60
|
-
self._exceptions = None
|
|
60
|
+
self._exceptions: dict[str, deque[Exception]] | None = None
|
|
61
61
|
|
|
62
62
|
@property
|
|
63
63
|
def graph(self) -> AsyncGraph:
|
|
@@ -72,7 +72,7 @@ class AsyncExecutor:
|
|
|
72
72
|
raised from the node.
|
|
73
73
|
"""
|
|
74
74
|
if self._exceptions is None:
|
|
75
|
-
return
|
|
75
|
+
return None
|
|
76
76
|
from_deque_to_list = {}
|
|
77
77
|
for node_name, excs in self._exceptions.items():
|
|
78
78
|
# `excs` is a deque. Turning it into a list for user-friendliness.
|
|
@@ -152,7 +152,7 @@ class AsyncExecutor:
|
|
|
152
152
|
"""Turn off data flow logging."""
|
|
153
153
|
self._data_flow_logging = False
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
def _log_data_flow_nodes(self):
|
|
156
156
|
for node, flow in self._data_flow_stats.items():
|
|
157
157
|
if (
|
|
158
158
|
self._data_flow_logging_node_filter
|
|
@@ -186,7 +186,7 @@ class AsyncExecutor:
|
|
|
186
186
|
current_timestamp - self._data_flow_logging_last_timestamp
|
|
187
187
|
> self._data_flow_logging_time_interval
|
|
188
188
|
):
|
|
189
|
-
|
|
189
|
+
self._log_data_flow_nodes()
|
|
190
190
|
self._data_flow_logging_last_timestamp = current_timestamp
|
|
191
191
|
|
|
192
192
|
queue = self._node_queues[node_name]
|
|
@@ -202,17 +202,17 @@ class AsyncExecutor:
|
|
|
202
202
|
try:
|
|
203
203
|
if len(params) == 0:
|
|
204
204
|
coro = node.func()
|
|
205
|
-
elif node.unpack_input and (
|
|
206
|
-
|
|
207
|
-
):
|
|
208
|
-
coro = node.func(
|
|
205
|
+
elif node.unpack_input and isinstance(data, tuple):
|
|
206
|
+
coro = node.func(*data)
|
|
207
|
+
elif node.unpack_input and isinstance(data, dict):
|
|
208
|
+
coro = node.func(**data)
|
|
209
209
|
else:
|
|
210
210
|
coro = node.func(data)
|
|
211
211
|
except asyncio.CancelledError:
|
|
212
212
|
continue
|
|
213
213
|
except Exception as exc:
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
self._update_data_flow_error_stats(node_name)
|
|
215
|
+
self._update_exceptions(node_name, exc)
|
|
216
216
|
self._logger.error(traceback.format_exc())
|
|
217
217
|
if self._graph.halt_on_exception or node.halt_on_exception:
|
|
218
218
|
self._logger.error(
|
|
@@ -238,8 +238,8 @@ class AsyncExecutor:
|
|
|
238
238
|
except asyncio.CancelledError:
|
|
239
239
|
break
|
|
240
240
|
except Exception as exc:
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
self._update_data_flow_error_stats(node_name)
|
|
242
|
+
self._update_exceptions(node_name, exc)
|
|
243
243
|
self._logger.error(traceback.format_exc())
|
|
244
244
|
if self._graph.halt_on_exception or node.halt_on_exception:
|
|
245
245
|
# close current agen
|
|
@@ -255,27 +255,33 @@ class AsyncExecutor:
|
|
|
255
255
|
continue
|
|
256
256
|
else:
|
|
257
257
|
node_edges = self._graph._nodes_to_edges[node_name]
|
|
258
|
-
|
|
258
|
+
self._update_data_flow_in_out_stats(node_name, node_edges)
|
|
259
259
|
await self._add_to_node_queue(node_edges, next_data_item)
|
|
260
260
|
|
|
261
261
|
queue.task_done()
|
|
262
262
|
except asyncio.CancelledError:
|
|
263
263
|
break
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
def _update_data_flow_in_out_stats(self, in_node: str, out_nodes: set[str]):
|
|
266
|
+
if self._data_flow_stats is None:
|
|
267
|
+
return None
|
|
266
268
|
self._data_flow_stats[in_node]["out"] += 1
|
|
267
269
|
for node in out_nodes:
|
|
268
270
|
self._data_flow_stats[node]["in"] += 1
|
|
269
271
|
|
|
270
|
-
|
|
272
|
+
def _update_data_flow_error_stats(self, node: str):
|
|
273
|
+
if self._data_flow_stats is None:
|
|
274
|
+
return None
|
|
271
275
|
self._data_flow_stats[node]["err"] += 1
|
|
272
276
|
|
|
273
|
-
|
|
277
|
+
def _update_exceptions(self, node: str, exc: Exception):
|
|
278
|
+
if self._exceptions is None:
|
|
279
|
+
return None
|
|
274
280
|
self._exceptions[node].append(exc)
|
|
275
281
|
|
|
276
282
|
async def _pipeline_execution(self):
|
|
277
|
-
self._data_flow_stats
|
|
278
|
-
self._exceptions
|
|
283
|
+
self._data_flow_stats = {}
|
|
284
|
+
self._exceptions = {}
|
|
279
285
|
|
|
280
286
|
for node_name, node in self._graph._nodes.items():
|
|
281
287
|
queue = asyncio.Queue(maxsize=node.queue_size)
|
|
@@ -299,7 +305,7 @@ class AsyncExecutor:
|
|
|
299
305
|
await asyncio.gather(*self._consumer_tasks.values())
|
|
300
306
|
|
|
301
307
|
if self._data_flow_logging:
|
|
302
|
-
|
|
308
|
+
self._log_data_flow_nodes()
|
|
303
309
|
|
|
304
310
|
def _get_start_node_args(self, start_node_args) -> dict[str, tuple]:
|
|
305
311
|
if start_node_args is None:
|
|
@@ -313,7 +319,7 @@ class AsyncExecutor:
|
|
|
313
319
|
raise TypeError(f"args for the node '{node}' isn't a tuple: {args}")
|
|
314
320
|
return start_node_args
|
|
315
321
|
|
|
316
|
-
def execute(self, start_nodes: dict[str, tuple] = None) -> None:
|
|
322
|
+
def execute(self, start_nodes: dict[str, tuple] | None = None) -> None:
|
|
317
323
|
"""Start executing the functions along the graph.
|
|
318
324
|
|
|
319
325
|
Parameters
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: async-graph-data-flow
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.1
|
|
4
4
|
Summary: Asynchronous functions that pass data along a directed acyclic graph
|
|
5
5
|
Author-email: Civis Analytics <opensource@civisanalytics.com>
|
|
6
6
|
License: BSD 3-Clause
|
|
@@ -27,6 +27,7 @@ Provides-Extra: dev
|
|
|
27
27
|
Requires-Dist: black==24.10.0; extra == "dev"
|
|
28
28
|
Requires-Dist: flake8==7.1.1; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest==8.3.3; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy==1.13.0; extra == "dev"
|
|
30
31
|
Requires-Dist: build==1.2.2; extra == "dev"
|
|
31
32
|
Requires-Dist: twine==5.1.1; extra == "dev"
|
|
32
33
|
Provides-Extra: docs
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/__init__.py
RENAMED
|
File without changes
|
{async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|