Skip to content

Commit 82fb0e9

Browse files
authored
support __pydantic_extra__ when serializing (#576)
1 parent 944bf91 commit 82fb0e9

File tree

17 files changed

+653
-395
lines changed

17 files changed

+653
-395
lines changed

pydantic_core/core_schema.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class CoreConfig(TypedDict, total=False):
3030
config_choose_priority: int
3131
# if configs are merged, which should take precedence, default 0, default means child takes precedence
3232
config_merge_priority: int
33-
# settings related to typed_dicts only
33+
# settings related to typed dicts, model fields, dataclass fields
3434
extra_fields_behavior: ExtraBehavior
3535
typed_dict_total: bool # default: True
3636
# used on typed-dicts and tagged union keys
@@ -2841,6 +2841,7 @@ class ModelSchema(TypedDict, total=False):
28412841
revalidate_instances: Literal['always', 'never', 'subclass-instances'] # default: 'never'
28422842
strict: bool
28432843
frozen: bool
2844+
extra_behavior: ExtraBehavior
28442845
config: CoreConfig
28452846
ref: str
28462847
metadata: Any
@@ -2855,6 +2856,7 @@ def model_schema(
28552856
revalidate_instances: Literal['always', 'never', 'subclass-instances'] | None = None,
28562857
strict: bool | None = None,
28572858
frozen: bool | None = None,
2859+
extra_behavior: ExtraBehavior | None = None,
28582860
config: CoreConfig | None = None,
28592861
ref: str | None = None,
28602862
metadata: Any = None,
@@ -2894,6 +2896,7 @@ class MyModel:
28942896
should re-validate defaults to config.revalidate_instances, else 'never'
28952897
strict: Whether the model is strict
28962898
frozen: Whether the model is frozen
2899+
extra_behavior: The extra behavior to use for the model, used in serialization
28972900
config: The config to use for the model
28982901
ref: optional unique identifier of the schema, used to reference the schema in other places
28992902
metadata: Any other information you want to include with the schema, not used by pydantic-core
@@ -2907,6 +2910,7 @@ class MyModel:
29072910
revalidate_instances=revalidate_instances,
29082911
strict=strict,
29092912
frozen=frozen,
2913+
extra_behavior=extra_behavior,
29102914
config=config,
29112915
ref=ref,
29122916
metadata=metadata,

src/build_tools.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::borrow::Cow;
2+
use std::cmp::Ordering;
23
use std::error::Error;
34
use std::fmt;
45

@@ -280,3 +281,43 @@ impl ExtraBehavior {
280281
Ok(res)
281282
}
282283
}
284+
285+
pub(crate) fn build_model_config<'a>(
286+
py: Python<'a>,
287+
schema: &'a PyDict,
288+
parent_config: Option<&'a PyDict>,
289+
) -> PyResult<Option<&'a PyDict>> {
290+
let child_config: Option<&PyDict> = schema.get_as(intern!(py, "config"))?;
291+
match (parent_config, child_config) {
292+
(Some(parent), None) => Ok(Some(parent)),
293+
(None, Some(child)) => Ok(Some(child)),
294+
(None, None) => Ok(None),
295+
(Some(parent), Some(child)) => {
296+
let key = intern!(py, "config_choose_priority");
297+
let parent_choose: i32 = parent.get_as(key)?.unwrap_or_default();
298+
let child_choose: i32 = child.get_as(key)?.unwrap_or_default();
299+
match parent_choose.cmp(&child_choose) {
300+
Ordering::Greater => Ok(Some(parent)),
301+
Ordering::Less => Ok(Some(child)),
302+
Ordering::Equal => {
303+
let key = intern!(py, "config_merge_priority");
304+
let parent_merge: i32 = parent.get_as(key)?.unwrap_or_default();
305+
let child_merge: i32 = child.get_as(key)?.unwrap_or_default();
306+
match parent_merge.cmp(&child_merge) {
307+
Ordering::Greater => {
308+
let new_child = child.copy()?;
309+
new_child.update(parent.as_mapping())?;
310+
Ok(Some(new_child))
311+
}
312+
// otherwise child is the winner
313+
_ => {
314+
let new_parent = parent.copy()?;
315+
new_parent.update(child.as_mapping())?;
316+
Ok(Some(new_parent))
317+
}
318+
}
319+
}
320+
}
321+
}
322+
}
323+
}

0 commit comments

Comments
 (0)