Refactor the exception macros
* Renamed `py_exception` to `create_exception` * The split up of the macros makes it possible to create exception structs with bodies to mimic python exceptions' members * Used `Once` to fix a (theoretical) race condition with the is_null check
This commit is contained in:
parent
de2108d719
commit
9102f2e364
|
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
|
||||
* Renamed `add_function` to `add_wrapped` as it now also supports modules.
|
||||
* Renamed `#[pymodinit]` to `#[pymodule]`.
|
||||
* Renamed `py_exception` to `create_exception` and refactored the error macros.
|
||||
|
||||
### Removed
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
## Define a new exception
|
||||
|
||||
You can use the `py_exception!` macro to define a new exception type:
|
||||
You can use the `create_exception!` macro to define a new exception type:
|
||||
|
||||
```rust
|
||||
#[macro_use] extern crate pyo3;
|
||||
|
||||
py_exception!(module, MyError, pyo3::exceptions::Exception);
|
||||
create_exception!(module, MyError, pyo3::exceptions::Exception);
|
||||
```
|
||||
|
||||
* `module` is the name of the containing module.
|
||||
|
@ -20,8 +20,9 @@ For example:
|
|||
|
||||
use pyo3::Python;
|
||||
use pyo3::types::PyDict;
|
||||
use pyo3::exceptions::Exception;
|
||||
|
||||
py_exception!(mymodule, CustomError, pyo3::exceptions::Exception);
|
||||
create_exception!(mymodule, CustomError, Exception);
|
||||
|
||||
fn main() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
@ -61,7 +62,7 @@ PyErr::from_instance(py, err).restore(py);
|
|||
|
||||
If rust type exists for exception, then it is possible to use `new` method.
|
||||
For example each standard exception defined in `exc` module
|
||||
has corresponding rust type, exceptions defined by `py_exception!` and `import_exception!` macro
|
||||
has corresponding rust type, exceptions defined by `create_exception!` and `import_exception!` macro
|
||||
have rust type as well.
|
||||
|
||||
```rust
|
||||
|
|
|
@ -18,7 +18,7 @@ use syn::parse::Parser;
|
|||
use syn::punctuated::Punctuated;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn mod2init(
|
||||
pub fn pymodule2(
|
||||
attr: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
|
@ -46,7 +46,7 @@ pub fn mod2init(
|
|||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn mod3init(
|
||||
pub fn pymodule3(
|
||||
attr: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
|
|
109
src/err.rs
109
src/err.rs
|
@ -1,10 +1,4 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use libc;
|
||||
use std;
|
||||
use std::error::Error;
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use crate::conversion::{IntoPyObject, ToBorrowedObject, ToPyObject};
|
||||
use crate::ffi;
|
||||
|
@ -13,101 +7,12 @@ use crate::object::PyObject;
|
|||
use crate::python::{IntoPyPointer, Python, ToPyPointer};
|
||||
use crate::typeob::PyTypeObject;
|
||||
use crate::types::{exceptions, PyObjectRef, PyType};
|
||||
|
||||
/// Defines a new exception type.
|
||||
///
|
||||
/// # Syntax
|
||||
/// `py_exception!(module, MyError, pyo3::exceptions::Exception)`
|
||||
///
|
||||
/// * `module` is the name of the containing module.
|
||||
/// * `MyError` is the name of the new exception type.
|
||||
/// * `pyo3::exceptions::Exception` is the name of the base type
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// extern crate pyo3;
|
||||
///
|
||||
/// use pyo3::Python;
|
||||
/// use pyo3::types::PyDict;
|
||||
///
|
||||
/// py_exception!(mymodule, CustomError, pyo3::exceptions::Exception);
|
||||
///
|
||||
/// fn main() {
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let ctx = PyDict::new(py);
|
||||
///
|
||||
/// ctx.set_item("CustomError", py.get_type::<CustomError>()).unwrap();
|
||||
///
|
||||
/// py.run("assert str(CustomError) == \"<class 'mymodule.CustomError'>\"",
|
||||
/// None, Some(&ctx)).unwrap();
|
||||
/// py.run("assert CustomError('oops').args == ('oops',)", None, Some(ctx)).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! py_exception {
|
||||
($module: ident, $name: ident, $base: ty) => {
|
||||
pub struct $name;
|
||||
|
||||
impl std::convert::From<$name> for $crate::PyErr {
|
||||
fn from(_err: $name) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, _>(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::convert::Into<$crate::PyResult<T>> for $name {
|
||||
fn into(self) -> $crate::PyResult<T> {
|
||||
$crate::PyErr::new::<$name, _>(()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn py_err<T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, T>(args)
|
||||
}
|
||||
pub fn into<R, T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyResult<R> {
|
||||
$crate::PyErr::new::<$name, T>(args).into()
|
||||
}
|
||||
#[inline]
|
||||
fn type_object() -> *mut $crate::ffi::PyTypeObject {
|
||||
static mut TYPE_OBJECT: *mut $crate::ffi::PyTypeObject =
|
||||
0 as *mut $crate::ffi::PyTypeObject;
|
||||
|
||||
unsafe {
|
||||
if TYPE_OBJECT.is_null() {
|
||||
let gil = $crate::Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
TYPE_OBJECT = $crate::PyErr::new_type(
|
||||
py,
|
||||
concat!(stringify!($module), ".", stringify!($name)),
|
||||
Some(py.get_type::<$base>()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
TYPE_OBJECT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::typeob::PyTypeObject for $name {
|
||||
#[inline]
|
||||
fn init_type() {
|
||||
let _ = $name::type_object();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn type_object() -> $crate::Py<$crate::types::PyType> {
|
||||
unsafe {
|
||||
$crate::Py::from_borrowed_ptr(
|
||||
$name::type_object() as *const _ as *mut $crate::ffi::PyObject
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
use libc::c_int;
|
||||
use std;
|
||||
use std::error::Error;
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Represents a `PyErr` value
|
||||
pub enum PyErrValue {
|
||||
|
@ -607,7 +512,7 @@ pub fn panic_after_error() -> ! {
|
|||
|
||||
/// Returns Ok if the error code is not -1.
|
||||
#[inline]
|
||||
pub fn error_on_minusone(py: Python, result: libc::c_int) -> PyResult<()> {
|
||||
pub fn error_on_minusone(py: Python, result: c_int) -> PyResult<()> {
|
||||
if result != -1 {
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::ffi3::pyport::{Py_hash_t, Py_ssize_t};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void};
|
||||
use std::ptr;
|
||||
|
||||
|
@ -24,7 +25,7 @@ pub const PyObject_HEAD_INIT: PyObject = PyObject {
|
|||
#[cfg(not(py_sys_config = "Py_TRACE_REFS"))]
|
||||
pub const PyObject_HEAD_INIT: PyObject = PyObject {
|
||||
ob_refcnt: 1,
|
||||
ob_type: ::std::ptr::null_mut(),
|
||||
ob_type: ptr::null_mut(),
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -87,6 +88,7 @@ pub type objobjargproc =
|
|||
#[cfg(not(Py_LIMITED_API))]
|
||||
mod bufferinfo {
|
||||
use crate::ffi3::pyport::Py_ssize_t;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -108,7 +110,7 @@ mod bufferinfo {
|
|||
impl Default for Py_buffer {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +200,9 @@ mod typeobject {
|
|||
mod typeobject {
|
||||
use crate::ffi3::pyport::Py_ssize_t;
|
||||
use crate::ffi3::{self, object};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_uint, c_ulong, c_void};
|
||||
use std::ptr;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -244,7 +248,7 @@ mod typeobject {
|
|||
impl Default for PyNumberMethods {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
macro_rules! as_expr {
|
||||
|
@ -320,7 +324,7 @@ mod typeobject {
|
|||
impl Default for PySequenceMethods {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
pub const PySequenceMethods_INIT: PySequenceMethods = PySequenceMethods {
|
||||
|
@ -328,9 +332,9 @@ mod typeobject {
|
|||
sq_concat: None,
|
||||
sq_repeat: None,
|
||||
sq_item: None,
|
||||
was_sq_slice: ::std::ptr::null_mut(),
|
||||
was_sq_slice: ptr::null_mut(),
|
||||
sq_ass_item: None,
|
||||
was_sq_ass_slice: ::std::ptr::null_mut(),
|
||||
was_sq_ass_slice: ptr::null_mut(),
|
||||
sq_contains: None,
|
||||
sq_inplace_concat: None,
|
||||
sq_inplace_repeat: None,
|
||||
|
@ -346,7 +350,7 @@ mod typeobject {
|
|||
impl Default for PyMappingMethods {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
pub const PyMappingMethods_INIT: PyMappingMethods = PyMappingMethods {
|
||||
|
@ -365,7 +369,7 @@ mod typeobject {
|
|||
impl Default for PyAsyncMethods {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
pub const PyAsyncMethods_INIT: PyAsyncMethods = PyAsyncMethods {
|
||||
|
@ -383,7 +387,7 @@ mod typeobject {
|
|||
impl Default for PyBufferProcs {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
pub const PyBufferProcs_INIT: PyBufferProcs = PyBufferProcs {
|
||||
|
@ -462,37 +466,37 @@ mod typeobject {
|
|||
ob_base: ffi3::object::PyObject_HEAD_INIT,
|
||||
ob_size: 0
|
||||
},
|
||||
tp_name: ::std::ptr::null(),
|
||||
tp_name: ptr::null(),
|
||||
tp_basicsize: 0,
|
||||
tp_itemsize: 0,
|
||||
tp_dealloc: None,
|
||||
tp_print: None,
|
||||
tp_getattr: None,
|
||||
tp_setattr: None,
|
||||
$tp_as_async: ::std::ptr::null_mut(),
|
||||
$tp_as_async: ptr::null_mut(),
|
||||
tp_repr: None,
|
||||
tp_as_number: ::std::ptr::null_mut(),
|
||||
tp_as_sequence: ::std::ptr::null_mut(),
|
||||
tp_as_mapping: ::std::ptr::null_mut(),
|
||||
tp_as_number: ptr::null_mut(),
|
||||
tp_as_sequence: ptr::null_mut(),
|
||||
tp_as_mapping: ptr::null_mut(),
|
||||
tp_hash: None,
|
||||
tp_call: None,
|
||||
tp_str: None,
|
||||
tp_getattro: None,
|
||||
tp_setattro: None,
|
||||
tp_as_buffer: ::std::ptr::null_mut(),
|
||||
tp_as_buffer: ptr::null_mut(),
|
||||
tp_flags: ffi3::object::Py_TPFLAGS_DEFAULT,
|
||||
tp_doc: ::std::ptr::null(),
|
||||
tp_doc: ptr::null(),
|
||||
tp_traverse: None,
|
||||
tp_clear: None,
|
||||
tp_richcompare: None,
|
||||
tp_weaklistoffset: 0,
|
||||
tp_iter: None,
|
||||
tp_iternext: None,
|
||||
tp_methods: ::std::ptr::null_mut(),
|
||||
tp_members: ::std::ptr::null_mut(),
|
||||
tp_getset: ::std::ptr::null_mut(),
|
||||
tp_base: ::std::ptr::null_mut(),
|
||||
tp_dict: ::std::ptr::null_mut(),
|
||||
tp_methods: ptr::null_mut(),
|
||||
tp_members: ptr::null_mut(),
|
||||
tp_getset: ptr::null_mut(),
|
||||
tp_base: ptr::null_mut(),
|
||||
tp_dict: ptr::null_mut(),
|
||||
tp_descr_get: None,
|
||||
tp_descr_set: None,
|
||||
tp_dictoffset: 0,
|
||||
|
@ -501,11 +505,11 @@ mod typeobject {
|
|||
tp_new: None,
|
||||
tp_free: None,
|
||||
tp_is_gc: None,
|
||||
tp_bases: ::std::ptr::null_mut(),
|
||||
tp_mro: ::std::ptr::null_mut(),
|
||||
tp_cache: ::std::ptr::null_mut(),
|
||||
tp_subclasses: ::std::ptr::null_mut(),
|
||||
tp_weaklist: ::std::ptr::null_mut(),
|
||||
tp_bases: ptr::null_mut(),
|
||||
tp_mro: ptr::null_mut(),
|
||||
tp_cache: ptr::null_mut(),
|
||||
tp_subclasses: ptr::null_mut(),
|
||||
tp_weaklist: ptr::null_mut(),
|
||||
tp_del: None,
|
||||
tp_version_tag: 0,
|
||||
$($tail)*
|
||||
|
@ -522,8 +526,8 @@ mod typeobject {
|
|||
tp_allocs: 0,
|
||||
tp_frees: 0,
|
||||
tp_maxalloc: 0,
|
||||
tp_prev: ::std::ptr::null_mut(),
|
||||
tp_next: ::std::ptr::null_mut(),
|
||||
tp_prev: ptr::null_mut(),
|
||||
tp_next: ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -556,7 +560,7 @@ mod typeobject {
|
|||
impl Default for PyHeapTypeObject {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,7 +586,7 @@ pub struct PyType_Slot {
|
|||
|
||||
impl Default for PyType_Slot {
|
||||
fn default() -> PyType_Slot {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -598,7 +602,7 @@ pub struct PyType_Spec {
|
|||
|
||||
impl Default for PyType_Spec {
|
||||
fn default() -> PyType_Spec {
|
||||
unsafe { ::std::mem::zeroed() }
|
||||
unsafe { mem::zeroed() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,9 +192,9 @@ pub mod types;
|
|||
/// The proc macros, which are also part of the prelude
|
||||
pub mod proc_macro {
|
||||
#[cfg(not(Py_3))]
|
||||
pub use pyo3cls::mod2init as pymodule;
|
||||
pub use pyo3cls::pymodule2 as pymodule;
|
||||
#[cfg(Py_3)]
|
||||
pub use pyo3cls::mod3init as pymodule;
|
||||
pub use pyo3cls::pymodule3 as pymodule;
|
||||
/// The proc macro attributes
|
||||
pub use pyo3cls::{pyclass, pyfunction, pymethods, pyproto};
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ pub use crate::PyRawObject;
|
|||
pub use pyo3cls::{pyclass, pyfunction, pymethods, pyproto};
|
||||
|
||||
#[cfg(Py_3)]
|
||||
pub use pyo3cls::mod3init as pymodule;
|
||||
pub use pyo3cls::pymodule3 as pymodule;
|
||||
|
||||
#[cfg(not(Py_3))]
|
||||
pub use pyo3cls::mod2init as pymodule;
|
||||
pub use pyo3cls::pymodule2 as pymodule;
|
||||
|
|
|
@ -13,18 +13,32 @@ use std::ffi::CStr;
|
|||
use std::os::raw::c_char;
|
||||
use std::{self, ops};
|
||||
|
||||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
/// Stringify a dotted path.
|
||||
#[doc(hidden)]
|
||||
/// The boilerplate to convert between a rust type and a python exception
|
||||
#[macro_export]
|
||||
macro_rules! dot_stringify {
|
||||
($e:ident) => (
|
||||
stringify!($e)
|
||||
);
|
||||
($e:ident. $($es:ident).+) => (
|
||||
concat!(stringify!($e), ".", dot_stringify!($($es).*))
|
||||
);
|
||||
macro_rules! impl_exception_boilerplate {
|
||||
($name: ident) => {
|
||||
impl ::std::convert::From<$name> for $crate::PyErr {
|
||||
fn from(_err: $name) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, _>(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ::std::convert::Into<$crate::PyResult<T>> for $name {
|
||||
fn into(self) -> $crate::PyResult<T> {
|
||||
$crate::PyErr::new::<$name, _>(()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn py_err<T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<Self, T>(args)
|
||||
}
|
||||
|
||||
pub fn into<R, T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyResult<R> {
|
||||
$crate::PyErr::new::<Self, T>(args).into()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Defines rust type for exception defined in Python code.
|
||||
|
@ -55,68 +69,156 @@ macro_rules! dot_stringify {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! import_exception {
|
||||
($($module:ident).+ , $name: ident) => {
|
||||
#[allow(non_camel_case_types)]
|
||||
($module: expr, $name: ident) => {
|
||||
#[allow(non_camel_case_types)] // E.g. `socket.herror`
|
||||
pub struct $name;
|
||||
|
||||
impl ::std::convert::From<$name> for $crate::PyErr {
|
||||
fn from(_err: $name) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, _>(())
|
||||
}
|
||||
}
|
||||
impl_exception_boilerplate!($name);
|
||||
|
||||
impl<T> ::std::convert::Into<$crate::PyResult<T>> for $name {
|
||||
fn into(self) -> $crate::PyResult<T> {
|
||||
$crate::PyErr::new::<$name, _>(()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn py_err<T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyErr
|
||||
where Self: $crate::typeob::PyTypeObject + Sized
|
||||
{
|
||||
$crate::PyErr::new::<Self, T>(args)
|
||||
}
|
||||
pub fn into<R, T: $crate::ToPyObject + 'static>(args: T) -> $crate::PyResult<R>
|
||||
where Self: $crate::typeob::PyTypeObject + Sized
|
||||
{
|
||||
$crate::PyErr::new::<Self, T>(args).into()
|
||||
}
|
||||
}
|
||||
import_exception_type_object!($module, $name);
|
||||
};
|
||||
}
|
||||
|
||||
/// `impl $crate::typeob::PyTypeObject for $name` where `$name` is an exception defined in python
|
||||
/// code.
|
||||
#[macro_export]
|
||||
macro_rules! import_exception_type_object {
|
||||
($module: expr, $name: ident) => {
|
||||
impl $crate::typeob::PyTypeObject for $name {
|
||||
#[inline]
|
||||
fn init_type() {}
|
||||
fn init_type() {
|
||||
let _ = Self::type_object();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn type_object() -> $crate::Py<$crate::types::PyType> {
|
||||
use $crate::IntoPyPointer;
|
||||
// We can't use lazy_static here because raw pointers aren't Send
|
||||
static TYPE_OBJECT_ONCE: ::std::sync::Once = ::std::sync::Once::new();
|
||||
static mut TYPE_OBJECT: *mut $crate::ffi::PyTypeObject = ::std::ptr::null_mut();
|
||||
|
||||
unsafe {
|
||||
if TYPE_OBJECT.is_null() {
|
||||
let gil = $crate::Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
TYPE_OBJECT_ONCE.call_once(|| {
|
||||
let gil = $crate::Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let imp = py.import(dot_stringify!($($module).*))
|
||||
.expect(concat!(
|
||||
"Can not import module: ", dot_stringify!($($module).*)));
|
||||
let cls = imp.get(stringify!($name))
|
||||
.expect(concat!(
|
||||
"Can not load exception class: {}.{}", dot_stringify!($($module).*),
|
||||
".", stringify!($name)));
|
||||
TYPE_OBJECT = cls.into_ptr() as *mut $crate::ffi::PyTypeObject;
|
||||
unsafe {
|
||||
let imp = py
|
||||
.import(stringify!($module))
|
||||
.expect(concat!("Can not import module: ", stringify!($module)));
|
||||
let cls = imp.get(stringify!($name)).expect(concat!(
|
||||
"Can not load exception class: {}.{}",
|
||||
stringify!($module),
|
||||
".",
|
||||
stringify!($name)
|
||||
));
|
||||
TYPE_OBJECT =
|
||||
$crate::IntoPyPointer::into_ptr(cls) as *mut $crate::ffi::PyTypeObject;
|
||||
}
|
||||
});
|
||||
|
||||
unsafe {
|
||||
$crate::Py::from_borrowed_ptr(
|
||||
TYPE_OBJECT as *const _ as *mut $crate::ffi::PyObject)
|
||||
TYPE_OBJECT as *const _ as *mut $crate::ffi::PyObject,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! exc_type (
|
||||
/// Defines a new exception type.
|
||||
///
|
||||
/// # Syntax
|
||||
/// `create_exception!(module, MyError, BaseException)`
|
||||
///
|
||||
/// * `module` is the name of the containing module.
|
||||
/// * `MyError` is the name of the new exception type.
|
||||
/// * `BaseException` is the superclass of MyError, usually `pyo3::exceptions::Exception`
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// extern crate pyo3;
|
||||
///
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyDict;
|
||||
/// use pyo3::exceptions::Exception;
|
||||
///
|
||||
/// create_exception!(mymodule, CustomError, Exception);
|
||||
///
|
||||
/// fn main() {
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let ctx = PyDict::new(py);
|
||||
/// let error_type = py.get_type::<CustomError>();
|
||||
/// ctx.set_item("CustomError", error_type).unwrap();
|
||||
/// let type_description: String = py
|
||||
/// .eval("str(CustomError)", None, Some(&ctx))
|
||||
/// .unwrap()
|
||||
/// .extract()
|
||||
/// .unwrap();
|
||||
/// assert_eq!(type_description, "<class 'mymodule.CustomError'>");
|
||||
/// py.run(
|
||||
/// "assert CustomError('oops').args == ('oops',)",
|
||||
/// None,
|
||||
/// Some(ctx),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! create_exception {
|
||||
($module: ident, $name: ident, $base: ty) => {
|
||||
#[allow(non_camel_case_types)] // E.g. `socket.herror`
|
||||
pub struct $name;
|
||||
|
||||
impl_exception_boilerplate!($name);
|
||||
|
||||
create_exception_type_object!($module, $name, $base);
|
||||
};
|
||||
}
|
||||
|
||||
/// `impl $crate::typeob::PyTypeObject for $name` where `$name` is an exception newly defined in
|
||||
/// rust code.
|
||||
#[macro_export]
|
||||
macro_rules! create_exception_type_object {
|
||||
($module: ident, $name: ident, $base: ty) => {
|
||||
impl $crate::typeob::PyTypeObject for $name {
|
||||
#[inline]
|
||||
fn init_type() {
|
||||
let _ = Self::type_object();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn type_object() -> $crate::Py<$crate::types::PyType> {
|
||||
// We can't use lazy_static here because raw pointers aren't Send
|
||||
static TYPE_OBJECT_ONCE: ::std::sync::Once = ::std::sync::Once::new();
|
||||
static mut TYPE_OBJECT: *mut $crate::ffi::PyTypeObject = ::std::ptr::null_mut();
|
||||
|
||||
TYPE_OBJECT_ONCE.call_once(|| {
|
||||
let gil = $crate::Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
unsafe {
|
||||
TYPE_OBJECT = $crate::PyErr::new_type(
|
||||
py,
|
||||
concat!(stringify!($module), ".", stringify!($name)),
|
||||
Some(py.get_type::<$base>()),
|
||||
None,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
unsafe {
|
||||
$crate::Py::from_borrowed_ptr(
|
||||
TYPE_OBJECT as *const _ as *mut $crate::ffi::PyObject,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_native_exception (
|
||||
($name:ident, $exc_name:ident) => (
|
||||
pub struct $name;
|
||||
|
||||
|
@ -152,84 +254,84 @@ macro_rules! exc_type (
|
|||
);
|
||||
);
|
||||
|
||||
exc_type!(BaseException, PyExc_BaseException);
|
||||
exc_type!(Exception, PyExc_Exception);
|
||||
impl_native_exception!(BaseException, PyExc_BaseException);
|
||||
impl_native_exception!(Exception, PyExc_Exception);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(StopAsyncIteration, PyExc_StopAsyncIteration);
|
||||
exc_type!(StopIteration, PyExc_StopIteration);
|
||||
exc_type!(GeneratorExit, PyExc_GeneratorExit);
|
||||
exc_type!(ArithmeticError, PyExc_ArithmeticError);
|
||||
exc_type!(LookupError, PyExc_LookupError);
|
||||
impl_native_exception!(StopAsyncIteration, PyExc_StopAsyncIteration);
|
||||
impl_native_exception!(StopIteration, PyExc_StopIteration);
|
||||
impl_native_exception!(GeneratorExit, PyExc_GeneratorExit);
|
||||
impl_native_exception!(ArithmeticError, PyExc_ArithmeticError);
|
||||
impl_native_exception!(LookupError, PyExc_LookupError);
|
||||
|
||||
exc_type!(AssertionError, PyExc_AssertionError);
|
||||
exc_type!(AttributeError, PyExc_AttributeError);
|
||||
exc_type!(BufferError, PyExc_BufferError);
|
||||
exc_type!(EOFError, PyExc_EOFError);
|
||||
exc_type!(FloatingPointError, PyExc_FloatingPointError);
|
||||
exc_type!(OSError, PyExc_OSError);
|
||||
exc_type!(ImportError, PyExc_ImportError);
|
||||
impl_native_exception!(AssertionError, PyExc_AssertionError);
|
||||
impl_native_exception!(AttributeError, PyExc_AttributeError);
|
||||
impl_native_exception!(BufferError, PyExc_BufferError);
|
||||
impl_native_exception!(EOFError, PyExc_EOFError);
|
||||
impl_native_exception!(FloatingPointError, PyExc_FloatingPointError);
|
||||
impl_native_exception!(OSError, PyExc_OSError);
|
||||
impl_native_exception!(ImportError, PyExc_ImportError);
|
||||
|
||||
#[cfg(Py_3_6)]
|
||||
exc_type!(ModuleNotFoundError, PyExc_ModuleNotFoundError);
|
||||
impl_native_exception!(ModuleNotFoundError, PyExc_ModuleNotFoundError);
|
||||
|
||||
exc_type!(IndexError, PyExc_IndexError);
|
||||
exc_type!(KeyError, PyExc_KeyError);
|
||||
exc_type!(KeyboardInterrupt, PyExc_KeyboardInterrupt);
|
||||
exc_type!(MemoryError, PyExc_MemoryError);
|
||||
exc_type!(NameError, PyExc_NameError);
|
||||
exc_type!(OverflowError, PyExc_OverflowError);
|
||||
exc_type!(RuntimeError, PyExc_RuntimeError);
|
||||
impl_native_exception!(IndexError, PyExc_IndexError);
|
||||
impl_native_exception!(KeyError, PyExc_KeyError);
|
||||
impl_native_exception!(KeyboardInterrupt, PyExc_KeyboardInterrupt);
|
||||
impl_native_exception!(MemoryError, PyExc_MemoryError);
|
||||
impl_native_exception!(NameError, PyExc_NameError);
|
||||
impl_native_exception!(OverflowError, PyExc_OverflowError);
|
||||
impl_native_exception!(RuntimeError, PyExc_RuntimeError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(RecursionError, PyExc_RecursionError);
|
||||
exc_type!(NotImplementedError, PyExc_NotImplementedError);
|
||||
exc_type!(SyntaxError, PyExc_SyntaxError);
|
||||
exc_type!(ReferenceError, PyExc_ReferenceError);
|
||||
exc_type!(SystemError, PyExc_SystemError);
|
||||
exc_type!(SystemExit, PyExc_SystemExit);
|
||||
exc_type!(TypeError, PyExc_TypeError);
|
||||
exc_type!(UnboundLocalError, PyExc_UnboundLocalError);
|
||||
exc_type!(UnicodeError, PyExc_UnicodeError);
|
||||
exc_type!(UnicodeDecodeError, PyExc_UnicodeDecodeError);
|
||||
exc_type!(UnicodeEncodeError, PyExc_UnicodeEncodeError);
|
||||
exc_type!(UnicodeTranslateError, PyExc_UnicodeTranslateError);
|
||||
exc_type!(ValueError, PyExc_ValueError);
|
||||
exc_type!(ZeroDivisionError, PyExc_ZeroDivisionError);
|
||||
impl_native_exception!(RecursionError, PyExc_RecursionError);
|
||||
impl_native_exception!(NotImplementedError, PyExc_NotImplementedError);
|
||||
impl_native_exception!(SyntaxError, PyExc_SyntaxError);
|
||||
impl_native_exception!(ReferenceError, PyExc_ReferenceError);
|
||||
impl_native_exception!(SystemError, PyExc_SystemError);
|
||||
impl_native_exception!(SystemExit, PyExc_SystemExit);
|
||||
impl_native_exception!(TypeError, PyExc_TypeError);
|
||||
impl_native_exception!(UnboundLocalError, PyExc_UnboundLocalError);
|
||||
impl_native_exception!(UnicodeError, PyExc_UnicodeError);
|
||||
impl_native_exception!(UnicodeDecodeError, PyExc_UnicodeDecodeError);
|
||||
impl_native_exception!(UnicodeEncodeError, PyExc_UnicodeEncodeError);
|
||||
impl_native_exception!(UnicodeTranslateError, PyExc_UnicodeTranslateError);
|
||||
impl_native_exception!(ValueError, PyExc_ValueError);
|
||||
impl_native_exception!(ZeroDivisionError, PyExc_ZeroDivisionError);
|
||||
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(BlockingIOError, PyExc_BlockingIOError);
|
||||
impl_native_exception!(BlockingIOError, PyExc_BlockingIOError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(BrokenPipeError, PyExc_BrokenPipeError);
|
||||
impl_native_exception!(BrokenPipeError, PyExc_BrokenPipeError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(ChildProcessError, PyExc_ChildProcessError);
|
||||
impl_native_exception!(ChildProcessError, PyExc_ChildProcessError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(ConnectionError, PyExc_ConnectionError);
|
||||
impl_native_exception!(ConnectionError, PyExc_ConnectionError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(ConnectionAbortedError, PyExc_ConnectionAbortedError);
|
||||
impl_native_exception!(ConnectionAbortedError, PyExc_ConnectionAbortedError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(ConnectionRefusedError, PyExc_ConnectionRefusedError);
|
||||
impl_native_exception!(ConnectionRefusedError, PyExc_ConnectionRefusedError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(ConnectionResetError, PyExc_ConnectionResetError);
|
||||
impl_native_exception!(ConnectionResetError, PyExc_ConnectionResetError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(FileExistsError, PyExc_FileExistsError);
|
||||
impl_native_exception!(FileExistsError, PyExc_FileExistsError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(FileNotFoundError, PyExc_FileNotFoundError);
|
||||
impl_native_exception!(FileNotFoundError, PyExc_FileNotFoundError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(InterruptedError, PyExc_InterruptedError);
|
||||
impl_native_exception!(InterruptedError, PyExc_InterruptedError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(IsADirectoryError, PyExc_IsADirectoryError);
|
||||
impl_native_exception!(IsADirectoryError, PyExc_IsADirectoryError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(NotADirectoryError, PyExc_NotADirectoryError);
|
||||
impl_native_exception!(NotADirectoryError, PyExc_NotADirectoryError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(PermissionError, PyExc_PermissionError);
|
||||
impl_native_exception!(PermissionError, PyExc_PermissionError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(ProcessLookupError, PyExc_ProcessLookupError);
|
||||
impl_native_exception!(ProcessLookupError, PyExc_ProcessLookupError);
|
||||
#[cfg(Py_3)]
|
||||
exc_type!(TimeoutError, PyExc_TimeoutError);
|
||||
impl_native_exception!(TimeoutError, PyExc_TimeoutError);
|
||||
|
||||
exc_type!(EnvironmentError, PyExc_EnvironmentError);
|
||||
exc_type!(IOError, PyExc_IOError);
|
||||
impl_native_exception!(EnvironmentError, PyExc_EnvironmentError);
|
||||
impl_native_exception!(IOError, PyExc_IOError);
|
||||
#[cfg(target_os = "windows")]
|
||||
exc_type!(WindowsError, PyExc_WindowsError);
|
||||
impl_native_exception!(WindowsError, PyExc_WindowsError);
|
||||
|
||||
impl UnicodeDecodeError {
|
||||
pub fn new_err<'p>(
|
||||
|
@ -299,6 +401,8 @@ pub mod socket {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::objectprotocol::ObjectProtocol;
|
||||
use crate::types::exceptions::Exception;
|
||||
use crate::types::PyDict;
|
||||
use crate::{PyErr, Python};
|
||||
|
||||
|
@ -356,4 +460,27 @@ mod test {
|
|||
.map_err(|e| e.print(py))
|
||||
.expect("assertion failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_exception() {
|
||||
create_exception!(mymodule, CustomError, Exception);
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let ctx = PyDict::new(py);
|
||||
let error_type = py.get_type::<CustomError>();
|
||||
ctx.set_item("CustomError", error_type).unwrap();
|
||||
let type_description: String = py
|
||||
.eval("str(CustomError)", None, Some(&ctx))
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert_eq!(type_description, "<class 'mymodule.CustomError'>");
|
||||
py.run(
|
||||
"assert CustomError('oops').args == ('oops',)",
|
||||
None,
|
||||
Some(ctx),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,8 +58,8 @@ macro_rules! int_convert_bignum (
|
|||
fn into_object(self, py: Python) -> PyObject {
|
||||
unsafe {
|
||||
// TODO: Replace this with functions from the from_bytes family
|
||||
// Once they are stabilized
|
||||
// https://github.com/rust-lang/rust/issues/52963
|
||||
// Once they are stabilized
|
||||
// https://github.com/rust-lang/rust/issues/52963
|
||||
let bytes = ::std::mem::transmute::<_, [c_uchar; $byte_size]>(self);
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr() as *const c_uchar,
|
||||
|
|
Loading…
Reference in a new issue