Skip to content

Commit ee72ba9

Browse files
committed
support typeddicts where not all fields are not required
1 parent 0d453a0 commit ee72ba9

File tree

2 files changed

+11
-7
lines changed

2 files changed

+11
-7
lines changed

src/validators/typed_dict.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ pub struct TypedDictValidator {
3535
extras_validator: Option<Box<CombinedValidator>>,
3636
strict: bool,
3737
loc_by_alias: bool,
38-
allow_partial: bool,
3938
}
4039

4140
impl BuildValidator for TypedDictValidator {
@@ -125,14 +124,12 @@ impl BuildValidator for TypedDictValidator {
125124
required,
126125
});
127126
}
128-
let allow_partial = fields.iter().all(|f| !f.required);
129127
Ok(Self {
130128
fields,
131129
extra_behavior,
132130
extras_validator,
133131
strict,
134132
loc_by_alias: config.get_as(intern!(py, "loc_by_alias"))?.unwrap_or(true),
135-
allow_partial,
136133
}
137134
.into())
138135
}
@@ -209,7 +206,7 @@ impl Validator for TypedDictValidator {
209206
}
210207
Err(ValError::Omit) => continue,
211208
Err(ValError::LineErrors(line_errors)) => {
212-
if !is_last_partial {
209+
if !is_last_partial || field.required {
213210
for err in line_errors {
214211
errors.push(lookup_path.apply_error_loc(err, self.loc_by_alias, &field.name));
215212
}
@@ -365,6 +362,6 @@ impl Validator for TypedDictValidator {
365362
}
366363

367364
fn supports_partial(&self) -> bool {
368-
self.allow_partial
365+
true
369366
}
370367
}

tests/validators/test_allow_partial.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ def test_partial_typed_dict():
138138

139139
assert v.validate_python({'a': 11, 'b': '12', 'c': 13}) == snapshot(IsStrictDict(a=11, b=12, c=13))
140140
assert v.validate_python({'a': 11, 'c': 13, 'b': '12'}) == snapshot(IsStrictDict(a=11, b=12, c=13))
141+
assert v.validate_python(MyMapping({'a': 11, 'c': 13, 'b': '12'})) == snapshot(IsStrictDict(a=11, b=12, c=13))
141142

142143
assert v.validate_python({'a': 11, 'b': '12', 'c': 13}, allow_partial=True) == snapshot({'a': 11, 'b': 12, 'c': 13})
143144
with pytest.raises(ValidationError) as exc_info:
@@ -154,6 +155,9 @@ def test_partial_typed_dict():
154155
]
155156
)
156157
assert v.validate_python({'a': 11, 'b': '12', 'c': 1}, allow_partial=True) == snapshot(IsStrictDict(a=11, b=12))
158+
assert v.validate_python(MyMapping({'a': 11, 'b': '12', 'c': 1}), allow_partial=True) == snapshot(
159+
IsStrictDict(a=11, b=12)
160+
)
157161
assert v.validate_python({'a': 11, 'c': 13, 'b': 1}, allow_partial=True) == snapshot(IsStrictDict(a=11, c=13))
158162
with pytest.raises(ValidationError) as exc_info:
159163
v.validate_python({'a': 11, 'c': 1, 'b': 12}, allow_partial=True)
@@ -202,8 +206,11 @@ def test_non_partial_typed_dict():
202206
assert v.validate_python({'a': 11, 'b': '12', 'c': 13}) == snapshot({'a': 11, 'b': 12, 'c': 13})
203207
with pytest.raises(ValidationError, match='Input should be greater than 10'):
204208
v.validate_python({'a': 11, 'b': '12', 'c': 1})
205-
with pytest.raises(ValidationError, match='Input should be greater than 10'):
206-
v.validate_python({'a': 11, 'b': '12', 'c': 1}, allow_partial=False)
209+
assert v.validate_python({'a': 11, 'b': '12', 'c': 1}, allow_partial=True) == snapshot({'a': 11, 'b': 12})
210+
with pytest.raises(ValidationError, match=r'b\s+Field required'):
211+
v.validate_python({'a': 11, 'c': 12}, allow_partial=True)
212+
with pytest.raises(ValidationError, match=r'b\s+Input should be greater than 10'):
213+
v.validate_python({'a': 11, 'c': 12, 'b': 1}, allow_partial=True)
207214

208215

209216
def test_double_nested():

0 commit comments

Comments
 (0)