Create rust type for exceptions defined in python #45
This commit is contained in:
parent
e45eb6e878
commit
a20230e73f
|
@ -114,7 +114,7 @@ impl std::convert::From<std::io::Error> for PyErr {
|
|||
}
|
||||
}
|
||||
|
||||
fn connect(py: Python, s: String) -> PyResult<bool> {
|
||||
fn connect(s: String) -> PyResult<bool> {
|
||||
TcpListener::bind("127.0.0.1:80")?;
|
||||
|
||||
Ok(true)
|
||||
|
@ -135,3 +135,28 @@ fn parse_int(py: Python, s: String) -> PyResult<usize> {
|
|||
```
|
||||
|
||||
The code snippet above will raise `ValueError` in Python if `String::parse()` return an error.
|
||||
|
||||
|
||||
## Using exceptions defined in python code
|
||||
|
||||
It is possible to use exception defined in python code as native rust types.
|
||||
`import_exception!` macro allows to import specific exception class and defined zst type
|
||||
for that exception.
|
||||
|
||||
```rust
|
||||
use pyo3::{PyErr, PyResult, ToPyErr, exc};
|
||||
|
||||
import_exception!(asyncio, CancelledError)
|
||||
|
||||
fn cancel(fut: PyFuture) -> PyResult<()> {
|
||||
if fut.cancelled() {
|
||||
Err(CancelledError.into())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
[`exc`](https://pyo3.github.io/PyO3/pyo3/exc/index.html) defines exceptions for
|
||||
several standard library modules.
|
||||
|
|
61
src/err.rs
61
src/err.rs
|
@ -14,42 +14,41 @@ use instance::Py;
|
|||
use typeob::PyTypeObject;
|
||||
use conversion::{ToPyObject, ToBorrowedObject, IntoPyTuple};
|
||||
|
||||
/**
|
||||
Defines a new exception type.
|
||||
|
||||
# Syntax
|
||||
`py_exception!(module, MyError)`
|
||||
|
||||
* `module` is the name of the containing module.
|
||||
* `MyError` is the name of the new exception type.
|
||||
|
||||
# Example
|
||||
```
|
||||
#[macro_use]
|
||||
extern crate pyo3;
|
||||
|
||||
use pyo3::{Python, PyDict};
|
||||
|
||||
py_exception!(mymodule, CustomError);
|
||||
|
||||
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();
|
||||
}
|
||||
```
|
||||
*/
|
||||
/// Defines a new exception type.
|
||||
///
|
||||
/// # Syntax
|
||||
/// `py_exception!(module, MyError)`
|
||||
///
|
||||
/// * `module` is the name of the containing module.
|
||||
/// * `MyError` is the name of the new exception type.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// extern crate pyo3;
|
||||
///
|
||||
/// use pyo3::{Python, PyDict};
|
||||
///
|
||||
/// py_exception!(mymodule, CustomError);
|
||||
///
|
||||
/// 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 {
|
||||
impl ::std::convert::From<$name> for $crate::PyErr {
|
||||
fn from(_err: $name) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, _>(())
|
||||
}
|
||||
|
|
|
@ -160,3 +160,21 @@ impl StopIteration {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Exceptions defined in `asyncio` module
|
||||
pub mod asyncio {
|
||||
import_exception!(asyncio, CancelledError);
|
||||
import_exception!(asyncio, InvalidStateError);
|
||||
import_exception!(asyncio, TimeoutError);
|
||||
import_exception!(asyncio, IncompleteReadError);
|
||||
import_exception!(asyncio, LimitOverrunError);
|
||||
import_exception!(asyncio, QueueEmpty);
|
||||
import_exception!(asyncio, QueueFull);
|
||||
}
|
||||
|
||||
/// Exceptions defined in `socket` module
|
||||
pub mod socket {
|
||||
import_exception!(socket, herror);
|
||||
import_exception!(socket, gaierror);
|
||||
import_exception!(socket, timeout);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use std;
|
||||
use ffi;
|
||||
use err::PyErr;
|
||||
use typeob::PyTypeObject;
|
||||
use conversion::ToPyObject;
|
||||
use python::{Python, IntoPyPointer};
|
||||
|
||||
/// Defines rust type for exception defined in Python code.
|
||||
///
|
||||
/// # Syntax
|
||||
/// `import_exception!(module, MyError)`
|
||||
///
|
||||
/// * `module` is the name of the containing module.
|
||||
/// * `MyError` is the name of the new exception type.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// extern crate pyo3;
|
||||
///
|
||||
/// use pyo3::{Python, PyDict};
|
||||
///
|
||||
/// import_exception!(socket, gaierror);
|
||||
///
|
||||
/// fn main() {
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let ctx = PyDict::new(py);
|
||||
///
|
||||
/// ctx.set_item("gaierror", py.get_type::<gaierror>()).unwrap();
|
||||
/// py.run("import socket; assert gaierror is socket.gaierror", None, Some(ctx)).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! import_exception {
|
||||
($module: ident, $name: ident) => {
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $name;
|
||||
|
||||
impl ::std::convert::From<$name> for $crate::PyErr {
|
||||
fn from(_err: $name) -> $crate::PyErr {
|
||||
$crate::PyErr::new::<$name, _>(())
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::PyNativeException for $name {
|
||||
const MOD: &'static str = stringify!($module);
|
||||
const NAME: &'static str = stringify!($name);
|
||||
}
|
||||
|
||||
impl $crate::typeob::PyTypeObject for $name {
|
||||
#[inline(always)]
|
||||
fn init_type() {
|
||||
use $crate::PyNativeException;
|
||||
let _ = <$name as PyNativeException>::type_object_ptr();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn type_object() -> $crate::Py<$crate::PyType> {
|
||||
use $crate::PyNativeException;
|
||||
unsafe {
|
||||
$crate::Py::from_borrowed_ptr(
|
||||
<$name as PyNativeException>::type_object_ptr()
|
||||
as *const _ as *mut $crate::ffi::PyObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Description of exception defined in python code.
|
||||
/// `import_exception!` defines this trait for new exception type.
|
||||
pub trait PyNativeException {
|
||||
|
||||
/// Module name, where exception is defined
|
||||
const MOD: &'static str;
|
||||
|
||||
/// Name of exception
|
||||
const NAME: &'static str;
|
||||
|
||||
fn new<S: PyTypeObject, T: ToPyObject + 'static>(args: T) -> PyErr {
|
||||
PyErr::new::<S, T>(args)
|
||||
}
|
||||
|
||||
fn type_object_ptr() -> *mut ffi::PyTypeObject {
|
||||
static mut TYPE_OBJECT: *mut ffi::PyTypeObject = std::ptr::null_mut();
|
||||
|
||||
unsafe {
|
||||
if TYPE_OBJECT.is_null() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let imp = py.import(Self::MOD)
|
||||
.expect(format!(
|
||||
"Can not import module: {}", Self::MOD).as_ref());
|
||||
let cls = imp.get(Self::NAME)
|
||||
.expect(format!(
|
||||
"Can not load exception class: {}.{}", Self::MOD, Self::NAME).as_ref());
|
||||
TYPE_OBJECT = cls.into_ptr() as *mut ffi::PyTypeObject;
|
||||
}
|
||||
TYPE_OBJECT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {PyErr, Python};
|
||||
use objects::PyDict;
|
||||
|
||||
import_exception!(socket, gaierror);
|
||||
|
||||
#[test]
|
||||
fn test_check_exception() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let err: PyErr = gaierror.into();
|
||||
|
||||
let d = PyDict::new(py);
|
||||
d.set_item("socket", py.import("socket").unwrap()).unwrap();
|
||||
d.set_item("exc", err.instance(py)).unwrap();
|
||||
|
||||
py.run("assert isinstance(exc, socket.gaierror)", None, Some(d)).unwrap();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
#[macro_use] mod exc_impl;
|
||||
|
||||
pub use self::typeobject::PyType;
|
||||
pub use self::module::PyModule;
|
||||
pub use self::iterator::PyIterator;
|
||||
|
@ -13,6 +15,7 @@ pub use self::sequence::PySequence;
|
|||
pub use self::slice::{PySlice, PySliceIndices};
|
||||
pub use self::set::{PySet, PyFrozenSet};
|
||||
pub use self::stringdata::PyStringData;
|
||||
pub use self::exc_impl::PyNativeException;
|
||||
|
||||
#[cfg(Py_3)]
|
||||
pub use self::string::{PyBytes, PyString};
|
||||
|
|
Loading…
Reference in New Issue