@@ -2,6 +2,9 @@ use std::fmt;
22
33use pyo3:: exceptions:: PyValueError ;
44use pyo3:: prelude:: * ;
5+ use pyo3:: types:: PyString ;
6+
7+ use crate :: tools:: truncate_safe_repr;
58
69use serde:: ser;
710
@@ -44,9 +47,9 @@ pub(super) fn se_err_py_err(error: PythonSerializerError) -> PyErr {
4447 let s = error. to_string ( ) ;
4548 if let Some ( msg) = s. strip_prefix ( UNEXPECTED_TYPE_SER_MARKER ) {
4649 if msg. is_empty ( ) {
47- PydanticSerializationUnexpectedValue :: new_err ( None )
50+ PydanticSerializationUnexpectedValue :: new_from_msg ( None )
4851 } else {
49- PydanticSerializationUnexpectedValue :: new_err ( Some ( msg. to_string ( ) ) )
52+ PydanticSerializationUnexpectedValue :: new_from_msg ( Some ( msg. to_string ( ) ) )
5053 }
5154 } else if let Some ( msg) = s. strip_prefix ( SERIALIZATION_ERR_MARKER ) {
5255 PydanticSerializationError :: new_err ( msg. to_string ( ) )
@@ -94,30 +97,57 @@ impl PydanticSerializationError {
9497#[ derive( Debug , Clone ) ]
9598pub struct PydanticSerializationUnexpectedValue {
9699 message : Option < String > ,
100+ field_type : Option < String > ,
101+ input_value : Option < PyObject > ,
97102}
98103
99104impl PydanticSerializationUnexpectedValue {
100- pub ( crate ) fn new_err ( msg : Option < String > ) -> PyErr {
101- PyErr :: new :: < Self , Option < String > > ( msg)
105+ pub fn new_from_msg ( message : Option < String > ) -> PyErr {
106+ PyErr :: new :: < Self , ( Option < String > , Option < String > , Option < PyObject > ) > ( ( message, None , None ) )
107+ }
108+
109+ pub fn new_from_parts ( field_type : String , input_value : PyObject ) -> PyErr {
110+ PyErr :: new :: < Self , ( Option < String > , Option < String > , Option < PyObject > ) > ( (
111+ None ,
112+ Some ( field_type) ,
113+ Some ( input_value) ,
114+ ) )
102115 }
103116}
104117
105118#[ pymethods]
106119impl PydanticSerializationUnexpectedValue {
107120 #[ new]
108- #[ pyo3( signature = ( message=None ) ) ]
109- fn py_new ( message : Option < String > ) -> Self {
110- Self { message }
121+ #[ pyo3( signature = ( message=None , field_type=None , input_value=None ) ) ]
122+ fn py_new ( message : Option < String > , field_type : Option < String > , input_value : Option < PyObject > ) -> Self {
123+ Self {
124+ message,
125+ field_type,
126+ input_value,
127+ }
111128 }
112129
113- fn __str__ ( & self ) -> & str {
114- match self . message {
115- Some ( ref s) => s,
116- None => "Unexpected Value" ,
130+ fn __str__ ( & self , py : Python ) -> String {
131+ match & self . message {
132+ Some ( s) => s. to_string ( ) ,
133+ None => match ( & self . field_type , & self . input_value ) {
134+ ( Some ( ref field_type) , Some ( ref input_value) ) => {
135+ let bound_input = input_value. bind ( py) ;
136+
137+ let type_name = bound_input
138+ . get_type ( )
139+ . qualname ( )
140+ . unwrap_or_else ( |_| PyString :: new ( py, "<unknown python object>" ) ) ;
141+
142+ let value_str = truncate_safe_repr ( bound_input, None ) ;
143+ format ! ( "Expected `{field_type}` but got `{type_name}` with value `{value_str}` - serialized value may not be as expected" )
144+ }
145+ _ => "Unexpected value - serialized value may not be as expected" . to_string ( ) ,
146+ } ,
117147 }
118148 }
119149
120- pub ( crate ) fn __repr__ ( & self ) -> String {
121- format ! ( "PydanticSerializationUnexpectedValue({})" , self . __str__( ) )
150+ pub ( crate ) fn __repr__ ( & self , py : Python ) -> String {
151+ format ! ( "PydanticSerializationUnexpectedValue({})" , self . __str__( py ) )
122152 }
123153}
0 commit comments