loom-core 0.1.0__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 (58) hide show
  1. loom_core-0.1.0/LICENSE +21 -0
  2. loom_core-0.1.0/MANIFEST.in +8 -0
  3. loom_core-0.1.0/PKG-INFO +342 -0
  4. loom_core-0.1.0/QUICKSTART.md +147 -0
  5. loom_core-0.1.0/README.md +303 -0
  6. loom_core-0.1.0/loom_core.egg-info/PKG-INFO +342 -0
  7. loom_core-0.1.0/loom_core.egg-info/SOURCES.txt +56 -0
  8. loom_core-0.1.0/loom_core.egg-info/dependency_links.txt +1 -0
  9. loom_core-0.1.0/loom_core.egg-info/entry_points.txt +2 -0
  10. loom_core-0.1.0/loom_core.egg-info/requires.txt +12 -0
  11. loom_core-0.1.0/loom_core.egg-info/top_level.txt +1 -0
  12. loom_core-0.1.0/pyproject.toml +88 -0
  13. loom_core-0.1.0/setup.cfg +4 -0
  14. loom_core-0.1.0/setup.py +52 -0
  15. loom_core-0.1.0/src/__init__.py +45 -0
  16. loom_core-0.1.0/src/cli/__init__.py +5 -0
  17. loom_core-0.1.0/src/cli/cli.py +246 -0
  18. loom_core-0.1.0/src/common/activity.py +30 -0
  19. loom_core-0.1.0/src/common/config.py +9 -0
  20. loom_core-0.1.0/src/common/errors.py +64 -0
  21. loom_core-0.1.0/src/common/workflow.py +56 -0
  22. loom_core-0.1.0/src/core/__init__.py +0 -0
  23. loom_core-0.1.0/src/core/compiled.py +41 -0
  24. loom_core-0.1.0/src/core/context.py +256 -0
  25. loom_core-0.1.0/src/core/engine.py +106 -0
  26. loom_core-0.1.0/src/core/handle.py +166 -0
  27. loom_core-0.1.0/src/core/logger.py +60 -0
  28. loom_core-0.1.0/src/core/runner.py +53 -0
  29. loom_core-0.1.0/src/core/state.py +96 -0
  30. loom_core-0.1.0/src/core/worker.py +147 -0
  31. loom_core-0.1.0/src/core/workflow.py +168 -0
  32. loom_core-0.1.0/src/database/__init__.py +0 -0
  33. loom_core-0.1.0/src/database/db.py +716 -0
  34. loom_core-0.1.0/src/decorators/__init__.py +0 -0
  35. loom_core-0.1.0/src/decorators/activity.py +126 -0
  36. loom_core-0.1.0/src/decorators/workflow.py +46 -0
  37. loom_core-0.1.0/src/lib/progress.py +109 -0
  38. loom_core-0.1.0/src/lib/utils.py +25 -0
  39. loom_core-0.1.0/src/migrations/down/001_setup_pragma.sql +5 -0
  40. loom_core-0.1.0/src/migrations/down/002_create_workflows.sql +3 -0
  41. loom_core-0.1.0/src/migrations/down/003.create_events.sql +3 -0
  42. loom_core-0.1.0/src/migrations/down/004.create_tasks.sql +3 -0
  43. loom_core-0.1.0/src/migrations/down/005.create_indexes.sql +5 -0
  44. loom_core-0.1.0/src/migrations/down/006_auto_update_triggers.sql +4 -0
  45. loom_core-0.1.0/src/migrations/down/007_create_logs.sql +1 -0
  46. loom_core-0.1.0/src/migrations/up/001_setup_pragma.sql +11 -0
  47. loom_core-0.1.0/src/migrations/up/002_create_workflows.sql +15 -0
  48. loom_core-0.1.0/src/migrations/up/003_create_events.sql +13 -0
  49. loom_core-0.1.0/src/migrations/up/004_create_tasks.sql +23 -0
  50. loom_core-0.1.0/src/migrations/up/005_create_indexes.sql +11 -0
  51. loom_core-0.1.0/src/migrations/up/006_auto_update_triggers.sql +19 -0
  52. loom_core-0.1.0/src/migrations/up/007_create_logs.sql +10 -0
  53. loom_core-0.1.0/src/schemas/__init__.py +0 -0
  54. loom_core-0.1.0/src/schemas/activity.py +13 -0
  55. loom_core-0.1.0/src/schemas/database.py +17 -0
  56. loom_core-0.1.0/src/schemas/events.py +70 -0
  57. loom_core-0.1.0/src/schemas/tasks.py +58 -0
  58. loom_core-0.1.0/src/schemas/workflow.py +33 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Loom Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ include README.md
2
+ include QUICKSTART.md
3
+ include LICENSE
4
+ recursive-include src/migrations *.sql
5
+ recursive-exclude tests *
6
+ recursive-exclude examples *
7
+ recursive-exclude __pycache__ *
8
+ recursive-exclude *.pyc *
@@ -0,0 +1,342 @@
1
+ Metadata-Version: 2.4
2
+ Name: loom-core
3
+ Version: 0.1.0
4
+ Summary: Durable workflow orchestration engine for Python
5
+ Home-page: https://github.com/satadeep3927/loom
6
+ Author: Satadeep Dasgupta
7
+ Author-email: Satadeep Dasgupta <satadeep.dasgupta@brainiuminfotech.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/satadeep3927/loom
10
+ Project-URL: Documentation, https://github.com/satadeep3927/loom/blob/main/README.md
11
+ Project-URL: Repository, https://github.com/satadeep3927/loom
12
+ Project-URL: Issues, https://github.com/satadeep3927/loom/issues
13
+ Keywords: workflow,orchestration,durable,event-sourcing,temporal
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Framework :: AsyncIO
21
+ Requires-Python: >=3.12
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: aiosqlite>=0.19.0
25
+ Requires-Dist: click>=8.0.0
26
+ Requires-Dist: rich>=13.0.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
30
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
31
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
32
+ Requires-Dist: black>=23.0.0; extra == "dev"
33
+ Requires-Dist: isort>=5.12.0; extra == "dev"
34
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
35
+ Dynamic: author
36
+ Dynamic: home-page
37
+ Dynamic: license-file
38
+ Dynamic: requires-python
39
+
40
+ # Loom - Durable Workflow Orchestration
41
+
42
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
43
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
44
+ [![PyPI](https://img.shields.io/badge/pypi-loom--core-blue)](https://pypi.org/project/loom-core/)
45
+
46
+ A Python-based durable workflow orchestration engine inspired by [Temporal](https://temporal.io/) and [Durable Task Framework](https://github.com/Azure/durabletask). Loom provides event-sourced, deterministic workflow execution with automatic recovery and replay capabilities.
47
+
48
+ ## Features
49
+
50
+ - **Event Sourcing**: All workflow state changes persisted as immutable events
51
+ - **Deterministic Replay**: Workflows reconstruct from event history for recovery
52
+ - **Type Safe**: Full generic typing support with `Workflow[InputT, StateT]`
53
+ - **Async First**: Built on asyncio for high-performance concurrent execution
54
+ - **Durable Execution**: Workflows survive process crashes and auto-recover
55
+ - **Beautiful CLI**: Rich console interface with progress tracking
56
+ - **Well Tested**: Comprehensive test suite with pytest
57
+
58
+ ## Quick Start
59
+
60
+ ### Installation
61
+
62
+ ```bash
63
+ pip install loom-core
64
+ ```
65
+
66
+ Or install from source:
67
+
68
+ ```bash
69
+ git clone https://github.com/yourusername/loom.git
70
+ cd loom
71
+ pip install -e .
72
+ ```
73
+
74
+ ### Define a Workflow
75
+
76
+ ```python
77
+ import asyncio
78
+ from typing import TypedDict
79
+ import loom
80
+
81
+
82
+ # Define your data types
83
+ class OrderInput(TypedDict):
84
+ order_id: str
85
+ customer_email: str
86
+
87
+
88
+ class OrderState(TypedDict):
89
+ payment_confirmed: bool
90
+ email_sent: bool
91
+
92
+
93
+ # Define activities (side effects)
94
+ @loom.activity(name="process_payment", retry_count=3, timeout_seconds=30)
95
+ async def process_payment(order_id: str) -> bool:
96
+ # Call payment API
97
+ return True
98
+
99
+
100
+ @loom.activity(name="send_email", retry_count=2)
101
+ async def send_confirmation_email(email: str, order_id: str) -> None:
102
+ # Send email via service
103
+ pass
104
+
105
+
106
+ # Define workflow
107
+ @loom.workflow(name="OrderProcessing", version="1.0.0")
108
+ class OrderWorkflow(loom.Workflow[OrderInput, OrderState]):
109
+
110
+ @loom.step(name="process_payment")
111
+ async def payment_step(self, ctx: loom.WorkflowContext[OrderInput, OrderState]):
112
+ success = await ctx.activity(process_payment, ctx.input["order_id"])
113
+ await ctx.state.set("payment_confirmed", success)
114
+ ctx.logger.info(f"Payment processed: {success}")
115
+
116
+ @loom.step(name="send_confirmation")
117
+ async def notification_step(self, ctx: loom.WorkflowContext[OrderInput, OrderState]):
118
+ if ctx.state["payment_confirmed"]:
119
+ await ctx.activity(
120
+ send_confirmation_email,
121
+ ctx.input["customer_email"],
122
+ ctx.input["order_id"]
123
+ )
124
+ await ctx.state.set("email_sent", True)
125
+ ctx.logger.info("Confirmation email sent")
126
+ ```
127
+
128
+ **Note**: For state updates, use:
129
+ - `await ctx.state.set("key", value)` for single values
130
+ - `await ctx.state.update(key=lambda _: asyncio.sleep(0, value))` for batch updates (requires awaitable)
131
+
132
+ See [STATE_MANAGEMENT.md](STATE_MANAGEMENT.md) for detailed examples.
133
+
134
+ ### Start a Workflow
135
+
136
+ ```python
137
+ async def main():
138
+ db = loom.Database()
139
+ async with db:
140
+ # Initialize database
141
+ await db.migrate_up()
142
+
143
+ # Start workflow
144
+ handle = await db.start_workflow(
145
+ OrderWorkflow,
146
+ workflow_input=OrderInput(
147
+ order_id="ORD-12345",
148
+ customer_email="customer@example.com"
149
+ ),
150
+ initial_state=OrderState(
151
+ payment_confirmed=False,
152
+ email_sent=False
153
+ ),
154
+ )
155
+
156
+ print(f"Workflow started: {handle.workflow_id}")
157
+
158
+ # Execute workflow tasks
159
+ while True:
160
+ task_executed = await loom.run_once()
161
+ if not task_executed:
162
+ break
163
+
164
+
165
+ if __name__ == "__main__":
166
+ asyncio.run(main())
167
+ ```
168
+
169
+ ### Run the Worker
170
+
171
+ ```bash
172
+ # Initialize database
173
+ loom init
174
+
175
+ # Start worker with 4 concurrent task processors
176
+ loom worker
177
+
178
+ # Custom configuration
179
+ loom worker --workers 8 --poll-interval 1.0
180
+ ```
181
+
182
+ ## CLI Commands
183
+
184
+ ```bash
185
+ # Initialize database
186
+ loom init
187
+
188
+ # Start distributed worker
189
+ loom worker [--workers 4] [--poll-interval 0.5]
190
+
191
+ # List workflows
192
+ loom list [--limit 50] [--status RUNNING]
193
+
194
+ # Inspect workflow details
195
+ loom inspect <workflow-id> [--events]
196
+
197
+ # Show database statistics
198
+ loom stats
199
+ ```
200
+
201
+ ## 🏗️ Architecture
202
+
203
+ ### Core Components
204
+
205
+ ```
206
+ ┌─────────────────────────────────────────────────────────┐
207
+ │ Workflow │
208
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
209
+ │ │ Step 1 │→ │ Step 2 │→ │ Step 3 │ │
210
+ │ └──────────┘ └──────────┘ └──────────┘ │
211
+ └─────────────────────────────────────────────────────────┘
212
+
213
+ ┌─────────────────────────────────────────────────────────┐
214
+ │ WorkflowContext │
215
+ │ • State Management (StateProxy) │
216
+ │ • Activity Execution │
217
+ │ • Event Replay & Cursor │
218
+ │ • Logger (replay-safe) │
219
+ └─────────────────────────────────────────────────────────┘
220
+
221
+ ┌─────────────────────────────────────────────────────────┐
222
+ │ Engine │
223
+ │ • replay_until_block() - Step execution │
224
+ │ • replay_activity() - Activity retry │
225
+ │ • Event matching & determinism │
226
+ └─────────────────────────────────────────────────────────┘
227
+
228
+ ┌─────────────────────────────────────────────────────────┐
229
+ │ Database (SQLite) │
230
+ │ • workflows • events • tasks • logs │
231
+ └─────────────────────────────────────────────────────────┘
232
+ ```
233
+
234
+ ### Event Types
235
+
236
+ - `WORKFLOW_STARTED` - Workflow initialization
237
+ - `WORKFLOW_COMPLETED` - Successful completion
238
+ - `WORKFLOW_FAILED` - Fatal error occurred
239
+ - `STATE_SET` - Single state key updated
240
+ - `STATE_UPDATE` - Batch state update
241
+ - `ACTIVITY_SCHEDULED` - Activity queued for execution
242
+ - `ACTIVITY_COMPLETED` - Activity finished successfully
243
+ - `ACTIVITY_FAILED` - Activity permanently failed
244
+ - `TIMER_FIRED` - Sleep/delay completed
245
+ - `SIGNAL_RECEIVED` - External signal received
246
+
247
+ ## 📚 Documentation
248
+
249
+ See [`.copilot-instructions.md`](.copilot-instructions.md) for comprehensive development guidelines including:
250
+
251
+ - Event sourcing patterns
252
+ - Deterministic execution rules
253
+ - Activity best practices
254
+ - Testing strategies
255
+ - Common pitfalls to avoid
256
+
257
+ ## 🧪 Testing
258
+
259
+ ```bash
260
+ # Run all tests
261
+ pytest
262
+
263
+ # Run with coverage
264
+ pytest --cov=src --cov-report=html
265
+
266
+ # Run specific test file
267
+ pytest tests/test_workflow.py
268
+
269
+ # Verbose output
270
+ pytest -v
271
+ ```
272
+
273
+ ## Project Structure
274
+
275
+ ```
276
+ loom/
277
+ ├── src/
278
+ │ ├── common/ # Shared utilities
279
+ │ ├── core/ # Core engine (context, engine, runner, worker)
280
+ │ ├── database/ # Database layer
281
+ │ ├── decorators/ # @workflow, @step, @activity
282
+ │ ├── lib/ # Utilities and progress tracking
283
+ │ ├── migrations/ # Database migrations
284
+ │ └── schemas/ # Type definitions
285
+ ├── tests/ # Test suite
286
+ ├── examples/ # Example workflows
287
+ ├── loom.py # Main package interface
288
+ └── pyproject.toml # Package configuration
289
+ ```
290
+
291
+ ## Configuration
292
+
293
+ Loom uses SQLite by default for simplicity. For production:
294
+
295
+ - Consider PostgreSQL/MySQL for scalability
296
+ - Implement connection pooling
297
+ - Add monitoring and alerting
298
+ - Deploy multiple workers for high availability
299
+
300
+ ## Contributing
301
+
302
+ Contributions welcome! Please ensure:
303
+
304
+ 1. Tests pass: `pytest`
305
+ 2. Code formatted: `black .`
306
+ 3. Type checking: `mypy .`
307
+ 4. Linting: `ruff check .`
308
+
309
+ ## License
310
+
311
+ MIT License - see LICENSE file for details
312
+
313
+ ## Acknowledgments
314
+
315
+ Inspired by:
316
+ - [Temporal](https://temporal.io/) - The workflow orchestration platform
317
+ - [Durable Task Framework](https://github.com/Azure/durabletask) - Microsoft's durable task library
318
+ - [Cadence](https://cadenceworkflow.io/) - Uber's workflow platform
319
+
320
+ ---
321
+
322
+ **Built withpy src`
323
+ 4. Linting: `ruff check src`
324
+
325
+ ## 📝 License
326
+
327
+ MIT License - see LICENSE file for details
328
+
329
+ ## 🙏 Acknowledgments
330
+
331
+ Inspired by:
332
+ - [Temporal](https://temporal.io/) - The workflow orchestration platform
333
+ - [Durable Task Framework](https://github.com/Azure/durabletask) - Microsoft's durable task library
334
+ - [Cadence](https://cadenceworkflow.io/) - Uber's workflow platform
335
+
336
+ ## 📧 Contact
337
+
338
+ For questions and support, please open an issue on GitHub.
339
+
340
+ ---
341
+
342
+ **Built with ❤️ using Python 3.12+**
@@ -0,0 +1,147 @@
1
+ # Quick Start Guide
2
+
3
+ This guide will help you get started with Loom in 5 minutes.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ pip install -r requirements.txt
10
+
11
+ # Initialize the database
12
+ python loom_cli.py init
13
+ ```
14
+
15
+ ## Your First Workflow
16
+
17
+ Create a file `my_first_workflow.py`:
18
+
19
+ ```python
20
+ from dataclasses import dataclass
21
+ from src.core.context import WorkflowContext
22
+ from src.core.workflow import Workflow
23
+ from src.decorators.workflow import workflow, step
24
+ from src.decorators.activity import activity
25
+
26
+
27
+ # 1. Define your data types
28
+ @dataclass
29
+ class HelloInput:
30
+ name: str
31
+
32
+
33
+ @dataclass
34
+ class HelloState:
35
+ greeting: str = ""
36
+
37
+
38
+ # 2. Define an activity (for side effects)
39
+ @activity(name="get_greeting")
40
+ async def get_greeting(name: str) -> str:
41
+ return f"Hello, {name}!"
42
+
43
+
44
+ # 3. Define your workflow
45
+ @workflow(name="HelloWorkflow", version="1.0.0")
46
+ class HelloWorkflow(Workflow[HelloInput, HelloState]):
47
+
48
+ @step(name="greet")
49
+ async def greet_step(self, ctx: WorkflowContext[HelloInput, HelloState]):
50
+ # Call activity
51
+ greeting = await ctx.activity(get_greeting, ctx.input.name)
52
+
53
+ # Update state
54
+ await ctx.state.set("greeting", greeting)
55
+
56
+ # Log
57
+ ctx.logger.info(f"Generated greeting: {greeting}")
58
+
59
+
60
+ # 4. Start the workflow
61
+ async def main():
62
+ import asyncio
63
+
64
+ workflow = HelloWorkflow.compile()
65
+ handle = await workflow.start(input=HelloInput(name="World"))
66
+
67
+ print(f"Workflow started: {handle.id}")
68
+ print(f"Status: {await handle.status()}")
69
+
70
+ if __name__ == "__main__":
71
+ import asyncio
72
+ asyncio.run(main())
73
+ ```
74
+
75
+ ## Run the Worker
76
+
77
+ In a separate terminal:
78
+
79
+ ```bash
80
+ python loom_cli.py worker
81
+ ```
82
+
83
+ ## Execute Your Workflow
84
+
85
+ ```bash
86
+ python my_first_workflow.py
87
+ ```
88
+
89
+ ## Check Workflow Status
90
+
91
+ ```bash
92
+ # List all workflows
93
+ python loom_cli.py list
94
+
95
+ # Inspect specific workflow
96
+ python loom_cli.py inspect <workflow-id>
97
+
98
+ # Show database stats
99
+ python loom_cli.py stats
100
+ ```
101
+
102
+ ## Next Steps
103
+
104
+ - Read the [README.md](README.md) for architecture details
105
+ - Check [examples/](examples/) for more complex workflows
106
+ - Review [.copilot-instructions.md](.copilot-instructions.md) for best practices
107
+ - Run tests: `pytest`
108
+
109
+ ## Common Patterns
110
+
111
+ ### Retry Failed Activities
112
+
113
+ ```python
114
+ @activity(name="api_call", retry_count=3, timeout_seconds=30)
115
+ async def call_external_api(url: str) -> dict:
116
+ # This will retry up to 3 times on failure
117
+ async with httpx.AsyncClient() as client:
118
+ response = await client.get(url)
119
+ response.raise_for_status()
120
+ return response.json()
121
+ ```
122
+
123
+ ### Sleep/Delay in Workflows
124
+
125
+ ```python
126
+ from datetime import timedelta
127
+
128
+ @step()
129
+ async def delayed_step(self, ctx: WorkflowContext):
130
+ # Sleep for 5 minutes (durable - survives process restart)
131
+ await ctx.sleep(timedelta(minutes=5))
132
+ ctx.logger.info("Woke up after 5 minutes!")
133
+ ```
134
+
135
+ ### Update Multiple State Fields
136
+
137
+ ```python
138
+ @step()
139
+ async def batch_update(self, ctx: WorkflowContext):
140
+ await ctx.state.update({
141
+ "processed": True,
142
+ "count": 42,
143
+ "timestamp": "2024-01-28T10:00:00Z"
144
+ })
145
+ ```
146
+
147
+ Happy workflow orchestrating! 🧵