pyworkflow-engine 0.2.1__tar.gz → 0.2.3__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 (229) hide show
  1. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/PKG-INFO +1 -1
  2. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyproject.toml +1 -1
  3. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/__init__.py +1 -1
  4. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/celery/app.py +7 -23
  5. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/celery/tasks.py +22 -162
  6. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/core/step.py +3 -2
  7. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/CLAUDE.md +0 -0
  8. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/DISTRIBUTED.md +0 -0
  9. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/LICENSE +0 -0
  10. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/MANIFEST.in +0 -0
  11. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/README.md +0 -0
  12. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/RELEASING.md +0 -0
  13. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/architecture.md +0 -0
  14. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/cancellation.mdx +0 -0
  15. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/continue-as-new.mdx +0 -0
  16. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/events.mdx +0 -0
  17. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/fault-tolerance.mdx +0 -0
  18. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/hooks.mdx +0 -0
  19. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/limitations.mdx +0 -0
  20. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/schedules.mdx +0 -0
  21. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/sleep.mdx +0 -0
  22. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/step-context.mdx +0 -0
  23. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/steps.mdx +0 -0
  24. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/concepts/workflows.mdx +0 -0
  25. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/conventions.md +0 -0
  26. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/guides/brokers.mdx +0 -0
  27. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/guides/cli.mdx +0 -0
  28. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/guides/configuration.mdx +0 -0
  29. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/harness-gaps.md +0 -0
  30. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/introduction.mdx +0 -0
  31. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/layers.md +0 -0
  32. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/docs/quickstart.mdx +0 -0
  33. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/__init__.py +0 -0
  34. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/__init__.py +0 -0
  35. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/docker-compose.yml +0 -0
  36. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/pyworkflow.config.yaml +0 -0
  37. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/__init__.py +0 -0
  38. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/basic.py +0 -0
  39. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/batch_processing.py +0 -0
  40. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/cancellation.py +0 -0
  41. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/child_workflow_from_step.py +0 -0
  42. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/child_workflow_patterns.py +0 -0
  43. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/child_workflows.py +0 -0
  44. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/continue_as_new.py +0 -0
  45. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/fault_tolerance.py +0 -0
  46. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/hooks.py +0 -0
  47. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/idempotency.py +0 -0
  48. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/long_running.py +0 -0
  49. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/retries.py +0 -0
  50. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/schedules.py +0 -0
  51. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/sleep_in_step.py +0 -0
  52. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/durable/workflows/step_context.py +0 -0
  53. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/transient/01_basic_workflow.py +0 -0
  54. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/transient/02_fault_tolerance.py +0 -0
  55. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/transient/__init__.py +0 -0
  56. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/celery/transient/pyworkflow.config.yaml +0 -0
  57. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/__init__.py +0 -0
  58. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/01_basic_workflow.py +0 -0
  59. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/02_file_storage.py +0 -0
  60. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/03_retries.py +0 -0
  61. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/04_long_running.py +0 -0
  62. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/05_event_log.py +0 -0
  63. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/06_idempotency.py +0 -0
  64. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/07_hooks.py +0 -0
  65. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/08_cancellation.py +0 -0
  66. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/09_child_workflows.py +0 -0
  67. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/10_child_workflow_patterns.py +0 -0
  68. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/11_continue_as_new.py +0 -0
  69. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/12_schedules.py +0 -0
  70. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/13_step_context.py +0 -0
  71. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/14_child_workflow_from_step.py +0 -0
  72. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/durable/__init__.py +0 -0
  73. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/transient/01_quick_tasks.py +0 -0
  74. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/transient/02_retries.py +0 -0
  75. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/transient/03_sleep.py +0 -0
  76. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/examples/local/transient/__init__.py +0 -0
  77. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/aws/__init__.py +0 -0
  78. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/aws/context.py +0 -0
  79. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/aws/handler.py +0 -0
  80. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/aws/testing.py +0 -0
  81. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/celery/__init__.py +0 -0
  82. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/celery/loop.py +0 -0
  83. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/celery/scheduler.py +0 -0
  84. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/celery/singleton.py +0 -0
  85. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/__init__.py +0 -0
  86. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/__main__.py +0 -0
  87. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/__init__.py +0 -0
  88. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/hooks.py +0 -0
  89. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/quickstart.py +0 -0
  90. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/runs.py +0 -0
  91. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/scheduler.py +0 -0
  92. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/schedules.py +0 -0
  93. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/setup.py +0 -0
  94. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/worker.py +0 -0
  95. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/commands/workflows.py +0 -0
  96. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/output/__init__.py +0 -0
  97. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/output/formatters.py +0 -0
  98. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/output/styles.py +0 -0
  99. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/__init__.py +0 -0
  100. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/async_helpers.py +0 -0
  101. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/config.py +0 -0
  102. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/config_generator.py +0 -0
  103. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/discovery.py +0 -0
  104. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/docker_manager.py +0 -0
  105. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/interactive.py +0 -0
  106. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/cli/utils/storage.py +0 -0
  107. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/config.py +0 -0
  108. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/context/__init__.py +0 -0
  109. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/context/aws.py +0 -0
  110. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/context/base.py +0 -0
  111. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/context/local.py +0 -0
  112. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/context/mock.py +0 -0
  113. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/context/step_context.py +0 -0
  114. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/core/__init__.py +0 -0
  115. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/core/exceptions.py +0 -0
  116. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/core/registry.py +0 -0
  117. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/core/scheduled.py +0 -0
  118. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/core/validation.py +0 -0
  119. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/core/workflow.py +0 -0
  120. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/discovery.py +0 -0
  121. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/engine/__init__.py +0 -0
  122. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/engine/events.py +0 -0
  123. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/engine/executor.py +0 -0
  124. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/engine/replay.py +0 -0
  125. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/observability/__init__.py +0 -0
  126. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/observability/logging.py +0 -0
  127. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/__init__.py +0 -0
  128. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/child_handle.py +0 -0
  129. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/child_workflow.py +0 -0
  130. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/continue_as_new.py +0 -0
  131. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/define_hook.py +0 -0
  132. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/hooks.py +0 -0
  133. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/resume_hook.py +0 -0
  134. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/schedule.py +0 -0
  135. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/shield.py +0 -0
  136. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/sleep.py +0 -0
  137. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/step_checkpoint.py +0 -0
  138. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/primitives/step_hook.py +0 -0
  139. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/runtime/__init__.py +0 -0
  140. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/runtime/base.py +0 -0
  141. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/runtime/celery.py +0 -0
  142. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/runtime/factory.py +0 -0
  143. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/runtime/local.py +0 -0
  144. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/scheduler/__init__.py +0 -0
  145. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/scheduler/local.py +0 -0
  146. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/serialization/__init__.py +0 -0
  147. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/serialization/decoder.py +0 -0
  148. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/serialization/encoder.py +0 -0
  149. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/__init__.py +0 -0
  150. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/base.py +0 -0
  151. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/cassandra.py +0 -0
  152. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/citus.py +0 -0
  153. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/config.py +0 -0
  154. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/dynamodb.py +0 -0
  155. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/file.py +0 -0
  156. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/memory.py +0 -0
  157. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/migrations/__init__.py +0 -0
  158. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/migrations/base.py +0 -0
  159. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/mysql.py +0 -0
  160. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/postgres.py +0 -0
  161. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/schemas.py +0 -0
  162. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/storage/sqlite.py +0 -0
  163. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/__init__.py +0 -0
  164. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/checkpoint.py +0 -0
  165. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/consumer.py +0 -0
  166. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/context.py +0 -0
  167. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/decorator.py +0 -0
  168. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/dispatcher.py +0 -0
  169. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/emit.py +0 -0
  170. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/registry.py +0 -0
  171. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/signal.py +0 -0
  172. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/streams/step_context.py +0 -0
  173. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/utils/__init__.py +0 -0
  174. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/utils/duration.py +0 -0
  175. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/utils/helpers.py +0 -0
  176. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow/utils/schedule.py +0 -0
  177. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/pyworkflow_engine.egg-info/SOURCES.txt +0 -0
  178. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/setup.cfg +0 -0
  179. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/__init__.py +0 -0
  180. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_cancellation.py +0 -0
  181. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_cassandra_storage.py +0 -0
  182. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_child_workflows.py +0 -0
  183. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_continue_as_new.py +0 -0
  184. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_dynamodb_storage.py +0 -0
  185. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_fault_tolerance.py +0 -0
  186. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_schedule_storage.py +0 -0
  187. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_schema_migrations.py +0 -0
  188. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_singleton.py +0 -0
  189. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_stream_e2e.py +0 -0
  190. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/integration/test_workflow_suspended.py +0 -0
  191. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/__init__.py +0 -0
  192. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/backends/__init__.py +0 -0
  193. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/backends/test_cassandra_storage.py +0 -0
  194. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/backends/test_citus_storage.py +0 -0
  195. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/backends/test_dynamodb_storage.py +0 -0
  196. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/backends/test_postgres_storage.py +0 -0
  197. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/backends/test_sqlite_storage.py +0 -0
  198. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/conftest.py +0 -0
  199. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/storage/__init__.py +0 -0
  200. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/storage/test_migrations.py +0 -0
  201. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_cancellation.py +0 -0
  202. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_child_workflows.py +0 -0
  203. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_cli_worker.py +0 -0
  204. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_continue_as_new.py +0 -0
  205. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_emit.py +0 -0
  206. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_event_limits.py +0 -0
  207. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_executor.py +0 -0
  208. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_fault_tolerance.py +0 -0
  209. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_force_local.py +0 -0
  210. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_hooks.py +0 -0
  211. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_parent_run_id.py +0 -0
  212. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_primitives_from_steps.py +0 -0
  213. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_registry.py +0 -0
  214. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_replay.py +0 -0
  215. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_retention.py +0 -0
  216. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_schedule_schemas.py +0 -0
  217. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_schedule_utils.py +0 -0
  218. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_scheduled_workflow.py +0 -0
  219. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_signal.py +0 -0
  220. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_singleton.py +0 -0
  221. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_step.py +0 -0
  222. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_step_checkpoint.py +0 -0
  223. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_step_context.py +0 -0
  224. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_step_hook.py +0 -0
  225. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_stream_storage.py +0 -0
  226. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_stream_workflow.py +0 -0
  227. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_validation.py +0 -0
  228. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_workflow.py +0 -0
  229. {pyworkflow_engine-0.2.1 → pyworkflow_engine-0.2.3}/tests/unit/test_workflow_suspended.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyworkflow-engine
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: A Python implementation of durable, event-sourced workflows inspired by Vercel Workflow
5
5
  Author: PyWorkflow Contributors
6
6
  License: MIT
@@ -7,7 +7,7 @@ packages = [{include = "pyworkflow"}]
7
7
 
8
8
  [project]
9
9
  name = "pyworkflow-engine"
10
- version = "0.2.1"
10
+ version = "0.2.3"
11
11
  description = "A Python implementation of durable, event-sourced workflows inspired by Vercel Workflow"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.11"
@@ -29,7 +29,7 @@ Quick Start:
29
29
  >>> run_id = await start(my_workflow, "Alice")
30
30
  """
31
31
 
32
- __version__ = "0.2.1"
32
+ __version__ = "0.2.3"
33
33
 
34
34
  # Configuration
35
35
  from pyworkflow.config import (
@@ -304,33 +304,17 @@ def create_celery_app(
304
304
  # Broker transport options - prevent task redelivery
305
305
  # See: https://github.com/celery/celery/issues/5935
306
306
  "broker_transport_options": final_broker_opts,
307
- # Task routing
307
+ # Task routing — use direct exchange (each task specifies queue= explicitly)
308
308
  "task_default_queue": "pyworkflow.default",
309
309
  "task_default_exchange": "pyworkflow",
310
- "task_default_exchange_type": "topic",
311
- "task_default_routing_key": "workflow.default",
310
+ "task_default_exchange_type": "direct",
311
+ "task_default_routing_key": "pyworkflow.default",
312
312
  # Task queues
313
313
  "task_queues": (
314
- Queue(
315
- "pyworkflow.default",
316
- Exchange("pyworkflow", type="topic"),
317
- routing_key="workflow.#",
318
- ),
319
- Queue(
320
- "pyworkflow.steps",
321
- Exchange("pyworkflow", type="topic"),
322
- routing_key="workflow.step.#",
323
- ),
324
- Queue(
325
- "pyworkflow.workflows",
326
- Exchange("pyworkflow", type="topic"),
327
- routing_key="workflow.workflow.#",
328
- ),
329
- Queue(
330
- "pyworkflow.schedules",
331
- Exchange("pyworkflow", type="topic"),
332
- routing_key="workflow.schedule.#",
333
- ),
314
+ Queue("pyworkflow.default", Exchange("pyworkflow", type="direct")),
315
+ Queue("pyworkflow.steps", Exchange("pyworkflow", type="direct")),
316
+ Queue("pyworkflow.workflows", Exchange("pyworkflow", type="direct")),
317
+ Queue("pyworkflow.schedules", Exchange("pyworkflow", type="direct")),
334
318
  ),
335
319
  # Task execution
336
320
  "task_acks_late": True,
@@ -10,7 +10,6 @@ These tasks enable:
10
10
  """
11
11
 
12
12
  import asyncio
13
- import contextlib
14
13
  import random
15
14
  import uuid
16
15
  from collections.abc import Callable
@@ -186,51 +185,6 @@ def execute_step_task(
186
185
  )
187
186
  return None
188
187
 
189
- # Execution-time guard: prevent duplicate step execution on concurrent workers.
190
- # The singleton lock (unique_on=["run_id", "step_id"]) prevents duplicate queueing,
191
- # but broker-level re-delivery or race conditions can still result in two workers
192
- # executing the same step. This Redis lock ensures only one proceeds.
193
- _exec_lock_key = f"pyworkflow:step_exec:{run_id}:{step_id}"
194
- _exec_lock_backend = self.singleton_backend
195
- _exec_lock_acquired = False
196
- if _exec_lock_backend is not None:
197
- _exec_lock_acquired = _exec_lock_backend.lock(
198
- _exec_lock_key, self.request.id or "unknown", expiry=self._lock_expiry
199
- )
200
- if not _exec_lock_acquired:
201
- # Check for worker-loss re-delivery: when a worker dies mid-step
202
- # (e.g., spot instance termination), Celery re-delivers the same
203
- # message with the same task_id (task_reject_on_worker_lost=True).
204
- # The dead worker's lock still exists in Redis (up to 1h TTL).
205
- # If the lock holder matches our own task_id, this IS a re-delivery
206
- # of the same task — force-acquire and proceed.
207
- _holding_task_id = _exec_lock_backend.get(_exec_lock_key)
208
- _our_task_id = self.request.id or "unknown"
209
- if _holding_task_id == _our_task_id:
210
- logger.warning(
211
- "Step lock held by same task_id — worker-loss re-delivery detected, "
212
- "force-acquiring lock",
213
- run_id=run_id,
214
- step_id=step_id,
215
- step_name=step_name,
216
- task_id=_our_task_id,
217
- )
218
- _exec_lock_backend.unlock(_exec_lock_key)
219
- _exec_lock_acquired = _exec_lock_backend.lock(
220
- _exec_lock_key, _our_task_id, expiry=self._lock_expiry
221
- )
222
- else:
223
- logger.warning(
224
- "Step already being executed by another worker, skipping duplicate",
225
- run_id=run_id,
226
- step_id=step_id,
227
- step_name=step_name,
228
- existing_worker_task=_holding_task_id,
229
- )
230
- return None
231
- else:
232
- _exec_lock_acquired = True # No Redis = no locking
233
-
234
188
  # Deserialize arguments
235
189
  args = deserialize_args(args_json)
236
190
  kwargs = deserialize_kwargs(kwargs_json)
@@ -497,11 +451,6 @@ def execute_step_task(
497
451
  raise FatalError(f"Step '{step_name}' failed after retries: {str(e)}") from e
498
452
 
499
453
  finally:
500
- # Release execution lock so retries or other tasks can proceed.
501
- if _exec_lock_acquired and _exec_lock_backend is not None:
502
- with contextlib.suppress(Exception):
503
- _exec_lock_backend.unlock(_exec_lock_key)
504
-
505
454
  # Clean up step execution context (checkpoint/hook primitives)
506
455
  if step_exec_tokens is not None:
507
456
  from pyworkflow.primitives.step_checkpoint import reset_step_execution_context
@@ -886,83 +835,33 @@ def start_workflow_task(
886
835
  celery_task_id=self.request.id,
887
836
  )
888
837
 
889
- # Execution-time guard: prevent duplicate workflow start on concurrent workers.
890
- # The singleton lock (unique_on=["run_id"]) prevents duplicate queueing, but
891
- # broker-level re-delivery or race conditions can still result in two workers
892
- # executing the same start task. This Redis lock ensures only one proceeds.
893
- _exec_lock_key = f"pyworkflow:wf_start:{run_id}"
894
- _exec_lock_backend = self.singleton_backend
895
- _exec_lock_acquired = False
896
- _is_redelivery = False
897
- if _exec_lock_backend is not None:
898
- _exec_lock_acquired = _exec_lock_backend.lock(
899
- _exec_lock_key, self.request.id or "unknown", expiry=self._lock_expiry
900
- )
901
- if not _exec_lock_acquired:
902
- # Check for worker-loss re-delivery (same logic as step tasks):
903
- # if the lock holder matches our task_id, this is the same message
904
- # re-delivered after the original worker died.
905
- _holding_task_id = _exec_lock_backend.get(_exec_lock_key)
906
- _our_task_id = self.request.id or "unknown"
907
- if _holding_task_id == _our_task_id:
908
- logger.warning(
909
- "Workflow start lock held by same task_id — worker-loss re-delivery "
910
- "detected, force-acquiring lock",
911
- run_id=run_id,
912
- workflow_name=workflow_name,
913
- task_id=_our_task_id,
914
- )
915
- _exec_lock_backend.unlock(_exec_lock_key)
916
- _exec_lock_acquired = _exec_lock_backend.lock(
917
- _exec_lock_key, _our_task_id, expiry=self._lock_expiry
918
- )
919
- _is_redelivery = True
920
- else:
921
- logger.warning(
922
- "Workflow start already being executed by another worker, skipping duplicate",
923
- run_id=run_id,
924
- workflow_name=workflow_name,
925
- existing_worker_task=_holding_task_id,
926
- )
927
- return run_id
928
- else:
929
- _exec_lock_acquired = True # No Redis = no locking
930
-
931
- try:
932
- # Get workflow metadata
933
- workflow_meta = get_workflow(workflow_name)
934
- if not workflow_meta:
935
- raise ValueError(f"Workflow '{workflow_name}' not found in registry")
838
+ # Get workflow metadata
839
+ workflow_meta = get_workflow(workflow_name)
840
+ if not workflow_meta:
841
+ raise ValueError(f"Workflow '{workflow_name}' not found in registry")
936
842
 
937
- # Deserialize arguments
938
- args = deserialize_args(args_json)
939
- kwargs = deserialize_kwargs(kwargs_json)
843
+ # Deserialize arguments
844
+ args = deserialize_args(args_json)
845
+ kwargs = deserialize_kwargs(kwargs_json)
940
846
 
941
- # Get storage backend
942
- storage = _get_storage_backend(storage_config)
847
+ # Get storage backend
848
+ storage = _get_storage_backend(storage_config)
943
849
 
944
- # Execute workflow directly on worker
945
- result_run_id = run_async(
946
- _start_workflow_on_worker(
947
- workflow_meta=workflow_meta,
948
- args=args,
949
- kwargs=kwargs,
950
- storage=storage,
951
- storage_config=storage_config,
952
- idempotency_key=idempotency_key,
953
- run_id=run_id,
954
- is_redelivery=_is_redelivery,
955
- )
850
+ # Execute workflow directly on worker
851
+ result_run_id = run_async(
852
+ _start_workflow_on_worker(
853
+ workflow_meta=workflow_meta,
854
+ args=args,
855
+ kwargs=kwargs,
856
+ storage=storage,
857
+ storage_config=storage_config,
858
+ idempotency_key=idempotency_key,
859
+ run_id=run_id,
956
860
  )
861
+ )
957
862
 
958
- logger.info(f"Workflow execution initiated: {workflow_name}", run_id=result_run_id)
959
- return result_run_id
960
-
961
- finally:
962
- # Release execution lock so retries or re-dispatches can proceed.
963
- if _exec_lock_acquired and _exec_lock_backend is not None:
964
- with contextlib.suppress(Exception):
965
- _exec_lock_backend.unlock(_exec_lock_key)
863
+ logger.info(f"Workflow execution initiated: {workflow_name}", run_id=result_run_id)
864
+ return result_run_id
966
865
 
967
866
 
968
867
  @celery_app.task(
@@ -1631,7 +1530,6 @@ async def _start_workflow_on_worker(
1631
1530
  storage_config: dict[str, Any] | None = None,
1632
1531
  idempotency_key: str | None = None,
1633
1532
  run_id: str | None = None,
1634
- is_redelivery: bool = False,
1635
1533
  ) -> str:
1636
1534
  """
1637
1535
  Internal function to start workflow on Celery worker.
@@ -1647,10 +1545,6 @@ async def _start_workflow_on_worker(
1647
1545
  storage_config: Storage configuration for child tasks
1648
1546
  idempotency_key: Optional idempotency key
1649
1547
  run_id: Pre-generated run ID (if None, generates a new one)
1650
- is_redelivery: True when Celery re-delivered the same task_id after
1651
- worker loss. Re-deliveries bypass the recovery_attempts counter
1652
- since they represent the same logical execution, not an additional
1653
- recovery attempt.
1654
1548
  """
1655
1549
  from pyworkflow.config import get_config
1656
1550
 
@@ -1691,24 +1585,6 @@ async def _start_workflow_on_worker(
1691
1585
  )
1692
1586
  return existing_run.run_id
1693
1587
 
1694
- if is_redelivery:
1695
- # Celery re-delivered the same task after worker loss.
1696
- # This is NOT an extra recovery attempt — it is the same
1697
- # logical execution being retried by the broker. Skip the
1698
- # recovery-attempts counter and go straight to replay.
1699
- logger.info(
1700
- "Worker-loss re-delivery: recovering without counting "
1701
- "against max_recovery_attempts",
1702
- run_id=existing_run.run_id,
1703
- workflow_name=existing_run.workflow_name,
1704
- )
1705
- return await _recover_workflow_on_worker(
1706
- run=existing_run,
1707
- workflow_meta=workflow_meta,
1708
- storage=storage,
1709
- storage_config=storage_config,
1710
- )
1711
-
1712
1588
  # This is a recovery scenario - worker crashed while running
1713
1589
  can_recover = await _handle_workflow_recovery(
1714
1590
  run=existing_run,
@@ -1765,22 +1641,6 @@ async def _start_workflow_on_worker(
1765
1641
  )
1766
1642
 
1767
1643
  if existing_run.status == RunStatus.RUNNING:
1768
- if is_redelivery:
1769
- # Celery re-delivered the same task after worker loss.
1770
- # Skip recovery-attempts counter — same logical execution.
1771
- logger.info(
1772
- "Worker-loss re-delivery: recovering without counting "
1773
- "against max_recovery_attempts",
1774
- run_id=run_id,
1775
- workflow_name=existing_run.workflow_name,
1776
- )
1777
- return await _recover_workflow_on_worker(
1778
- run=existing_run,
1779
- workflow_meta=workflow_meta,
1780
- storage=storage,
1781
- storage_config=storage_config,
1782
- )
1783
-
1784
1644
  # Recovery scenario - worker crashed while running
1785
1645
  can_recover = await _handle_workflow_recovery(
1786
1646
  run=existing_run,
@@ -669,7 +669,8 @@ async def _dispatch_step_to_celery(
669
669
 
670
670
  # Defense-in-depth: check if STEP_STARTED was already recorded for this step.
671
671
  # This guards against duplicate dispatch when two resume tasks race and both
672
- # replay past the same step. If already started, re-suspend to wait.
672
+ # replay past the same step. If already started, re-suspend to wait for
673
+ # the running task to complete via task_reject_on_worker_lost re-delivery.
673
674
  # Exception: if STEP_SUSPENDED was recorded (step_hook suspension), the step
674
675
  # needs re-dispatch so it can re-execute and find the HOOK_RECEIVED event.
675
676
  events = await ctx.storage.get_events(ctx.run_id)
@@ -682,7 +683,7 @@ async def _dispatch_step_to_celery(
682
683
  )
683
684
  if already_started and not was_suspended:
684
685
  logger.info(
685
- f"Step {step_name} already has STEP_STARTED event, re-suspending",
686
+ f"Step {step_name} already has STEP_STARTED event, re-suspending (task running)",
686
687
  run_id=ctx.run_id,
687
688
  step_id=step_id,
688
689
  )