celery-fastapi 0.1.4__tar.gz → 0.1.6__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.
@@ -0,0 +1,513 @@
1
+ Metadata-Version: 2.4
2
+ Name: celery-fastapi
3
+ Version: 0.1.6
4
+ Summary: Automatic REST API generation for Celery tasks with FastAPI
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: celery,fastapi,rest,api,tasks,async,queue
8
+ Author: ilkerkara
9
+ Author-email: ilkerkara@outlook.com.tr
10
+ Requires-Python: >=3.11,<4.0
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: 3.15
23
+ Classifier: Programming Language :: Python :: 3.16
24
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
25
+ Classifier: Topic :: System :: Distributed Computing
26
+ Classifier: Typing :: Typed
27
+ Provides-Extra: all
28
+ Provides-Extra: cli
29
+ Provides-Extra: eventlet
30
+ Provides-Extra: gevent
31
+ Provides-Extra: gunicorn
32
+ Provides-Extra: multipart
33
+ Provides-Extra: orjson
34
+ Provides-Extra: otel
35
+ Provides-Extra: rabbitmq
36
+ Provides-Extra: redis
37
+ Provides-Extra: server
38
+ Provides-Extra: standard
39
+ Provides-Extra: ujson
40
+ Provides-Extra: uvicorn
41
+ Requires-Dist: celery (>=5.3.0,<=5.6.3)
42
+ Requires-Dist: eventlet (>=0.33.0) ; extra == "eventlet"
43
+ Requires-Dist: fastapi (>=0.100.0)
44
+ Requires-Dist: gevent (>=23.0.0) ; extra == "gevent"
45
+ Requires-Dist: gunicorn (>=21.0.0) ; extra == "gunicorn" or extra == "all"
46
+ Requires-Dist: httpx (>=0.27.0) ; extra == "all"
47
+ Requires-Dist: kombu (>=5.3.0) ; extra == "rabbitmq" or extra == "all"
48
+ Requires-Dist: opentelemetry-api (>=1.20.0) ; extra == "otel" or extra == "all"
49
+ Requires-Dist: orjson (>=3.9.0) ; extra == "orjson" or extra == "all"
50
+ Requires-Dist: pydantic (>=2.0.0)
51
+ Requires-Dist: python-multipart (>=0.0.6) ; extra == "multipart" or extra == "all"
52
+ Requires-Dist: redis (>=5.0.0) ; extra == "redis" or extra == "standard" or extra == "all"
53
+ Requires-Dist: rich (>=13.0.0) ; extra == "cli" or extra == "standard" or extra == "all"
54
+ Requires-Dist: typer (>=0.9.0) ; extra == "cli" or extra == "standard" or extra == "all"
55
+ Requires-Dist: ujson (>=5.8.0) ; extra == "ujson"
56
+ Requires-Dist: uvicorn[standard] (>=0.23.0) ; extra == "uvicorn" or extra == "gunicorn" or extra == "server" or extra == "cli" or extra == "standard" or extra == "all"
57
+ Project-URL: Documentation, https://github.com/karailker/celery-fastapi#readme
58
+ Project-URL: Homepage, https://github.com/karailker/celery-fastapi
59
+ Project-URL: Repository, https://github.com/karailker/celery-fastapi
60
+ Description-Content-Type: text/markdown
61
+
62
+ # Celery FastAPI
63
+
64
+ [![CI](https://github.com/karailker/celery-fastapi/actions/workflows/ci.yml/badge.svg)](https://github.com/karailker/celery-fastapi/actions/workflows/ci.yml)
65
+ [![PyPI version](https://badge.fury.io/py/celery-fastapi.svg)](https://badge.fury.io/py/celery-fastapi)
66
+ [![Python Version](https://img.shields.io/pypi/pyversions/celery-fastapi.svg)](https://pypi.org/project/celery-fastapi/)
67
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
68
+ [![GitHub Repo stars](https://img.shields.io/github/stars/karailker/celery-fastapi)](https://github.com/karailker/celery-fastapi/stargazers)
69
+
70
+ Automatic REST API generation for Celery tasks with FastAPI. This package seamlessly bridges Celery and FastAPI, automatically creating REST endpoints for all your registered Celery tasks.
71
+
72
+ ## Why celery-fastapi?
73
+
74
+ Without it, exposing Celery to other services means hand-writing a FastAPI
75
+ route, a request model and a status route for every task, then keeping them in
76
+ sync with the task signatures. celery-fastapi derives all of that from your
77
+ Celery app:
78
+
79
+ | | Hand-written routes | celery-fastapi |
80
+ |---|---|---|
81
+ | New task becomes an endpoint | write route + model | automatic |
82
+ | Request validation / OpenAPI | per route | from the task signature and type hints |
83
+ | Status, revoke, workers, health | write yourself | built in |
84
+ | Auth, rate limit, idempotency, metrics | write yourself | one parameter each |
85
+ | Typed clients (TypeScript, Python, ...) | write the schema | `celery-fastapi openapi` |
86
+
87
+ It is a thin bridge, not a new task queue: workers, brokers and results stay
88
+ plain Celery.
89
+
90
+ ## Features
91
+
92
+ - 🚀 **Automatic endpoint generation** — REST APIs created automatically for all Celery tasks
93
+ - 🔧 **Zero configuration** — Works out of the box with sensible defaults
94
+ - 📊 **Task monitoring** — Built-in endpoints for task status, revocation, and worker info
95
+ - 🎯 **App-scoped operations** — Only manages tasks from your specific Celery app, not the entire cluster
96
+ - 🖥️ **CLI support** — Run as a standalone server from command line
97
+ - 📦 **Modular design** — Use as a library or standalone application
98
+ - 🔄 **Queue-aware routing** — Respects Celery queue assignments
99
+ - 📝 **OpenAPI documentation** — Full Swagger/ReDoc support
100
+ - ⚡ **Full Celery options** — All task options (countdown, eta, priority, etc.)
101
+ - 🧮 **Batch execution** — Submit groups of tasks in a single request via `/tasks/batch`
102
+ - 🔗 **Workflow primitives** — Chain and chord orchestration via `/tasks/chain` and `/tasks/chord`
103
+ - 🛡️ **Input validation** — Pydantic-driven validation on `task_name`/`queue` at trust boundary
104
+ - 📦 **Pydantic task params** — Tasks annotated with `BaseModel` subclasses get first-class payload models and OpenAPI schemas
105
+ - 🪝 **Bridge hooks** — `pre_hooks`/`post_hooks` run around task dispatch (auth, audit, notify)
106
+ - 🗺️ **Discovery & mapping** — Hide tasks with `exclude` and rename public names with `name_mapping`
107
+ - 🔀 **Custom error mapping** — Map exception types to HTTP status codes via `error_mapping`
108
+ - 🧩 **Middleware & dependencies** — Inject FastAPI middleware and `Depends` providers
109
+ - 💾 **Pluggable rate limiting** — In-memory by default; bring your own store via `BaseRateLimitStorage`
110
+ - 🔌 **WebSocket streaming** — Live task status updates via `/tasks/{task_id}/ws`
111
+ - 📡 **Server-Sent Events** — Proxy-friendly status stream via `/tasks/{task_id}/events`
112
+ - 🔐 **Secure by default** — Destructive endpoints are opt-in; one `api_key` protects the rest
113
+ - ♻️ **Idempotency keys** — Retries with the same `Idempotency-Key` never double-submit
114
+ - 📈 **Prometheus metrics & OpenTelemetry** — Opt-in `/metrics` and dispatch spans
115
+ - 🗓️ **Beat schedule listing** — Read-only `/schedules`
116
+ - 🤖 **MCP server (experimental)** — Expose tasks as tools for AI agents
117
+ - 🧾 **OpenAPI export** — `celery-fastapi openapi` for client generation in CI
118
+
119
+ ## Requirements
120
+
121
+ - Python 3.11 – 3.16 (3.11–3.14 tested in CI; 3.15 (release candidate) and 3.16 run as non-blocking jobs once available)
122
+ - FastAPI 0.100.0+
123
+ - Celery 5.3.0+ (tested up to 5.6.3)
124
+
125
+ ## Installation
126
+
127
+ ```bash
128
+ # Basic installation
129
+ pip install celery-fastapi
130
+
131
+ # With CLI support
132
+ pip install celery-fastapi[cli]
133
+
134
+ # With uvicorn server
135
+ pip install celery-fastapi[server]
136
+
137
+ # With gunicorn for production
138
+ pip install celery-fastapi[gunicorn]
139
+
140
+ # With Redis broker
141
+ pip install celery-fastapi[redis]
142
+
143
+ # With RabbitMQ broker
144
+ pip install celery-fastapi[rabbitmq]
145
+
146
+ # All extras (recommended for production)
147
+ pip install celery-fastapi[all]
148
+ ```
149
+
150
+ Or with Poetry:
151
+
152
+ ```bash
153
+ poetry add celery-fastapi
154
+ ```
155
+
156
+ ## Quick Start
157
+
158
+ ### As a Python Module
159
+
160
+ ```python
161
+ from celery import Celery
162
+ from celery_fastapi import CeleryFastAPIBridge, create_app
163
+
164
+ celery_app = Celery("tasks", broker="redis://localhost:6379/0")
165
+
166
+
167
+ @celery_app.task
168
+ def add(x, y):
169
+ return x + y
170
+
171
+
172
+ # Option 1: Using create_app factory
173
+ app = create_app(celery_app)
174
+
175
+ # Option 2: Using the Bridge class for more control
176
+ from fastapi import FastAPI
177
+
178
+ fastapi_app = FastAPI(title="My Task API")
179
+ bridge = CeleryFastAPIBridge(celery_app, fastapi_app)
180
+ bridge.register_routes()
181
+ ```
182
+
183
+ Run with uvicorn:
184
+
185
+ ```bash
186
+ uvicorn myapp:app --reload
187
+ ```
188
+
189
+ ### Try it in five minutes
190
+
191
+ ```bash
192
+ cd examples/quickstart && docker compose up --build # then open http://localhost:8000/docs
193
+ ```
194
+
195
+ See [examples/quickstart](examples/quickstart) and, for Kubernetes health probes,
196
+ [examples/k8s-sidecar.yaml](examples/k8s-sidecar.yaml).
197
+
198
+ ### Using the CLI
199
+
200
+ ```bash
201
+ celery-fastapi serve examples.celery_app:celery_app --port 8000 --reload
202
+ celery-fastapi serve examples.celery_app:celery_app -w 4 --host 0.0.0.0
203
+ celery-fastapi routes examples.celery_app:celery_app
204
+ celery-fastapi tasks examples.celery_app:celery_app
205
+ celery-fastapi workers examples.celery_app:celery_app
206
+ ```
207
+
208
+ ## Security
209
+
210
+ celery-fastapi is **secure by default**, because an HTTP API in front of a task
211
+ queue is a powerful thing to expose:
212
+
213
+ - **Admin endpoints are off.** `POST /purge`, `POST /trigger` (runs *any* task
214
+ by name), `DELETE /tasks/{id}` and `POST /tasks/batch/revoke` only exist when
215
+ you pass `enable_admin_endpoints=True` (`--enable-admin` on the CLI).
216
+ - **One key protects everything.** `api_key="..."` (`CELERY_FASTAPI_API_KEY`)
217
+ requires the `X-API-Key` header (configurable) on every endpoint except the
218
+ `/healthz` and `/ping` probes. WebSockets also accept `?api_key=`. Keys are
219
+ compared in constant time.
220
+ - **Your own auth still works.** `dependencies=[...]` are applied to every HTTP
221
+ endpoint except the probes (use them for OAuth/JWT).
222
+ - **Only your tasks.** Per-task, batch, chain and chord endpoints refuse task
223
+ names outside the app; task and queue names are validated.
224
+ - **Rate limiting** covers every endpoint that submits work.
225
+
226
+ Always run it behind TLS, and prefer environment variables over command-line
227
+ arguments for secrets. See [SECURITY.md](SECURITY.md) for reporting issues.
228
+
229
+ > **Upgrading from 0.1.5:** if you call `/purge`, `/trigger` or the revoke
230
+ > endpoints, add `enable_admin_endpoints=True`. Everything else keeps working.
231
+
232
+ ## API Endpoints
233
+
234
+ All endpoints are prefixed with the configured `prefix` (empty by default).
235
+
236
+ ### Task Execution
237
+
238
+ `POST /{task_name_with_slashes}` — Execute a task.
239
+
240
+ ```json
241
+ {
242
+ "args": [1, 2],
243
+ "kwargs": {},
244
+ "countdown": 60,
245
+ "priority": 5,
246
+ "queue": "high_priority"
247
+ }
248
+ ```
249
+
250
+ ```json
251
+ {
252
+ "task_id": "abc123-def456-...",
253
+ "status": "PENDING"
254
+ }
255
+ ```
256
+
257
+ Send an `Idempotency-Key` header to make retries safe: the same key with the
258
+ same body returns the original `task_id` (response header
259
+ `Idempotent-Replayed: true`); the same key with a different body returns `409`.
260
+ Keys are remembered in memory for `idempotency_ttl` seconds (24h by default),
261
+ per process.
262
+
263
+ `POST /trigger` *(admin)* — Trigger any task by name (`queue` required here).
264
+
265
+ ```json
266
+ {
267
+ "task_name": "myapp.add",
268
+ "queue": "celery",
269
+ "args": [1, 2]
270
+ }
271
+ ```
272
+
273
+ #### Pydantic task parameters
274
+
275
+ When a task is annotated with a `pydantic.BaseModel` subclass, the generated
276
+ payload model uses that model directly, so nested schemas appear in OpenAPI:
277
+
278
+ ```python
279
+ class Item(BaseModel):
280
+ name: str
281
+ qty: int = 1
282
+
283
+
284
+ @celery_app.task(name="myapp.process")
285
+ def process(payload: Item) -> dict:
286
+ return payload.model_dump()
287
+ ```
288
+
289
+ ```json
290
+ {"payload": {"name": "widget", "qty": 3}}
291
+ ```
292
+
293
+ ### Workflow Primitives
294
+
295
+ `POST /tasks/chain` — Run tasks sequentially, passing results forward.
296
+
297
+ ```json
298
+ {
299
+ "tasks": [
300
+ {"task_name": "myapp.add", "args": [1, 2]},
301
+ {"task_name": "myapp.add", "args": [3, 4]}
302
+ ]
303
+ }
304
+ ```
305
+
306
+ `POST /tasks/chord` — Run a header group, then a callback once all complete.
307
+
308
+ ```json
309
+ {
310
+ "header": [
311
+ {"task_name": "myapp.add", "args": [1, 2]},
312
+ {"task_name": "myapp.add", "args": [3, 4]}
313
+ ],
314
+ "callback": "myapp.greet"
315
+ }
316
+ ```
317
+
318
+ ### Batch Execution
319
+
320
+ `POST /tasks/batch` — Submit a group of tasks.
321
+
322
+ `POST /tasks/batch/revoke` *(admin)* — Revoke tasks by list of IDs.
323
+
324
+ ### Task Status
325
+
326
+ - `GET /tasks/{task_id}` — Full task status (state, result, traceback, date_done).
327
+ - `GET /tasks/{task_id}/result` — Task result only.
328
+ - `GET /tasks` — List active, scheduled, reserved, revoked tasks (filtered to this
329
+ app). Query: `worker`, `name` (substring), `limit` and `offset` (per worker list).
330
+ - `GET /tasks/{task_id}/events` — Server-Sent Events status stream.
331
+ - `DELETE /tasks/{task_id}` *(admin)* — Revoke a single task.
332
+
333
+ A task that is unknown or has not started reports `state: "PENDING"` (HTTP 200),
334
+ which matches Celery; `GET /tasks/{id}/result` answers `202` until it finishes.
335
+
336
+ ### Discovery & Management
337
+
338
+ - `GET /available-tasks` — List tasks registered in THIS app (respects `name_mapping`/`exclude`).
339
+ - `GET /workers` — List active workers, filtered to this app's tasks (includes `active_queues`).
340
+ - `GET /schedules` — Celery Beat entries (`beat_schedule`) for exposed tasks. Read-only:
341
+ enabling/disabling entries depends on your scheduler and is not managed here.
342
+ - `POST /purge` *(admin)* — Purge all pending tasks.
343
+
344
+ ### Health Check
345
+
346
+ - `GET /healthz` — Health check for local Celery worker.
347
+ - `GET /ping` — Ping local Celery worker.
348
+
349
+ ### Observability
350
+
351
+ - `GET /metrics` *(opt-in: `enable_metrics=True`)* — Prometheus text format:
352
+ `celery_fastapi_tasks_submitted_total{task}`, `..._dispatch_errors_total{task}`,
353
+ `..._rate_limited_total`, `..._broker_up`, `..._workers_online`.
354
+ - **OpenTelemetry** *(opt-in: `enable_tracing=True`, `pip install celery-fastapi[otel]`)* —
355
+ each dispatch runs in a `celery_fastapi.send_task` span with `celery.task_name`
356
+ and `celery.queue` attributes, using whatever tracer provider your app configured.
357
+
358
+ ### MCP (experimental)
359
+
360
+ `enable_mcp=True` adds `POST /mcp`, a minimal Model Context Protocol endpoint
361
+ (JSON-RPC over HTTP: `initialize`, `tools/list`, `tools/call`). Every exposed
362
+ task becomes a tool whose input schema comes from the task signature, plus a
363
+ `get_task_status` tool. It honours `api_key`, `dependencies`, hooks and the rate
364
+ limit. Streaming/SSE transports and resources/prompts are not implemented.
365
+
366
+ ### Typed clients
367
+
368
+ ```bash
369
+ celery-fastapi openapi myapp.celery:app -o openapi.json
370
+ npx @openapitools/openapi-generator-cli generate -i openapi.json -g typescript-fetch -o client
371
+ ```
372
+
373
+ ### WebSocket Streaming
374
+
375
+ `WS /tasks/{task_id}/ws` — Stream task status updates as JSON frames.
376
+
377
+ ## Configuration
378
+
379
+ ### CeleryFastAPIBridge Options
380
+
381
+ ```python
382
+ from celery_fastapi import (
383
+ CeleryFastAPIBridge,
384
+ BaseRateLimitStorage,
385
+ )
386
+
387
+ bridge = CeleryFastAPIBridge(
388
+ celery_app=celery_app,
389
+ fastapi_app=fastapi_app, # Optional
390
+ prefix="/api/v1", # URL prefix
391
+ include_status_endpoints=True,
392
+ task_filter=lambda name: not name.startswith("internal."),
393
+ rate_limit=100, # req/min per client
394
+ rate_limit_storage=None, # BaseRateLimitStorage instance
395
+ enable_admin_endpoints=False, # /purge, /trigger, revoke
396
+ api_key=None, # require X-API-Key everywhere except /healthz, /ping
397
+ enable_metrics=False, # /metrics (Prometheus)
398
+ enable_mcp=False, # /mcp (experimental)
399
+ enable_tracing=False, # OpenTelemetry spans
400
+ idempotency_ttl=86400, # seconds an Idempotency-Key is remembered
401
+ exclude={"internal.secret"}, # Hide from API
402
+ name_mapping={"my.add": "public_add"}, # Rename in listings
403
+ middleware=[my_http_middleware],
404
+ dependencies=[auth_provider],
405
+ pre_hooks=[audit_hook], # receive payload
406
+ post_hooks=[notify_hook], # receive response
407
+ error_mapping={ValueError: 422},
408
+ )
409
+ ```
410
+
411
+ ### Pluggable Rate Limit Storage
412
+
413
+ Rate limiting is backed by `BaseRateLimitStorage`. The default is in-memory
414
+ (single process). For several workers use the bundled Redis store, or implement
415
+ the ABC for another shared store:
416
+
417
+ ```python
418
+ from redis import Redis
419
+ from celery_fastapi import CeleryFastAPIBridge, RedisRateLimitStorage
420
+
421
+ bridge = CeleryFastAPIBridge(
422
+ celery_app,
423
+ rate_limit=100,
424
+ rate_limit_storage=RedisRateLimitStorage(Redis()),
425
+ )
426
+ ```
427
+
428
+ `pip install celery-fastapi[redis]` provides the `redis` package.
429
+
430
+ ## Integration with Existing FastAPI App
431
+
432
+ ```python
433
+ from fastapi import FastAPI
434
+ from celery_fastapi import CeleryFastAPIBridge
435
+
436
+ app = FastAPI()
437
+
438
+
439
+ @app.get("/health")
440
+ def health_check():
441
+ return {"status": "healthy"}
442
+
443
+
444
+ bridge = CeleryFastAPIBridge(celery_app, app, prefix="/celery")
445
+ bridge.register_routes()
446
+ ```
447
+
448
+ ## CLI Reference
449
+
450
+ ```
451
+ celery-fastapi serve Start the server (uvicorn)
452
+ celery-fastapi serve-gunicorn Start with Gunicorn
453
+ celery-fastapi openapi Print the OpenAPI schema (no server)
454
+ celery-fastapi routes List all generated routes
455
+ celery-fastapi tasks List registered Celery tasks
456
+ celery-fastapi workers Show active workers
457
+ ```
458
+
459
+ `serve`, `serve-gunicorn`, `routes` and `openapi` accept `--enable-admin`,
460
+ `--api-key` (or `CELERY_FASTAPI_API_KEY`), `--metrics` and `--mcp`.
461
+
462
+ ```bash
463
+ celery-fastapi serve examples.celery_app:celery_app \
464
+ --host 0.0.0.0 --port 8000 --reload --workers 4 \
465
+ --log-level info --ssl-keyfile key.pem --ssl-certfile cert.pem
466
+ ```
467
+
468
+ ## Development
469
+
470
+ ```bash
471
+ git clone https://github.com/karailker/celery-fastapi.git
472
+ cd celery-fastapi
473
+ poetry install --extras all
474
+ poetry run pytest
475
+ poetry run ruff check .
476
+ poetry run mypy celery_fastapi
477
+ ```
478
+
479
+ ### Integration test suite (broker/backend matrix)
480
+
481
+ The repo ships a `docker-compose.yml` that brings up every broker/backend the
482
+ test matrix exercises:
483
+
484
+ ```bash
485
+ docker compose up -d # redis, rabbitmq, postgres, mysql, memcached, mongodb
486
+ poetry run pytest tests/test_integration_broker_backend.py
487
+ ```
488
+
489
+ Then start one worker for the end-to-end test and run it:
490
+
491
+ ```bash
492
+ poetry run celery -A tests.broker_workers:live_app worker --loglevel=info
493
+ poetry run pytest tests/test_integration_broker_backend.py::test_live_redis_broker_backend
494
+ ```
495
+
496
+ The matrix covers all stable Celery brokers (Redis, RabbitMQ) crossed with nine
497
+ result backends (redis, rpc, cache+memory, cache+memcached, db+sqlite,
498
+ db+postgresql, db+mysql, mongodb, filesystem) — 18 combinations, each verified
499
+ for bridge construction, broker dispatch, and result-backend round-trip. The
500
+ `rpc` backend skips the round-trip layer (it needs a live reply consumer); the
501
+ single live worker test uses Redis end to end. Missing drivers skip individually.
502
+
503
+ ## Contributing & project
504
+
505
+ - [CONTRIBUTING.md](CONTRIBUTING.md) — dev setup, checks, PR conventions
506
+ - [SECURITY.md](SECURITY.md) — how to report a vulnerability
507
+ - [ROADMAP.md](ROADMAP.md) — what is shipped and what is next
508
+ - [CHANGELOG.md](CHANGELOG.md)
509
+
510
+ ## License
511
+
512
+ MIT License — see [LICENSE](LICENSE).
513
+