diff --git a/src/errors/validation_exception.rs b/src/errors/validation_exception.rs index 91ef60a0d..e77b21974 100644 --- a/src/errors/validation_exception.rs +++ b/src/errors/validation_exception.rs @@ -341,6 +341,20 @@ impl ValidationError { fn __str__(&self, py: Python) -> String { self.__repr__(py) } + + fn __reduce__(slf: &PyCell) -> PyResult<(&PyAny, PyObject)> { + let py = slf.py(); + let callable = slf.getattr("from_exception_data")?; + let borrow = slf.try_borrow()?; + let args = ( + borrow.title.as_ref(py), + borrow.errors(py, include_url_env(py), true, true)?, + borrow.input_type.into_py(py), + borrow.hide_input, + ) + .into_py(slf.py()); + Ok((callable, args)) + } } // TODO: is_utf8_char_boundary, floor_char_boundary and ceil_char_boundary diff --git a/tests/test_errors.py b/tests/test_errors.py index 05815aec5..88dcace8f 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -1,4 +1,5 @@ import enum +import pickle import re import sys from decimal import Decimal @@ -1074,3 +1075,17 @@ def test_hide_input_in_json() -> None: for error in exc_info.value.errors(include_input=False): assert 'input' not in error + + +@pytest.mark.skipif( + sys.version_info < (3, 9) and sys.implementation.name == 'pypy', + reason='PyPy before 3.9 cannot pickle this correctly', +) +def test_validation_error_pickle() -> None: + s = SchemaValidator({'type': 'int'}) + with pytest.raises(ValidationError) as exc_info: + s.validate_python('definitely not an int') + + original = exc_info.value + roundtripped = pickle.loads(pickle.dumps(original)) + assert original.errors() == roundtripped.errors()