Skip to content

Commit 51be332

Browse files
committed
Do not inherit config in model fields
1 parent 2a7a2f6 commit 51be332

File tree

5 files changed

+6
-175
lines changed

5 files changed

+6
-175
lines changed

pydantic_core/core_schema.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ def dict_not_none(**kwargs: Any) -> Any:
2626
class CoreConfig(TypedDict, total=False):
2727
title: str
2828
strict: bool
29-
# higher priority configs take precedence of over lower, if priority matches the two configs are merged, default 0
30-
config_choose_priority: int
31-
# if configs are merged, which should take precedence, default 0, default means child takes precedence
32-
config_merge_priority: int
3329
# settings related to typed dicts, model fields, dataclass fields
3430
extra_fields_behavior: ExtraBehavior
3531
typed_dict_total: bool # default: True

src/build_tools.rs

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

@@ -281,43 +280,3 @@ impl ExtraBehavior {
281280
Ok(res)
282281
}
283282
}
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-
}

src/serializers/type_serializers/model.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use pyo3::{intern, PyTraverseError, PyVisit};
66

77
use ahash::AHashMap;
88

9-
use crate::build_tools::{build_model_config, py_error_type, ExtraBehavior, SchemaDict};
9+
use crate::build_tools::{py_error_type, ExtraBehavior, SchemaDict};
1010
use crate::definitions::DefinitionsBuilder;
1111

1212
use super::{
@@ -74,13 +74,13 @@ impl BuildSerializer for ModelSerializer {
7474

7575
fn build(
7676
schema: &PyDict,
77-
config: Option<&PyDict>,
77+
_config: Option<&PyDict>,
7878
definitions: &mut DefinitionsBuilder<CombinedSerializer>,
7979
) -> PyResult<CombinedSerializer> {
8080
let py = schema.py();
8181

8282
// models ignore the parent config and always use the config from this model
83-
let config = build_model_config(py, schema, config)?;
83+
let config = schema.get_as(intern!(py, "config"))?;
8484

8585
let class: &PyType = schema.get_as_req(intern!(py, "cls"))?;
8686
let sub_schema: &PyDict = schema.get_as_req(intern!(py, "schema"))?;

src/validators/model.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use pyo3::prelude::*;
66
use pyo3::types::{PyDict, PySet, PyString, PyTuple, PyType};
77
use pyo3::{ffi, intern};
88

9-
use crate::build_tools::{build_model_config, py_err, schema_or_config_same, SchemaDict};
9+
use crate::build_tools::{py_err, schema_or_config_same, SchemaDict};
1010
use crate::errors::{ErrorType, ValError, ValResult};
1111
use crate::input::{py_error_on_minusone, Input, InputType};
1212
use crate::recursion_guard::RecursionGuard;
@@ -64,12 +64,12 @@ impl BuildValidator for ModelValidator {
6464

6565
fn build(
6666
schema: &PyDict,
67-
config: Option<&PyDict>,
67+
_config: Option<&PyDict>,
6868
definitions: &mut DefinitionsBuilder<CombinedValidator>,
6969
) -> PyResult<CombinedValidator> {
7070
let py = schema.py();
7171
// models ignore the parent config and always use the config from this model
72-
let config = build_model_config(py, schema, config)?;
72+
let config = schema.get_as(intern!(py, "config"))?;
7373

7474
let class: &PyType = schema.get_as_req(intern!(py, "cls"))?;
7575
let sub_schema: &PyAny = schema.get_as_req(intern!(py, "schema"))?;

tests/test_config.py

Lines changed: 0 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -68,130 +68,6 @@ def test_field_priority_model():
6868
assert v.isinstance_python({'f': 'test long'}) is False
6969

7070

71-
def test_parent_priority():
72-
v = SchemaValidator(
73-
{
74-
'type': 'model',
75-
'cls': MyModel,
76-
'schema': {'type': 'model-fields', 'fields': {'f': {'type': 'model-field', 'schema': {'type': 'str'}}}},
77-
'config': {'str_min_length': 2, 'str_max_length': 10},
78-
},
79-
{'str_max_length': 5, 'config_choose_priority': 1},
80-
)
81-
r = plain_repr(v)
82-
assert 'min_length:Some(2)' not in r # child is ignored
83-
assert 'max_length:Some(5)' in r
84-
assert v.isinstance_python({'f': 'test'}) is True
85-
assert v.isinstance_python({'f': 't'}) is True
86-
assert v.isinstance_python({'f': 'test long'}) is False
87-
88-
89-
def test_child_priority():
90-
v = SchemaValidator(
91-
{
92-
'type': 'model',
93-
'cls': MyModel,
94-
'schema': {'type': 'model-fields', 'fields': {'f': {'type': 'model-field', 'schema': {'type': 'str'}}}},
95-
'config': {'str_max_length': 5, 'config_choose_priority': 1},
96-
},
97-
{'str_min_length': 2, 'str_max_length': 10},
98-
)
99-
r = plain_repr(v)
100-
assert 'min_length:Some(2)' not in r # parent is ignored
101-
assert 'max_length:Some(5)' in r
102-
assert v.isinstance_python({'f': 'test'}) is True
103-
assert v.isinstance_python({'f': 't'}) is True
104-
assert v.isinstance_python({'f': 'test long'}) is False
105-
106-
107-
def test_merge_child_wins():
108-
v = SchemaValidator(
109-
{
110-
'type': 'model',
111-
'cls': MyModel,
112-
'schema': {'type': 'model-fields', 'fields': {'f': {'type': 'model-field', 'schema': {'type': 'str'}}}},
113-
'config': {'str_max_length': 5},
114-
},
115-
{'str_min_length': 2, 'str_max_length': 10},
116-
)
117-
r = plain_repr(v)
118-
assert 'min_length:Some(2)' in r
119-
assert 'max_length:Some(5)' in r
120-
assert v.isinstance_python({'f': 'test'}) is True
121-
assert v.isinstance_python({'f': 't'}) is False
122-
assert v.isinstance_python({'f': 'test long'}) is False
123-
124-
125-
def test_merge_parent_wins():
126-
v = SchemaValidator(
127-
{
128-
'type': 'model',
129-
'cls': MyModel,
130-
'schema': {'type': 'model-fields', 'fields': {'f': {'type': 'model-field', 'schema': {'type': 'str'}}}},
131-
'config': {'str_max_length': 5},
132-
},
133-
{'str_min_length': 2, 'str_max_length': 10, 'config_merge_priority': 1},
134-
)
135-
r = plain_repr(v)
136-
assert 'min_length:Some(2)' in r
137-
assert 'max_length:Some(10)' in r
138-
assert 'max_length:Some(5)' not in r
139-
assert v.isinstance_python({'f': 'test'}) is True
140-
assert v.isinstance_python({'f': 't'}) is False
141-
assert v.isinstance_python({'f': 'test long'}) is True
142-
assert v.isinstance_python({'f': 'test very long'}) is False
143-
144-
145-
def test_sub_model_merge():
146-
v = SchemaValidator(
147-
{
148-
'type': 'model',
149-
'cls': MyModel,
150-
'schema': {
151-
'type': 'model-fields',
152-
'fields': {
153-
'f': {'type': 'model-field', 'schema': {'type': 'str'}},
154-
'sub_model': {
155-
'type': 'model-field',
156-
'schema': {
157-
'type': 'model',
158-
'cls': MyModel,
159-
'schema': {
160-
'type': 'model-fields',
161-
'fields': {'f': {'type': 'model-field', 'schema': {'type': 'str'}}},
162-
},
163-
'config': {'str_max_length': 6, 'str_to_upper': True},
164-
},
165-
},
166-
},
167-
},
168-
'config': {'str_min_length': 1, 'str_max_length': 4},
169-
}
170-
)
171-
# f should have bounds 1-4
172-
# sub_model.f should have bounds 1-6, and should be upper-cased
173-
output = v.validate_python({'f': 'test', 'sub_model': {'f': 'tests'}})
174-
assert output == IsInstance(MyModel) & HasAttributes(f='test', sub_model=HasAttributes(f='TESTS'))
175-
with pytest.raises(ValidationError) as exc_info:
176-
v.validate_python({'f': 'tests', 'sub_model': {'f': ''}})
177-
assert exc_info.value.errors(include_url=False) == [
178-
{
179-
'type': 'string_too_long',
180-
'loc': ('f',),
181-
'msg': 'String should have at most 4 characters',
182-
'input': 'tests',
183-
'ctx': {'max_length': 4},
184-
},
185-
{
186-
'type': 'string_too_short',
187-
'loc': ('sub_model', 'f'),
188-
'msg': 'String should have at least 1 characters',
189-
'input': '',
190-
'ctx': {'min_length': 1},
191-
},
192-
]
193-
194-
19571
@pytest.mark.parametrize(
19672
'config,float_field_schema,input_value,expected',
19773
[

0 commit comments

Comments
 (0)