celestialflow 3.2.8__tar.gz → 3.2.9__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 (55) hide show
  1. {celestialflow-3.2.8/src/celestialflow.egg-info → celestialflow-3.2.9}/PKG-INFO +67 -70
  2. {celestialflow-3.2.8 → celestialflow-3.2.9}/README.md +66 -69
  3. {celestialflow-3.2.8 → celestialflow-3.2.9}/pyproject.toml +1 -1
  4. celestialflow-3.2.9/src/celestialflow/benchmark/util_benchmark.py +129 -0
  5. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/benchmark/util_clone.py +7 -10
  6. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/graph/core_graph.py +139 -169
  7. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/graph/core_structure.py +24 -47
  8. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/graph/util_estimators.py +1 -1
  9. celestialflow-3.2.8/src/celestialflow/graph/util_graph.py → celestialflow-3.2.9/src/celestialflow/graph/util_order_graph.py +56 -32
  10. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/graph/util_serialize.py +149 -150
  11. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/observability/core_report.py +25 -25
  12. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/observability/util_types.py +1 -1
  13. celestialflow-3.2.9/src/celestialflow/persistence/__init__.py +31 -0
  14. celestialflow-3.2.8/src/celestialflow/persistence/core_fallback.py → celestialflow-3.2.9/src/celestialflow/persistence/core_lifecycle.py +27 -55
  15. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/persistence/core_log.py +19 -21
  16. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/persistence/core_scope.py +41 -41
  17. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/persistence/util_sqlite.py +0 -30
  18. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_errors.py +207 -207
  19. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_event.py +0 -1
  20. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/stage/core_dispatch.py +8 -4
  21. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/stage/core_executor.py +652 -692
  22. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/stage/core_stage.py +3 -45
  23. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/stage/core_stages.py +6 -16
  24. {celestialflow-3.2.8 → celestialflow-3.2.9/src/celestialflow.egg-info}/PKG-INFO +67 -70
  25. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow.egg-info/SOURCES.txt +2 -2
  26. celestialflow-3.2.8/src/celestialflow/benchmark/util_benchmark.py +0 -142
  27. celestialflow-3.2.8/src/celestialflow/persistence/__init__.py +0 -31
  28. {celestialflow-3.2.8 → celestialflow-3.2.9}/LICENSE +0 -0
  29. {celestialflow-3.2.8 → celestialflow-3.2.9}/setup.cfg +0 -0
  30. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/__init__.py +0 -0
  31. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/benchmark/__init__.py +0 -0
  32. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/funnel/__init__.py +0 -0
  33. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/funnel/core_inlet.py +0 -0
  34. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/funnel/core_spout.py +0 -0
  35. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/funnel/util_count.py +0 -0
  36. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/graph/__init__.py +0 -0
  37. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/observability/__init__.py +0 -0
  38. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/observability/core_observer.py +0 -0
  39. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/persistence/util_payload.py +0 -0
  40. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/__init__.py +0 -0
  41. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/core_envelope.py +0 -0
  42. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/core_metrics.py +0 -0
  43. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/core_queue.py +0 -0
  44. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_config.py +0 -0
  45. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_constant.py +0 -0
  46. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_estimators.py +0 -0
  47. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_format.py +0 -0
  48. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_hash.py +0 -0
  49. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/runtime/util_types.py +0 -0
  50. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/stage/__init__.py +0 -0
  51. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/stage/util_callable.py +0 -0
  52. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow/stage/util_types.py +0 -0
  53. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow.egg-info/dependency_links.txt +0 -0
  54. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow.egg-info/requires.txt +0 -0
  55. {celestialflow-3.2.8 → celestialflow-3.2.9}/src/celestialflow.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: celestialflow
3
- Version: 3.2.8
3
+ Version: 3.2.9
4
4
  Summary: A flexible GRAPH-based task orchestration framework.
5
5
  Author-email: Mr-xiaotian <mingxiaomingtian@gmail.com>
6
6
  License: MIT
@@ -258,125 +258,122 @@ flowchart TD
258
258
  ## 文件结构(File Structure)
259
259
 
260
260
  ```
261
- 📁 CelestialFlow (587MB 684KB 313B)
262
- 📁 bench (296KB 842B)
261
+ 📁 CelestialFlow (588MB 419KB 930B)
262
+ 📁 bench (296KB 806B)
263
263
  📁 [1项排除的目录] (194KB 222B)
264
264
  🐍 bench_datastructures.py (6KB 690B)
265
- 🐍 bench_execution_mode.py (2KB 831B)
265
+ 🐍 bench_execution_mode.py (2KB 707B)
266
266
  🐍 bench_futures_memory.py (2KB 269B)
267
- 🐍 bench_gil_vs_nogil.py (10KB 7B)
267
+ 🐍 bench_gil_vs_nogil.py (10KB 121B)
268
268
  🐍 bench_graph_mode.py (7KB 450B)
269
269
  🐍 bench_hash.py (7KB 67B)
270
270
  🐍 bench_hash_container.py (3KB 1009B)
271
271
  🐍 bench_hash_memory.py (3KB 642B)
272
- 🐍 bench_http_grpc.py (2KB 608B)
272
+ 🐍 bench_http_grpc.py (2KB 521B)
273
273
  🐍 bench_ipc_queue.py (7KB 104B)
274
274
  🐍 bench_lock_overhead.py (9KB 421B)
275
275
  🐍 bench_mpqueue_vs_shared_memory.py (13KB 127B)
276
276
  🐍 bench_observer.py (7KB 860B)
277
- 🐍 bench_persistence_spout.py (4KB 279B)
277
+ 🐍 bench_persistence_spout.py (4KB 340B)
278
278
  🐍 bench_queue.py (5KB 857B)
279
279
  🐍 bench_requests.py (6KB 813B)
280
280
  🐍 bench_tqdm.py (1KB 235B)
281
281
  🐍 bench_utils.py (543B)
282
- 📁 demo (147KB 87B)
282
+ 📁 demo (145KB 1012B)
283
283
  📁 [1项排除的目录] (100KB 9B)
284
284
  🐍 demo_executor.py (1KB 495B)
285
285
  🐍 demo_funnel.py (2KB 289B)
286
- 🐍 demo_graph.py (3KB 565B)
287
- 🐍 demo_network.py (3KB 879B)
286
+ 🐍 demo_graph.py (3KB 227B)
287
+ 🐍 demo_network.py (3KB 729B)
288
288
  🐍 demo_observer.py (4KB 270B)
289
- 🐍 demo_redis.py (9KB 144B)
290
- 🐍 demo_stages.py (4KB 336B)
291
- 🐍 demo_structure.py (12KB 24B)
289
+ 🐍 demo_redis.py (9KB 81B)
290
+ 🐍 demo_stages.py (4KB 219B)
291
+ 🐍 demo_structure.py (11KB 593B)
292
292
  🐍 demo_utils.py (6KB 148B)
293
- 📁 dist (147KB 48B)
293
+ 📁 dist (302KB 861B)
294
294
  ❓ .gitignore (1B)
295
295
  ❓ celestialflow-3.2.7-py3-none-any.whl (79KB 235B)
296
296
  📦 celestialflow-3.2.7.tar.gz (67KB 836B)
297
- 📁 docs (1MB 722KB 21B)
297
+ celestialflow-3.2.8-py3-none-any.whl (83KB 244B)
298
+ 📦 celestialflow-3.2.8.tar.gz (72KB 569B)
299
+ 📁 docs (1MB 756KB 830B)
298
300
  📁 en[已折叠] (568KB 991B)
299
301
  📁 ja[已折叠] (642KB 383B)
300
- 📁 zh-CN[已折叠] (534KB 695B)
301
- 📁 experiments (2KB 1015B)
302
- 🐍 experiment_networkx.py (1KB 878B)
302
+ 📁 zh-CN[已折叠] (569KB 480B)
303
+ 📁 experiments (2KB 1021B)
304
+ 🐍 experiment_networkx.py (1KB 884B)
303
305
  🐍 experiment_tqdm.py (1KB 137B)
304
306
  📁 img (5MB 871KB 242B)
305
307
  📷 file_structure.svg (4MB 918KB 1000B)
306
308
  📷 logo(old).png (836KB 542B)
307
309
  📷 logo.png (122KB 747B)
308
310
  📷 scc_condensation.svg (17KB 1B)
309
- 📁 src (1MB 858KB 89B)
310
- 📁 celestialflow[已折叠] (1MB 839KB 629B)
311
- 📁 celestialflow.egg-info[已折叠] (18KB 484B)
312
- 📁 tests (4MB 167KB 552B)
313
- 📁 benchmark[已折叠] (38KB 197B)
311
+ 📁 src (1MB 873KB 44B)
312
+ 📁 celestialflow[已折叠] (1MB 852KB 902B)
313
+ 📁 celestialflow.egg-info[已折叠] (20KB 166B)
314
+ 📁 tests (4MB 170KB 251B)
315
+ 📁 benchmark[已折叠] (39KB 852B)
314
316
  📁 funnel[已折叠] (96KB 363B)
315
- 📁 graph[已折叠] (721KB 564B)
316
- 📁 observability[已折叠] (189KB 236B)
317
- 📁 persistence[已折叠] (388KB 426B)
318
- 📁 runtime[已折叠] (1MB 295KB 529B)
319
- 📁 stage[已折叠] (382KB 155B)
317
+ 📁 graph[已折叠] (724KB 404B)
318
+ 📁 observability[已折叠] (189KB 230B)
319
+ 📁 persistence[已折叠] (387KB 877B)
320
+ 📁 runtime[已折叠] (1MB 295KB 264B)
321
+ 📁 stage[已折叠] (381KB 203B)
320
322
  📁 utils[已折叠] (639KB 527B)
321
323
  📁 [1项排除的目录] (487KB 589B)
322
324
  🐍 conftest.py (1KB 38B)
323
325
  🐍 __init__.py (0B)
324
- 📁 [12项排除的目录] (573MB 420KB 160B)
326
+ 📁 [12项排除的目录] (573MB 949KB 913B)
325
327
  ❓ .env (468B)
326
- ❓ .gitignore (1KB 314B)
327
- 📝 AGENTS.md (1KB 173B)
328
+ ❓ .gitignore (1KB 315B)
329
+ 📝 AGENTS.md (1KB 434B)
328
330
  ❓ LICENSE (1KB 65B)
329
331
  ❓ Makefile (155B)
330
332
  ⚙️ pyproject.toml (2KB 668B)
331
- 📝 README.md (19KB 24B)
332
- 🔒 uv.lock (97KB 510B)
333
+ 📝 README.md (17KB 465B)
334
+ 🔒 uv.lock (121KB 572B)
333
335
  ```
334
336
  <p align="center">
335
- <em>celestial-flow 3.2.8</em>
337
+ <em>celestial-flow 3.2.9</em>
336
338
  </p>
337
339
 
338
340
  (该视图由我的另一个项目[CelestialVault](https://github.com/Mr-xiaotian/CelestialVault)中inst_file.FileTree.print_tree()生成。转换为图片则借助[Carbon](https://carbon.now.sh)。)
339
341
 
340
342
  ## 版本日志(Version Log)
341
- - 3.2.8
343
+ - 3.2.9
342
344
  - feat:
343
- - [IMPORTANT] 添加 `TaskGraph.run_async`, 现在可以直接进行图级别的异步
344
- - 根据 `bench_graph_mode.py` 最新测试, 在I/O密集型任务中, `serial`+`async`(6.05s),比 `serial`+`serial`(69.04s)快 **11.4x**
345
- - 在 `core_structure` 中添加报错校验
346
- - 不再允许设置 `execution_mode=async` 时调用 `TashExecutor.start`(如今的`TashExecutor.run`), 只能调用 `TashExecutor.run_async`
347
- - 另外调用 `TashExecutor.run_async` 时也会进行模式检查, 不是 `execution_mode=async` 就会抛出异常
348
- - 在 `BaseObserver` 中添加 `observer_error`, 用于处理 `BaseObserver.on_*` 函数的报错
349
- - 在 `TaskGraph._finish_start_graph` `TaskExecutor._finish_start` 中对所有执行步骤进行 `try-except`, 以使收尾步骤尽量全部完成
350
- - 添加 `core_scope`, 用于独立控制 `funnel` 的生命周期, 并用于 `TaskGraph.run/run_async` `TaskExecutor.run/run_async`
351
- - refactor:
352
- - [IMPORTANT] 原 `TaskGraph.start_graph/start_graph_async` `TaskExecutor.start/start_async` 已被重命名为 `TaskGraph.run/run_async` `TaskExecutor.run/run_async`
345
+ - [IMPORTANT] 移除 `stage` 中 `stage_mode`,并添加 `graph_mode`
353
346
  - 破坏性更新
354
- - 其底层缘由是为了合并 `TaskStage.start_stage/start_stage_async` `TaskExecutor.start/start_async` 而做出的一系列重构之一
355
- - 现在的 `TaskGraph.start/start_async` `TaskExecutor.start/start_async` 不再接受任务, 而是专注于处理现有任务列表中的任务
356
- - [IMPORTANT] 将funnel从 `TaskGraph` `TaskExecutor` 中的显性调用与显性传递, 改为所有使用端均从独立文件中import
357
- - 一来是因为原先的传递链太丑了, 二来是为了简化 `TaskExecutor.start/start_async` 逻辑, 为其与 `TaskStage.start_stage/start_stage_async` 的合并做准备
358
- - 移动部分文件以解决部分模块级的循环引用问题
359
- - 原先并非文件级循环引用, 在使用上并无问题
360
- - 修改 `TaskDispatch.dispatch_thread` 中处理已完成future的逻辑, 避免cpu浪费
361
- - 删除 `util_errors` 中一些不必要的错误类
362
- - 将 `TaskGraph._finalize_stages` 进行拆分, 删除不必要的机制, 将剩余机制移至其他方法
363
- - 修改 `TaskGraph.set_reporter` 的逻辑, 使其与 `set_ctree` 保持一致
364
- - `TaskOutQueue` 中的 `queue_list|target_name|_name_to_idx` 改为 `_queues`, 并删除 `put_channel`
365
- - 我有些困惑为什么最初我没有这么做
366
- - `TaskExecutor` 中合并 `_get_task_repr` and `_get_result_repr`
367
- - 这两个方法原先差异巨大, 但后来经过多次其他部分的重构, 现在逻辑已经一致
368
- - `TaskStage` 中的 `_status` 交给 `TaskMetrics` 维护
369
- - 依旧是为了合并 `TaskExecutor.start/start_async` 与 `TaskStage.start_stage/start_stage_async`
370
- - 移除 `TaskGraph` 中的 `put_stage_queue`
371
- - 任务输入完全使用 `TaskExecutor` 中的 `put_task` `put_signal` 方法
347
+ - `stage_mode` 可以细粒度的控制每个 `TaskStage` 在图中的模式,但经过多年使用,我认为这种细粒度的控制并无必要,反而无谓的增加理解成本
348
+ - `graph_mode` 则提供了更粗粒度的控制,用于统一控制所有 `TaskStage` 在图中是串行/多线程/并发运行,适用于大多数场景
349
+ - 同时完善了 `graph_mode`(serial/thread/async) * `execution_mode`(serial/thread/async) 总共 9 种组合模式
350
+ - 移除 `schedule_mode`
351
+ - 破坏性更新
352
+ - 这个模式带来了许多复杂度,但没有与原先的 `stage_mode / execution_mode` 产生明显的组合优势
353
+ - `get_graph_analysis` 中添加 `graph_mode`
354
+ - 这个函数主要用于给web端提供信息
355
+ - web端代码已经同步修改
356
+ - 添加新的 `warning` 项,当图不是 dag `graph_mode=serial` 时触发
357
+ - 移除 `task_executor` 中的参数 `persist_result`
358
+ - 这是为了简化代码逻辑,同时也使所有任务的最终状态(包括:成功/失败/未执行)都能被统一存储
359
+ - `task.retry` 事件降级,不再申请单独事件ID,不再在 `lifecycle` 中留痕
360
+ - reafactor:
361
+ - 合并 `benchmark` 相关函数对 sync async 两种模式的执行
362
+ - 性能无差异,代码好看一些
363
+ - 将原本的 `fallback` 改名为 `lifecycle`
364
+ - 早在重构原本的 `fail` 时就想起个更恰当的名字,这个版本才实现
365
+ - 将 `graph.source_lists` 改为 `graph.source_names`
366
+ - `graph.source_lists` 直接存储节点的引用,而 `graph.source_names` 存储节点的名称
367
+ - 所有的 `log` / `lifecycle` 文件统一改名为 `flow_log` / `flow_lifecycle`
368
+ - 删除 `graph` 中的 `out_edges` `in_edges`
369
+ - 现在由 `OrderGraph` 负责管理边关系
370
+ - 在 `tarjan_scc` 中使用 DFS 取代原先的递归逻辑
371
+ - 将 `util_graph` 改名为 `util_order_graph`
372
372
  - fix:
373
- - `TaskDispatch.worker/worker_async` 中添加错误捕捉, 捕捉为 `CRITICAL` 级错误
374
- - 避免 `worker` 级出错导致计数错误, 永远无法退出
375
- - 在 `spout` 与 `reporter` 的 `stop` 操作中添加对于线程没有成功 `join` 的报错
376
- - 修复 `TaskGraph.restore_db` 与 `TaskExecutor.restore_db` 中开启 `filter_by_error_type=Ture` 时跳过 `status=pending` 任务记录的问题
377
- - 修复 `TaskGraph` 中部分 `get_*` 方法依赖 `_build_analysis` 的产物, 但 `_build_analysis` 未执行导致的问题
378
- - 修复 `TaskMetrics` 中 `get_counts` 与 `is_tasks_finished` 中可能导致死锁的问题
379
- - 修复 `TaskReporter` 中 `stop` 里进行的最后一次 `_refresh_all` 没有错误捕捉, 导致后续收尾未完成的问题
373
+ - 修复最后一次 report 无法正常上传的问题
374
+ - 修复 `graph.run` / `graph.run_async` 中 `is_put_signal` 参数没有生效的问题
375
+ - chore:
376
+ - 更新文档
380
377
 
381
378
  更多过往日志可看:
382
379
 
@@ -239,125 +239,122 @@ flowchart TD
239
239
  ## 文件结构(File Structure)
240
240
 
241
241
  ```
242
- 📁 CelestialFlow (587MB 684KB 313B)
243
- 📁 bench (296KB 842B)
242
+ 📁 CelestialFlow (588MB 419KB 930B)
243
+ 📁 bench (296KB 806B)
244
244
  📁 [1项排除的目录] (194KB 222B)
245
245
  🐍 bench_datastructures.py (6KB 690B)
246
- 🐍 bench_execution_mode.py (2KB 831B)
246
+ 🐍 bench_execution_mode.py (2KB 707B)
247
247
  🐍 bench_futures_memory.py (2KB 269B)
248
- 🐍 bench_gil_vs_nogil.py (10KB 7B)
248
+ 🐍 bench_gil_vs_nogil.py (10KB 121B)
249
249
  🐍 bench_graph_mode.py (7KB 450B)
250
250
  🐍 bench_hash.py (7KB 67B)
251
251
  🐍 bench_hash_container.py (3KB 1009B)
252
252
  🐍 bench_hash_memory.py (3KB 642B)
253
- 🐍 bench_http_grpc.py (2KB 608B)
253
+ 🐍 bench_http_grpc.py (2KB 521B)
254
254
  🐍 bench_ipc_queue.py (7KB 104B)
255
255
  🐍 bench_lock_overhead.py (9KB 421B)
256
256
  🐍 bench_mpqueue_vs_shared_memory.py (13KB 127B)
257
257
  🐍 bench_observer.py (7KB 860B)
258
- 🐍 bench_persistence_spout.py (4KB 279B)
258
+ 🐍 bench_persistence_spout.py (4KB 340B)
259
259
  🐍 bench_queue.py (5KB 857B)
260
260
  🐍 bench_requests.py (6KB 813B)
261
261
  🐍 bench_tqdm.py (1KB 235B)
262
262
  🐍 bench_utils.py (543B)
263
- 📁 demo (147KB 87B)
263
+ 📁 demo (145KB 1012B)
264
264
  📁 [1项排除的目录] (100KB 9B)
265
265
  🐍 demo_executor.py (1KB 495B)
266
266
  🐍 demo_funnel.py (2KB 289B)
267
- 🐍 demo_graph.py (3KB 565B)
268
- 🐍 demo_network.py (3KB 879B)
267
+ 🐍 demo_graph.py (3KB 227B)
268
+ 🐍 demo_network.py (3KB 729B)
269
269
  🐍 demo_observer.py (4KB 270B)
270
- 🐍 demo_redis.py (9KB 144B)
271
- 🐍 demo_stages.py (4KB 336B)
272
- 🐍 demo_structure.py (12KB 24B)
270
+ 🐍 demo_redis.py (9KB 81B)
271
+ 🐍 demo_stages.py (4KB 219B)
272
+ 🐍 demo_structure.py (11KB 593B)
273
273
  🐍 demo_utils.py (6KB 148B)
274
- 📁 dist (147KB 48B)
274
+ 📁 dist (302KB 861B)
275
275
  ❓ .gitignore (1B)
276
276
  ❓ celestialflow-3.2.7-py3-none-any.whl (79KB 235B)
277
277
  📦 celestialflow-3.2.7.tar.gz (67KB 836B)
278
- 📁 docs (1MB 722KB 21B)
278
+ celestialflow-3.2.8-py3-none-any.whl (83KB 244B)
279
+ 📦 celestialflow-3.2.8.tar.gz (72KB 569B)
280
+ 📁 docs (1MB 756KB 830B)
279
281
  📁 en[已折叠] (568KB 991B)
280
282
  📁 ja[已折叠] (642KB 383B)
281
- 📁 zh-CN[已折叠] (534KB 695B)
282
- 📁 experiments (2KB 1015B)
283
- 🐍 experiment_networkx.py (1KB 878B)
283
+ 📁 zh-CN[已折叠] (569KB 480B)
284
+ 📁 experiments (2KB 1021B)
285
+ 🐍 experiment_networkx.py (1KB 884B)
284
286
  🐍 experiment_tqdm.py (1KB 137B)
285
287
  📁 img (5MB 871KB 242B)
286
288
  📷 file_structure.svg (4MB 918KB 1000B)
287
289
  📷 logo(old).png (836KB 542B)
288
290
  📷 logo.png (122KB 747B)
289
291
  📷 scc_condensation.svg (17KB 1B)
290
- 📁 src (1MB 858KB 89B)
291
- 📁 celestialflow[已折叠] (1MB 839KB 629B)
292
- 📁 celestialflow.egg-info[已折叠] (18KB 484B)
293
- 📁 tests (4MB 167KB 552B)
294
- 📁 benchmark[已折叠] (38KB 197B)
292
+ 📁 src (1MB 873KB 44B)
293
+ 📁 celestialflow[已折叠] (1MB 852KB 902B)
294
+ 📁 celestialflow.egg-info[已折叠] (20KB 166B)
295
+ 📁 tests (4MB 170KB 251B)
296
+ 📁 benchmark[已折叠] (39KB 852B)
295
297
  📁 funnel[已折叠] (96KB 363B)
296
- 📁 graph[已折叠] (721KB 564B)
297
- 📁 observability[已折叠] (189KB 236B)
298
- 📁 persistence[已折叠] (388KB 426B)
299
- 📁 runtime[已折叠] (1MB 295KB 529B)
300
- 📁 stage[已折叠] (382KB 155B)
298
+ 📁 graph[已折叠] (724KB 404B)
299
+ 📁 observability[已折叠] (189KB 230B)
300
+ 📁 persistence[已折叠] (387KB 877B)
301
+ 📁 runtime[已折叠] (1MB 295KB 264B)
302
+ 📁 stage[已折叠] (381KB 203B)
301
303
  📁 utils[已折叠] (639KB 527B)
302
304
  📁 [1项排除的目录] (487KB 589B)
303
305
  🐍 conftest.py (1KB 38B)
304
306
  🐍 __init__.py (0B)
305
- 📁 [12项排除的目录] (573MB 420KB 160B)
307
+ 📁 [12项排除的目录] (573MB 949KB 913B)
306
308
  ❓ .env (468B)
307
- ❓ .gitignore (1KB 314B)
308
- 📝 AGENTS.md (1KB 173B)
309
+ ❓ .gitignore (1KB 315B)
310
+ 📝 AGENTS.md (1KB 434B)
309
311
  ❓ LICENSE (1KB 65B)
310
312
  ❓ Makefile (155B)
311
313
  ⚙️ pyproject.toml (2KB 668B)
312
- 📝 README.md (19KB 24B)
313
- 🔒 uv.lock (97KB 510B)
314
+ 📝 README.md (17KB 465B)
315
+ 🔒 uv.lock (121KB 572B)
314
316
  ```
315
317
  <p align="center">
316
- <em>celestial-flow 3.2.8</em>
318
+ <em>celestial-flow 3.2.9</em>
317
319
  </p>
318
320
 
319
321
  (该视图由我的另一个项目[CelestialVault](https://github.com/Mr-xiaotian/CelestialVault)中inst_file.FileTree.print_tree()生成。转换为图片则借助[Carbon](https://carbon.now.sh)。)
320
322
 
321
323
  ## 版本日志(Version Log)
322
- - 3.2.8
324
+ - 3.2.9
323
325
  - feat:
324
- - [IMPORTANT] 添加 `TaskGraph.run_async`, 现在可以直接进行图级别的异步
325
- - 根据 `bench_graph_mode.py` 最新测试, 在I/O密集型任务中, `serial`+`async`(6.05s),比 `serial`+`serial`(69.04s)快 **11.4x**
326
- - 在 `core_structure` 中添加报错校验
327
- - 不再允许设置 `execution_mode=async` 时调用 `TashExecutor.start`(如今的`TashExecutor.run`), 只能调用 `TashExecutor.run_async`
328
- - 另外调用 `TashExecutor.run_async` 时也会进行模式检查, 不是 `execution_mode=async` 就会抛出异常
329
- - 在 `BaseObserver` 中添加 `observer_error`, 用于处理 `BaseObserver.on_*` 函数的报错
330
- - 在 `TaskGraph._finish_start_graph` `TaskExecutor._finish_start` 中对所有执行步骤进行 `try-except`, 以使收尾步骤尽量全部完成
331
- - 添加 `core_scope`, 用于独立控制 `funnel` 的生命周期, 并用于 `TaskGraph.run/run_async` `TaskExecutor.run/run_async`
332
- - refactor:
333
- - [IMPORTANT] 原 `TaskGraph.start_graph/start_graph_async` `TaskExecutor.start/start_async` 已被重命名为 `TaskGraph.run/run_async` `TaskExecutor.run/run_async`
326
+ - [IMPORTANT] 移除 `stage` 中 `stage_mode`,并添加 `graph_mode`
334
327
  - 破坏性更新
335
- - 其底层缘由是为了合并 `TaskStage.start_stage/start_stage_async` `TaskExecutor.start/start_async` 而做出的一系列重构之一
336
- - 现在的 `TaskGraph.start/start_async` `TaskExecutor.start/start_async` 不再接受任务, 而是专注于处理现有任务列表中的任务
337
- - [IMPORTANT] 将funnel从 `TaskGraph` `TaskExecutor` 中的显性调用与显性传递, 改为所有使用端均从独立文件中import
338
- - 一来是因为原先的传递链太丑了, 二来是为了简化 `TaskExecutor.start/start_async` 逻辑, 为其与 `TaskStage.start_stage/start_stage_async` 的合并做准备
339
- - 移动部分文件以解决部分模块级的循环引用问题
340
- - 原先并非文件级循环引用, 在使用上并无问题
341
- - 修改 `TaskDispatch.dispatch_thread` 中处理已完成future的逻辑, 避免cpu浪费
342
- - 删除 `util_errors` 中一些不必要的错误类
343
- - 将 `TaskGraph._finalize_stages` 进行拆分, 删除不必要的机制, 将剩余机制移至其他方法
344
- - 修改 `TaskGraph.set_reporter` 的逻辑, 使其与 `set_ctree` 保持一致
345
- - `TaskOutQueue` 中的 `queue_list|target_name|_name_to_idx` 改为 `_queues`, 并删除 `put_channel`
346
- - 我有些困惑为什么最初我没有这么做
347
- - `TaskExecutor` 中合并 `_get_task_repr` and `_get_result_repr`
348
- - 这两个方法原先差异巨大, 但后来经过多次其他部分的重构, 现在逻辑已经一致
349
- - `TaskStage` 中的 `_status` 交给 `TaskMetrics` 维护
350
- - 依旧是为了合并 `TaskExecutor.start/start_async` 与 `TaskStage.start_stage/start_stage_async`
351
- - 移除 `TaskGraph` 中的 `put_stage_queue`
352
- - 任务输入完全使用 `TaskExecutor` 中的 `put_task` `put_signal` 方法
328
+ - `stage_mode` 可以细粒度的控制每个 `TaskStage` 在图中的模式,但经过多年使用,我认为这种细粒度的控制并无必要,反而无谓的增加理解成本
329
+ - `graph_mode` 则提供了更粗粒度的控制,用于统一控制所有 `TaskStage` 在图中是串行/多线程/并发运行,适用于大多数场景
330
+ - 同时完善了 `graph_mode`(serial/thread/async) * `execution_mode`(serial/thread/async) 总共 9 种组合模式
331
+ - 移除 `schedule_mode`
332
+ - 破坏性更新
333
+ - 这个模式带来了许多复杂度,但没有与原先的 `stage_mode / execution_mode` 产生明显的组合优势
334
+ - `get_graph_analysis` 中添加 `graph_mode`
335
+ - 这个函数主要用于给web端提供信息
336
+ - web端代码已经同步修改
337
+ - 添加新的 `warning` 项,当图不是 dag `graph_mode=serial` 时触发
338
+ - 移除 `task_executor` 中的参数 `persist_result`
339
+ - 这是为了简化代码逻辑,同时也使所有任务的最终状态(包括:成功/失败/未执行)都能被统一存储
340
+ - `task.retry` 事件降级,不再申请单独事件ID,不再在 `lifecycle` 中留痕
341
+ - reafactor:
342
+ - 合并 `benchmark` 相关函数对 sync async 两种模式的执行
343
+ - 性能无差异,代码好看一些
344
+ - 将原本的 `fallback` 改名为 `lifecycle`
345
+ - 早在重构原本的 `fail` 时就想起个更恰当的名字,这个版本才实现
346
+ - 将 `graph.source_lists` 改为 `graph.source_names`
347
+ - `graph.source_lists` 直接存储节点的引用,而 `graph.source_names` 存储节点的名称
348
+ - 所有的 `log` / `lifecycle` 文件统一改名为 `flow_log` / `flow_lifecycle`
349
+ - 删除 `graph` 中的 `out_edges` `in_edges`
350
+ - 现在由 `OrderGraph` 负责管理边关系
351
+ - 在 `tarjan_scc` 中使用 DFS 取代原先的递归逻辑
352
+ - 将 `util_graph` 改名为 `util_order_graph`
353
353
  - fix:
354
- - `TaskDispatch.worker/worker_async` 中添加错误捕捉, 捕捉为 `CRITICAL` 级错误
355
- - 避免 `worker` 级出错导致计数错误, 永远无法退出
356
- - 在 `spout` 与 `reporter` 的 `stop` 操作中添加对于线程没有成功 `join` 的报错
357
- - 修复 `TaskGraph.restore_db` 与 `TaskExecutor.restore_db` 中开启 `filter_by_error_type=Ture` 时跳过 `status=pending` 任务记录的问题
358
- - 修复 `TaskGraph` 中部分 `get_*` 方法依赖 `_build_analysis` 的产物, 但 `_build_analysis` 未执行导致的问题
359
- - 修复 `TaskMetrics` 中 `get_counts` 与 `is_tasks_finished` 中可能导致死锁的问题
360
- - 修复 `TaskReporter` 中 `stop` 里进行的最后一次 `_refresh_all` 没有错误捕捉, 导致后续收尾未完成的问题
354
+ - 修复最后一次 report 无法正常上传的问题
355
+ - 修复 `graph.run` / `graph.run_async` 中 `is_put_signal` 参数没有生效的问题
356
+ - chore:
357
+ - 更新文档
361
358
 
362
359
  更多过往日志可看:
363
360
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "celestialflow"
7
- version = "3.2.8"
7
+ version = "3.2.9"
8
8
  description = "A flexible GRAPH-based task orchestration framework."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1,129 @@
1
+ # benchmark/util_benchmark.py
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import time
6
+ from collections.abc import Iterable, Mapping
7
+ from typing import Any
8
+
9
+ from ..graph import TaskGraph
10
+ from ..runtime.util_format import format_table
11
+ from ..stage import TaskExecutor
12
+ from .util_clone import clone_executor, clone_graph
13
+
14
+ type AnyTaskExecutor = TaskExecutor[Any, Any]
15
+
16
+
17
+ async def benchmark_executor(
18
+ sync_executor: AnyTaskExecutor,
19
+ async_executor: AnyTaskExecutor,
20
+ task_source: Iterable[Any],
21
+ execution_modes: list[str] | None = None,
22
+ ) -> dict[str, Any]:
23
+ """
24
+ 对执行器进行基准测试
25
+
26
+ :param sync_executor: 同步执行器模板(用于 serial/thread execution_mode)
27
+ :param async_executor: 异步执行器模板(用于 async execution_mode)
28
+ :param task_source: 任务源,用于生成任务列表
29
+ :param execution_modes: 执行模式列表,默认 ["serial", "thread", "async"]
30
+ :return: 包含测试结果的字典
31
+ """
32
+ task_list: list[Any] = list(task_source)
33
+ execution_modes = execution_modes or ["serial", "thread", "async"]
34
+
35
+ use_time: list[list[float]] = []
36
+ for mode in execution_modes:
37
+ if mode == "async":
38
+ cloned_executor = clone_executor(async_executor)
39
+ else:
40
+ cloned_executor = clone_executor(sync_executor)
41
+ cloned_executor.set_execution_mode(mode)
42
+
43
+ start = time.perf_counter()
44
+ if mode == "async":
45
+ await cloned_executor.run_async(task_list)
46
+ else:
47
+ cloned_executor.run(task_list)
48
+ use_time.append([time.perf_counter() - start])
49
+
50
+ use_time_table: str = format_table(use_time, execution_modes, ["Time"])
51
+ print(f"Use time:\n{use_time_table}\n")
52
+
53
+ return {
54
+ "use_time": use_time,
55
+ "execution_modes": execution_modes,
56
+ "table": use_time_table,
57
+ }
58
+
59
+
60
+ async def benchmark_graph(
61
+ sync_graph: TaskGraph,
62
+ async_graph: TaskGraph,
63
+ init_tasks_dict: Mapping[str, Iterable[Any]],
64
+ graph_modes: list[str] | None = None,
65
+ execution_modes: list[str] | None = None,
66
+ ) -> dict[str, Any]:
67
+ """
68
+ 对任务图进行基准测试,覆盖 ``graph_mode × execution_mode`` 的全部组合。
69
+
70
+ - ``sync_graph`` 用于 ``execution_mode in {"serial", "thread"}`` 的单元格;
71
+ - ``async_graph`` 用于 ``execution_mode == "async"`` 的单元格;
72
+ - ``graph_mode`` 决定当前单元格使用 ``run()`` 还是 ``run_async()`` 启动。
73
+
74
+ :param sync_graph: 同步任务图模板(用于 serial/thread execution_mode)
75
+ :param async_graph: 异步任务图模板(用于 async execution_mode)
76
+ :param init_tasks_dict: 初始任务字典,键为任务标签,值为任务列表
77
+ :param graph_modes: 要测试的图执行模式列表,默认包括 "serial", "thread", "async"
78
+ :param execution_modes: 执行模式列表,默认 ["serial", "thread", "async"]
79
+ :return: 包含测试结果的字典
80
+ """
81
+ graph_modes = graph_modes or ["serial", "thread", "async"]
82
+ execution_modes = execution_modes or ["serial", "thread", "async"]
83
+
84
+ base_tasks: dict[str, list[Any]] = {
85
+ stage_name: list(tasks) for stage_name, tasks in init_tasks_dict.items()
86
+ }
87
+
88
+ test_table_list: list[list[float]] = []
89
+
90
+ for graph_mode in graph_modes:
91
+ time_list: list[float] = []
92
+
93
+ for execution_mode in execution_modes:
94
+ cloned_graph = (
95
+ clone_graph(async_graph)
96
+ if execution_mode == "async"
97
+ else clone_graph(sync_graph)
98
+ )
99
+ cloned_graph.set_graph_mode(graph_mode)
100
+ cloned_graph.set_stage_execution_mode(execution_mode)
101
+
102
+ run_tasks: dict[str, Iterable[Any]] = {
103
+ stage_name: list(tasks) for stage_name, tasks in base_tasks.items()
104
+ }
105
+ start_time = time.perf_counter()
106
+ if graph_mode == "async":
107
+ await cloned_graph.run_async(run_tasks)
108
+ else:
109
+ if execution_mode == "async":
110
+ await asyncio.to_thread(cloned_graph.run, run_tasks)
111
+ else:
112
+ cloned_graph.run(run_tasks)
113
+ time_list.append(time.perf_counter() - start_time)
114
+
115
+ test_table_list.append(time_list)
116
+
117
+ time_table: str = format_table(
118
+ test_table_list,
119
+ graph_modes,
120
+ execution_modes,
121
+ "graph/execution",
122
+ )
123
+ print(f"Time table:\n{time_table}")
124
+ return {
125
+ "use_time": test_table_list,
126
+ "table": time_table,
127
+ "graph_modes": graph_modes,
128
+ "execution_modes": execution_modes,
129
+ }
@@ -31,7 +31,6 @@ def _get_clone_init_kwargs[T, R](
31
31
  "max_retries": executor.max_retries,
32
32
  "max_info": executor.max_info,
33
33
  "enable_duplicate_check": executor.enable_duplicate_check,
34
- "persist_result": executor.persist_result,
35
34
  }
36
35
 
37
36
 
@@ -59,7 +58,6 @@ def clone_stage[T, R](
59
58
  :return: 克隆节点
60
59
  """
61
60
  kwargs: dict[str, Any] = _get_clone_init_kwargs(stage)
62
- kwargs["stage_mode"] = stage.get_stage_mode()
63
61
 
64
62
  stage_cls = type(stage)
65
63
  init_params = set(inspect.signature(stage_cls.__init__).parameters.keys()) - {
@@ -106,10 +104,12 @@ def clone_graph(graph: TaskGraph) -> TaskGraph:
106
104
  :param graph: 要克隆的任务图
107
105
  :return: 克隆任务图
108
106
  """
109
- # 通过广度优先遍历收集所有节点(沿用任务图出边表的顺序)
107
+ # 通过广度优先遍历收集所有节点(沿用任务图有序图的出边顺序)
110
108
  visited: set[str] = set()
111
109
  ordered_stages: list[AnyTaskStage] = []
112
- queue: deque[AnyTaskStage] = deque(graph.get_source_stages())
110
+ queue: deque[AnyTaskStage] = deque(
111
+ graph.stage_dict[source_name] for source_name in graph.get_source_names()
112
+ )
113
113
  while queue:
114
114
  stage: AnyTaskStage = queue.popleft()
115
115
  stage_name: str = stage.get_name()
@@ -117,7 +117,7 @@ def clone_graph(graph: TaskGraph) -> TaskGraph:
117
117
  continue
118
118
  visited.add(stage_name)
119
119
  ordered_stages.append(stage)
120
- for next_stage_name in graph.out_edges.get(stage_name, []):
120
+ for next_stage_name in graph.order_graph.out_edges.get(stage_name, []):
121
121
  next_stage: AnyTaskStage = graph.stage_dict[next_stage_name]
122
122
  queue.append(next_stage)
123
123
 
@@ -129,14 +129,11 @@ def clone_graph(graph: TaskGraph) -> TaskGraph:
129
129
  # 构建新的任务图
130
130
  all_cloned_stages: list[AnyTaskStage] = list(name_map.values())
131
131
 
132
- cloned_graph: TaskGraph = TaskGraph(
133
- name=graph.name,
134
- schedule_mode=graph.schedule_mode,
135
- )
132
+ cloned_graph: TaskGraph = TaskGraph(name=graph.name, graph_mode=graph.graph_mode)
136
133
  cloned_graph.set_stages(all_cloned_stages)
137
134
 
138
135
  # 重建连接
139
- for from_name, to_names in graph.out_edges.items():
136
+ for from_name, to_names in graph.order_graph.out_edges.items():
140
137
  if not to_names:
141
138
  continue
142
139
  cloned_from: AnyTaskStage = name_map[from_name]