Skip to content

Commit 5eab2a7

Browse files
committed
tests for info.data
1 parent 7a10d8a commit 5eab2a7

File tree

2 files changed

+45
-11
lines changed

2 files changed

+45
-11
lines changed

src/validators/function.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use pyo3::exceptions::{PyAssertionError, PyAttributeError, PyRuntimeError, PyTypeError, PyValueError};
22
use pyo3::intern;
33
use pyo3::prelude::*;
4-
use pyo3::types::{PyAny, PyDict};
4+
use pyo3::types::{PyAny, PyDict, PyString};
55

66
use crate::build_tools::{function_name, py_err, SchemaDict};
77
use crate::errors::{
@@ -354,17 +354,18 @@ impl ValidationInfo {
354354
#[pymethods]
355355
impl ValidationInfo {
356356
#[getter]
357-
fn get_data(&self) -> PyResult<Py<PyDict>> {
358-
self.data
359-
.as_ref()
360-
.cloned()
361-
.ok_or_else(|| PyAttributeError::new_err("No attribute named 'data'"))
357+
fn get_data(&self, py: Python) -> PyResult<Py<PyDict>> {
358+
match self.data {
359+
Some(ref data) => Ok(data.clone_ref(py)),
360+
None => Err(PyAttributeError::new_err("No attribute named 'data'")),
361+
}
362362
}
363+
363364
#[getter]
364-
fn get_field_name(&self) -> PyResult<String> {
365-
self.field_name
366-
.as_ref()
367-
.cloned()
368-
.ok_or_else(|| PyAttributeError::new_err("No attribute named 'field_name'"))
365+
fn get_field_name<'py>(&self, py: Python<'py>) -> PyResult<&'py PyString> {
366+
match self.field_name {
367+
Some(ref field_name) => Ok(PyString::new(py, field_name)),
368+
None => Err(PyAttributeError::new_err("No attribute named 'field_name'")),
369+
}
369370
}
370371
}

tests/validators/test_function.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,3 +716,36 @@ def f(*args: Any, **kwargs: Any) -> Any: # pragma: no cover
716716
match='This validator expected to be run inside the context of a model field but no model field was found',
717717
):
718718
v.validate_python(123)
719+
720+
721+
def test_typed_dict_data() -> None:
722+
info_stuff = None
723+
724+
def f(input_value: Any, info: core_schema.ModelFieldValidationInfo) -> Any:
725+
nonlocal info_stuff
726+
info_stuff = {'field_name': info.field_name, 'data': info.data.copy()}
727+
assert isinstance(input_value, str)
728+
return f'input: {input_value}'
729+
730+
v = SchemaValidator(
731+
core_schema.typed_dict_schema(
732+
{
733+
'a': core_schema.typed_dict_field(core_schema.int_schema()),
734+
'b': core_schema.typed_dict_field(core_schema.int_schema()),
735+
'c': core_schema.typed_dict_field(
736+
core_schema.field_after_validation_function(f, core_schema.str_schema())
737+
),
738+
}
739+
)
740+
)
741+
742+
data = v.validate_python({'a': 1, 'b': '2', 'c': b'foo'})
743+
assert data == {'a': 1, 'b': 2, 'c': 'input: foo'}
744+
assert info_stuff == {'field_name': 'c', 'data': {'a': 1, 'b': 2}}
745+
746+
info_stuff = None
747+
748+
with pytest.raises(ValidationError, match=r'b\s+Input should be a valid integer'):
749+
v.validate_python({'a': 1, 'b': 'wrong', 'c': b'foo'})
750+
751+
assert info_stuff == {'field_name': 'c', 'data': {'a': 1}}

0 commit comments

Comments
 (0)