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.
Files changed (20) hide show
  1. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/CHANGELOG.md +8 -0
  2. {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
  3. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/pyproject.toml +2 -1
  4. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/executor.py +33 -27
  5. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/graph.py +0 -3
  6. {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
  7. {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
  8. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/LICENSE.txt +0 -0
  9. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/MANIFEST.in +0 -0
  10. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/README.md +0 -0
  11. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/setup.cfg +0 -0
  12. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/__init__.py +0 -0
  13. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/src/async_graph_data_flow/py.typed +0 -0
  14. {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
  15. {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
  16. {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
  17. {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
  18. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/tests/test_executor.py +0 -0
  19. {async_graph_data_flow-1.5.0 → async_graph_data_flow-1.5.1}/tests/test_graph.py +0 -0
  20. {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.0
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.0"
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",
@@ -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
- async def _log_data_flow_nodes(self):
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
- await self._log_data_flow_nodes()
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
- (is_tuple := isinstance(data, tuple)) or isinstance(data, dict)
207
- ):
208
- coro = node.func(*data) if is_tuple else node.func(**data)
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
- await self._update_data_flow_error_stats(node_name)
215
- await self._update_exceptions(node_name, exc)
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
- await self._update_data_flow_error_stats(node_name)
242
- await self._update_exceptions(node_name, exc)
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
- await self._update_data_flow_in_out_stats(node_name, node_edges)
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
- async def _update_data_flow_in_out_stats(self, in_node: str, out_nodes: set[str]):
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
- async def _update_data_flow_error_stats(self, node: str):
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
- async def _update_exceptions(self, node: str, exc: Exception):
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: dict[str, dict[str, int]] = {}
278
- self._exceptions: dict[str, deque[Exception]] = {}
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
- await self._log_data_flow_nodes()
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
@@ -3,9 +3,6 @@ from collections import OrderedDict
3
3
  from typing import Any, Callable, NamedTuple
4
4
 
5
5
 
6
- _DEFAULT_NUM_OF_WORKERS = 1
7
-
8
-
9
6
  class InvalidAsyncGraphError(Exception):
10
7
  pass
11
8
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: async-graph-data-flow
3
- Version: 1.5.0
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
@@ -3,6 +3,7 @@
3
3
  black==24.10.0
4
4
  flake8==7.1.1
5
5
  pytest==8.3.3
6
+ mypy==1.13.0
6
7
  build==1.2.2
7
8
  twine==5.1.1
8
9