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.
- dashboard/backend/app/__init__.py +1 -0
- dashboard/backend/app/config.py +32 -0
- dashboard/backend/app/controllers/__init__.py +6 -0
- dashboard/backend/app/controllers/run_controller.py +86 -0
- dashboard/backend/app/controllers/workflow_controller.py +33 -0
- dashboard/backend/app/dependencies/__init__.py +5 -0
- dashboard/backend/app/dependencies/storage.py +50 -0
- dashboard/backend/app/repositories/__init__.py +6 -0
- dashboard/backend/app/repositories/run_repository.py +80 -0
- dashboard/backend/app/repositories/workflow_repository.py +27 -0
- dashboard/backend/app/rest/__init__.py +8 -0
- dashboard/backend/app/rest/v1/__init__.py +12 -0
- dashboard/backend/app/rest/v1/health.py +33 -0
- dashboard/backend/app/rest/v1/runs.py +133 -0
- dashboard/backend/app/rest/v1/workflows.py +41 -0
- dashboard/backend/app/schemas/__init__.py +23 -0
- dashboard/backend/app/schemas/common.py +16 -0
- dashboard/backend/app/schemas/event.py +24 -0
- dashboard/backend/app/schemas/hook.py +25 -0
- dashboard/backend/app/schemas/run.py +54 -0
- dashboard/backend/app/schemas/step.py +28 -0
- dashboard/backend/app/schemas/workflow.py +31 -0
- dashboard/backend/app/server.py +87 -0
- dashboard/backend/app/services/__init__.py +6 -0
- dashboard/backend/app/services/run_service.py +240 -0
- dashboard/backend/app/services/workflow_service.py +155 -0
- dashboard/backend/main.py +18 -0
- docs/concepts/cancellation.mdx +362 -0
- docs/concepts/continue-as-new.mdx +434 -0
- docs/concepts/events.mdx +266 -0
- docs/concepts/fault-tolerance.mdx +370 -0
- docs/concepts/hooks.mdx +552 -0
- docs/concepts/limitations.mdx +167 -0
- docs/concepts/schedules.mdx +775 -0
- docs/concepts/sleep.mdx +312 -0
- docs/concepts/steps.mdx +301 -0
- docs/concepts/workflows.mdx +255 -0
- docs/guides/cli.mdx +942 -0
- docs/guides/configuration.mdx +560 -0
- docs/introduction.mdx +155 -0
- docs/quickstart.mdx +279 -0
- examples/__init__.py +1 -0
- examples/celery/__init__.py +1 -0
- examples/celery/durable/docker-compose.yml +55 -0
- examples/celery/durable/pyworkflow.config.yaml +12 -0
- examples/celery/durable/workflows/__init__.py +122 -0
- examples/celery/durable/workflows/basic.py +87 -0
- examples/celery/durable/workflows/batch_processing.py +102 -0
- examples/celery/durable/workflows/cancellation.py +273 -0
- examples/celery/durable/workflows/child_workflow_patterns.py +240 -0
- examples/celery/durable/workflows/child_workflows.py +202 -0
- examples/celery/durable/workflows/continue_as_new.py +260 -0
- examples/celery/durable/workflows/fault_tolerance.py +210 -0
- examples/celery/durable/workflows/hooks.py +211 -0
- examples/celery/durable/workflows/idempotency.py +112 -0
- examples/celery/durable/workflows/long_running.py +99 -0
- examples/celery/durable/workflows/retries.py +101 -0
- examples/celery/durable/workflows/schedules.py +209 -0
- examples/celery/transient/01_basic_workflow.py +91 -0
- examples/celery/transient/02_fault_tolerance.py +257 -0
- examples/celery/transient/__init__.py +20 -0
- examples/celery/transient/pyworkflow.config.yaml +25 -0
- examples/local/__init__.py +1 -0
- examples/local/durable/01_basic_workflow.py +94 -0
- examples/local/durable/02_file_storage.py +132 -0
- examples/local/durable/03_retries.py +169 -0
- examples/local/durable/04_long_running.py +119 -0
- examples/local/durable/05_event_log.py +145 -0
- examples/local/durable/06_idempotency.py +148 -0
- examples/local/durable/07_hooks.py +334 -0
- examples/local/durable/08_cancellation.py +233 -0
- examples/local/durable/09_child_workflows.py +198 -0
- examples/local/durable/10_child_workflow_patterns.py +265 -0
- examples/local/durable/11_continue_as_new.py +249 -0
- examples/local/durable/12_schedules.py +198 -0
- examples/local/durable/__init__.py +1 -0
- examples/local/transient/01_quick_tasks.py +87 -0
- examples/local/transient/02_retries.py +130 -0
- examples/local/transient/03_sleep.py +141 -0
- examples/local/transient/__init__.py +1 -0
- pyworkflow/__init__.py +256 -0
- pyworkflow/aws/__init__.py +68 -0
- pyworkflow/aws/context.py +234 -0
- pyworkflow/aws/handler.py +184 -0
- pyworkflow/aws/testing.py +310 -0
- pyworkflow/celery/__init__.py +41 -0
- pyworkflow/celery/app.py +198 -0
- pyworkflow/celery/scheduler.py +315 -0
- pyworkflow/celery/tasks.py +1746 -0
- pyworkflow/cli/__init__.py +132 -0
- pyworkflow/cli/__main__.py +6 -0
- pyworkflow/cli/commands/__init__.py +1 -0
- pyworkflow/cli/commands/hooks.py +640 -0
- pyworkflow/cli/commands/quickstart.py +495 -0
- pyworkflow/cli/commands/runs.py +773 -0
- pyworkflow/cli/commands/scheduler.py +130 -0
- pyworkflow/cli/commands/schedules.py +794 -0
- pyworkflow/cli/commands/setup.py +703 -0
- pyworkflow/cli/commands/worker.py +413 -0
- pyworkflow/cli/commands/workflows.py +1257 -0
- pyworkflow/cli/output/__init__.py +1 -0
- pyworkflow/cli/output/formatters.py +321 -0
- pyworkflow/cli/output/styles.py +121 -0
- pyworkflow/cli/utils/__init__.py +1 -0
- pyworkflow/cli/utils/async_helpers.py +30 -0
- pyworkflow/cli/utils/config.py +130 -0
- pyworkflow/cli/utils/config_generator.py +344 -0
- pyworkflow/cli/utils/discovery.py +53 -0
- pyworkflow/cli/utils/docker_manager.py +651 -0
- pyworkflow/cli/utils/interactive.py +364 -0
- pyworkflow/cli/utils/storage.py +115 -0
- pyworkflow/config.py +329 -0
- pyworkflow/context/__init__.py +63 -0
- pyworkflow/context/aws.py +230 -0
- pyworkflow/context/base.py +416 -0
- pyworkflow/context/local.py +930 -0
- pyworkflow/context/mock.py +381 -0
- pyworkflow/core/__init__.py +0 -0
- pyworkflow/core/exceptions.py +353 -0
- pyworkflow/core/registry.py +313 -0
- pyworkflow/core/scheduled.py +328 -0
- pyworkflow/core/step.py +494 -0
- pyworkflow/core/workflow.py +294 -0
- pyworkflow/discovery.py +248 -0
- pyworkflow/engine/__init__.py +0 -0
- pyworkflow/engine/events.py +879 -0
- pyworkflow/engine/executor.py +682 -0
- pyworkflow/engine/replay.py +273 -0
- pyworkflow/observability/__init__.py +19 -0
- pyworkflow/observability/logging.py +234 -0
- pyworkflow/primitives/__init__.py +33 -0
- pyworkflow/primitives/child_handle.py +174 -0
- pyworkflow/primitives/child_workflow.py +372 -0
- pyworkflow/primitives/continue_as_new.py +101 -0
- pyworkflow/primitives/define_hook.py +150 -0
- pyworkflow/primitives/hooks.py +97 -0
- pyworkflow/primitives/resume_hook.py +210 -0
- pyworkflow/primitives/schedule.py +545 -0
- pyworkflow/primitives/shield.py +96 -0
- pyworkflow/primitives/sleep.py +100 -0
- pyworkflow/runtime/__init__.py +21 -0
- pyworkflow/runtime/base.py +179 -0
- pyworkflow/runtime/celery.py +310 -0
- pyworkflow/runtime/factory.py +101 -0
- pyworkflow/runtime/local.py +706 -0
- pyworkflow/scheduler/__init__.py +9 -0
- pyworkflow/scheduler/local.py +248 -0
- pyworkflow/serialization/__init__.py +0 -0
- pyworkflow/serialization/decoder.py +146 -0
- pyworkflow/serialization/encoder.py +162 -0
- pyworkflow/storage/__init__.py +54 -0
- pyworkflow/storage/base.py +612 -0
- pyworkflow/storage/config.py +185 -0
- pyworkflow/storage/dynamodb.py +1315 -0
- pyworkflow/storage/file.py +827 -0
- pyworkflow/storage/memory.py +549 -0
- pyworkflow/storage/postgres.py +1161 -0
- pyworkflow/storage/schemas.py +486 -0
- pyworkflow/storage/sqlite.py +1136 -0
- pyworkflow/utils/__init__.py +0 -0
- pyworkflow/utils/duration.py +177 -0
- pyworkflow/utils/schedule.py +391 -0
- pyworkflow_engine-0.1.7.dist-info/METADATA +687 -0
- pyworkflow_engine-0.1.7.dist-info/RECORD +196 -0
- pyworkflow_engine-0.1.7.dist-info/WHEEL +5 -0
- pyworkflow_engine-0.1.7.dist-info/entry_points.txt +2 -0
- pyworkflow_engine-0.1.7.dist-info/licenses/LICENSE +21 -0
- pyworkflow_engine-0.1.7.dist-info/top_level.txt +5 -0
- tests/examples/__init__.py +0 -0
- tests/integration/__init__.py +0 -0
- tests/integration/test_cancellation.py +330 -0
- tests/integration/test_child_workflows.py +439 -0
- tests/integration/test_continue_as_new.py +428 -0
- tests/integration/test_dynamodb_storage.py +1146 -0
- tests/integration/test_fault_tolerance.py +369 -0
- tests/integration/test_schedule_storage.py +484 -0
- tests/unit/__init__.py +0 -0
- tests/unit/backends/__init__.py +1 -0
- tests/unit/backends/test_dynamodb_storage.py +1554 -0
- tests/unit/backends/test_postgres_storage.py +1281 -0
- tests/unit/backends/test_sqlite_storage.py +1460 -0
- tests/unit/conftest.py +41 -0
- tests/unit/test_cancellation.py +364 -0
- tests/unit/test_child_workflows.py +680 -0
- tests/unit/test_continue_as_new.py +441 -0
- tests/unit/test_event_limits.py +316 -0
- tests/unit/test_executor.py +320 -0
- tests/unit/test_fault_tolerance.py +334 -0
- tests/unit/test_hooks.py +495 -0
- tests/unit/test_registry.py +261 -0
- tests/unit/test_replay.py +420 -0
- tests/unit/test_schedule_schemas.py +285 -0
- tests/unit/test_schedule_utils.py +286 -0
- tests/unit/test_scheduled_workflow.py +274 -0
- tests/unit/test_step.py +353 -0
- tests/unit/test_workflow.py +243 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified storage backend configuration utilities.
|
|
3
|
+
|
|
4
|
+
This module provides functions to serialize storage backends to configuration
|
|
5
|
+
dicts and recreate storage backends from configuration dicts. This is used
|
|
6
|
+
for passing storage configuration to Celery tasks and other cross-process
|
|
7
|
+
communication.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pyworkflow.storage.base import StorageBackend
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def storage_to_config(storage: StorageBackend | None) -> dict[str, Any] | None:
|
|
16
|
+
"""
|
|
17
|
+
Serialize storage backend to configuration dict.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
storage: Storage backend instance
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Configuration dict or None if storage is None
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
>>> from pyworkflow.storage.file import FileStorageBackend
|
|
27
|
+
>>> storage = FileStorageBackend(base_path="./data")
|
|
28
|
+
>>> config = storage_to_config(storage)
|
|
29
|
+
>>> config
|
|
30
|
+
{'type': 'file', 'base_path': './data'}
|
|
31
|
+
"""
|
|
32
|
+
if storage is None:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
# Use class name to avoid import cycles
|
|
36
|
+
class_name = storage.__class__.__name__
|
|
37
|
+
|
|
38
|
+
if class_name == "FileStorageBackend":
|
|
39
|
+
return {
|
|
40
|
+
"type": "file",
|
|
41
|
+
"base_path": str(getattr(storage, "base_path", "./workflow_data")),
|
|
42
|
+
}
|
|
43
|
+
elif class_name == "InMemoryStorageBackend":
|
|
44
|
+
return {"type": "memory"}
|
|
45
|
+
elif class_name == "SQLiteStorageBackend":
|
|
46
|
+
return {
|
|
47
|
+
"type": "sqlite",
|
|
48
|
+
"base_path": str(getattr(storage, "db_path", "./pyworkflow_data/pyworkflow.db")),
|
|
49
|
+
}
|
|
50
|
+
elif class_name == "RedisStorageBackend":
|
|
51
|
+
return {
|
|
52
|
+
"type": "redis",
|
|
53
|
+
"host": getattr(storage, "host", "localhost"),
|
|
54
|
+
"port": getattr(storage, "port", 6379),
|
|
55
|
+
"db": getattr(storage, "db", 0),
|
|
56
|
+
}
|
|
57
|
+
elif class_name == "PostgresStorageBackend":
|
|
58
|
+
config: dict[str, Any] = {"type": "postgres"}
|
|
59
|
+
dsn = getattr(storage, "dsn", None)
|
|
60
|
+
if dsn:
|
|
61
|
+
config["dsn"] = dsn
|
|
62
|
+
else:
|
|
63
|
+
config["host"] = getattr(storage, "host", "localhost")
|
|
64
|
+
config["port"] = getattr(storage, "port", 5432)
|
|
65
|
+
config["user"] = getattr(storage, "user", "pyworkflow")
|
|
66
|
+
config["password"] = getattr(storage, "password", "")
|
|
67
|
+
config["database"] = getattr(storage, "database", "pyworkflow")
|
|
68
|
+
return config
|
|
69
|
+
elif class_name == "DynamoDBStorageBackend":
|
|
70
|
+
return {
|
|
71
|
+
"type": "dynamodb",
|
|
72
|
+
"table_name": getattr(storage, "table_name", "pyworkflow"),
|
|
73
|
+
"region": getattr(storage, "region", "us-east-1"),
|
|
74
|
+
"endpoint_url": getattr(storage, "endpoint_url", None),
|
|
75
|
+
}
|
|
76
|
+
else:
|
|
77
|
+
# Unknown backend - return minimal config
|
|
78
|
+
return {"type": "unknown"}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def config_to_storage(config: dict[str, Any] | None = None) -> StorageBackend:
|
|
82
|
+
"""
|
|
83
|
+
Create storage backend from configuration dict.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
config: Configuration dict with 'type' and backend-specific params.
|
|
87
|
+
If None, returns default FileStorageBackend.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Storage backend instance
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
ValueError: If storage type is unknown
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
>>> config = {"type": "file", "base_path": "./data"}
|
|
97
|
+
>>> storage = config_to_storage(config)
|
|
98
|
+
>>> isinstance(storage, FileStorageBackend)
|
|
99
|
+
True
|
|
100
|
+
"""
|
|
101
|
+
if not config:
|
|
102
|
+
from pyworkflow.storage.file import FileStorageBackend
|
|
103
|
+
|
|
104
|
+
return FileStorageBackend()
|
|
105
|
+
|
|
106
|
+
storage_type = config.get("type", "file")
|
|
107
|
+
|
|
108
|
+
if storage_type == "file":
|
|
109
|
+
from pyworkflow.storage.file import FileStorageBackend
|
|
110
|
+
|
|
111
|
+
base_path = config.get("base_path") or "./workflow_data"
|
|
112
|
+
return FileStorageBackend(base_path=base_path)
|
|
113
|
+
|
|
114
|
+
elif storage_type == "memory":
|
|
115
|
+
from pyworkflow.storage.memory import InMemoryStorageBackend
|
|
116
|
+
|
|
117
|
+
return InMemoryStorageBackend()
|
|
118
|
+
|
|
119
|
+
elif storage_type == "sqlite":
|
|
120
|
+
try:
|
|
121
|
+
from pyworkflow.storage.sqlite import SQLiteStorageBackend
|
|
122
|
+
except ImportError:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
"SQLite storage backend is not available. "
|
|
125
|
+
"Python was compiled without SQLite support (_sqlite3 module missing). "
|
|
126
|
+
"Please use 'file' or 'memory' storage instead, or rebuild Python with SQLite support."
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
db_path = config.get("base_path") or "./pyworkflow_data/pyworkflow.db"
|
|
130
|
+
return SQLiteStorageBackend(db_path=db_path)
|
|
131
|
+
|
|
132
|
+
elif storage_type == "redis":
|
|
133
|
+
try:
|
|
134
|
+
from pyworkflow.storage.redis import RedisStorageBackend
|
|
135
|
+
except ImportError:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
"Redis storage backend is not yet implemented. "
|
|
138
|
+
"Use 'file', 'sqlite', or 'postgres' storage. "
|
|
139
|
+
"Redis support is planned for a future release. "
|
|
140
|
+
"Note: Redis can still be used as a Celery broker with 'pip install pyworkflow[redis]'."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return RedisStorageBackend(
|
|
144
|
+
host=config.get("host", "localhost"),
|
|
145
|
+
port=config.get("port", 6379),
|
|
146
|
+
db=config.get("db", 0),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
elif storage_type == "postgres":
|
|
150
|
+
try:
|
|
151
|
+
from pyworkflow.storage.postgres import PostgresStorageBackend
|
|
152
|
+
except ImportError:
|
|
153
|
+
raise ValueError(
|
|
154
|
+
"PostgreSQL storage backend is not available. Install asyncpg: pip install asyncpg"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Support both DSN and individual parameters
|
|
158
|
+
if "dsn" in config:
|
|
159
|
+
return PostgresStorageBackend(dsn=config["dsn"])
|
|
160
|
+
else:
|
|
161
|
+
return PostgresStorageBackend(
|
|
162
|
+
host=config.get("host", "localhost"),
|
|
163
|
+
port=config.get("port", 5432),
|
|
164
|
+
user=config.get("user", "pyworkflow"),
|
|
165
|
+
password=config.get("password", ""),
|
|
166
|
+
database=config.get("database", "pyworkflow"),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
elif storage_type == "dynamodb":
|
|
170
|
+
try:
|
|
171
|
+
from pyworkflow.storage.dynamodb import DynamoDBStorageBackend
|
|
172
|
+
except ImportError:
|
|
173
|
+
raise ValueError(
|
|
174
|
+
"DynamoDB storage backend is not available. "
|
|
175
|
+
"Please install the required dependencies with: pip install 'pyworkflow[dynamodb]'"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return DynamoDBStorageBackend(
|
|
179
|
+
table_name=config.get("table_name", "pyworkflow"),
|
|
180
|
+
region=config.get("region", "us-east-1"),
|
|
181
|
+
endpoint_url=config.get("endpoint_url"),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
else:
|
|
185
|
+
raise ValueError(f"Unknown storage type: {storage_type}")
|