pyworkflow-engine 0.1.7__py3-none-any.whl

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 (196) hide show
  1. dashboard/backend/app/__init__.py +1 -0
  2. dashboard/backend/app/config.py +32 -0
  3. dashboard/backend/app/controllers/__init__.py +6 -0
  4. dashboard/backend/app/controllers/run_controller.py +86 -0
  5. dashboard/backend/app/controllers/workflow_controller.py +33 -0
  6. dashboard/backend/app/dependencies/__init__.py +5 -0
  7. dashboard/backend/app/dependencies/storage.py +50 -0
  8. dashboard/backend/app/repositories/__init__.py +6 -0
  9. dashboard/backend/app/repositories/run_repository.py +80 -0
  10. dashboard/backend/app/repositories/workflow_repository.py +27 -0
  11. dashboard/backend/app/rest/__init__.py +8 -0
  12. dashboard/backend/app/rest/v1/__init__.py +12 -0
  13. dashboard/backend/app/rest/v1/health.py +33 -0
  14. dashboard/backend/app/rest/v1/runs.py +133 -0
  15. dashboard/backend/app/rest/v1/workflows.py +41 -0
  16. dashboard/backend/app/schemas/__init__.py +23 -0
  17. dashboard/backend/app/schemas/common.py +16 -0
  18. dashboard/backend/app/schemas/event.py +24 -0
  19. dashboard/backend/app/schemas/hook.py +25 -0
  20. dashboard/backend/app/schemas/run.py +54 -0
  21. dashboard/backend/app/schemas/step.py +28 -0
  22. dashboard/backend/app/schemas/workflow.py +31 -0
  23. dashboard/backend/app/server.py +87 -0
  24. dashboard/backend/app/services/__init__.py +6 -0
  25. dashboard/backend/app/services/run_service.py +240 -0
  26. dashboard/backend/app/services/workflow_service.py +155 -0
  27. dashboard/backend/main.py +18 -0
  28. docs/concepts/cancellation.mdx +362 -0
  29. docs/concepts/continue-as-new.mdx +434 -0
  30. docs/concepts/events.mdx +266 -0
  31. docs/concepts/fault-tolerance.mdx +370 -0
  32. docs/concepts/hooks.mdx +552 -0
  33. docs/concepts/limitations.mdx +167 -0
  34. docs/concepts/schedules.mdx +775 -0
  35. docs/concepts/sleep.mdx +312 -0
  36. docs/concepts/steps.mdx +301 -0
  37. docs/concepts/workflows.mdx +255 -0
  38. docs/guides/cli.mdx +942 -0
  39. docs/guides/configuration.mdx +560 -0
  40. docs/introduction.mdx +155 -0
  41. docs/quickstart.mdx +279 -0
  42. examples/__init__.py +1 -0
  43. examples/celery/__init__.py +1 -0
  44. examples/celery/durable/docker-compose.yml +55 -0
  45. examples/celery/durable/pyworkflow.config.yaml +12 -0
  46. examples/celery/durable/workflows/__init__.py +122 -0
  47. examples/celery/durable/workflows/basic.py +87 -0
  48. examples/celery/durable/workflows/batch_processing.py +102 -0
  49. examples/celery/durable/workflows/cancellation.py +273 -0
  50. examples/celery/durable/workflows/child_workflow_patterns.py +240 -0
  51. examples/celery/durable/workflows/child_workflows.py +202 -0
  52. examples/celery/durable/workflows/continue_as_new.py +260 -0
  53. examples/celery/durable/workflows/fault_tolerance.py +210 -0
  54. examples/celery/durable/workflows/hooks.py +211 -0
  55. examples/celery/durable/workflows/idempotency.py +112 -0
  56. examples/celery/durable/workflows/long_running.py +99 -0
  57. examples/celery/durable/workflows/retries.py +101 -0
  58. examples/celery/durable/workflows/schedules.py +209 -0
  59. examples/celery/transient/01_basic_workflow.py +91 -0
  60. examples/celery/transient/02_fault_tolerance.py +257 -0
  61. examples/celery/transient/__init__.py +20 -0
  62. examples/celery/transient/pyworkflow.config.yaml +25 -0
  63. examples/local/__init__.py +1 -0
  64. examples/local/durable/01_basic_workflow.py +94 -0
  65. examples/local/durable/02_file_storage.py +132 -0
  66. examples/local/durable/03_retries.py +169 -0
  67. examples/local/durable/04_long_running.py +119 -0
  68. examples/local/durable/05_event_log.py +145 -0
  69. examples/local/durable/06_idempotency.py +148 -0
  70. examples/local/durable/07_hooks.py +334 -0
  71. examples/local/durable/08_cancellation.py +233 -0
  72. examples/local/durable/09_child_workflows.py +198 -0
  73. examples/local/durable/10_child_workflow_patterns.py +265 -0
  74. examples/local/durable/11_continue_as_new.py +249 -0
  75. examples/local/durable/12_schedules.py +198 -0
  76. examples/local/durable/__init__.py +1 -0
  77. examples/local/transient/01_quick_tasks.py +87 -0
  78. examples/local/transient/02_retries.py +130 -0
  79. examples/local/transient/03_sleep.py +141 -0
  80. examples/local/transient/__init__.py +1 -0
  81. pyworkflow/__init__.py +256 -0
  82. pyworkflow/aws/__init__.py +68 -0
  83. pyworkflow/aws/context.py +234 -0
  84. pyworkflow/aws/handler.py +184 -0
  85. pyworkflow/aws/testing.py +310 -0
  86. pyworkflow/celery/__init__.py +41 -0
  87. pyworkflow/celery/app.py +198 -0
  88. pyworkflow/celery/scheduler.py +315 -0
  89. pyworkflow/celery/tasks.py +1746 -0
  90. pyworkflow/cli/__init__.py +132 -0
  91. pyworkflow/cli/__main__.py +6 -0
  92. pyworkflow/cli/commands/__init__.py +1 -0
  93. pyworkflow/cli/commands/hooks.py +640 -0
  94. pyworkflow/cli/commands/quickstart.py +495 -0
  95. pyworkflow/cli/commands/runs.py +773 -0
  96. pyworkflow/cli/commands/scheduler.py +130 -0
  97. pyworkflow/cli/commands/schedules.py +794 -0
  98. pyworkflow/cli/commands/setup.py +703 -0
  99. pyworkflow/cli/commands/worker.py +413 -0
  100. pyworkflow/cli/commands/workflows.py +1257 -0
  101. pyworkflow/cli/output/__init__.py +1 -0
  102. pyworkflow/cli/output/formatters.py +321 -0
  103. pyworkflow/cli/output/styles.py +121 -0
  104. pyworkflow/cli/utils/__init__.py +1 -0
  105. pyworkflow/cli/utils/async_helpers.py +30 -0
  106. pyworkflow/cli/utils/config.py +130 -0
  107. pyworkflow/cli/utils/config_generator.py +344 -0
  108. pyworkflow/cli/utils/discovery.py +53 -0
  109. pyworkflow/cli/utils/docker_manager.py +651 -0
  110. pyworkflow/cli/utils/interactive.py +364 -0
  111. pyworkflow/cli/utils/storage.py +115 -0
  112. pyworkflow/config.py +329 -0
  113. pyworkflow/context/__init__.py +63 -0
  114. pyworkflow/context/aws.py +230 -0
  115. pyworkflow/context/base.py +416 -0
  116. pyworkflow/context/local.py +930 -0
  117. pyworkflow/context/mock.py +381 -0
  118. pyworkflow/core/__init__.py +0 -0
  119. pyworkflow/core/exceptions.py +353 -0
  120. pyworkflow/core/registry.py +313 -0
  121. pyworkflow/core/scheduled.py +328 -0
  122. pyworkflow/core/step.py +494 -0
  123. pyworkflow/core/workflow.py +294 -0
  124. pyworkflow/discovery.py +248 -0
  125. pyworkflow/engine/__init__.py +0 -0
  126. pyworkflow/engine/events.py +879 -0
  127. pyworkflow/engine/executor.py +682 -0
  128. pyworkflow/engine/replay.py +273 -0
  129. pyworkflow/observability/__init__.py +19 -0
  130. pyworkflow/observability/logging.py +234 -0
  131. pyworkflow/primitives/__init__.py +33 -0
  132. pyworkflow/primitives/child_handle.py +174 -0
  133. pyworkflow/primitives/child_workflow.py +372 -0
  134. pyworkflow/primitives/continue_as_new.py +101 -0
  135. pyworkflow/primitives/define_hook.py +150 -0
  136. pyworkflow/primitives/hooks.py +97 -0
  137. pyworkflow/primitives/resume_hook.py +210 -0
  138. pyworkflow/primitives/schedule.py +545 -0
  139. pyworkflow/primitives/shield.py +96 -0
  140. pyworkflow/primitives/sleep.py +100 -0
  141. pyworkflow/runtime/__init__.py +21 -0
  142. pyworkflow/runtime/base.py +179 -0
  143. pyworkflow/runtime/celery.py +310 -0
  144. pyworkflow/runtime/factory.py +101 -0
  145. pyworkflow/runtime/local.py +706 -0
  146. pyworkflow/scheduler/__init__.py +9 -0
  147. pyworkflow/scheduler/local.py +248 -0
  148. pyworkflow/serialization/__init__.py +0 -0
  149. pyworkflow/serialization/decoder.py +146 -0
  150. pyworkflow/serialization/encoder.py +162 -0
  151. pyworkflow/storage/__init__.py +54 -0
  152. pyworkflow/storage/base.py +612 -0
  153. pyworkflow/storage/config.py +185 -0
  154. pyworkflow/storage/dynamodb.py +1315 -0
  155. pyworkflow/storage/file.py +827 -0
  156. pyworkflow/storage/memory.py +549 -0
  157. pyworkflow/storage/postgres.py +1161 -0
  158. pyworkflow/storage/schemas.py +486 -0
  159. pyworkflow/storage/sqlite.py +1136 -0
  160. pyworkflow/utils/__init__.py +0 -0
  161. pyworkflow/utils/duration.py +177 -0
  162. pyworkflow/utils/schedule.py +391 -0
  163. pyworkflow_engine-0.1.7.dist-info/METADATA +687 -0
  164. pyworkflow_engine-0.1.7.dist-info/RECORD +196 -0
  165. pyworkflow_engine-0.1.7.dist-info/WHEEL +5 -0
  166. pyworkflow_engine-0.1.7.dist-info/entry_points.txt +2 -0
  167. pyworkflow_engine-0.1.7.dist-info/licenses/LICENSE +21 -0
  168. pyworkflow_engine-0.1.7.dist-info/top_level.txt +5 -0
  169. tests/examples/__init__.py +0 -0
  170. tests/integration/__init__.py +0 -0
  171. tests/integration/test_cancellation.py +330 -0
  172. tests/integration/test_child_workflows.py +439 -0
  173. tests/integration/test_continue_as_new.py +428 -0
  174. tests/integration/test_dynamodb_storage.py +1146 -0
  175. tests/integration/test_fault_tolerance.py +369 -0
  176. tests/integration/test_schedule_storage.py +484 -0
  177. tests/unit/__init__.py +0 -0
  178. tests/unit/backends/__init__.py +1 -0
  179. tests/unit/backends/test_dynamodb_storage.py +1554 -0
  180. tests/unit/backends/test_postgres_storage.py +1281 -0
  181. tests/unit/backends/test_sqlite_storage.py +1460 -0
  182. tests/unit/conftest.py +41 -0
  183. tests/unit/test_cancellation.py +364 -0
  184. tests/unit/test_child_workflows.py +680 -0
  185. tests/unit/test_continue_as_new.py +441 -0
  186. tests/unit/test_event_limits.py +316 -0
  187. tests/unit/test_executor.py +320 -0
  188. tests/unit/test_fault_tolerance.py +334 -0
  189. tests/unit/test_hooks.py +495 -0
  190. tests/unit/test_registry.py +261 -0
  191. tests/unit/test_replay.py +420 -0
  192. tests/unit/test_schedule_schemas.py +285 -0
  193. tests/unit/test_schedule_utils.py +286 -0
  194. tests/unit/test_scheduled_workflow.py +274 -0
  195. tests/unit/test_step.py +353 -0
  196. tests/unit/test_workflow.py +243 -0
@@ -0,0 +1,255 @@
1
+ ---
2
+ title: 'Workflows'
3
+ description: 'Top-level orchestration functions that coordinate steps and handle business logic'
4
+ ---
5
+
6
+ ## What is a Workflow?
7
+
8
+ A workflow is the top-level orchestration function that coordinates multiple steps, handles business logic, and can pause for extended periods using sleep or webhooks. Workflows are the entry point for your business processes.
9
+
10
+ ```python
11
+ from pyworkflow import workflow, step, start, sleep
12
+
13
+ @workflow()
14
+ async def order_processing(order_id: str):
15
+ # Validate and process the order
16
+ order = await validate_order(order_id)
17
+ payment = await process_payment(order)
18
+
19
+ # Wait for fulfillment
20
+ await sleep("2h")
21
+
22
+ # Ship and notify
23
+ await create_shipment(order)
24
+ await send_confirmation(order)
25
+
26
+ return {"status": "completed", "order_id": order_id}
27
+ ```
28
+
29
+ ## Key Characteristics
30
+
31
+ <CardGroup cols={2}>
32
+ <Card title="Durable" icon="database">
33
+ Workflows survive crashes, restarts, and deployments. State is preserved through event sourcing.
34
+ </Card>
35
+ <Card title="Suspendable" icon="pause">
36
+ Workflows can pause for minutes, hours, or days without consuming resources.
37
+ </Card>
38
+ <Card title="Distributed" icon="server">
39
+ Workflows execute across Celery workers, enabling horizontal scaling.
40
+ </Card>
41
+ <Card title="Deterministic" icon="rotate">
42
+ Workflows can be replayed from any point using the recorded event log.
43
+ </Card>
44
+ </CardGroup>
45
+
46
+ ## Creating Workflows
47
+
48
+ <Tabs>
49
+ <Tab title="Decorator">
50
+ ```python
51
+ from pyworkflow import workflow
52
+
53
+ @workflow()
54
+ async def my_workflow(user_id: str, amount: float):
55
+ # Your workflow logic here
56
+ result = await some_step(user_id, amount)
57
+ return result
58
+ ```
59
+ </Tab>
60
+ <Tab title="Class">
61
+ ```python
62
+ from pyworkflow import Workflow
63
+
64
+ class MyWorkflow(Workflow):
65
+ async def run(self, user_id: str, amount: float):
66
+ result = await SomeStep()(user_id, amount)
67
+ return result
68
+ ```
69
+ </Tab>
70
+ </Tabs>
71
+
72
+ ### Configuration Options
73
+
74
+ <Tabs>
75
+ <Tab title="Decorator">
76
+ ```python
77
+ @workflow(
78
+ name="custom_workflow_name", # Override the function name
79
+ max_duration="24h", # Maximum workflow runtime
80
+ max_retries=3 # Retry the entire workflow on failure
81
+ )
82
+ async def my_workflow():
83
+ pass
84
+ ```
85
+ </Tab>
86
+ <Tab title="Class">
87
+ ```python
88
+ class MyWorkflow(Workflow):
89
+ name = "custom_workflow_name"
90
+ max_duration = "24h"
91
+ max_retries = 3
92
+
93
+ async def run(self):
94
+ pass
95
+ ```
96
+ </Tab>
97
+ </Tabs>
98
+
99
+ | Option | Type | Default | Description |
100
+ |--------|------|---------|-------------|
101
+ | `name` | `str` | Function/class name | Unique identifier for the workflow |
102
+ | `max_duration` | `str` | `"7d"` | Maximum time the workflow can run |
103
+ | `max_retries` | `int` | `0` | Number of times to retry the entire workflow |
104
+
105
+ ## Starting Workflows
106
+
107
+ ### Synchronous Start
108
+
109
+ The `start()` function dispatches a workflow to Celery and returns immediately:
110
+
111
+ ```python
112
+ from pyworkflow import start
113
+
114
+ # Start the workflow (non-blocking)
115
+ run_id = start(order_processing, order_id="ORD-123")
116
+ print(f"Workflow started with ID: {run_id}")
117
+ ```
118
+
119
+ ### With Idempotency Key
120
+
121
+ Prevent duplicate workflow executions:
122
+
123
+ ```python
124
+ run_id = start(
125
+ order_processing,
126
+ order_id="ORD-123",
127
+ idempotency_key="order-ORD-123"
128
+ )
129
+
130
+ # Calling again with same key returns the same run_id
131
+ run_id_2 = start(
132
+ order_processing,
133
+ order_id="ORD-123",
134
+ idempotency_key="order-ORD-123"
135
+ )
136
+
137
+ assert run_id == run_id_2 # True - same workflow
138
+ ```
139
+
140
+ ## Workflow Lifecycle
141
+
142
+ ```
143
+ ┌─────────────┐
144
+ │ PENDING │ Workflow created, waiting to start
145
+ └──────┬──────┘
146
+
147
+
148
+ ┌─────────────┐
149
+ │ RUNNING │ Workflow is executing
150
+ └──────┬──────┘
151
+
152
+ ├────────────────┬────────────────┐
153
+ │ │ │
154
+ ▼ ▼ ▼
155
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
156
+ │ SUSPENDED │ │ INTERRUPTED │ │ FAILED │
157
+ │ │ │(worker crash)│ │ │
158
+ └──────┬──────┘ └──────┬──────┘ └─────────────┘
159
+ │ │
160
+ │ (sleep ends) │ (auto recovery)
161
+ ▼ ▼
162
+ ┌─────────────┐ ┌─────────────┐
163
+ │ RUNNING │ │ RUNNING │ Recovered
164
+ └──────┬──────┘ └──────┬──────┘
165
+ │ │
166
+ └────────┬───────┘
167
+
168
+
169
+ ┌─────────────┐
170
+ │ COMPLETED │ Workflow finished successfully
171
+ └─────────────┘
172
+ ```
173
+
174
+ <Note>
175
+ When a worker crashes, the workflow enters `INTERRUPTED` status and automatically recovers on another worker. See [Fault Tolerance](/concepts/fault-tolerance) for details.
176
+ </Note>
177
+
178
+ ## Workflow Context
179
+
180
+ Inside a workflow, you can access the execution context:
181
+
182
+ ```python
183
+ from pyworkflow import workflow, get_context
184
+
185
+ @workflow()
186
+ async def my_workflow():
187
+ ctx = get_context()
188
+
189
+ print(f"Run ID: {ctx.run_id}")
190
+ print(f"Workflow: {ctx.workflow_name}")
191
+
192
+ # Access step results from replay
193
+ previous_result = ctx.step_results.get("step_id")
194
+ ```
195
+
196
+ ## Error Handling
197
+
198
+ Workflows automatically handle errors based on their type:
199
+
200
+ ```python
201
+ from pyworkflow import workflow, FatalError, RetryableError
202
+
203
+ @workflow(max_retries=3)
204
+ async def my_workflow():
205
+ try:
206
+ result = await risky_operation()
207
+ return result
208
+ except ValidationError as e:
209
+ # Fatal errors stop the workflow immediately
210
+ raise FatalError(f"Invalid input: {e}")
211
+ except TemporaryError as e:
212
+ # Retryable errors trigger workflow retry
213
+ raise RetryableError(f"Temporary failure: {e}")
214
+ ```
215
+
216
+ ## Best Practices
217
+
218
+ <AccordionGroup>
219
+ <Accordion title="Keep workflows focused">
220
+ Each workflow should handle a single business process. If a workflow is getting complex, consider breaking it into smaller workflows that call each other.
221
+ </Accordion>
222
+
223
+ <Accordion title="Use meaningful names">
224
+ Workflow names should clearly describe their purpose: `process_order`, `onboard_user`, `send_notification_sequence`.
225
+ </Accordion>
226
+
227
+ <Accordion title="Handle idempotency">
228
+ Use idempotency keys for workflows that shouldn't run twice for the same input. This prevents duplicate processing during retries.
229
+ </Accordion>
230
+
231
+ <Accordion title="Set appropriate timeouts">
232
+ Use `max_duration` to prevent workflows from running indefinitely. Consider the longest possible execution path.
233
+ </Accordion>
234
+
235
+ <Accordion title="Configure fault tolerance">
236
+ Enable `recover_on_worker_loss` for critical workflows to ensure automatic recovery from worker crashes. See [Fault Tolerance](/concepts/fault-tolerance) for configuration options.
237
+ </Accordion>
238
+ </AccordionGroup>
239
+
240
+ ## Next Steps
241
+
242
+ <CardGroup cols={2}>
243
+ <Card title="Steps" icon="stairs" href="/concepts/steps">
244
+ Learn about steps - the building blocks of workflows.
245
+ </Card>
246
+ <Card title="Schedules" icon="calendar" href="/concepts/schedules">
247
+ Automatically run workflows on cron, interval, or calendar schedules.
248
+ </Card>
249
+ <Card title="Sleep" icon="clock" href="/concepts/sleep">
250
+ Pause workflows for any duration without consuming resources.
251
+ </Card>
252
+ <Card title="Fault Tolerance" icon="shield-check" href="/concepts/fault-tolerance">
253
+ Configure auto recovery from worker crashes.
254
+ </Card>
255
+ </CardGroup>