taskq-py 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.
- taskq_py-0.1.0/.gitignore +62 -0
- taskq_py-0.1.0/LICENSE +21 -0
- taskq_py-0.1.0/PKG-INFO +364 -0
- taskq_py-0.1.0/README.md +285 -0
- taskq_py-0.1.0/examples/.dockerignore +10 -0
- taskq_py-0.1.0/examples/.env.example +27 -0
- taskq_py-0.1.0/examples/Dockerfile +9 -0
- taskq_py-0.1.0/examples/README.md +76 -0
- taskq_py-0.1.0/examples/__init__.py +0 -0
- taskq_py-0.1.0/examples/actors/__init__.py +66 -0
- taskq_py-0.1.0/examples/actors/advanced.py +68 -0
- taskq_py-0.1.0/examples/actors/basic.py +38 -0
- taskq_py-0.1.0/examples/actors/batch.py +73 -0
- taskq_py-0.1.0/examples/actors/chained.py +49 -0
- taskq_py-0.1.0/examples/actors/di.py +105 -0
- taskq_py-0.1.0/examples/actors/failure.py +35 -0
- taskq_py-0.1.0/examples/actors/progress.py +46 -0
- taskq_py-0.1.0/examples/actors/ratelimit.py +82 -0
- taskq_py-0.1.0/examples/actors/sync_demo.py +45 -0
- taskq_py-0.1.0/examples/actors/tags_demo.py +45 -0
- taskq_py-0.1.0/examples/actors/ticker.py +25 -0
- taskq_py-0.1.0/examples/admin_app.py +61 -0
- taskq_py-0.1.0/examples/app.py +422 -0
- taskq_py-0.1.0/examples/docker-compose.yml +117 -0
- taskq_py-0.1.0/examples/fastapi_app/README.md +65 -0
- taskq_py-0.1.0/examples/fastapi_app/__init__.py +0 -0
- taskq_py-0.1.0/examples/fastapi_app/actors.py +34 -0
- taskq_py-0.1.0/examples/fastapi_app/main.py +35 -0
- taskq_py-0.1.0/examples/fastapi_app/routes.py +41 -0
- taskq_py-0.1.0/examples/templates/index.html +218 -0
- taskq_py-0.1.0/examples/templates/result.html +40 -0
- taskq_py-0.1.0/examples/worker.py +93 -0
- taskq_py-0.1.0/pyproject.toml +424 -0
- taskq_py-0.1.0/src/taskq/__init__.py +147 -0
- taskq_py-0.1.0/src/taskq/_di/__init__.py +45 -0
- taskq_py-0.1.0/src/taskq/_di/_utils.py +13 -0
- taskq_py-0.1.0/src/taskq/_di/_validate.py +456 -0
- taskq_py-0.1.0/src/taskq/_di/lifecycle.py +52 -0
- taskq_py-0.1.0/src/taskq/_di/registry.py +342 -0
- taskq_py-0.1.0/src/taskq/_di/scope.py +12 -0
- taskq_py-0.1.0/src/taskq/_di/scopes.py +467 -0
- taskq_py-0.1.0/src/taskq/_di/solver.py +159 -0
- taskq_py-0.1.0/src/taskq/_di/types.py +121 -0
- taskq_py-0.1.0/src/taskq/_dsn.py +18 -0
- taskq_py-0.1.0/src/taskq/_ids.py +128 -0
- taskq_py-0.1.0/src/taskq/_json.py +105 -0
- taskq_py-0.1.0/src/taskq/_scope.py +39 -0
- taskq_py-0.1.0/src/taskq/actor.py +752 -0
- taskq_py-0.1.0/src/taskq/backend/__init__.py +69 -0
- taskq_py-0.1.0/src/taskq/backend/_cursor.py +32 -0
- taskq_py-0.1.0/src/taskq/backend/_dispatch.py +114 -0
- taskq_py-0.1.0/src/taskq/backend/_dispatch_sql.py +301 -0
- taskq_py-0.1.0/src/taskq/backend/_enqueue.py +490 -0
- taskq_py-0.1.0/src/taskq/backend/_notify.py +49 -0
- taskq_py-0.1.0/src/taskq/backend/_protocol.py +854 -0
- taskq_py-0.1.0/src/taskq/backend/_reads.py +177 -0
- taskq_py-0.1.0/src/taskq/backend/_records.py +116 -0
- taskq_py-0.1.0/src/taskq/backend/_schedules.py +226 -0
- taskq_py-0.1.0/src/taskq/backend/_sql.py +103 -0
- taskq_py-0.1.0/src/taskq/backend/_sql_templates.py +543 -0
- taskq_py-0.1.0/src/taskq/backend/_sweeps.py +487 -0
- taskq_py-0.1.0/src/taskq/backend/_terminal.py +830 -0
- taskq_py-0.1.0/src/taskq/backend/clock.py +47 -0
- taskq_py-0.1.0/src/taskq/backend/postgres.py +656 -0
- taskq_py-0.1.0/src/taskq/backend/statemachine.py +50 -0
- taskq_py-0.1.0/src/taskq/batch.py +274 -0
- taskq_py-0.1.0/src/taskq/cli.py +670 -0
- taskq_py-0.1.0/src/taskq/client/__init__.py +25 -0
- taskq_py-0.1.0/src/taskq/client/_args.py +233 -0
- taskq_py-0.1.0/src/taskq/client/_enqueuer.py +347 -0
- taskq_py-0.1.0/src/taskq/client/_handle.py +321 -0
- taskq_py-0.1.0/src/taskq/client/_jobs.py +756 -0
- taskq_py-0.1.0/src/taskq/client/_taskq.py +647 -0
- taskq_py-0.1.0/src/taskq/client/_transport.py +112 -0
- taskq_py-0.1.0/src/taskq/constants.py +152 -0
- taskq_py-0.1.0/src/taskq/context.py +171 -0
- taskq_py-0.1.0/src/taskq/contrib/__init__.py +1 -0
- taskq_py-0.1.0/src/taskq/contrib/kubernetes/__init__.py +1 -0
- taskq_py-0.1.0/src/taskq/contrib/kubernetes/prometheus_rule.yaml +125 -0
- taskq_py-0.1.0/src/taskq/contrib/prometheus/__init__.py +10 -0
- taskq_py-0.1.0/src/taskq/contrib/prometheus/_metrics.py +63 -0
- taskq_py-0.1.0/src/taskq/contrib/prometheus/rules.yaml +113 -0
- taskq_py-0.1.0/src/taskq/cron.py +332 -0
- taskq_py-0.1.0/src/taskq/di.py +7 -0
- taskq_py-0.1.0/src/taskq/exceptions.py +398 -0
- taskq_py-0.1.0/src/taskq/migrate.py +248 -0
- taskq_py-0.1.0/src/taskq/migrations/01.00.00_01_pre_initial.sql +444 -0
- taskq_py-0.1.0/src/taskq/migrations/01.00.01_01_pre_per_property_cron.sql +21 -0
- taskq_py-0.1.0/src/taskq/migrations/__init__.py +13 -0
- taskq_py-0.1.0/src/taskq/obs/__init__.py +120 -0
- taskq_py-0.1.0/src/taskq/obs/_otel.py +583 -0
- taskq_py-0.1.0/src/taskq/obs/_structlog.py +241 -0
- taskq_py-0.1.0/src/taskq/obs/error_reporter.py +120 -0
- taskq_py-0.1.0/src/taskq/progress/__init__.py +5 -0
- taskq_py-0.1.0/src/taskq/progress/_buffer.py +96 -0
- taskq_py-0.1.0/src/taskq/progress/_events.py +34 -0
- taskq_py-0.1.0/src/taskq/progress/_flush.py +140 -0
- taskq_py-0.1.0/src/taskq/progress/_publish.py +200 -0
- taskq_py-0.1.0/src/taskq/py.typed +0 -0
- taskq_py-0.1.0/src/taskq/ratelimit/__init__.py +42 -0
- taskq_py-0.1.0/src/taskq/ratelimit/_decision_log.py +38 -0
- taskq_py-0.1.0/src/taskq/ratelimit/_provider.py +90 -0
- taskq_py-0.1.0/src/taskq/ratelimit/_redis_utils.py +79 -0
- taskq_py-0.1.0/src/taskq/ratelimit/_scripts.py +265 -0
- taskq_py-0.1.0/src/taskq/ratelimit/_sliding_window_pg.py +432 -0
- taskq_py-0.1.0/src/taskq/ratelimit/_sliding_window_redis.py +398 -0
- taskq_py-0.1.0/src/taskq/ratelimit/composition.py +93 -0
- taskq_py-0.1.0/src/taskq/ratelimit/decision.py +47 -0
- taskq_py-0.1.0/src/taskq/ratelimit/refs.py +87 -0
- taskq_py-0.1.0/src/taskq/ratelimit/registry.py +678 -0
- taskq_py-0.1.0/src/taskq/ratelimit/reservation.py +593 -0
- taskq_py-0.1.0/src/taskq/ratelimit/sliding_window.py +570 -0
- taskq_py-0.1.0/src/taskq/ratelimit/token_bucket.py +754 -0
- taskq_py-0.1.0/src/taskq/retry.py +582 -0
- taskq_py-0.1.0/src/taskq/scheduler.py +59 -0
- taskq_py-0.1.0/src/taskq/settings.py +797 -0
- taskq_py-0.1.0/src/taskq/testing/__init__.py +102 -0
- taskq_py-0.1.0/src/taskq/testing/_dispatch.py +158 -0
- taskq_py-0.1.0/src/taskq/testing/_enqueue.py +225 -0
- taskq_py-0.1.0/src/taskq/testing/_reads.py +101 -0
- taskq_py-0.1.0/src/taskq/testing/_runner.py +650 -0
- taskq_py-0.1.0/src/taskq/testing/_slots.py +108 -0
- taskq_py-0.1.0/src/taskq/testing/_sweeps.py +184 -0
- taskq_py-0.1.0/src/taskq/testing/_terminal.py +666 -0
- taskq_py-0.1.0/src/taskq/testing/actor.py +322 -0
- taskq_py-0.1.0/src/taskq/testing/assertions.py +311 -0
- taskq_py-0.1.0/src/taskq/testing/asyncpg_chaos.py +157 -0
- taskq_py-0.1.0/src/taskq/testing/clock.py +49 -0
- taskq_py-0.1.0/src/taskq/testing/fixtures.py +860 -0
- taskq_py-0.1.0/src/taskq/testing/in_memory.py +773 -0
- taskq_py-0.1.0/src/taskq/testing/job_context.py +63 -0
- taskq_py-0.1.0/src/taskq/testing/jobs.py +190 -0
- taskq_py-0.1.0/src/taskq/testing/otel.py +251 -0
- taskq_py-0.1.0/src/taskq/testing/pg.py +310 -0
- taskq_py-0.1.0/src/taskq/testing/settings.py +71 -0
- taskq_py-0.1.0/src/taskq/testing/spy.py +15 -0
- taskq_py-0.1.0/src/taskq/types.py +48 -0
- taskq_py-0.1.0/src/taskq/web/__init__.py +1 -0
- taskq_py-0.1.0/src/taskq/web/admin/__init__.py +28 -0
- taskq_py-0.1.0/src/taskq/web/admin/_constants.py +26 -0
- taskq_py-0.1.0/src/taskq/web/admin/_factory.py +403 -0
- taskq_py-0.1.0/src/taskq/web/admin/_history.py +250 -0
- taskq_py-0.1.0/src/taskq/web/admin/_jsonb.py +23 -0
- taskq_py-0.1.0/src/taskq/web/admin/_listen.py +107 -0
- taskq_py-0.1.0/src/taskq/web/admin/_static.py +25 -0
- taskq_py-0.1.0/src/taskq/web/admin/auth/__init__.py +42 -0
- taskq_py-0.1.0/src/taskq/web/admin/auth/_session.py +190 -0
- taskq_py-0.1.0/src/taskq/web/admin/auth/oidc.py +299 -0
- taskq_py-0.1.0/src/taskq/web/admin/auth/saml.py +213 -0
- taskq_py-0.1.0/src/taskq/web/admin/auth/token.py +35 -0
- taskq_py-0.1.0/src/taskq/web/admin/jobs.py +555 -0
- taskq_py-0.1.0/src/taskq/web/admin/ops.py +658 -0
- taskq_py-0.1.0/src/taskq/web/admin/queues.py +197 -0
- taskq_py-0.1.0/src/taskq/web/admin/sse.py +104 -0
- taskq_py-0.1.0/src/taskq/web/admin/workers.py +105 -0
- taskq_py-0.1.0/src/taskq/web/health.py +81 -0
- taskq_py-0.1.0/src/taskq/web/progress.py +445 -0
- taskq_py-0.1.0/src/taskq/web/static/admin.css +2 -0
- taskq_py-0.1.0/src/taskq/web/static/admin.js +218 -0
- taskq_py-0.1.0/src/taskq/web/static/alpine.min.js +5 -0
- taskq_py-0.1.0/src/taskq/web/static/htmx.min.js +1 -0
- taskq_py-0.1.0/src/taskq/web/static/realtime.js +246 -0
- taskq_py-0.1.0/src/taskq/web/static/sse.min.js +1 -0
- taskq_py-0.1.0/src/taskq/web/static/tailwind.css +3 -0
- taskq_py-0.1.0/src/taskq/web/templates/_base.html +75 -0
- taskq_py-0.1.0/src/taskq/web/templates/_partials/job_card.html +97 -0
- taskq_py-0.1.0/src/taskq/web/templates/_partials/job_table.html +148 -0
- taskq_py-0.1.0/src/taskq/web/templates/_partials/sse_console.html +4 -0
- taskq_py-0.1.0/src/taskq/web/templates/_partials/table.html +37 -0
- taskq_py-0.1.0/src/taskq/web/templates/history.html +98 -0
- taskq_py-0.1.0/src/taskq/web/templates/job_detail.html +367 -0
- taskq_py-0.1.0/src/taskq/web/templates/jobs.html +193 -0
- taskq_py-0.1.0/src/taskq/web/templates/leader.html +73 -0
- taskq_py-0.1.0/src/taskq/web/templates/queue_detail.html +59 -0
- taskq_py-0.1.0/src/taskq/web/templates/queues.html +85 -0
- taskq_py-0.1.0/src/taskq/web/templates/rate_limits.html +104 -0
- taskq_py-0.1.0/src/taskq/web/templates/reservations.html +93 -0
- taskq_py-0.1.0/src/taskq/web/templates/schedules.html +76 -0
- taskq_py-0.1.0/src/taskq/web/templates/workers.html +69 -0
- taskq_py-0.1.0/src/taskq/worker/__init__.py +69 -0
- taskq_py-0.1.0/src/taskq/worker/_bootstrap.py +509 -0
- taskq_py-0.1.0/src/taskq/worker/_consumer.py +896 -0
- taskq_py-0.1.0/src/taskq/worker/_handlers.py +709 -0
- taskq_py-0.1.0/src/taskq/worker/_leader_shared.py +390 -0
- taskq_py-0.1.0/src/taskq/worker/_leader_sweeps.py +441 -0
- taskq_py-0.1.0/src/taskq/worker/actor_config.py +19 -0
- taskq_py-0.1.0/src/taskq/worker/budget.py +93 -0
- taskq_py-0.1.0/src/taskq/worker/cancel.py +438 -0
- taskq_py-0.1.0/src/taskq/worker/cron_loop.py +300 -0
- taskq_py-0.1.0/src/taskq/worker/deps.py +296 -0
- taskq_py-0.1.0/src/taskq/worker/dev.py +160 -0
- taskq_py-0.1.0/src/taskq/worker/dispatch.py +290 -0
- taskq_py-0.1.0/src/taskq/worker/health.py +330 -0
- taskq_py-0.1.0/src/taskq/worker/heartbeat.py +270 -0
- taskq_py-0.1.0/src/taskq/worker/leader.py +384 -0
- taskq_py-0.1.0/src/taskq/worker/notify.py +331 -0
- taskq_py-0.1.0/src/taskq/worker/run.py +576 -0
- taskq_py-0.1.0/src/taskq/worker/shutdown.py +287 -0
- taskq_py-0.1.0/src/taskq/worker/startup.py +214 -0
- taskq_py-0.1.0/src/taskq/worker/workgroup.py +778 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
build/
|
|
9
|
+
dist/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
*.egg
|
|
12
|
+
|
|
13
|
+
# Environments
|
|
14
|
+
.env
|
|
15
|
+
.env.local
|
|
16
|
+
.env.*.local
|
|
17
|
+
.envrc
|
|
18
|
+
.venv/
|
|
19
|
+
venv/
|
|
20
|
+
|
|
21
|
+
# Test / coverage
|
|
22
|
+
htmlcov/
|
|
23
|
+
.coverage
|
|
24
|
+
.coverage.*
|
|
25
|
+
.cache
|
|
26
|
+
.hypothesis/
|
|
27
|
+
.pytest_cache/
|
|
28
|
+
coverage.xml
|
|
29
|
+
|
|
30
|
+
# Node.js (Tailwind CSS CLI for admin UI)
|
|
31
|
+
node_modules/
|
|
32
|
+
|
|
33
|
+
# MkDocs build output
|
|
34
|
+
/site/
|
|
35
|
+
|
|
36
|
+
# Type checker / linter caches
|
|
37
|
+
.mypy_cache/
|
|
38
|
+
.pyright_cache/
|
|
39
|
+
.ruff_cache/
|
|
40
|
+
|
|
41
|
+
# OS junk
|
|
42
|
+
.DS_Store
|
|
43
|
+
Thumbs.db
|
|
44
|
+
desktop.ini
|
|
45
|
+
|
|
46
|
+
# IDE / editor
|
|
47
|
+
.idea/
|
|
48
|
+
.vscode/
|
|
49
|
+
*.swp
|
|
50
|
+
*.swo
|
|
51
|
+
*~
|
|
52
|
+
|
|
53
|
+
# Docker compose local overrides
|
|
54
|
+
docker-compose.override.yml
|
|
55
|
+
docker-compose.override.yaml
|
|
56
|
+
|
|
57
|
+
# Postgres data volume if a future compose maps it as a bind mount
|
|
58
|
+
postgres-data/
|
|
59
|
+
|
|
60
|
+
# Agent workspace
|
|
61
|
+
.claude/
|
|
62
|
+
.serena/
|
taskq_py-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AZX, PBC.
|
|
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.
|
taskq_py-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: taskq-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async-native, Postgres-backed background job library for Python 3.12+
|
|
5
|
+
Project-URL: Homepage, https://github.com/AZX-PBC-OSS/TaskQ
|
|
6
|
+
Project-URL: Documentation, https://AZX-PBC-OSS.github.io/TaskQ/
|
|
7
|
+
Project-URL: Repository, https://github.com/AZX-PBC-OSS/TaskQ
|
|
8
|
+
Project-URL: Issues, https://github.com/AZX-PBC-OSS/TaskQ/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/AZX-PBC-OSS/TaskQ/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: "AZX, PBC." <oss@azx.io>
|
|
11
|
+
License: MIT License
|
|
12
|
+
|
|
13
|
+
Copyright (c) 2026 AZX, PBC.
|
|
14
|
+
|
|
15
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
16
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
17
|
+
in the Software without restriction, including without limitation the rights
|
|
18
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
19
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
20
|
+
furnished to do so, subject to the following conditions:
|
|
21
|
+
|
|
22
|
+
The above copyright notice and this permission notice shall be included in all
|
|
23
|
+
copies or substantial portions of the Software.
|
|
24
|
+
|
|
25
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
SOFTWARE.
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Keywords: asyncio,jobs,opentelemetry,postgres,queue
|
|
34
|
+
Classifier: Development Status :: 3 - Alpha
|
|
35
|
+
Classifier: Framework :: AsyncIO
|
|
36
|
+
Classifier: Intended Audience :: Developers
|
|
37
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
41
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
42
|
+
Requires-Python: >=3.12
|
|
43
|
+
Requires-Dist: asyncpg<1,>=0.31.0
|
|
44
|
+
Requires-Dist: croniter>=6.2.3
|
|
45
|
+
Requires-Dist: dotenvmodel<1,>=0.3.0
|
|
46
|
+
Requires-Dist: opentelemetry-api>=1.43.0
|
|
47
|
+
Requires-Dist: orjson>=3.11.9
|
|
48
|
+
Requires-Dist: pydantic<3,>=2.13.4
|
|
49
|
+
Requires-Dist: structlog<27,>=26.1.0
|
|
50
|
+
Requires-Dist: typer<1,>=0.26.8
|
|
51
|
+
Requires-Dist: uuid-utils<1,>=0.17.0
|
|
52
|
+
Provides-Extra: fastapi
|
|
53
|
+
Requires-Dist: fastapi<1,>=0.139.0; extra == 'fastapi'
|
|
54
|
+
Requires-Dist: humanize>=4.16.0; extra == 'fastapi'
|
|
55
|
+
Requires-Dist: jinja2>=3.1.6; extra == 'fastapi'
|
|
56
|
+
Requires-Dist: sse-starlette>=3.4.5; extra == 'fastapi'
|
|
57
|
+
Requires-Dist: starlette>=1.3.1; extra == 'fastapi'
|
|
58
|
+
Requires-Dist: uvicorn<1,>=0.51.0; extra == 'fastapi'
|
|
59
|
+
Provides-Extra: oidc
|
|
60
|
+
Requires-Dist: authlib>=1.7.2; extra == 'oidc'
|
|
61
|
+
Requires-Dist: httpx2>=2.5.0; extra == 'oidc'
|
|
62
|
+
Requires-Dist: httpx<1,>=0.28.1; extra == 'oidc'
|
|
63
|
+
Requires-Dist: itsdangerous<3,>=2.2.0; extra == 'oidc'
|
|
64
|
+
Provides-Extra: otel
|
|
65
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.43.0; extra == 'otel'
|
|
66
|
+
Requires-Dist: opentelemetry-instrumentation<1,>=0.62b1; extra == 'otel'
|
|
67
|
+
Requires-Dist: opentelemetry-sdk>=1.43.0; extra == 'otel'
|
|
68
|
+
Provides-Extra: prometheus
|
|
69
|
+
Requires-Dist: opentelemetry-exporter-prometheus<1,>=0.63b0; extra == 'prometheus'
|
|
70
|
+
Requires-Dist: prometheus-client<1,>=0.25.0; extra == 'prometheus'
|
|
71
|
+
Provides-Extra: redis
|
|
72
|
+
Requires-Dist: redis<9,>=8.0.1; extra == 'redis'
|
|
73
|
+
Provides-Extra: reload
|
|
74
|
+
Requires-Dist: watchfiles>=1.2.0; extra == 'reload'
|
|
75
|
+
Provides-Extra: saml
|
|
76
|
+
Requires-Dist: itsdangerous<3,>=2.2.0; extra == 'saml'
|
|
77
|
+
Requires-Dist: python3-saml>=1.16.0; extra == 'saml'
|
|
78
|
+
Description-Content-Type: text/markdown
|
|
79
|
+
|
|
80
|
+
# TaskQ
|
|
81
|
+
|
|
82
|
+
Async-native, Postgres-backed background job library for Python 3.12+.
|
|
83
|
+
|
|
84
|
+
[](https://github.com/AZX-PBC-OSS/TaskQ/actions/workflows/ci.yaml)
|
|
85
|
+
[](https://pypi.org/project/taskq-py/)
|
|
86
|
+
[](https://pypi.org/project/taskq-py/)
|
|
87
|
+
[](https://opensource.org/licenses/MIT)
|
|
88
|
+
[](https://AZX-PBC-OSS.github.io/TaskQ/)
|
|
89
|
+
|
|
90
|
+
> **Stability:** TaskQ is pre-1.0 and follows SemVer 0.x conventions — breaking
|
|
91
|
+
> changes may land in minor version bumps (`0.x.0`), not just majors. Pin an
|
|
92
|
+
> exact or narrow version range in production until 1.0.
|
|
93
|
+
|
|
94
|
+
> [!WARNING]
|
|
95
|
+
> **The admin UI fails closed by default in non-dev environments.** It raises
|
|
96
|
+
> `RuntimeError` at startup if no `auth_dependency` is configured and
|
|
97
|
+
> `TASKQ_ENVIRONMENT` is not `dev`. Set `TASKQ_ADMIN_UI_REQUIRE_AUTH=false` to
|
|
98
|
+
> opt out (e.g. when relying on a reverse proxy), or configure SSO via the
|
|
99
|
+
> `taskq[oidc]` or `taskq[saml]` extras. See [guides/admin-ui.md](docs/guides/admin-ui.md#security).
|
|
100
|
+
|
|
101
|
+
## Features
|
|
102
|
+
|
|
103
|
+
- **Actors** — decorate plain `async def` (or sync) functions with `@actor`;
|
|
104
|
+
payloads are validated with Pydantic models and dispatched as typed
|
|
105
|
+
`ActorRef` handles.
|
|
106
|
+
- **Postgres-backed** — durable jobs, `SKIP LOCKED` dispatch, advisory-lock
|
|
107
|
+
leader election, and a forward-only SQL migration runner. No external
|
|
108
|
+
broker required.
|
|
109
|
+
- **Async-native** — built on `asyncio` and `asyncpg` from the ground up; no
|
|
110
|
+
thread pools or sync wrappers on the hot path.
|
|
111
|
+
- **Rate limiting** — sliding-window and token-bucket algorithms with
|
|
112
|
+
composition, a provider/registry layer, and Postgres fallback when Redis is
|
|
113
|
+
unavailable.
|
|
114
|
+
- **Dependency injection** — scoped providers (LOOP, TRANSIENT, ...), cycle
|
|
115
|
+
detection, and validation via the `_di` subsystem.
|
|
116
|
+
- **Admin UI** — FastAPI + htmx dashboard for inspecting jobs, queues, and
|
|
117
|
+
workers, with live progress streaming over SSE.
|
|
118
|
+
- **Observability** — vendor-neutral OpenTelemetry spans/metrics and
|
|
119
|
+
structured logging via `structlog`. Wire any OTLP-compatible backend
|
|
120
|
+
(Datadog, Sentry, App Insights, ...) without importing vendor SDKs.
|
|
121
|
+
- **Cron scheduling** — declarative periodic actors with `cron(...)` /
|
|
122
|
+
`ScheduleHandle` and a leader-elected cron loop.
|
|
123
|
+
- **Batch processing** — `enqueue_batch` / `enqueue_batch_fast` for fan-out.
|
|
124
|
+
`wait_for_batch(db, batch_id)` is an in-actor finalizer helper (call it from
|
|
125
|
+
a finalizer actor holding an `asyncpg` connection); client-side code that
|
|
126
|
+
isn't inside an actor should instead poll `BatchHandle.status(db_connection)`.
|
|
127
|
+
See [Jobs & Clients](docs/guides/jobs-clients.md#enqueue_batch).
|
|
128
|
+
- **Cancellation** — cooperative cancellation with grace periods and
|
|
129
|
+
force-cancel sweeps; `ctx.check_cancelled()` inside actor bodies.
|
|
130
|
+
- **Progress tracking** — `ctx.progress(...)` events buffered and published
|
|
131
|
+
to subscribers and the admin UI.
|
|
132
|
+
- **Workgroups** — multi-worker process supervision with a shared heartbeat
|
|
133
|
+
and shutdown coordinator.
|
|
134
|
+
- **Retries** — pluggable `RetryPolicy` with backoff, snooze, and
|
|
135
|
+
`RetryDecision` control flow.
|
|
136
|
+
|
|
137
|
+
## Installation
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pip install taskq-py
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Or with [`uv`](https://docs.astral.sh/uv/):
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uv add taskq-py
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Optional extras:
|
|
150
|
+
|
|
151
|
+
| Extra | Adds |
|
|
152
|
+
| -------------- | ------------------------------------------------------------------- |
|
|
153
|
+
| `[redis]` | Redis client for real-time progress fanout and Redis rate limiters |
|
|
154
|
+
| `[fastapi]` | FastAPI, Jinja2, sse-starlette, uvicorn for the admin UI and SSE |
|
|
155
|
+
| `[otel]` | OpenTelemetry SDK + OTLP exporter + instrumentation for provider setup, export, and testing |
|
|
156
|
+
| `[prometheus]` | OpenTelemetry Prometheus exporter for metric scrapes |
|
|
157
|
+
| `[oidc]` | OIDC SSO auth for the admin UI (authlib, httpx2, itsdangerous) |
|
|
158
|
+
| `[saml]` | SAML SSO auth for the admin UI (python3-saml, itsdangerous) |
|
|
159
|
+
| `[reload]` | `watchfiles` for autoreload during local development |
|
|
160
|
+
|
|
161
|
+
The core install depends only on `opentelemetry-api` — no SDK or exporters
|
|
162
|
+
(see [Observability](docs/guides/observability.md)).
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pip install "taskq-py[redis,fastapi,otel,prometheus]"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Quick start
|
|
169
|
+
|
|
170
|
+
### Prerequisites
|
|
171
|
+
|
|
172
|
+
- Python 3.12+
|
|
173
|
+
- [`uv`](https://docs.astral.sh/uv/) for dependency management
|
|
174
|
+
- Docker (for the bundled Postgres 18 / Redis stack)
|
|
175
|
+
- PostgreSQL: tested against **PostgreSQL 18** (CI and `docker-compose.yml`
|
|
176
|
+
both pin PG 18). No PG18-specific SQL has been identified in the bundled
|
|
177
|
+
migrations, but earlier major versions are not covered by CI — treat
|
|
178
|
+
PG 18 as the supported baseline until a version matrix is added.
|
|
179
|
+
|
|
180
|
+
### Bring up local infra
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
docker compose up -d postgres redis
|
|
184
|
+
cp .env.example .env
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Install and run migrations
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
uv sync
|
|
191
|
+
uv run taskq migrate status
|
|
192
|
+
uv run taskq migrate up
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`migrate up` is idempotent — re-running is a no-op until new migrations land.
|
|
196
|
+
|
|
197
|
+
### Define an actor
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from pydantic import BaseModel
|
|
201
|
+
|
|
202
|
+
from taskq import JobContext, actor
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class EmailPayload(BaseModel):
|
|
206
|
+
to: str
|
|
207
|
+
subject: str
|
|
208
|
+
body: str
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@actor(name="send_email", queue="default")
|
|
212
|
+
async def send_email(payload: EmailPayload, ctx: JobContext[EmailPayload]) -> None:
|
|
213
|
+
ctx.check_cancelled()
|
|
214
|
+
await ctx.progress(step=1, percent=50.0, detail="rendering template")
|
|
215
|
+
# ... send the email ...
|
|
216
|
+
await ctx.progress(step=2, percent=100.0, detail="sent")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# The worker's --actors flag resolves this dotted path (myapp.actors:registry).
|
|
220
|
+
registry = [send_email]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Enqueue a job
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
import asyncio
|
|
227
|
+
|
|
228
|
+
from taskq import TaskQ
|
|
229
|
+
from taskq.settings import WorkerSettings
|
|
230
|
+
|
|
231
|
+
from myapp.actors import EmailPayload, send_email
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
async def main() -> None:
|
|
235
|
+
settings = WorkerSettings.load()
|
|
236
|
+
async with TaskQ(dsn=str(settings.pg_dsn)) as tq:
|
|
237
|
+
handle = await tq.enqueue(
|
|
238
|
+
send_email,
|
|
239
|
+
EmailPayload(to="alice@example.com", subject="Hi", body="Hello"),
|
|
240
|
+
)
|
|
241
|
+
print(f"enqueued job {handle.job_id}")
|
|
242
|
+
await handle.wait(timeout=30.0)
|
|
243
|
+
print("job finished")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
asyncio.run(main())
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Run a worker
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
uv run taskq worker --actors myapp.actors:registry --queues default
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The worker applies pending migrations at startup when
|
|
256
|
+
`TASKQ_MIGRATE_ON_START=true`, elects a leader via Postgres advisory locks,
|
|
257
|
+
and consumes jobs with `SKIP LOCKED` dispatch.
|
|
258
|
+
|
|
259
|
+
## Layout
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
src/taskq/
|
|
263
|
+
__init__.py - public API surface (re-exports, __version__)
|
|
264
|
+
actor.py - @actor decorator, ActorRef, ActorHandler
|
|
265
|
+
backend/ - PostgreSQL backend (postgres.py), protocol, dispatch SQL, records,
|
|
266
|
+
sweeps, schedules, notify, SQL templates, state machine, clock
|
|
267
|
+
client/ - TaskQ facade, JobsClient, JobHandle, sub-job enqueuer
|
|
268
|
+
worker/ - consumer, leader election, shutdown, heartbeat, workgroup, cron loop
|
|
269
|
+
ratelimit/ - sliding window, token bucket, composition, registry, reservations
|
|
270
|
+
_di/ - dependency injection, scopes, registry, solver, validation
|
|
271
|
+
di.py - public DI re-exports (ProviderRegistry, Scope)
|
|
272
|
+
web/ - admin UI (FastAPI + htmx), progress router, health, static/templates
|
|
273
|
+
obs/ - OpenTelemetry helpers, structlog configuration
|
|
274
|
+
progress/ - progress events, buffering, flush, publishing
|
|
275
|
+
testing/ - in-memory backend, fixtures, assertions, chaos helpers
|
|
276
|
+
contrib/ - Prometheus metrics, Kubernetes alerting rules
|
|
277
|
+
migrations/ - bundled SQL migration files ({schema} placeholder templated)
|
|
278
|
+
cli.py - `taskq` console entry point (typer)
|
|
279
|
+
settings.py - dotenvmodel-based TASKQ_* config
|
|
280
|
+
retry.py - RetryPolicy, RetryDecision, backoff
|
|
281
|
+
exceptions.py - control-flow + error hierarchy
|
|
282
|
+
batch.py - BatchHandle, EnqueueItem, wait_for_batch
|
|
283
|
+
cron.py - cron() function, ScheduleHandle, CronScheduleSpec
|
|
284
|
+
scheduler.py - register_cron registration helper
|
|
285
|
+
context.py - JobContext (cancellation, progress, sub-enqueue)
|
|
286
|
+
migrate.py - forward-only SQL migration runner
|
|
287
|
+
_json.py - orjson-backed dumps/loads (stdlib json never imported)
|
|
288
|
+
examples/ - runnable FastAPI trigger app + worker entrypoint
|
|
289
|
+
docker-compose.yml - Postgres 18 + Redis 8 for local dev
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Toolchain
|
|
293
|
+
|
|
294
|
+
| Tool | Purpose |
|
|
295
|
+
| ------------ | ------------------------------------------------------ |
|
|
296
|
+
| uv | Dependency + virtualenv management |
|
|
297
|
+
| ruff | Linting AND formatting (single source of truth) |
|
|
298
|
+
| pyright | Strict type checking |
|
|
299
|
+
| pytest + asyncio + testcontainers | Integration testing against real PG |
|
|
300
|
+
| typer | CLI definitions |
|
|
301
|
+
| pydantic v2 | Data models and validation |
|
|
302
|
+
| dotenvmodel | Typed env config with cascading `.env` discovery |
|
|
303
|
+
| orjson | JSON serialization |
|
|
304
|
+
| structlog | Structured logging |
|
|
305
|
+
| OpenTelemetry SDK (+ optional OTLP exporter) | Vendor-neutral observability |
|
|
306
|
+
|
|
307
|
+
## Observability
|
|
308
|
+
|
|
309
|
+
TaskQ never imports vendor SDKs (Sentry, Datadog, PostHog, App Insights).
|
|
310
|
+
Wiring is via OTLP — point `OTEL_EXPORTER_OTLP_ENDPOINT` at the Datadog
|
|
311
|
+
Agent, Sentry's OTel ingest, App Insights, or PostHog Cloud and the
|
|
312
|
+
spans/metrics flow through unchanged. The `ErrorReporter` Protocol is the
|
|
313
|
+
place to plug vendor-specific error routing without coupling the library to
|
|
314
|
+
any one backend.
|
|
315
|
+
|
|
316
|
+
## Configuration
|
|
317
|
+
|
|
318
|
+
All runtime config is namespaced with the `TASKQ_` prefix and loaded
|
|
319
|
+
through [`dotenvmodel`](https://pypi.org/project/dotenvmodel/) — drop a
|
|
320
|
+
`.env` in the project root, or set vars in your environment.
|
|
321
|
+
|
|
322
|
+
| Variable | Default | Purpose |
|
|
323
|
+
| ----------------------------- | ---------------------------------------------------- | --------------------------------------------- |
|
|
324
|
+
| `TASKQ_PG_DSN` | `postgresql://taskq:taskq@localhost:5432/taskq` | Direct PG DSN (sessions, LISTEN, advisory locks) |
|
|
325
|
+
| `TASKQ_SCHEMA_NAME` | `taskq` | Schema for all TaskQ tables |
|
|
326
|
+
| `TASKQ_REDIS_URL` | _unset_ | Optional Redis URL for progress fanout |
|
|
327
|
+
| `TASKQ_MIGRATE_ON_START` | `false` | Apply pending migrations on startup |
|
|
328
|
+
|
|
329
|
+
See `src/taskq/settings.py` for the full set of knobs (pool sizes, heartbeat
|
|
330
|
+
intervals, grace periods, rate-limit fallback, metrics port, admin UI
|
|
331
|
+
options).
|
|
332
|
+
|
|
333
|
+
## Testing
|
|
334
|
+
|
|
335
|
+
The test suite is integration-first: pytest spins up a Postgres 18 container
|
|
336
|
+
via [`testcontainers`](https://testcontainers-python.readthedocs.io/) and
|
|
337
|
+
applies the bundled migrations against it.
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
uv run pytest # all tests
|
|
341
|
+
uv run pytest -m "not integration" # skip the testcontainers tier
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Type checking and linting:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
uv run pyright
|
|
348
|
+
uv run ruff check
|
|
349
|
+
uv run ruff format --check
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Documentation
|
|
353
|
+
|
|
354
|
+
Full documentation is hosted at
|
|
355
|
+
[https://AZX-PBC-OSS.github.io/TaskQ/](https://AZX-PBC-OSS.github.io/TaskQ/).
|
|
356
|
+
|
|
357
|
+
## Contributing
|
|
358
|
+
|
|
359
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Changes are tracked in
|
|
360
|
+
[CHANGELOG.md](CHANGELOG.md).
|
|
361
|
+
|
|
362
|
+
## License
|
|
363
|
+
|
|
364
|
+
[MIT](LICENSE)
|