@@ -7,7 +7,7 @@ use pyo3::ffi::{self, c_str};
77use pyo3:: intern;
88use pyo3:: prelude:: * ;
99use pyo3:: sync:: GILOnceCell ;
10- use pyo3:: types:: { PyDict , PyList , PyString , PyType } ;
10+ use pyo3:: types:: { PyDict , PyList , PyString , PyTuple , PyType } ;
1111use serde:: ser:: { Error , SerializeMap , SerializeSeq } ;
1212use serde:: { Serialize , Serializer } ;
1313
@@ -57,12 +57,19 @@ impl ValidationError {
5757 ) -> PyErr {
5858 match error {
5959 ValError :: LineErrors ( raw_errors) => {
60- let line_errors: Vec < PyLineError > = match outer_location {
60+ let line_errors = match outer_location {
6161 Some ( outer_location) => raw_errors
6262 . into_iter ( )
63- . map ( |e| e. with_outer_location ( outer_location. clone ( ) ) . into_py ( py) )
63+ . map ( |e| PyLineError :: from_val_line_error ( py, e. with_outer_location ( outer_location. clone ( ) ) ) )
64+ . collect ( ) ,
65+ None => raw_errors
66+ . into_iter ( )
67+ . map ( |e| PyLineError :: from_val_line_error ( py, e) )
6468 . collect ( ) ,
65- None => raw_errors. into_iter ( ) . map ( |e| e. into_py ( py) ) . collect ( ) ,
69+ } ;
70+ let line_errors = match line_errors {
71+ Ok ( errors) => errors,
72+ Err ( err) => return err,
6673 } ;
6774 let validation_error = Self :: new ( line_errors, title, input_type, hide_input) ;
6875 match Py :: new ( py, validation_error) {
@@ -117,15 +124,17 @@ impl ValidationError {
117124 context : _,
118125 } = & line_error. error_type
119126 {
120- let note: PyObject = if let Location :: Empty = & line_error. location {
121- "Pydantic: cause of loc: root" . into_py ( py )
127+ let note = if let Location :: Empty = & line_error. location {
128+ PyString :: new ( py , "Pydantic: cause of loc: root" )
122129 } else {
123- format ! (
124- "Pydantic: cause of loc: {}" ,
125- // Location formats with a newline at the end, hence the trim()
126- line_error. location. to_string( ) . trim( )
130+ PyString :: new (
131+ py,
132+ & format ! (
133+ "Pydantic: cause of loc: {}" ,
134+ // Location formats with a newline at the end, hence the trim()
135+ line_error. location. to_string( ) . trim( )
136+ ) ,
127137 )
128- . into_py ( py)
129138 } ;
130139
131140 // Notes only support 3.11 upwards:
@@ -159,7 +168,7 @@ impl ValidationError {
159168 #[ cfg( Py_3_11 ) ]
160169 let cause = {
161170 use pyo3:: exceptions:: PyBaseExceptionGroup ;
162- Some ( PyBaseExceptionGroup :: new_err ( ( title, user_py_errs) ) . into_py ( py ) )
171+ Some ( PyBaseExceptionGroup :: new_err ( ( title, user_py_errs) ) )
163172 } ;
164173
165174 // Pre 3.11 ExceptionGroup support, use the python backport instead:
@@ -170,7 +179,7 @@ impl ValidationError {
170179 match py. import ( "exceptiongroup" ) {
171180 Ok ( py_mod) => match py_mod. getattr ( "ExceptionGroup" ) {
172181 Ok ( group_cls) => match group_cls. call1 ( ( title, user_py_errs) ) {
173- Ok ( group_instance) => Some ( group_instance. into_py ( py ) ) ,
182+ Ok ( group_instance) => Some ( group_instance) ,
174183 Err ( _) => None ,
175184 } ,
176185 Err ( _) => None ,
@@ -183,7 +192,7 @@ impl ValidationError {
183192 if let Some ( cause) = cause {
184193 unsafe {
185194 // PyException_SetCause _steals_ a reference to cause, so must use .into_ptr()
186- ffi:: PyException_SetCause ( self_. as_ptr ( ) , cause. into_ptr ( ) ) ;
195+ ffi:: PyException_SetCause ( self_. as_ptr ( ) , cause. value ( py ) . clone ( ) . into_ptr ( ) ) ;
187196 }
188197 }
189198 }
@@ -308,10 +317,13 @@ impl ValidationError {
308317 return py. None ( ) ;
309318 }
310319 e. as_dict ( py, url_prefix, include_context, self . input_type , include_input)
311- . unwrap_or_else ( |err| {
312- iteration_error = Some ( err) ;
313- py. None ( )
314- } )
320+ . map_or_else (
321+ |err| {
322+ iteration_error = Some ( err) ;
323+ py. None ( )
324+ } ,
325+ Into :: into,
326+ )
315327 } ) ,
316328 ) ?;
317329 if let Some ( err) = iteration_error {
@@ -379,7 +391,7 @@ impl ValidationError {
379391 self . __repr__ ( py)
380392 }
381393
382- fn __reduce__ < ' py > ( slf : & Bound < ' py , Self > ) -> PyResult < ( Bound < ' py , PyAny > , PyObject ) > {
394+ fn __reduce__ < ' py > ( slf : & Bound < ' py , Self > ) -> PyResult < ( Bound < ' py , PyAny > , Bound < ' py , PyTuple > ) > {
383395 let py = slf. py ( ) ;
384396 let callable = slf. getattr ( "from_exception_data" ) ?;
385397 let borrow = slf. try_borrow ( ) ?;
@@ -389,7 +401,7 @@ impl ValidationError {
389401 borrow. input_type . into_pyobject ( py) ?,
390402 borrow. hide_input ,
391403 )
392- . into_py ( slf. py ( ) ) ;
404+ . into_pyobject ( slf. py ( ) ) ? ;
393405 Ok ( ( callable, args) )
394406 }
395407}
@@ -418,16 +430,6 @@ pub struct PyLineError {
418430 input_value : PyObject ,
419431}
420432
421- impl IntoPy < PyLineError > for ValLineError {
422- fn into_py ( self , py : Python < ' _ > ) -> PyLineError {
423- PyLineError {
424- error_type : self . error_type ,
425- location : self . location ,
426- input_value : self . input_value . to_object ( py) ,
427- }
428- }
429- }
430-
431433impl From < PyLineError > for ValLineError {
432434 /// Used to extract line errors from a validation error for wrap functions
433435 fn from ( other : PyLineError ) -> ValLineError {
@@ -464,7 +466,7 @@ impl TryFrom<&Bound<'_, PyAny>> for PyLineError {
464466 let location = Location :: try_from ( dict. get_item ( "loc" ) ?. as_ref ( ) ) ?;
465467
466468 let input_value = match dict. get_item ( "input" ) ? {
467- Some ( i) => i. into_py ( py ) ,
469+ Some ( i) => i. unbind ( ) ,
468470 None => py. None ( ) ,
469471 } ;
470472
@@ -477,18 +479,26 @@ impl TryFrom<&Bound<'_, PyAny>> for PyLineError {
477479}
478480
479481impl PyLineError {
482+ pub fn from_val_line_error ( py : Python , error : ValLineError ) -> PyResult < Self > {
483+ Ok ( Self {
484+ error_type : error. error_type ,
485+ location : error. location ,
486+ input_value : error. input_value . into_pyobject ( py) ?. unbind ( ) ,
487+ } )
488+ }
489+
480490 fn get_error_url ( & self , url_prefix : & str ) -> String {
481491 format ! ( "{url_prefix}{}" , self . error_type. type_string( ) )
482492 }
483493
484- pub fn as_dict (
494+ pub fn as_dict < ' py > (
485495 & self ,
486- py : Python ,
496+ py : Python < ' py > ,
487497 url_prefix : Option < & str > ,
488498 include_context : bool ,
489499 input_type : InputType ,
490500 include_input : bool ,
491- ) -> PyResult < PyObject > {
501+ ) -> PyResult < Bound < ' py , PyDict > > {
492502 let dict = PyDict :: new ( py) ;
493503 dict. set_item ( "type" , self . error_type . type_string ( ) ) ?;
494504 dict. set_item ( "loc" , self . location . to_object ( py) ) ?;
@@ -511,7 +521,7 @@ impl PyLineError {
511521 }
512522 }
513523 }
514- Ok ( dict. into_py ( py ) )
524+ Ok ( dict)
515525 }
516526
517527 fn pretty (
0 commit comments