@@ -4,7 +4,7 @@ use crate::type_object::PyTypeInfo;
4
4
use crate :: types:: any:: PyAnyMethods ;
5
5
use crate :: types:: {
6
6
string:: PyStringMethods , traceback:: PyTracebackMethods , typeobject:: PyTypeMethods , PyTraceback ,
7
- PyType ,
7
+ PyTuple , PyTupleMethods , PyType ,
8
8
} ;
9
9
use crate :: {
10
10
exceptions:: { self , PyBaseException } ,
@@ -46,25 +46,23 @@ pub type PyResult<T> = Result<T, PyErr>;
46
46
#[ derive( Debug ) ]
47
47
pub struct DowncastError < ' a , ' py > {
48
48
from : Borrowed < ' a , ' py , PyAny > ,
49
- to : Cow < ' static , str > ,
49
+ to : TypeNameOrValue < ' py > ,
50
50
}
51
51
52
52
impl < ' a , ' py > DowncastError < ' a , ' py > {
53
53
/// Create a new `PyDowncastError` representing a failure to convert the object
54
54
/// `from` into the type named in `to`.
55
55
pub fn new ( from : & ' a Bound < ' py , PyAny > , to : impl Into < Cow < ' static , str > > ) -> Self {
56
- DowncastError {
56
+ Self {
57
57
from : from. as_borrowed ( ) ,
58
- to : to. into ( ) ,
58
+ to : TypeNameOrValue :: Name ( to. into ( ) ) ,
59
59
}
60
60
}
61
- pub ( crate ) fn new_from_borrowed (
62
- from : Borrowed < ' a , ' py , PyAny > ,
63
- to : impl Into < Cow < ' static , str > > ,
64
- ) -> Self {
65
- DowncastError {
61
+
62
+ pub ( crate ) fn new_from_type ( from : Borrowed < ' a , ' py , PyAny > , to : Bound < ' py , PyAny > ) -> Self {
63
+ Self {
66
64
from,
67
- to : to . into ( ) ,
65
+ to : TypeNameOrValue :: Value ( to ) ,
68
66
}
69
67
}
70
68
}
@@ -73,16 +71,23 @@ impl<'a, 'py> DowncastError<'a, 'py> {
73
71
#[ derive( Debug ) ]
74
72
pub struct DowncastIntoError < ' py > {
75
73
from : Bound < ' py , PyAny > ,
76
- to : Cow < ' static , str > ,
74
+ to : TypeNameOrValue < ' py > ,
77
75
}
78
76
79
77
impl < ' py > DowncastIntoError < ' py > {
80
78
/// Create a new `DowncastIntoError` representing a failure to convert the object
81
79
/// `from` into the type named in `to`.
82
80
pub fn new ( from : Bound < ' py , PyAny > , to : impl Into < Cow < ' static , str > > ) -> Self {
83
- DowncastIntoError {
81
+ Self {
82
+ from,
83
+ to : TypeNameOrValue :: Name ( to. into ( ) ) ,
84
+ }
85
+ }
86
+
87
+ pub ( crate ) fn new_from_type ( from : Bound < ' py , PyAny > , to : Bound < ' py , PyAny > ) -> Self {
88
+ Self {
84
89
from,
85
- to : to . into ( ) ,
90
+ to : TypeNameOrValue :: Value ( to ) ,
86
91
}
87
92
}
88
93
@@ -95,6 +100,12 @@ impl<'py> DowncastIntoError<'py> {
95
100
}
96
101
}
97
102
103
+ // Helper to store either a concrete type or a type name
104
+ #[ derive( Debug ) ]
105
+ enum TypeNameOrValue < ' py > {
106
+ Name ( Cow < ' static , str > ) ,
107
+ Value ( Bound < ' py , PyAny > ) ,
108
+ }
98
109
/// Helper conversion trait that allows to use custom arguments for lazy exception construction.
99
110
pub trait PyErrArguments : Send + Sync {
100
111
/// Arguments for exception
@@ -721,25 +732,33 @@ impl<'py> IntoPyObject<'py> for &PyErr {
721
732
722
733
struct PyDowncastErrorArguments {
723
734
from : Py < PyType > ,
724
- to : Cow < ' static , str > ,
735
+ to : OwnedTypeNameOrValue ,
725
736
}
726
737
727
738
impl PyErrArguments for PyDowncastErrorArguments {
728
739
fn arguments ( self , py : Python < ' _ > ) -> Py < PyAny > {
729
- const FAILED_TO_EXTRACT : Cow < ' _ , str > = Cow :: Borrowed ( "<failed to extract type name>" ) ;
730
740
let from = self . from . bind ( py) . qualname ( ) ;
731
- let from = match & from {
732
- Ok ( qn) => qn. to_cow ( ) . unwrap_or ( FAILED_TO_EXTRACT ) ,
733
- Err ( _) => FAILED_TO_EXTRACT ,
741
+ let from = from
742
+ . as_ref ( )
743
+ . map ( |name| name. to_string_lossy ( ) )
744
+ . unwrap_or ( Cow :: Borrowed ( "<failed to extract type name>" ) ) ;
745
+ let to = match self . to {
746
+ OwnedTypeNameOrValue :: Name ( name) => TypeNameOrValue :: Name ( name) ,
747
+ OwnedTypeNameOrValue :: Value ( t) => TypeNameOrValue :: Value ( t. into_bound ( py) ) ,
734
748
} ;
735
- format ! ( "'{}' object cannot be converted to '{}'" , from, self . to)
749
+ format ! ( "'{}' object cannot be converted to '{}'" , from, to)
736
750
. into_pyobject ( py)
737
751
. unwrap ( )
738
752
. into_any ( )
739
753
. unbind ( )
740
754
}
741
755
}
742
756
757
+ enum OwnedTypeNameOrValue {
758
+ Name ( Cow < ' static , str > ) ,
759
+ Value ( Py < PyAny > ) ,
760
+ }
761
+
743
762
/// Python exceptions that can be converted to [`PyErr`].
744
763
///
745
764
/// This is used to implement [`From<Bound<'_, T>> for PyErr`].
@@ -763,7 +782,10 @@ impl std::convert::From<DowncastError<'_, '_>> for PyErr {
763
782
fn from ( err : DowncastError < ' _ , ' _ > ) -> PyErr {
764
783
let args = PyDowncastErrorArguments {
765
784
from : err. from . get_type ( ) . into ( ) ,
766
- to : err. to ,
785
+ to : match err. to {
786
+ TypeNameOrValue :: Name ( name) => OwnedTypeNameOrValue :: Name ( name) ,
787
+ TypeNameOrValue :: Value ( t) => OwnedTypeNameOrValue :: Value ( t. into ( ) ) ,
788
+ } ,
767
789
} ;
768
790
769
791
exceptions:: PyTypeError :: new_err ( args)
@@ -783,7 +805,10 @@ impl std::convert::From<DowncastIntoError<'_>> for PyErr {
783
805
fn from ( err : DowncastIntoError < ' _ > ) -> PyErr {
784
806
let args = PyDowncastErrorArguments {
785
807
from : err. from . get_type ( ) . into ( ) ,
786
- to : err. to ,
808
+ to : match err. to {
809
+ TypeNameOrValue :: Name ( name) => OwnedTypeNameOrValue :: Name ( name) ,
810
+ TypeNameOrValue :: Value ( t) => OwnedTypeNameOrValue :: Value ( t. into ( ) ) ,
811
+ } ,
787
812
} ;
788
813
789
814
exceptions:: PyTypeError :: new_err ( args)
@@ -801,7 +826,7 @@ impl std::fmt::Display for DowncastIntoError<'_> {
801
826
fn display_downcast_error (
802
827
f : & mut std:: fmt:: Formatter < ' _ > ,
803
828
from : & Bound < ' _ , PyAny > ,
804
- to : & str ,
829
+ to : & TypeNameOrValue < ' _ > ,
805
830
) -> std:: fmt:: Result {
806
831
write ! (
807
832
f,
@@ -811,6 +836,32 @@ fn display_downcast_error(
811
836
)
812
837
}
813
838
839
+ impl std:: fmt:: Display for TypeNameOrValue < ' _ > {
840
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
841
+ match self {
842
+ Self :: Name ( name) => name. fmt ( f) ,
843
+ Self :: Value ( t) => {
844
+ if let Ok ( t) = t. downcast :: < PyType > ( ) {
845
+ t. qualname ( )
846
+ . map_err ( |_| std:: fmt:: Error ) ?
847
+ . to_string_lossy ( )
848
+ . fmt ( f)
849
+ } else if let Ok ( t) = t. downcast :: < PyTuple > ( ) {
850
+ for ( i, t) in t. iter ( ) . enumerate ( ) {
851
+ if i > 0 {
852
+ f. write_str ( " | " ) ?;
853
+ }
854
+ TypeNameOrValue :: Value ( t) . fmt ( f) ?;
855
+ }
856
+ Ok ( ( ) )
857
+ } else {
858
+ t. fmt ( f)
859
+ }
860
+ }
861
+ }
862
+ }
863
+ }
864
+
814
865
#[ track_caller]
815
866
pub fn panic_after_error ( _py : Python < ' _ > ) -> ! {
816
867
unsafe {
0 commit comments