taskito 0.2.0__tar.gz → 0.2.2__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.
- {taskito-0.2.0 → taskito-0.2.2}/Cargo.lock +10 -84
- taskito-0.2.2/PKG-INFO +286 -0
- taskito-0.2.2/README.md +263 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/Cargo.toml +1 -1
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-python/Cargo.toml +1 -1
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/canvas.py +3 -9
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/context.py +1 -3
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/dashboard.py +6 -2
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/mixins.py +1 -3
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/result.py +1 -3
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/testing.py +1 -3
- {taskito-0.2.0 → taskito-0.2.2}/pyproject.toml +3 -2
- taskito-0.2.0/PKG-INFO +0 -20
- {taskito-0.2.0 → taskito-0.2.2}/Cargo.toml +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/LICENSE +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/error.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/job.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/lib.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/periodic.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/resilience/circuit_breaker.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/resilience/dlq.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/resilience/mod.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/resilience/rate_limiter.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/resilience/retry.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/scheduler.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/mod.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/models.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/schema.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/circuit_breakers.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/dead_letter.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/jobs.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/logs.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/metrics.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/mod.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/periodic.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/rate_limits.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/tests.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/trait_impl.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/sqlite/workers.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-core/src/storage/traits.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-python/src/lib.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-python/src/py_config.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-python/src/py_job.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-python/src/py_queue.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/crates/taskito-python/src/py_worker.rs +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/__init__.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/_taskito.pyi +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/app.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/cli.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/contrib/__init__.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/contrib/fastapi.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/contrib/otel.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/exceptions.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/middleware.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/py.typed +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/serializers.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/task.py +0 -0
- {taskito-0.2.0 → taskito-0.2.2}/py_src/taskito/templates/dashboard.html +0 -0
|
@@ -221,7 +221,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
221
221
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
|
222
222
|
dependencies = [
|
|
223
223
|
"libc",
|
|
224
|
-
"windows-sys
|
|
224
|
+
"windows-sys",
|
|
225
225
|
]
|
|
226
226
|
|
|
227
227
|
[[package]]
|
|
@@ -448,7 +448,7 @@ checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
|
|
|
448
448
|
dependencies = [
|
|
449
449
|
"libc",
|
|
450
450
|
"wasi",
|
|
451
|
-
"windows-sys
|
|
451
|
+
"windows-sys",
|
|
452
452
|
]
|
|
453
453
|
|
|
454
454
|
[[package]]
|
|
@@ -705,7 +705,7 @@ dependencies = [
|
|
|
705
705
|
"errno",
|
|
706
706
|
"libc",
|
|
707
707
|
"linux-raw-sys",
|
|
708
|
-
"windows-sys
|
|
708
|
+
"windows-sys",
|
|
709
709
|
]
|
|
710
710
|
|
|
711
711
|
[[package]]
|
|
@@ -802,12 +802,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
|
802
802
|
|
|
803
803
|
[[package]]
|
|
804
804
|
name = "socket2"
|
|
805
|
-
version = "0.6.
|
|
805
|
+
version = "0.6.3"
|
|
806
806
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
807
|
-
checksum = "
|
|
807
|
+
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
|
808
808
|
dependencies = [
|
|
809
809
|
"libc",
|
|
810
|
-
"windows-sys
|
|
810
|
+
"windows-sys",
|
|
811
811
|
]
|
|
812
812
|
|
|
813
813
|
[[package]]
|
|
@@ -847,7 +847,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
|
847
847
|
|
|
848
848
|
[[package]]
|
|
849
849
|
name = "taskito-core"
|
|
850
|
-
version = "0.2.
|
|
850
|
+
version = "0.2.2"
|
|
851
851
|
dependencies = [
|
|
852
852
|
"chrono",
|
|
853
853
|
"cron",
|
|
@@ -866,7 +866,7 @@ dependencies = [
|
|
|
866
866
|
|
|
867
867
|
[[package]]
|
|
868
868
|
name = "taskito-python"
|
|
869
|
-
version = "0.2.
|
|
869
|
+
version = "0.2.2"
|
|
870
870
|
dependencies = [
|
|
871
871
|
"crossbeam-channel",
|
|
872
872
|
"pyo3",
|
|
@@ -885,7 +885,7 @@ dependencies = [
|
|
|
885
885
|
"getrandom 0.4.2",
|
|
886
886
|
"once_cell",
|
|
887
887
|
"rustix",
|
|
888
|
-
"windows-sys
|
|
888
|
+
"windows-sys",
|
|
889
889
|
]
|
|
890
890
|
|
|
891
891
|
[[package]]
|
|
@@ -973,7 +973,7 @@ dependencies = [
|
|
|
973
973
|
"signal-hook-registry",
|
|
974
974
|
"socket2",
|
|
975
975
|
"tokio-macros",
|
|
976
|
-
"windows-sys
|
|
976
|
+
"windows-sys",
|
|
977
977
|
]
|
|
978
978
|
|
|
979
979
|
[[package]]
|
|
@@ -1184,15 +1184,6 @@ dependencies = [
|
|
|
1184
1184
|
"windows-link",
|
|
1185
1185
|
]
|
|
1186
1186
|
|
|
1187
|
-
[[package]]
|
|
1188
|
-
name = "windows-sys"
|
|
1189
|
-
version = "0.60.2"
|
|
1190
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1191
|
-
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
|
1192
|
-
dependencies = [
|
|
1193
|
-
"windows-targets",
|
|
1194
|
-
]
|
|
1195
|
-
|
|
1196
1187
|
[[package]]
|
|
1197
1188
|
name = "windows-sys"
|
|
1198
1189
|
version = "0.61.2"
|
|
@@ -1202,71 +1193,6 @@ dependencies = [
|
|
|
1202
1193
|
"windows-link",
|
|
1203
1194
|
]
|
|
1204
1195
|
|
|
1205
|
-
[[package]]
|
|
1206
|
-
name = "windows-targets"
|
|
1207
|
-
version = "0.53.5"
|
|
1208
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1209
|
-
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
|
1210
|
-
dependencies = [
|
|
1211
|
-
"windows-link",
|
|
1212
|
-
"windows_aarch64_gnullvm",
|
|
1213
|
-
"windows_aarch64_msvc",
|
|
1214
|
-
"windows_i686_gnu",
|
|
1215
|
-
"windows_i686_gnullvm",
|
|
1216
|
-
"windows_i686_msvc",
|
|
1217
|
-
"windows_x86_64_gnu",
|
|
1218
|
-
"windows_x86_64_gnullvm",
|
|
1219
|
-
"windows_x86_64_msvc",
|
|
1220
|
-
]
|
|
1221
|
-
|
|
1222
|
-
[[package]]
|
|
1223
|
-
name = "windows_aarch64_gnullvm"
|
|
1224
|
-
version = "0.53.1"
|
|
1225
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1226
|
-
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
|
1227
|
-
|
|
1228
|
-
[[package]]
|
|
1229
|
-
name = "windows_aarch64_msvc"
|
|
1230
|
-
version = "0.53.1"
|
|
1231
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1232
|
-
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
|
1233
|
-
|
|
1234
|
-
[[package]]
|
|
1235
|
-
name = "windows_i686_gnu"
|
|
1236
|
-
version = "0.53.1"
|
|
1237
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1238
|
-
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
|
1239
|
-
|
|
1240
|
-
[[package]]
|
|
1241
|
-
name = "windows_i686_gnullvm"
|
|
1242
|
-
version = "0.53.1"
|
|
1243
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1244
|
-
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
|
1245
|
-
|
|
1246
|
-
[[package]]
|
|
1247
|
-
name = "windows_i686_msvc"
|
|
1248
|
-
version = "0.53.1"
|
|
1249
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1250
|
-
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
|
1251
|
-
|
|
1252
|
-
[[package]]
|
|
1253
|
-
name = "windows_x86_64_gnu"
|
|
1254
|
-
version = "0.53.1"
|
|
1255
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1256
|
-
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
|
1257
|
-
|
|
1258
|
-
[[package]]
|
|
1259
|
-
name = "windows_x86_64_gnullvm"
|
|
1260
|
-
version = "0.53.1"
|
|
1261
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1262
|
-
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
|
1263
|
-
|
|
1264
|
-
[[package]]
|
|
1265
|
-
name = "windows_x86_64_msvc"
|
|
1266
|
-
version = "0.53.1"
|
|
1267
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1268
|
-
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
|
1269
|
-
|
|
1270
1196
|
[[package]]
|
|
1271
1197
|
name = "wit-bindgen"
|
|
1272
1198
|
version = "0.51.0"
|
taskito-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: taskito
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Requires-Dist: cloudpickle>=3.0
|
|
5
|
+
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
6
|
+
Requires-Dist: pytest-asyncio>=0.21 ; extra == 'dev'
|
|
7
|
+
Requires-Dist: ruff>=0.8 ; extra == 'dev'
|
|
8
|
+
Requires-Dist: mypy>=1.13 ; extra == 'dev'
|
|
9
|
+
Requires-Dist: zensical ; extra == 'docs'
|
|
10
|
+
Requires-Dist: fastapi>=0.100.0 ; extra == 'fastapi'
|
|
11
|
+
Requires-Dist: pydantic>=2.0 ; extra == 'fastapi'
|
|
12
|
+
Requires-Dist: opentelemetry-api ; extra == 'otel'
|
|
13
|
+
Requires-Dist: opentelemetry-sdk ; extra == 'otel'
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Provides-Extra: docs
|
|
16
|
+
Provides-Extra: fastapi
|
|
17
|
+
Provides-Extra: otel
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Summary: Rust-powered task queue for Python. No broker required.
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
22
|
+
|
|
23
|
+
# taskito
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/taskito/)
|
|
26
|
+
[](https://pypi.org/project/taskito/)
|
|
27
|
+
[](https://github.com/pratyush618/taskito/blob/master/LICENSE)
|
|
28
|
+
|
|
29
|
+
A Rust-powered task queue for Python. No broker required — just SQLite.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
pip install taskito
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
**1. Define tasks** in `tasks.py`:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from taskito import Queue
|
|
41
|
+
|
|
42
|
+
queue = Queue(db_path="tasks.db")
|
|
43
|
+
|
|
44
|
+
@queue.task()
|
|
45
|
+
def add(a: int, b: int) -> int:
|
|
46
|
+
return a + b
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**2. Start a worker** in one terminal:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
taskito worker --app tasks:queue
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**3. Enqueue jobs** from another terminal or script:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from tasks import add
|
|
59
|
+
|
|
60
|
+
job = add.delay(2, 3)
|
|
61
|
+
print(job.result(timeout=10)) # 5
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Why taskito?
|
|
65
|
+
|
|
66
|
+
Most Python task queues require a separate broker (Redis, RabbitMQ) even for single-machine workloads. taskito embeds everything — storage, scheduling, and worker management — into a single `pip install` with no external dependencies beyond Python itself.
|
|
67
|
+
|
|
68
|
+
The heavy lifting runs in Rust: a Tokio async scheduler, OS thread worker pool with crossbeam channels, and Diesel ORM over SQLite in WAL mode. Python's GIL is only held during task execution.
|
|
69
|
+
|
|
70
|
+
## Features
|
|
71
|
+
|
|
72
|
+
- **Priority queues** — higher priority jobs run first
|
|
73
|
+
- **Retry with exponential backoff** — automatic retries with jitter
|
|
74
|
+
- **Dead letter queue** — inspect and replay failed jobs
|
|
75
|
+
- **Rate limiting** — token bucket with `"100/m"` syntax
|
|
76
|
+
- **Task dependencies** — `depends_on` for DAG workflows with cascade cancel
|
|
77
|
+
- **Task workflows** — `chain`, `group`, `chord` primitives
|
|
78
|
+
- **Periodic tasks** — cron scheduling with seconds granularity
|
|
79
|
+
- **Progress tracking** — report and read progress from inside tasks
|
|
80
|
+
- **Job cancellation** — cancel pending or running jobs
|
|
81
|
+
- **Unique tasks** — deduplicate active jobs by key
|
|
82
|
+
- **Batch enqueue** — `task.map()` for high-throughput bulk inserts
|
|
83
|
+
- **Named queues** — route tasks to isolated queues
|
|
84
|
+
- **Hooks** — before/after/success/failure middleware
|
|
85
|
+
- **Per-task middleware** — `TaskMiddleware` with `before`/`after`/`on_retry` hooks
|
|
86
|
+
- **Pluggable serializers** — `CloudpickleSerializer` (default), `JsonSerializer`, or custom
|
|
87
|
+
- **Cancel running tasks** — cooperative cancellation with `check_cancelled()`
|
|
88
|
+
- **Soft timeouts** — `check_timeout()` inside tasks
|
|
89
|
+
- **Worker heartbeat** — monitor worker health via `queue.workers()`
|
|
90
|
+
- **Job expiration** — `expires` parameter for time-sensitive jobs
|
|
91
|
+
- **Exception filtering** — `retry_on` / `dont_retry_on` for selective retries
|
|
92
|
+
- **OpenTelemetry** — optional tracing integration via `pip install taskito[otel]`
|
|
93
|
+
- **Async support** — `await job.aresult()`, `await queue.astats()`
|
|
94
|
+
- **Web dashboard** — `taskito dashboard --app myapp:queue` serves a built-in monitoring UI
|
|
95
|
+
- **FastAPI integration** — `TaskitoRouter` for instant REST API over the queue
|
|
96
|
+
- **CLI** — `taskito worker`, `taskito info --watch`, `taskito dashboard`
|
|
97
|
+
|
|
98
|
+
## Examples
|
|
99
|
+
|
|
100
|
+
### Retry with Backoff
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
@queue.task(max_retries=5, retry_backoff=2.0)
|
|
104
|
+
def fetch_url(url: str) -> str:
|
|
105
|
+
return requests.get(url).text
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Priority Queues
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
urgent_report.apply_async(args=[data], priority=10)
|
|
112
|
+
bulk_report.delay(data) # default priority 0
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Rate Limiting
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
@queue.task(rate_limit="100/m")
|
|
119
|
+
def call_api(endpoint: str) -> dict:
|
|
120
|
+
return requests.get(endpoint).json()
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Task Dependencies
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
download = fetch_file.delay("data.csv")
|
|
127
|
+
parsed = parse_file.apply_async(
|
|
128
|
+
args=["data.csv"],
|
|
129
|
+
depends_on=[download.id],
|
|
130
|
+
)
|
|
131
|
+
# parsed waits until download completes; if download fails, parsed is cancelled
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Workflows
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from taskito import chain, group, chord
|
|
138
|
+
|
|
139
|
+
# Sequential pipeline — each step receives the previous result
|
|
140
|
+
chain(fetch.s(url), parse.s(), store.s()).apply()
|
|
141
|
+
|
|
142
|
+
# Parallel fan-out
|
|
143
|
+
group(process.s(chunk) for chunk in chunks).apply()
|
|
144
|
+
|
|
145
|
+
# Parallel + callback when all complete
|
|
146
|
+
chord([download.s(u) for u in urls], merge.s()).apply()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Periodic Tasks
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
@queue.periodic(cron="0 */6 * * *")
|
|
153
|
+
def cleanup_temp_files():
|
|
154
|
+
...
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Progress Tracking
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from taskito import current_job
|
|
161
|
+
|
|
162
|
+
@queue.task()
|
|
163
|
+
def train_model(epochs: int):
|
|
164
|
+
for i in range(epochs):
|
|
165
|
+
...
|
|
166
|
+
current_job.update_progress(int((i + 1) / epochs * 100))
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Hooks
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
@queue.on_failure
|
|
173
|
+
def alert_on_failure(task_name, args, kwargs, error):
|
|
174
|
+
slack.post(f"Task {task_name} failed: {error}")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Exception Filtering
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
@queue.task(
|
|
181
|
+
max_retries=5,
|
|
182
|
+
retry_on=[ConnectionError, TimeoutError],
|
|
183
|
+
dont_retry_on=[ValueError],
|
|
184
|
+
)
|
|
185
|
+
def fetch_data(url: str) -> dict:
|
|
186
|
+
return requests.get(url).json()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Per-Task Middleware
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from taskito import TaskMiddleware
|
|
193
|
+
|
|
194
|
+
class TimingMiddleware(TaskMiddleware):
|
|
195
|
+
def before(self, ctx):
|
|
196
|
+
ctx._start = time.time()
|
|
197
|
+
|
|
198
|
+
def after(self, ctx, result, error):
|
|
199
|
+
elapsed = time.time() - ctx._start
|
|
200
|
+
print(f"{ctx.task_name} took {elapsed:.2f}s")
|
|
201
|
+
|
|
202
|
+
@queue.task(middleware=[TimingMiddleware()])
|
|
203
|
+
def process(data):
|
|
204
|
+
...
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Delayed Scheduling
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
# Run 30 minutes from now
|
|
211
|
+
reminder.apply_async(args=[user_id, msg], delay=1800)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Unique Tasks
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
report.apply_async(args=[user_id], unique_key=f"report:{user_id}")
|
|
218
|
+
# Second enqueue with same key is silently deduplicated while first is active
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### FastAPI Integration
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from fastapi import FastAPI
|
|
225
|
+
from taskito.contrib.fastapi import TaskitoRouter
|
|
226
|
+
|
|
227
|
+
app = FastAPI()
|
|
228
|
+
app.include_router(TaskitoRouter(queue), prefix="/tasks")
|
|
229
|
+
# GET /tasks/stats, GET /tasks/jobs/{id}, GET /tasks/jobs/{id}/progress (SSE), ...
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Batch Enqueue
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
jobs = send_email.map([("alice@x.com",), ("bob@x.com",), ("carol@x.com",)])
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Async Support
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
job = expensive_task.delay(data)
|
|
242
|
+
result = await job.aresult(timeout=30)
|
|
243
|
+
stats = await queue.astats()
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Testing
|
|
247
|
+
|
|
248
|
+
taskito includes a built-in test mode — no worker needed:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
def test_add():
|
|
252
|
+
with queue.test_mode() as results:
|
|
253
|
+
add.delay(2, 3)
|
|
254
|
+
assert results[0].return_value == 5
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Documentation
|
|
258
|
+
|
|
259
|
+
Full documentation with guides, API reference, architecture diagrams, and examples:
|
|
260
|
+
|
|
261
|
+
**[Read the docs →](https://taskito-sepia.vercel.app)**
|
|
262
|
+
|
|
263
|
+
Coming from Celery? See the **[Migration Guide](https://taskito-sepia.vercel.app/guide/migration/)**.
|
|
264
|
+
|
|
265
|
+
## Comparison
|
|
266
|
+
|
|
267
|
+
| Feature | taskito | Celery | RQ | Dramatiq | Huey |
|
|
268
|
+
|---|---|---|---|---|---|
|
|
269
|
+
| Broker required | **No** | Yes | Yes | Yes | Yes |
|
|
270
|
+
| Core language | **Rust + Python** | Python | Python | Python | Python |
|
|
271
|
+
| Priority queues | **Yes** | Yes | No | No | Yes |
|
|
272
|
+
| Rate limiting | **Yes** | Yes | No | Yes | No |
|
|
273
|
+
| Dead letter queue | **Yes** | No | Yes | No | No |
|
|
274
|
+
| Task dependencies | **Yes** | No | No | No | No |
|
|
275
|
+
| Task chaining | **Yes** | Yes | No | Yes | No |
|
|
276
|
+
| Built-in dashboard | **Yes** | No | No | No | No |
|
|
277
|
+
| FastAPI integration | **Yes** | No | No | No | No |
|
|
278
|
+
| Per-task middleware | **Yes** | No | No | Yes | No |
|
|
279
|
+
| Cancel running tasks | **Yes** | Yes | No | No | No |
|
|
280
|
+
| Custom serializers | **Yes** | Yes | No | No | No |
|
|
281
|
+
| Setup | **`pip install`** | Broker + backend | Redis | Broker | Redis |
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
MIT
|
|
286
|
+
|
taskito-0.2.2/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# taskito
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/taskito/)
|
|
4
|
+
[](https://pypi.org/project/taskito/)
|
|
5
|
+
[](https://github.com/pratyush618/taskito/blob/master/LICENSE)
|
|
6
|
+
|
|
7
|
+
A Rust-powered task queue for Python. No broker required — just SQLite.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install taskito
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
**1. Define tasks** in `tasks.py`:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from taskito import Queue
|
|
19
|
+
|
|
20
|
+
queue = Queue(db_path="tasks.db")
|
|
21
|
+
|
|
22
|
+
@queue.task()
|
|
23
|
+
def add(a: int, b: int) -> int:
|
|
24
|
+
return a + b
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**2. Start a worker** in one terminal:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
taskito worker --app tasks:queue
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**3. Enqueue jobs** from another terminal or script:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from tasks import add
|
|
37
|
+
|
|
38
|
+
job = add.delay(2, 3)
|
|
39
|
+
print(job.result(timeout=10)) # 5
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Why taskito?
|
|
43
|
+
|
|
44
|
+
Most Python task queues require a separate broker (Redis, RabbitMQ) even for single-machine workloads. taskito embeds everything — storage, scheduling, and worker management — into a single `pip install` with no external dependencies beyond Python itself.
|
|
45
|
+
|
|
46
|
+
The heavy lifting runs in Rust: a Tokio async scheduler, OS thread worker pool with crossbeam channels, and Diesel ORM over SQLite in WAL mode. Python's GIL is only held during task execution.
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Priority queues** — higher priority jobs run first
|
|
51
|
+
- **Retry with exponential backoff** — automatic retries with jitter
|
|
52
|
+
- **Dead letter queue** — inspect and replay failed jobs
|
|
53
|
+
- **Rate limiting** — token bucket with `"100/m"` syntax
|
|
54
|
+
- **Task dependencies** — `depends_on` for DAG workflows with cascade cancel
|
|
55
|
+
- **Task workflows** — `chain`, `group`, `chord` primitives
|
|
56
|
+
- **Periodic tasks** — cron scheduling with seconds granularity
|
|
57
|
+
- **Progress tracking** — report and read progress from inside tasks
|
|
58
|
+
- **Job cancellation** — cancel pending or running jobs
|
|
59
|
+
- **Unique tasks** — deduplicate active jobs by key
|
|
60
|
+
- **Batch enqueue** — `task.map()` for high-throughput bulk inserts
|
|
61
|
+
- **Named queues** — route tasks to isolated queues
|
|
62
|
+
- **Hooks** — before/after/success/failure middleware
|
|
63
|
+
- **Per-task middleware** — `TaskMiddleware` with `before`/`after`/`on_retry` hooks
|
|
64
|
+
- **Pluggable serializers** — `CloudpickleSerializer` (default), `JsonSerializer`, or custom
|
|
65
|
+
- **Cancel running tasks** — cooperative cancellation with `check_cancelled()`
|
|
66
|
+
- **Soft timeouts** — `check_timeout()` inside tasks
|
|
67
|
+
- **Worker heartbeat** — monitor worker health via `queue.workers()`
|
|
68
|
+
- **Job expiration** — `expires` parameter for time-sensitive jobs
|
|
69
|
+
- **Exception filtering** — `retry_on` / `dont_retry_on` for selective retries
|
|
70
|
+
- **OpenTelemetry** — optional tracing integration via `pip install taskito[otel]`
|
|
71
|
+
- **Async support** — `await job.aresult()`, `await queue.astats()`
|
|
72
|
+
- **Web dashboard** — `taskito dashboard --app myapp:queue` serves a built-in monitoring UI
|
|
73
|
+
- **FastAPI integration** — `TaskitoRouter` for instant REST API over the queue
|
|
74
|
+
- **CLI** — `taskito worker`, `taskito info --watch`, `taskito dashboard`
|
|
75
|
+
|
|
76
|
+
## Examples
|
|
77
|
+
|
|
78
|
+
### Retry with Backoff
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
@queue.task(max_retries=5, retry_backoff=2.0)
|
|
82
|
+
def fetch_url(url: str) -> str:
|
|
83
|
+
return requests.get(url).text
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Priority Queues
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
urgent_report.apply_async(args=[data], priority=10)
|
|
90
|
+
bulk_report.delay(data) # default priority 0
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Rate Limiting
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
@queue.task(rate_limit="100/m")
|
|
97
|
+
def call_api(endpoint: str) -> dict:
|
|
98
|
+
return requests.get(endpoint).json()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Task Dependencies
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
download = fetch_file.delay("data.csv")
|
|
105
|
+
parsed = parse_file.apply_async(
|
|
106
|
+
args=["data.csv"],
|
|
107
|
+
depends_on=[download.id],
|
|
108
|
+
)
|
|
109
|
+
# parsed waits until download completes; if download fails, parsed is cancelled
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Workflows
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from taskito import chain, group, chord
|
|
116
|
+
|
|
117
|
+
# Sequential pipeline — each step receives the previous result
|
|
118
|
+
chain(fetch.s(url), parse.s(), store.s()).apply()
|
|
119
|
+
|
|
120
|
+
# Parallel fan-out
|
|
121
|
+
group(process.s(chunk) for chunk in chunks).apply()
|
|
122
|
+
|
|
123
|
+
# Parallel + callback when all complete
|
|
124
|
+
chord([download.s(u) for u in urls], merge.s()).apply()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Periodic Tasks
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
@queue.periodic(cron="0 */6 * * *")
|
|
131
|
+
def cleanup_temp_files():
|
|
132
|
+
...
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Progress Tracking
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from taskito import current_job
|
|
139
|
+
|
|
140
|
+
@queue.task()
|
|
141
|
+
def train_model(epochs: int):
|
|
142
|
+
for i in range(epochs):
|
|
143
|
+
...
|
|
144
|
+
current_job.update_progress(int((i + 1) / epochs * 100))
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Hooks
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
@queue.on_failure
|
|
151
|
+
def alert_on_failure(task_name, args, kwargs, error):
|
|
152
|
+
slack.post(f"Task {task_name} failed: {error}")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Exception Filtering
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
@queue.task(
|
|
159
|
+
max_retries=5,
|
|
160
|
+
retry_on=[ConnectionError, TimeoutError],
|
|
161
|
+
dont_retry_on=[ValueError],
|
|
162
|
+
)
|
|
163
|
+
def fetch_data(url: str) -> dict:
|
|
164
|
+
return requests.get(url).json()
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Per-Task Middleware
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from taskito import TaskMiddleware
|
|
171
|
+
|
|
172
|
+
class TimingMiddleware(TaskMiddleware):
|
|
173
|
+
def before(self, ctx):
|
|
174
|
+
ctx._start = time.time()
|
|
175
|
+
|
|
176
|
+
def after(self, ctx, result, error):
|
|
177
|
+
elapsed = time.time() - ctx._start
|
|
178
|
+
print(f"{ctx.task_name} took {elapsed:.2f}s")
|
|
179
|
+
|
|
180
|
+
@queue.task(middleware=[TimingMiddleware()])
|
|
181
|
+
def process(data):
|
|
182
|
+
...
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Delayed Scheduling
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
# Run 30 minutes from now
|
|
189
|
+
reminder.apply_async(args=[user_id, msg], delay=1800)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Unique Tasks
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
report.apply_async(args=[user_id], unique_key=f"report:{user_id}")
|
|
196
|
+
# Second enqueue with same key is silently deduplicated while first is active
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### FastAPI Integration
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from fastapi import FastAPI
|
|
203
|
+
from taskito.contrib.fastapi import TaskitoRouter
|
|
204
|
+
|
|
205
|
+
app = FastAPI()
|
|
206
|
+
app.include_router(TaskitoRouter(queue), prefix="/tasks")
|
|
207
|
+
# GET /tasks/stats, GET /tasks/jobs/{id}, GET /tasks/jobs/{id}/progress (SSE), ...
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Batch Enqueue
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
jobs = send_email.map([("alice@x.com",), ("bob@x.com",), ("carol@x.com",)])
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Async Support
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
job = expensive_task.delay(data)
|
|
220
|
+
result = await job.aresult(timeout=30)
|
|
221
|
+
stats = await queue.astats()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Testing
|
|
225
|
+
|
|
226
|
+
taskito includes a built-in test mode — no worker needed:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
def test_add():
|
|
230
|
+
with queue.test_mode() as results:
|
|
231
|
+
add.delay(2, 3)
|
|
232
|
+
assert results[0].return_value == 5
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Documentation
|
|
236
|
+
|
|
237
|
+
Full documentation with guides, API reference, architecture diagrams, and examples:
|
|
238
|
+
|
|
239
|
+
**[Read the docs →](https://taskito-sepia.vercel.app)**
|
|
240
|
+
|
|
241
|
+
Coming from Celery? See the **[Migration Guide](https://taskito-sepia.vercel.app/guide/migration/)**.
|
|
242
|
+
|
|
243
|
+
## Comparison
|
|
244
|
+
|
|
245
|
+
| Feature | taskito | Celery | RQ | Dramatiq | Huey |
|
|
246
|
+
|---|---|---|---|---|---|
|
|
247
|
+
| Broker required | **No** | Yes | Yes | Yes | Yes |
|
|
248
|
+
| Core language | **Rust + Python** | Python | Python | Python | Python |
|
|
249
|
+
| Priority queues | **Yes** | Yes | No | No | Yes |
|
|
250
|
+
| Rate limiting | **Yes** | Yes | No | Yes | No |
|
|
251
|
+
| Dead letter queue | **Yes** | No | Yes | No | No |
|
|
252
|
+
| Task dependencies | **Yes** | No | No | No | No |
|
|
253
|
+
| Task chaining | **Yes** | Yes | No | Yes | No |
|
|
254
|
+
| Built-in dashboard | **Yes** | No | No | No | No |
|
|
255
|
+
| FastAPI integration | **Yes** | No | No | No | No |
|
|
256
|
+
| Per-task middleware | **Yes** | No | No | Yes | No |
|
|
257
|
+
| Cancel running tasks | **Yes** | Yes | No | No | No |
|
|
258
|
+
| Custom serializers | **Yes** | Yes | No | No | No |
|
|
259
|
+
| Setup | **`pip install`** | Broker + backend | Redis | Broker | Redis |
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
MIT
|
|
@@ -79,9 +79,7 @@ class group:
|
|
|
79
79
|
concurrently. Members are dispatched in waves.
|
|
80
80
|
"""
|
|
81
81
|
|
|
82
|
-
def __init__(
|
|
83
|
-
self, *signatures: Signature, max_concurrency: int | None = None
|
|
84
|
-
):
|
|
82
|
+
def __init__(self, *signatures: Signature, max_concurrency: int | None = None):
|
|
85
83
|
if len(signatures) < 1:
|
|
86
84
|
raise ValueError("group requires at least one signature")
|
|
87
85
|
self.signatures = list(signatures)
|
|
@@ -154,9 +152,7 @@ class chord:
|
|
|
154
152
|
)
|
|
155
153
|
|
|
156
154
|
|
|
157
|
-
def chunks(
|
|
158
|
-
task: TaskWrapper, items: list[Any], chunk_size: int
|
|
159
|
-
) -> group:
|
|
155
|
+
def chunks(task: TaskWrapper, items: list[Any], chunk_size: int) -> group:
|
|
160
156
|
"""Split items into chunks and create a group of tasks processing each chunk.
|
|
161
157
|
|
|
162
158
|
Args:
|
|
@@ -179,9 +175,7 @@ def chunks(
|
|
|
179
175
|
return group(*sigs)
|
|
180
176
|
|
|
181
177
|
|
|
182
|
-
def starmap(
|
|
183
|
-
task: TaskWrapper, args_list: list[tuple[Any, ...]]
|
|
184
|
-
) -> group:
|
|
178
|
+
def starmap(task: TaskWrapper, args_list: list[tuple[Any, ...]]) -> group:
|
|
185
179
|
"""Create a group with one task per args tuple.
|
|
186
180
|
|
|
187
181
|
Args:
|
|
@@ -75,9 +75,7 @@ class JobContext:
|
|
|
75
75
|
if _queue_ref is None:
|
|
76
76
|
raise RuntimeError("Queue reference not set. Cannot write log.")
|
|
77
77
|
extra_str = json.dumps(extra) if extra else None
|
|
78
|
-
_queue_ref._inner.write_task_log(
|
|
79
|
-
ctx.job_id, ctx.task_name, level, message, extra_str
|
|
80
|
-
)
|
|
78
|
+
_queue_ref._inner.write_task_log(ctx.job_id, ctx.task_name, level, message, extra_str)
|
|
81
79
|
|
|
82
80
|
def check_cancelled(self) -> None:
|
|
83
81
|
"""Check if cancellation has been requested for this job.
|
|
@@ -25,7 +25,9 @@ if TYPE_CHECKING:
|
|
|
25
25
|
|
|
26
26
|
def _load_spa_html() -> str:
|
|
27
27
|
"""Load the dashboard SPA HTML from the bundled template file."""
|
|
28
|
-
return
|
|
28
|
+
return (
|
|
29
|
+
resources.files("taskito").joinpath("templates/dashboard.html").read_text(encoding="utf-8")
|
|
30
|
+
)
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
_SPA_HTML: str | None = None
|
|
@@ -106,7 +108,9 @@ def _make_handler(queue: Queue) -> type:
|
|
|
106
108
|
level = qs.get("level", [None])[0]
|
|
107
109
|
since = int(qs.get("since", ["3600"])[0])
|
|
108
110
|
limit = int(qs.get("limit", ["100"])[0])
|
|
109
|
-
self._json_response(
|
|
111
|
+
self._json_response(
|
|
112
|
+
queue.query_logs(task_name=task, level=level, since=since, limit=limit)
|
|
113
|
+
)
|
|
110
114
|
elif path == "/api/circuit-breakers":
|
|
111
115
|
self._json_response(queue.circuit_breakers())
|
|
112
116
|
elif path == "/api/workers":
|
|
@@ -19,9 +19,7 @@ class _AsyncMixin:
|
|
|
19
19
|
async def _run_sync(self, fn: Any, *args: Any, **kwargs: Any) -> Any:
|
|
20
20
|
loop = asyncio.get_event_loop()
|
|
21
21
|
if kwargs:
|
|
22
|
-
return await loop.run_in_executor(
|
|
23
|
-
self._executor, lambda: fn(*args, **kwargs)
|
|
24
|
-
)
|
|
22
|
+
return await loop.run_in_executor(self._executor, lambda: fn(*args, **kwargs))
|
|
25
23
|
return await loop.run_in_executor(self._executor, lambda: fn(*args))
|
|
26
24
|
|
|
27
25
|
|
|
@@ -90,9 +90,7 @@ class JobResult:
|
|
|
90
90
|
return status, cloudpickle.loads(result_bytes)
|
|
91
91
|
|
|
92
92
|
if status in ("failed", "dead"):
|
|
93
|
-
raise RuntimeError(
|
|
94
|
-
f"Job {self.id} {status}: {self._py_job.error or 'unknown error'}"
|
|
95
|
-
)
|
|
93
|
+
raise RuntimeError(f"Job {self.id} {status}: {self._py_job.error or 'unknown error'}")
|
|
96
94
|
|
|
97
95
|
return status, None
|
|
98
96
|
|
|
@@ -181,9 +181,7 @@ class _FakeJobResult:
|
|
|
181
181
|
|
|
182
182
|
def result(self, timeout: float = 30.0, **kwargs: Any) -> Any:
|
|
183
183
|
if self._tr.error:
|
|
184
|
-
raise RuntimeError(
|
|
185
|
-
f"Job {self._tr.job_id} failed: {self._tr.error}"
|
|
186
|
-
)
|
|
184
|
+
raise RuntimeError(f"Job {self._tr.job_id} failed: {self._tr.error}")
|
|
187
185
|
return self._tr.return_value
|
|
188
186
|
|
|
189
187
|
async def aresult(self, timeout: float = 30.0, **kwargs: Any) -> Any:
|
|
@@ -4,10 +4,11 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "taskito"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Rust-powered task queue for Python. No broker required."
|
|
9
9
|
requires-python = ">=3.9"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
11
|
+
readme = "README.md"
|
|
11
12
|
dependencies = ["cloudpickle>=3.0"]
|
|
12
13
|
|
|
13
14
|
[project.optional-dependencies]
|
|
@@ -23,7 +24,7 @@ module-name = "taskito._taskito"
|
|
|
23
24
|
features = ["pyo3/extension-module"]
|
|
24
25
|
|
|
25
26
|
[project.scripts]
|
|
26
|
-
taskito = "taskito.cli:main"
|
|
27
|
+
taskito = "taskito.cli:main"
|
|
27
28
|
|
|
28
29
|
[tool.pytest.ini_options]
|
|
29
30
|
asyncio_mode = "auto"
|
taskito-0.2.0/PKG-INFO
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: taskito
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Requires-Dist: cloudpickle>=3.0
|
|
5
|
-
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
6
|
-
Requires-Dist: pytest-asyncio>=0.21 ; extra == 'dev'
|
|
7
|
-
Requires-Dist: ruff>=0.8 ; extra == 'dev'
|
|
8
|
-
Requires-Dist: mypy>=1.13 ; extra == 'dev'
|
|
9
|
-
Requires-Dist: zensical ; extra == 'docs'
|
|
10
|
-
Requires-Dist: fastapi>=0.100.0 ; extra == 'fastapi'
|
|
11
|
-
Requires-Dist: pydantic>=2.0 ; extra == 'fastapi'
|
|
12
|
-
Requires-Dist: opentelemetry-api ; extra == 'otel'
|
|
13
|
-
Requires-Dist: opentelemetry-sdk ; extra == 'otel'
|
|
14
|
-
Provides-Extra: dev
|
|
15
|
-
Provides-Extra: docs
|
|
16
|
-
Provides-Extra: fastapi
|
|
17
|
-
Provides-Extra: otel
|
|
18
|
-
License-File: LICENSE
|
|
19
|
-
Summary: Rust-powered task queue for Python. No broker required.
|
|
20
|
-
Requires-Python: >=3.9
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|