From 9102f2e3643b7030a9ed847e2d13b6b88d3e54ca Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 27 Nov 2018 23:07:15 +0100 Subject: [PATCH] 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 --- CHANGELOG.md | 1 + guide/src/exception.md | 9 +- pyo3cls/src/lib.rs | 4 +- src/err.rs | 109 +------------ src/ffi3/object.rs | 66 ++++---- src/lib.rs | 4 +- src/prelude.rs | 4 +- src/types/exceptions.rs | 337 +++++++++++++++++++++++++++------------- src/types/num_common.rs | 4 +- 9 files changed, 288 insertions(+), 250 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cafbbcf1f9..ecd0afa4bcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/guide/src/exception.md b/guide/src/exception.md index f9811e0da76..e534c2c240c 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -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 diff --git a/pyo3cls/src/lib.rs b/pyo3cls/src/lib.rs index c414249c68f..8ff150db68c 100644 --- a/pyo3cls/src/lib.rs +++ b/pyo3cls/src/lib.rs @@ -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 { diff --git a/src/err.rs b/src/err.rs index f90a2b27b9e..6112edd909b 100644 --- a/src/err.rs +++ b/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::()).unwrap(); -/// -/// py.run("assert str(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 std::convert::Into<$crate::PyResult> for $name { - fn into(self) -> $crate::PyResult { - $crate::PyErr::new::<$name, _>(()).into() - } - } - - impl $name { - pub fn py_err(args: T) -> $crate::PyErr { - $crate::PyErr::new::<$name, T>(args) - } - pub fn into(args: T) -> $crate::PyResult { - $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 { diff --git a/src/ffi3/object.rs b/src/ffi3/object.rs index 599fe129c9b..f791a44c31c 100644 --- a/src/ffi3/object.rs +++ b/src/ffi3/object.rs @@ -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() } } } diff --git a/src/lib.rs b/src/lib.rs index 178d5b26486..89868f22ac7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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}; } diff --git a/src/prelude.rs b/src/prelude.rs index 8f6ddee5ac8..c22efb15533 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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; diff --git a/src/types/exceptions.rs b/src/types/exceptions.rs index e4c55e2c5af..219b2586e8d 100644 --- a/src/types/exceptions.rs +++ b/src/types/exceptions.rs @@ -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 ::std::convert::Into<$crate::PyResult> for $name { + fn into(self) -> $crate::PyResult { + $crate::PyErr::new::<$name, _>(()).into() + } + } + + impl $name { + pub fn py_err(args: T) -> $crate::PyErr { + $crate::PyErr::new::(args) + } + + pub fn into(args: T) -> $crate::PyResult { + $crate::PyErr::new::(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 ::std::convert::Into<$crate::PyResult> for $name { - fn into(self) -> $crate::PyResult { - $crate::PyErr::new::<$name, _>(()).into() - } - } + import_exception_type_object!($module, $name); + }; +} - impl $name { - pub fn py_err(args: T) -> $crate::PyErr - where Self: $crate::typeob::PyTypeObject + Sized - { - $crate::PyErr::new::(args) +/// `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() { + let _ = Self::type_object(); } - pub fn into(args: T) -> $crate::PyResult - where Self: $crate::typeob::PyTypeObject + Sized - { - $crate::PyErr::new::(args).into() + + #[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 { + 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, + ) + } } } + }; +} + +/// 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::(); +/// 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, ""); +/// 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() {} + 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(); - - 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; + 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) + TYPE_OBJECT as *const _ as *mut $crate::ffi::PyObject, + ) } } } }; } -macro_rules! exc_type ( +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); - -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!(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); + +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); - -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!(ModuleNotFoundError, PyExc_ModuleNotFoundError); + +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::(); + 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, ""); + py.run( + "assert CustomError('oops').args == ('oops',)", + None, + Some(ctx), + ) + .unwrap(); + } } diff --git a/src/types/num_common.rs b/src/types/num_common.rs index faf499095c2..06749798d5e 100644 --- a/src/types/num_common.rs +++ b/src/types/num_common.rs @@ -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,