Skip to content

Commit 1083986

Browse files
ornarieceornariece
andauthored
ability to pass context to serialization (pydantic#7143) (#1215)
Co-authored-by: ornariece <[email protected]>
1 parent c6301fe commit 1083986

File tree

12 files changed

+177
-21
lines changed

12 files changed

+177
-21
lines changed

python/pydantic_core/_pydantic_core.pyi

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class SchemaValidator:
101101
*,
102102
strict: bool | None = None,
103103
from_attributes: bool | None = None,
104-
context: 'dict[str, Any] | None' = None,
104+
context: dict[str, Any] | None = None,
105105
self_instance: Any | None = None,
106106
) -> Any:
107107
"""
@@ -131,7 +131,7 @@ class SchemaValidator:
131131
*,
132132
strict: bool | None = None,
133133
from_attributes: bool | None = None,
134-
context: 'dict[str, Any] | None' = None,
134+
context: dict[str, Any] | None = None,
135135
self_instance: Any | None = None,
136136
) -> bool:
137137
"""
@@ -148,7 +148,7 @@ class SchemaValidator:
148148
input: str | bytes | bytearray,
149149
*,
150150
strict: bool | None = None,
151-
context: 'dict[str, Any] | None' = None,
151+
context: dict[str, Any] | None = None,
152152
self_instance: Any | None = None,
153153
) -> Any:
154154
"""
@@ -176,7 +176,7 @@ class SchemaValidator:
176176
The validated Python object.
177177
"""
178178
def validate_strings(
179-
self, input: _StringInput, *, strict: bool | None = None, context: 'dict[str, Any] | None' = None
179+
self, input: _StringInput, *, strict: bool | None = None, context: dict[str, Any] | None = None
180180
) -> Any:
181181
"""
182182
Validate a string against the schema and return the validated Python object.
@@ -206,7 +206,7 @@ class SchemaValidator:
206206
*,
207207
strict: bool | None = None,
208208
from_attributes: bool | None = None,
209-
context: 'dict[str, Any] | None' = None,
209+
context: dict[str, Any] | None = None,
210210
) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any] | None, set[str]]:
211211
"""
212212
Validate an assignment to a field on a model.
@@ -278,6 +278,7 @@ class SchemaSerializer:
278278
round_trip: bool = False,
279279
warnings: bool = True,
280280
fallback: Callable[[Any], Any] | None = None,
281+
context: dict[str, Any] | None = None,
281282
) -> Any:
282283
"""
283284
Serialize/marshal a Python object to a Python object including transforming and filtering data.
@@ -297,6 +298,8 @@ class SchemaSerializer:
297298
warnings: Whether to log warnings when invalid fields are encountered.
298299
fallback: A function to call when an unknown value is encountered,
299300
if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
301+
context: The context to use for serialization, this is passed to functional serializers as
302+
[`info.context`][pydantic_core.core_schema.SerializationInfo.context].
300303
301304
Raises:
302305
PydanticSerializationError: If serialization fails and no `fallback` function is provided.
@@ -318,6 +321,7 @@ class SchemaSerializer:
318321
round_trip: bool = False,
319322
warnings: bool = True,
320323
fallback: Callable[[Any], Any] | None = None,
324+
context: dict[str, Any] | None = None,
321325
) -> bytes:
322326
"""
323327
Serialize a Python object to JSON including transforming and filtering data.
@@ -336,6 +340,8 @@ class SchemaSerializer:
336340
warnings: Whether to log warnings when invalid fields are encountered.
337341
fallback: A function to call when an unknown value is encountered,
338342
if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
343+
context: The context to use for serialization, this is passed to functional serializers as
344+
[`info.context`][pydantic_core.core_schema.SerializationInfo.context].
339345
340346
Raises:
341347
PydanticSerializationError: If serialization fails and no `fallback` function is provided.
@@ -358,6 +364,7 @@ def to_json(
358364
inf_nan_mode: Literal['null', 'constants'] = 'constants',
359365
serialize_unknown: bool = False,
360366
fallback: Callable[[Any], Any] | None = None,
367+
context: dict[str, Any] | None = None,
361368
) -> bytes:
362369
"""
363370
Serialize a Python object to JSON including transforming and filtering data.
@@ -379,6 +386,8 @@ def to_json(
379386
`"<Unserializable {value_type} object>"` will be used.
380387
fallback: A function to call when an unknown value is encountered,
381388
if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
389+
context: The context to use for serialization, this is passed to functional serializers as
390+
[`info.context`][pydantic_core.core_schema.SerializationInfo.context].
382391
383392
Raises:
384393
PydanticSerializationError: If serialization fails and no `fallback` function is provided.
@@ -419,6 +428,7 @@ def to_jsonable_python(
419428
inf_nan_mode: Literal['null', 'constants'] = 'constants',
420429
serialize_unknown: bool = False,
421430
fallback: Callable[[Any], Any] | None = None,
431+
context: dict[str, Any] | None = None,
422432
) -> Any:
423433
"""
424434
Serialize/marshal a Python object to a JSON-serializable Python object including transforming and filtering data.
@@ -440,6 +450,8 @@ def to_jsonable_python(
440450
`"<Unserializable {value_type} object>"` will be used.
441451
fallback: A function to call when an unknown value is encountered,
442452
if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
453+
context: The context to use for serialization, this is passed to functional serializers as
454+
[`info.context`][pydantic_core.core_schema.SerializationInfo.context].
443455
444456
Raises:
445457
PydanticSerializationError: If serialization fails and no `fallback` function is provided.

python/pydantic_core/core_schema.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ def include(self) -> IncExCall: ...
122122
@property
123123
def exclude(self) -> IncExCall: ...
124124

125+
@property
126+
def context(self) -> Any | None:
127+
"""Current serialization context."""
128+
125129
@property
126130
def mode(self) -> str: ...
127131

src/errors/validation_exception.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ impl ValidationError {
320320
include_input: bool,
321321
) -> PyResult<&'py PyString> {
322322
let state = SerializationState::new("iso8601", "utf8", "constants")?;
323-
let extra = state.extra(py, &SerMode::Json, true, false, false, true, None);
323+
let extra = state.extra(py, &SerMode::Json, true, false, false, true, None, None);
324324
let serializer = ValidationErrorSerializer {
325325
py,
326326
line_errors: &self.line_errors,

src/serializers/extra.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl SerializationState {
4545
round_trip: bool,
4646
serialize_unknown: bool,
4747
fallback: Option<&'py PyAny>,
48+
context: Option<&'py PyAny>,
4849
) -> Extra<'py> {
4950
Extra::new(
5051
py,
@@ -59,6 +60,7 @@ impl SerializationState {
5960
&self.rec_guard,
6061
serialize_unknown,
6162
fallback,
63+
context,
6264
)
6365
}
6466

@@ -90,6 +92,7 @@ pub(crate) struct Extra<'a> {
9092
pub field_name: Option<&'a str>,
9193
pub serialize_unknown: bool,
9294
pub fallback: Option<&'a PyAny>,
95+
pub context: Option<&'a PyAny>,
9396
}
9497

9598
impl<'a> Extra<'a> {
@@ -107,6 +110,7 @@ impl<'a> Extra<'a> {
107110
rec_guard: &'a SerRecursionState,
108111
serialize_unknown: bool,
109112
fallback: Option<&'a PyAny>,
113+
context: Option<&'a PyAny>,
110114
) -> Self {
111115
Self {
112116
mode,
@@ -124,6 +128,7 @@ impl<'a> Extra<'a> {
124128
field_name: None,
125129
serialize_unknown,
126130
fallback,
131+
context,
127132
}
128133
}
129134

@@ -178,10 +183,11 @@ pub(crate) struct ExtraOwned {
178183
config: SerializationConfig,
179184
rec_guard: SerRecursionState,
180185
check: SerCheck,
181-
model: Option<PyObject>,
186+
pub model: Option<PyObject>,
182187
field_name: Option<String>,
183188
serialize_unknown: bool,
184-
fallback: Option<PyObject>,
189+
pub fallback: Option<PyObject>,
190+
pub context: Option<PyObject>,
185191
}
186192

187193
impl ExtraOwned {
@@ -201,6 +207,7 @@ impl ExtraOwned {
201207
field_name: extra.field_name.map(ToString::to_string),
202208
serialize_unknown: extra.serialize_unknown,
203209
fallback: extra.fallback.map(Into::into),
210+
context: extra.context.map(Into::into),
204211
}
205212
}
206213

@@ -221,6 +228,7 @@ impl ExtraOwned {
221228
field_name: self.field_name.as_deref(),
222229
serialize_unknown: self.serialize_unknown,
223230
fallback: self.fallback.as_ref().map(|m| m.as_ref(py)),
231+
context: self.context.as_ref().map(|m| m.as_ref(py)),
224232
}
225233
}
226234
}

src/serializers/infer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ pub(crate) fn infer_to_python_known(
9999
extra.rec_guard,
100100
extra.serialize_unknown,
101101
extra.fallback,
102+
extra.context,
102103
);
103104
serializer.serializer.to_python(value, include, exclude, &extra)
104105
};
@@ -468,6 +469,7 @@ pub(crate) fn infer_serialize_known<S: Serializer>(
468469
extra.rec_guard,
469470
extra.serialize_unknown,
470471
extra.fallback,
472+
extra.context,
471473
);
472474
let pydantic_serializer =
473475
PydanticSerializer::new(value, &extracted_serializer.serializer, include, exclude, &extra);

src/serializers/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ impl SchemaSerializer {
5555
rec_guard: &'a SerRecursionState,
5656
serialize_unknown: bool,
5757
fallback: Option<&'a PyAny>,
58+
context: Option<&'a PyAny>,
5859
) -> Extra<'b> {
5960
Extra::new(
6061
py,
@@ -69,6 +70,7 @@ impl SchemaSerializer {
6970
rec_guard,
7071
serialize_unknown,
7172
fallback,
73+
context,
7274
)
7375
}
7476
}
@@ -95,7 +97,7 @@ impl SchemaSerializer {
9597
#[allow(clippy::too_many_arguments)]
9698
#[pyo3(signature = (value, *, mode = None, include = None, exclude = None, by_alias = true,
9799
exclude_unset = false, exclude_defaults = false, exclude_none = false, round_trip = false, warnings = true,
98-
fallback = None))]
100+
fallback = None, context = None))]
99101
pub fn to_python(
100102
&self,
101103
py: Python,
@@ -110,6 +112,7 @@ impl SchemaSerializer {
110112
round_trip: bool,
111113
warnings: bool,
112114
fallback: Option<&PyAny>,
115+
context: Option<&PyAny>,
113116
) -> PyResult<PyObject> {
114117
let mode: SerMode = mode.into();
115118
let warnings = CollectWarnings::new(warnings);
@@ -126,6 +129,7 @@ impl SchemaSerializer {
126129
&rec_guard,
127130
false,
128131
fallback,
132+
context,
129133
);
130134
let v = self.serializer.to_python(value, include, exclude, &extra)?;
131135
warnings.final_check(py)?;
@@ -135,7 +139,7 @@ impl SchemaSerializer {
135139
#[allow(clippy::too_many_arguments)]
136140
#[pyo3(signature = (value, *, indent = None, include = None, exclude = None, by_alias = true,
137141
exclude_unset = false, exclude_defaults = false, exclude_none = false, round_trip = false, warnings = true,
138-
fallback = None))]
142+
fallback = None, context = None))]
139143
pub fn to_json(
140144
&self,
141145
py: Python,
@@ -150,6 +154,7 @@ impl SchemaSerializer {
150154
round_trip: bool,
151155
warnings: bool,
152156
fallback: Option<&PyAny>,
157+
context: Option<&PyAny>,
153158
) -> PyResult<PyObject> {
154159
let warnings = CollectWarnings::new(warnings);
155160
let rec_guard = SerRecursionState::default();
@@ -165,6 +170,7 @@ impl SchemaSerializer {
165170
&rec_guard,
166171
false,
167172
fallback,
173+
context,
168174
);
169175
let bytes = to_json_bytes(
170176
value,
@@ -213,7 +219,7 @@ impl SchemaSerializer {
213219
#[pyfunction]
214220
#[pyo3(signature = (value, *, indent = None, include = None, exclude = None, by_alias = true,
215221
exclude_none = false, round_trip = false, timedelta_mode = "iso8601", bytes_mode = "utf8",
216-
inf_nan_mode = "constants", serialize_unknown = false, fallback = None))]
222+
inf_nan_mode = "constants", serialize_unknown = false, fallback = None, context = None))]
217223
pub fn to_json(
218224
py: Python,
219225
value: &PyAny,
@@ -228,6 +234,7 @@ pub fn to_json(
228234
inf_nan_mode: &str,
229235
serialize_unknown: bool,
230236
fallback: Option<&PyAny>,
237+
context: Option<&PyAny>,
231238
) -> PyResult<PyObject> {
232239
let state = SerializationState::new(timedelta_mode, bytes_mode, inf_nan_mode)?;
233240
let extra = state.extra(
@@ -238,6 +245,7 @@ pub fn to_json(
238245
round_trip,
239246
serialize_unknown,
240247
fallback,
248+
context,
241249
);
242250
let serializer = type_serializers::any::AnySerializer.into();
243251
let bytes = to_json_bytes(value, &serializer, include, exclude, &extra, indent, 1024)?;
@@ -249,7 +257,7 @@ pub fn to_json(
249257
#[allow(clippy::too_many_arguments)]
250258
#[pyfunction]
251259
#[pyo3(signature = (value, *, include = None, exclude = None, by_alias = true, exclude_none = false, round_trip = false,
252-
timedelta_mode = "iso8601", bytes_mode = "utf8", inf_nan_mode = "constants", serialize_unknown = false, fallback = None))]
260+
timedelta_mode = "iso8601", bytes_mode = "utf8", inf_nan_mode = "constants", serialize_unknown = false, fallback = None, context = None))]
253261
pub fn to_jsonable_python(
254262
py: Python,
255263
value: &PyAny,
@@ -263,6 +271,7 @@ pub fn to_jsonable_python(
263271
inf_nan_mode: &str,
264272
serialize_unknown: bool,
265273
fallback: Option<&PyAny>,
274+
context: Option<&PyAny>,
266275
) -> PyResult<PyObject> {
267276
let state = SerializationState::new(timedelta_mode, bytes_mode, inf_nan_mode)?;
268277
let extra = state.extra(
@@ -273,6 +282,7 @@ pub fn to_jsonable_python(
273282
round_trip,
274283
serialize_unknown,
275284
fallback,
285+
context,
276286
);
277287
let v = infer::infer_to_python(value, include, exclude, &extra)?;
278288
state.final_check(py)?;

0 commit comments

Comments
 (0)