@@ -29,18 +29,7 @@ macro_rules! impl_exception_boilerplate {
2929 }
3030 }
3131
32- impl $name {
33- /// Creates a new [`PyErr`] of this type.
34- ///
35- /// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3"
36- #[ inline]
37- pub fn new_err<A >( args: A ) -> $crate:: PyErr
38- where
39- A : $crate:: PyErrArguments + :: std:: marker:: Send + :: std:: marker:: Sync + ' static ,
40- {
41- $crate:: PyErr :: new:: <$name, A >( args)
42- }
43- }
32+ $crate:: impl_exception_boilerplate_bound!( $name) ;
4433
4534 impl :: std:: error:: Error for $name {
4635 fn source( & self ) -> :: std:: option:: Option <& ( dyn :: std:: error:: Error + ' static ) > {
@@ -59,6 +48,25 @@ macro_rules! impl_exception_boilerplate {
5948 } ;
6049}
6150
51+ #[ doc( hidden) ]
52+ #[ macro_export]
53+ macro_rules! impl_exception_boilerplate_bound {
54+ ( $name: ident) => {
55+ impl $name {
56+ /// Creates a new [`PyErr`] of this type.
57+ ///
58+ /// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3"
59+ #[ inline]
60+ pub fn new_err<A >( args: A ) -> $crate:: PyErr
61+ where
62+ A : $crate:: PyErrArguments + :: std:: marker:: Send + :: std:: marker:: Sync + ' static ,
63+ {
64+ $crate:: PyErr :: new:: <$name, A >( args)
65+ }
66+ }
67+ } ;
68+ }
69+
6270/// Defines a Rust type for an exception defined in Python code.
6371///
6472/// # Syntax
@@ -105,34 +113,57 @@ macro_rules! import_exception {
105113
106114 impl $name {
107115 fn type_object_raw( py: $crate:: Python <' _>) -> * mut $crate:: ffi:: PyTypeObject {
108- use $crate:: sync:: GILOnceCell ;
109- use $crate:: prelude:: PyTracebackMethods ;
110- use $crate:: prelude:: PyAnyMethods ;
111- static TYPE_OBJECT : GILOnceCell <$crate:: Py <$crate:: types:: PyType >> =
112- GILOnceCell :: new( ) ;
116+ use $crate:: types:: PyTypeMethods ;
117+ static TYPE_OBJECT : $crate:: impl_:: exceptions:: ImportedExceptionTypeObject =
118+ $crate:: impl_:: exceptions:: ImportedExceptionTypeObject :: new( stringify!( $module) , stringify!( $name) ) ;
119+ TYPE_OBJECT . get( py) . as_type_ptr( )
120+ }
121+ }
122+ } ;
123+ }
113124
114- TYPE_OBJECT
115- . get_or_init( py, || {
116- let imp = py
117- . import_bound( stringify!( $module) )
118- . unwrap_or_else( |err| {
119- let traceback = err
120- . traceback_bound( py)
121- . map( |tb| tb. format( ) . expect( "raised exception will have a traceback" ) )
122- . unwrap_or_default( ) ;
123- :: std:: panic!( "Can not import module {}: {}\n {}" , stringify!( $module) , err, traceback) ;
124- } ) ;
125- let cls = imp. getattr( stringify!( $name) ) . expect( concat!(
126- "Can not load exception class: " ,
127- stringify!( $module) ,
128- "." ,
129- stringify!( $name)
130- ) ) ;
131-
132- cls. extract( )
133- . expect( "Imported exception should be a type object" )
134- } )
135- . as_ptr( ) as * mut _
125+ /// Variant of [`import_exception`](crate::import_exception) that does not emit code needed to
126+ /// use the imported exception type as a GIL Ref.
127+ ///
128+ /// This is useful only during migration as a way to avoid generating needless code.
129+ #[ macro_export]
130+ macro_rules! import_exception_bound {
131+ ( $module: expr, $name: ident) => {
132+ /// A Rust type representing an exception defined in Python code.
133+ ///
134+ /// This type was created by the [`pyo3::import_exception_bound!`] macro - see its documentation
135+ /// for more information.
136+ ///
137+ /// [`pyo3::import_exception_bound!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3"
138+ #[ repr( transparent) ]
139+ #[ allow( non_camel_case_types) ] // E.g. `socket.herror`
140+ pub struct $name( $crate:: PyAny ) ;
141+
142+ $crate:: impl_exception_boilerplate_bound!( $name) ;
143+
144+ // FIXME remove this: was necessary while `PyTypeInfo` requires `HasPyGilRef`,
145+ // should change in 0.22.
146+ unsafe impl $crate:: type_object:: HasPyGilRef for $name {
147+ type AsRefTarget = $crate:: PyAny ;
148+ }
149+
150+ $crate:: pyobject_native_type_info!(
151+ $name,
152+ $name:: type_object_raw,
153+ :: std:: option:: Option :: Some ( stringify!( $module) )
154+ ) ;
155+
156+ impl $crate:: types:: DerefToPyAny for $name { }
157+
158+ impl $name {
159+ fn type_object_raw( py: $crate:: Python <' _>) -> * mut $crate:: ffi:: PyTypeObject {
160+ use $crate:: types:: PyTypeMethods ;
161+ static TYPE_OBJECT : $crate:: impl_:: exceptions:: ImportedExceptionTypeObject =
162+ $crate:: impl_:: exceptions:: ImportedExceptionTypeObject :: new(
163+ stringify!( $module) ,
164+ stringify!( $name) ,
165+ ) ;
166+ TYPE_OBJECT . get( py) . as_type_ptr( )
136167 }
137168 }
138169 } ;
@@ -849,8 +880,8 @@ mod tests {
849880 use crate :: types:: { IntoPyDict , PyDict } ;
850881 use crate :: { PyErr , PyNativeType } ;
851882
852- import_exception ! ( socket, gaierror) ;
853- import_exception ! ( email. errors, MessageError ) ;
883+ import_exception_bound ! ( socket, gaierror) ;
884+ import_exception_bound ! ( email. errors, MessageError ) ;
854885
855886 #[ test]
856887 fn test_check_exception ( ) {
0 commit comments