From c554f08d41778fd5e4660724966e9cfac609b689 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 4 Nov 2024 20:01:11 +0000 Subject: [PATCH] fix panic in `validate_assignment` when field has gone missing --- src/validators/model_fields.rs | 15 ++++++++++----- tests/validators/test_model.py | 26 ++++++++++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/validators/model_fields.rs b/src/validators/model_fields.rs index eb4829c7f..39f905d84 100644 --- a/src/validators/model_fields.rs +++ b/src/validators/model_fields.rs @@ -421,11 +421,16 @@ impl Validator for ModelFieldsValidator { let new_extra = match &self.extra_behavior { ExtraBehavior::Allow => { let non_extra_data = PyDict::new_bound(py); - self.fields.iter().for_each(|f| { - let popped_value = PyAnyMethods::get_item(&**new_data, &f.name).unwrap(); - new_data.del_item(&f.name).unwrap(); - non_extra_data.set_item(&f.name, popped_value).unwrap(); - }); + self.fields.iter().try_for_each(|f| -> PyResult<()> { + let Some(popped_value) = new_data.get_item(&f.name)? else { + // field not present in __dict__ for some reason; let the rest of the + // validation pipeline handle it later + return Ok(()); + }; + new_data.del_item(&f.name)?; + non_extra_data.set_item(&f.name, popped_value)?; + Ok(()) + })?; let new_extra = new_data.copy()?; new_data.clear(); new_data.update(non_extra_data.as_mapping())?; diff --git a/tests/validators/test_model.py b/tests/validators/test_model.py index b6a60f55c..8814a5e65 100644 --- a/tests/validators/test_model.py +++ b/tests/validators/test_model.py @@ -1036,17 +1036,17 @@ def __init__(self): self.__pydantic_extra__ = None v = SchemaValidator( - { - 'type': 'model', - 'cls': MyModel, - 'schema': { - 'type': 'model-fields', - 'fields': { - 'field_a': {'type': 'model-field', 'schema': {'type': 'str'}}, - 'field_b': {'type': 'model-field', 'schema': {'type': 'int'}}, + core_schema.model_schema( + MyModel, + core_schema.model_fields_schema( + { + 'field_a': core_schema.model_field(core_schema.str_schema()), + 'field_b': core_schema.model_field(core_schema.int_schema()), }, - }, - } + extra_behavior='allow', + ), + extra_behavior='allow', + ) ) m = MyModel() @@ -1063,6 +1063,12 @@ def __init__(self): v.validate_assignment(m, 'field_b', '322', from_attributes=True) assert m.field_b == 322 + # try deleting a field + del m.field_b + # assignment to `field_a` should not care about `field_b` missing + v.validate_assignment(m, 'field_a', 'hello world', from_attributes=True) + assert m.field_a == 'hello world' + def test_validate_assignment_function(): class MyModel: