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:
konstin 2018-11-27 23:07:15 +01:00
parent de2108d719
commit 9102f2e364
9 changed files with 289 additions and 251 deletions

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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() }
}
}

View file

@ -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};
}

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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,