You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Bidirectional Pydantic typing for [Celery](https://docs.celeryq.dev/), [Dramatiq](https://dramatiq.io/), and [Arq](https://arq-docs.helpmanual.io/) with one shared wire codec.
8
+
Bidirectional [Pydantic](https://docs.pydantic.dev/) serialization for [Celery](https://docs.celeryq.dev/), [Dramatiq](https://dramatiq.io/), and [Arq](https://arq-docs.helpmanual.io/). One shared wire codec — pass models on enqueue, get models back from results.
6
9
7
-
## The problem
10
+
Celery 5.5+ `pydantic=True` only validates on the worker. Callers still `model_dump()` before `.delay()`, and `.get()` returns a `dict`. Dramatiq chokes on models and UUIDs. Arq defaults to pickle. **queuebridge** fixes all three with a thin codec + backend adapters.
8
11
9
-
Celery 5.5+ added `pydantic=True`, but it only validates on the **worker**:
10
-
11
-
- Callers must still `model_dump()` before `.delay()` — passing a model raises `TypeError: Object of type X is not JSON serializable` ([celery#9442](https://github.com/celery/celery/issues/9442))
12
-
-`.get()` returns a `dict`, not your model
13
-
14
-
Dramatiq's default JSON encoder fails on models, UUIDs, and datetimes ([dramatiq#660](https://github.com/Bogdanp/dramatiq/issues/660)).
15
-
16
-
Arq defaults to pickle with no Pydantic story ([arq#497](https://github.com/python-arq/arq/issues/497)).
ar = process_order.delay(OrderCreate(id=1, sku="ABC"))
54
-
55
-
# Get a model back (not a dict)
56
46
result = typed_result(ar, OrderResult).get(timeout=10)
57
47
```
58
48
59
-
> **Note:** Celery cannot safely monkey-patch `AsyncResult.get()` globally. Use `typed_result()` for typed client results.
60
-
61
49
### Dramatiq
62
50
63
51
```python
@@ -98,9 +86,162 @@ class WorkerSettings:
98
86
job_deserializer = deserialize
99
87
```
100
88
89
+
## API
90
+
91
+
### `encode(value, *, tag_models=True)`
92
+
93
+
Recursively transform a Python value into a JSON-serializable structure.
94
+
95
+
#### value
96
+
97
+
*Required*
98
+
Type: `Any`
99
+
100
+
The value to encode — Pydantic models, nested containers, `UUID`, `datetime`, `Decimal`, `Enum`, etc.
101
+
102
+
#### tag_models
103
+
104
+
Type: `boolean`
105
+
Default: `true`
106
+
107
+
When `true`, `BaseModel` instances are wrapped in a `__qb__` envelope with a fully-qualified type name. When `false`, models are dumped with `model_dump(mode="json")` only.
108
+
109
+
```python
110
+
from queuebridge import encode, decode
111
+
from myapp.models import OrderCreate
112
+
113
+
wire = encode(OrderCreate(id=1, sku="ABC"))
114
+
restored = decode(wire, OrderCreate)
115
+
```
116
+
117
+
---
118
+
119
+
### `decode(value, hint=Any, *, strict=False)`
120
+
121
+
Recursively decode a wire value back to Python using an optional type hint.
122
+
123
+
#### value
124
+
125
+
*Required*
126
+
Type: `Any`
127
+
128
+
Wire value — primitives, lists, dicts, or `__qb__` envelopes.
129
+
130
+
#### hint
131
+
132
+
Type: `Any`
133
+
Default: `Any`
134
+
135
+
Type hint used for validation. `TypeAdapter(hint).validate_python()` is used when the hint is concrete.
136
+
137
+
#### strict
138
+
139
+
Type: `boolean`
140
+
Default: `false`
141
+
142
+
When `true`, raise `QueuebridgeDecodeError` if the value cannot be decoded.
143
+
144
+
---
145
+
146
+
### `decode_wire(value)`
147
+
148
+
Recursively unwrap `__qb__` envelopes without type hints. Used internally by Dramatiq's decoder.
|`list`, `dict`, `set`, `tuple`| recurse | recurse via hint |
261
+
| Primitives | pass through | pass through |
116
262
117
-
## Security
263
+
A plain `dict` + `OrderCreate` hint still validates — tags are for ambiguity, not required when hints are known.
264
+
265
+
## Why not Celery `pydantic=True` alone?
118
266
119
-
Deserialization resolves types by fully-qualified name (`import_fqn`). **Only deserialize from brokers you trust.** Module allowlisting is planned for v0.2.
0 commit comments