commandnet 0.6.0__tar.gz → 0.6.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commandnet
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: A lightweight, Pydantic-powered, distributed event-driven state machine and typed node graph runtime.
5
5
  Author: Christopher Vaz
6
6
  Author-email: christophervaz160@gmail.com
@@ -106,23 +106,17 @@ class Engine:
106
106
  if hasattr(ctx, "is_cancelled"):
107
107
  ctx.is_cancelled = await self.db.is_cancelled(subject_id)
108
108
 
109
- # 2. RELEASE LOCK before long-running compute
110
- await self.db.unlock_subject(subject_id)
111
-
112
- # 3. COMPUTE (no DB lock held)
109
+ # 2. COMPUTE (Lock is HELD via Redis during compute)
113
110
  start_t = asyncio.get_event_loop().time()
114
- result = await node_cls().run(ctx, payload)
111
+ try:
112
+ result = await node_cls().run(ctx, payload)
113
+ except asyncio.CancelledError:
114
+ self.logger.warning(f"Task {subject_id} cancelled during compute. Emitting Interrupt.")
115
+ result = Interrupt(subject_id=subject_id, hard=True)
116
+
115
117
  duration = (asyncio.get_event_loop().time() - start_t) * 1000
116
-
117
- # 4. RE-LOCK before writing
118
- node_name_check, _ = await self.db.lock_and_load(subject_id)
119
-
120
- # 🔴 IMPORTANT SAFETY CHECK (prevents stale writes)
121
- if node_name_check != event.node_name and node_name_check != "AWAITING_CALL":
122
- await self.db.unlock_subject(subject_id)
123
- return
124
-
125
- await self._apply_target(subject_id, ctx, result, duration)
118
+
119
+ await self._apply_target(subject_id, ctx, result, duration, payload=payload)
126
120
 
127
121
  except Exception as e:
128
122
  await self.observer.on_error(subject_id, event.node_name, e)
@@ -264,6 +258,22 @@ class Engine:
264
258
  subject_id, join_name, len(target.branches)
265
259
  )
266
260
 
261
+ async def _run_branch(task: ParallelTask):
262
+ branch_payload = (
263
+ task.payload if task.payload is not None else payload
264
+ )
265
+
266
+ sub_ctx = self._get_path(context, task.sub_context_path)
267
+
268
+ await self._apply_target(
269
+ f"{subject_id}#{task.sub_context_path}",
270
+ sub_ctx,
271
+ task.action,
272
+ payload=branch_payload,
273
+ )
274
+
275
+ branch_tasks = []
276
+
267
277
  for branch in target.branches:
268
278
  # Normalize branch into ParallelTask
269
279
  if isinstance(branch, ParallelTask):
@@ -276,19 +286,10 @@ class Engine:
276
286
  )
277
287
  task = ParallelTask(action=branch, sub_context_path=path)
278
288
 
279
- # PROPAGATION: fallback to incoming payload if branch has none
280
- branch_payload = (
281
- task.payload if task.payload is not None else payload
282
- )
289
+ branch_tasks.append(asyncio.create_task(_run_branch(task)))
283
290
 
284
- sub_ctx = self._get_path(context, task.sub_context_path)
285
-
286
- await self._apply_target(
287
- f"{subject_id}#{task.sub_context_path}",
288
- sub_ctx,
289
- task.action,
290
- payload=branch_payload,
291
- )
291
+ if branch_tasks:
292
+ await asyncio.gather(*branch_tasks)
292
293
 
293
294
  if target.join_node:
294
295
  await self.db.save_state(
@@ -379,7 +380,7 @@ class Engine:
379
380
  )
380
381
 
381
382
  result = await node_inst.on_signal(ctx, signal_id, payload)
382
- await self._apply_target(subject_id, ctx, result)
383
+ await self._apply_target(subject_id, ctx, result, payload=payload)
383
384
  finally:
384
385
  await self.db.unlock_subject(subject_id)
385
386
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "commandnet"
3
- version = "0.6.0"
3
+ version = "0.6.2"
4
4
  description = "A lightweight, Pydantic-powered, distributed event-driven state machine and typed node graph runtime."
5
5
  authors = [
6
6
  { name = "Christopher Vaz", email = "christophervaz160@gmail.com" }
File without changes