@@ -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 ) . to_py_err ( )
4851 } else {
49- PydanticSerializationUnexpectedValue :: new_err ( Some ( msg. to_string ( ) ) )
52+ PydanticSerializationUnexpectedValue :: new_from_msg ( Some ( msg. to_string ( ) ) ) . to_py_err ( )
5053 }
5154 } else if let Some ( msg) = s. strip_prefix ( SERIALIZATION_ERR_MARKER ) {
5255 PydanticSerializationError :: new_err ( msg. to_string ( ) )
@@ -94,30 +97,90 @@ 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 > ) -> Self {
106+ Self {
107+ message,
108+ field_type : None ,
109+ input_value : None ,
110+ }
111+ }
112+
113+ pub fn new_from_parts ( field_type : Option < String > , input_value : Option < PyObject > ) -> Self {
114+ Self {
115+ message : None ,
116+ field_type,
117+ input_value,
118+ }
119+ }
120+
121+ pub fn new ( message : Option < String > , field_type : Option < String > , input_value : Option < PyObject > ) -> Self {
122+ Self {
123+ message,
124+ field_type,
125+ input_value,
126+ }
127+ }
128+
129+ pub fn to_py_err ( & self ) -> PyErr {
130+ PyErr :: new :: < Self , ( Option < String > , Option < String > , Option < PyObject > ) > ( (
131+ self . message . clone ( ) ,
132+ self . field_type . clone ( ) ,
133+ self . input_value . clone ( ) ,
134+ ) )
102135 }
103136}
104137
105138#[ pymethods]
106139impl PydanticSerializationUnexpectedValue {
107140 #[ new]
108- #[ pyo3( signature = ( message=None ) ) ]
109- fn py_new ( message : Option < String > ) -> Self {
110- Self { message }
141+ #[ pyo3( signature = ( message=None , field_type=None , input_value=None ) ) ]
142+ fn py_new ( message : Option < String > , field_type : Option < String > , input_value : Option < PyObject > ) -> Self {
143+ Self {
144+ message,
145+ field_type,
146+ input_value,
147+ }
111148 }
112149
113- fn __str__ ( & self ) -> & str {
114- match self . message {
115- Some ( ref s) => s,
116- None => "Unexpected Value" ,
150+ pub ( crate ) fn __str__ ( & self , py : Python ) -> String {
151+ let mut message = self . message . as_deref ( ) . unwrap_or ( "" ) . to_string ( ) ;
152+
153+ if let Some ( field_type) = & self . field_type {
154+ if !message. is_empty ( ) {
155+ message. push_str ( ": " ) ;
156+ }
157+ message. push_str ( & format ! ( "Expected `{field_type}`" ) ) ;
158+ if self . input_value . is_some ( ) {
159+ message. push_str ( " - serialized value may not be as expected" ) ;
160+ }
161+ }
162+
163+ if let Some ( input_value) = & self . input_value {
164+ let bound_input = input_value. bind ( py) ;
165+ let input_type = bound_input
166+ . get_type ( )
167+ . name ( )
168+ . unwrap_or_else ( |_| PyString :: new ( py, "<unknown python object>" ) )
169+ . to_string ( ) ;
170+
171+ let value_str = truncate_safe_repr ( bound_input, None ) ;
172+
173+ message. push_str ( & format ! ( " [input_value={value_str}, input_type={input_type}]" ) ) ;
117174 }
175+
176+ if message. is_empty ( ) {
177+ message = "Unexpected Value" . to_string ( ) ;
178+ }
179+
180+ message
118181 }
119182
120- pub ( crate ) fn __repr__ ( & self ) -> String {
121- format ! ( "PydanticSerializationUnexpectedValue({})" , self . __str__( ) )
183+ pub ( crate ) fn __repr__ ( & self , py : Python ) -> String {
184+ format ! ( "PydanticSerializationUnexpectedValue({})" , self . __str__( py ) )
122185 }
123186}
0 commit comments