@@ -2,6 +2,9 @@ use std::fmt;
2
2
3
3
use pyo3:: exceptions:: PyValueError ;
4
4
use pyo3:: prelude:: * ;
5
+ use pyo3:: types:: PyString ;
6
+
7
+ use crate :: tools:: truncate_safe_repr;
5
8
6
9
use serde:: ser;
7
10
@@ -44,9 +47,9 @@ pub(super) fn se_err_py_err(error: PythonSerializerError) -> PyErr {
44
47
let s = error. to_string ( ) ;
45
48
if let Some ( msg) = s. strip_prefix ( UNEXPECTED_TYPE_SER_MARKER ) {
46
49
if msg. is_empty ( ) {
47
- PydanticSerializationUnexpectedValue :: new_err ( None )
50
+ PydanticSerializationUnexpectedValue :: new_from_msg ( None ) . to_py_err ( )
48
51
} else {
49
- PydanticSerializationUnexpectedValue :: new_err ( Some ( msg. to_string ( ) ) )
52
+ PydanticSerializationUnexpectedValue :: new_from_msg ( Some ( msg. to_string ( ) ) ) . to_py_err ( )
50
53
}
51
54
} else if let Some ( msg) = s. strip_prefix ( SERIALIZATION_ERR_MARKER ) {
52
55
PydanticSerializationError :: new_err ( msg. to_string ( ) )
@@ -94,30 +97,90 @@ impl PydanticSerializationError {
94
97
#[ derive( Debug , Clone ) ]
95
98
pub struct PydanticSerializationUnexpectedValue {
96
99
message : Option < String > ,
100
+ field_type : Option < String > ,
101
+ input_value : Option < PyObject > ,
97
102
}
98
103
99
104
impl 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
+ ) )
102
135
}
103
136
}
104
137
105
138
#[ pymethods]
106
139
impl PydanticSerializationUnexpectedValue {
107
140
#[ 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
+ }
111
148
}
112
149
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}]" ) ) ;
117
174
}
175
+
176
+ if message. is_empty ( ) {
177
+ message = "Unexpected Value" . to_string ( ) ;
178
+ }
179
+
180
+ message
118
181
}
119
182
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 ) )
122
185
}
123
186
}
0 commit comments