add `import_exception_bound!` macro (#4027)
* add `import_exception_bound!` macro * newsfragment and tidy up
This commit is contained in:
parent
3af9a1f4e0
commit
336b1c982b
|
@ -0,0 +1 @@
|
||||||
|
Add `import_exception_bound!` macro to import exception types without generating GIL Ref functionality for them.
|
|
@ -29,18 +29,7 @@ macro_rules! impl_exception_boilerplate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $name {
|
$crate::impl_exception_boilerplate_bound!($name);
|
||||||
/// Creates a new [`PyErr`] of this type.
|
|
||||||
///
|
|
||||||
/// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3"
|
|
||||||
#[inline]
|
|
||||||
pub fn new_err<A>(args: A) -> $crate::PyErr
|
|
||||||
where
|
|
||||||
A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static,
|
|
||||||
{
|
|
||||||
$crate::PyErr::new::<$name, A>(args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::error::Error for $name {
|
impl ::std::error::Error for $name {
|
||||||
fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> {
|
fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> {
|
||||||
|
@ -59,6 +48,25 @@ macro_rules! impl_exception_boilerplate {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_exception_boilerplate_bound {
|
||||||
|
($name: ident) => {
|
||||||
|
impl $name {
|
||||||
|
/// Creates a new [`PyErr`] of this type.
|
||||||
|
///
|
||||||
|
/// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3"
|
||||||
|
#[inline]
|
||||||
|
pub fn new_err<A>(args: A) -> $crate::PyErr
|
||||||
|
where
|
||||||
|
A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static,
|
||||||
|
{
|
||||||
|
$crate::PyErr::new::<$name, A>(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Defines a Rust type for an exception defined in Python code.
|
/// Defines a Rust type for an exception defined in Python code.
|
||||||
///
|
///
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
|
@ -105,34 +113,57 @@ macro_rules! import_exception {
|
||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
|
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
|
||||||
use $crate::sync::GILOnceCell;
|
use $crate::types::PyTypeMethods;
|
||||||
use $crate::prelude::PyTracebackMethods;
|
static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject =
|
||||||
use $crate::prelude::PyAnyMethods;
|
$crate::impl_::exceptions::ImportedExceptionTypeObject::new(stringify!($module), stringify!($name));
|
||||||
static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
|
TYPE_OBJECT.get(py).as_type_ptr()
|
||||||
GILOnceCell::new();
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
TYPE_OBJECT
|
/// Variant of [`import_exception`](crate::import_exception) that does not emit code needed to
|
||||||
.get_or_init(py, || {
|
/// use the imported exception type as a GIL Ref.
|
||||||
let imp = py
|
///
|
||||||
.import_bound(stringify!($module))
|
/// This is useful only during migration as a way to avoid generating needless code.
|
||||||
.unwrap_or_else(|err| {
|
#[macro_export]
|
||||||
let traceback = err
|
macro_rules! import_exception_bound {
|
||||||
.traceback_bound(py)
|
($module: expr, $name: ident) => {
|
||||||
.map(|tb| tb.format().expect("raised exception will have a traceback"))
|
/// A Rust type representing an exception defined in Python code.
|
||||||
.unwrap_or_default();
|
///
|
||||||
::std::panic!("Can not import module {}: {}\n{}", stringify!($module), err, traceback);
|
/// This type was created by the [`pyo3::import_exception_bound!`] macro - see its documentation
|
||||||
});
|
/// for more information.
|
||||||
let cls = imp.getattr(stringify!($name)).expect(concat!(
|
///
|
||||||
"Can not load exception class: ",
|
/// [`pyo3::import_exception_bound!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3"
|
||||||
stringify!($module),
|
#[repr(transparent)]
|
||||||
".",
|
#[allow(non_camel_case_types)] // E.g. `socket.herror`
|
||||||
stringify!($name)
|
pub struct $name($crate::PyAny);
|
||||||
));
|
|
||||||
|
|
||||||
cls.extract()
|
$crate::impl_exception_boilerplate_bound!($name);
|
||||||
.expect("Imported exception should be a type object")
|
|
||||||
})
|
// FIXME remove this: was necessary while `PyTypeInfo` requires `HasPyGilRef`,
|
||||||
.as_ptr() as *mut _
|
// should change in 0.22.
|
||||||
|
unsafe impl $crate::type_object::HasPyGilRef for $name {
|
||||||
|
type AsRefTarget = $crate::PyAny;
|
||||||
|
}
|
||||||
|
|
||||||
|
$crate::pyobject_native_type_info!(
|
||||||
|
$name,
|
||||||
|
$name::type_object_raw,
|
||||||
|
::std::option::Option::Some(stringify!($module))
|
||||||
|
);
|
||||||
|
|
||||||
|
impl $crate::types::DerefToPyAny for $name {}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
|
||||||
|
use $crate::types::PyTypeMethods;
|
||||||
|
static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject =
|
||||||
|
$crate::impl_::exceptions::ImportedExceptionTypeObject::new(
|
||||||
|
stringify!($module),
|
||||||
|
stringify!($name),
|
||||||
|
);
|
||||||
|
TYPE_OBJECT.get(py).as_type_ptr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -849,8 +880,8 @@ mod tests {
|
||||||
use crate::types::{IntoPyDict, PyDict};
|
use crate::types::{IntoPyDict, PyDict};
|
||||||
use crate::{PyErr, PyNativeType};
|
use crate::{PyErr, PyNativeType};
|
||||||
|
|
||||||
import_exception!(socket, gaierror);
|
import_exception_bound!(socket, gaierror);
|
||||||
import_exception!(email.errors, MessageError);
|
import_exception_bound!(email.errors, MessageError);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_exception() {
|
fn test_check_exception() {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#[cfg(feature = "experimental-async")]
|
#[cfg(feature = "experimental-async")]
|
||||||
pub mod coroutine;
|
pub mod coroutine;
|
||||||
pub mod deprecations;
|
pub mod deprecations;
|
||||||
|
pub mod exceptions;
|
||||||
pub mod extract_argument;
|
pub mod extract_argument;
|
||||||
pub mod freelist;
|
pub mod freelist;
|
||||||
pub mod frompyobject;
|
pub mod frompyobject;
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
use crate::{sync::GILOnceCell, types::PyType, Bound, Py, Python};
|
||||||
|
|
||||||
|
pub struct ImportedExceptionTypeObject {
|
||||||
|
imported_value: GILOnceCell<Py<PyType>>,
|
||||||
|
module: &'static str,
|
||||||
|
name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImportedExceptionTypeObject {
|
||||||
|
pub const fn new(module: &'static str, name: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
imported_value: GILOnceCell::new(),
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get<'py>(&self, py: Python<'py>) -> &Bound<'py, PyType> {
|
||||||
|
self.imported_value
|
||||||
|
.get_or_try_init_type_ref(py, self.module, self.name)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
panic!(
|
||||||
|
"failed to import exception {}.{}: {}",
|
||||||
|
self.module, self.name, e
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -201,13 +201,17 @@ impl GILOnceCell<Py<PyType>> {
|
||||||
///
|
///
|
||||||
/// This is a shorthand method for `get_or_init` which imports the type from Python on init.
|
/// This is a shorthand method for `get_or_init` which imports the type from Python on init.
|
||||||
pub(crate) fn get_or_try_init_type_ref<'py>(
|
pub(crate) fn get_or_try_init_type_ref<'py>(
|
||||||
&'py self,
|
&self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
attr_name: &str,
|
attr_name: &str,
|
||||||
) -> PyResult<&Bound<'py, PyType>> {
|
) -> PyResult<&Bound<'py, PyType>> {
|
||||||
self.get_or_try_init(py, || {
|
self.get_or_try_init(py, || {
|
||||||
py.import_bound(module_name)?.getattr(attr_name)?.extract()
|
let type_object = py
|
||||||
|
.import_bound(module_name)?
|
||||||
|
.getattr(attr_name)?
|
||||||
|
.downcast_into()?;
|
||||||
|
Ok(type_object.unbind())
|
||||||
})
|
})
|
||||||
.map(|ty| ty.bind(py))
|
.map(|ty| ty.bind(py))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue