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")?;
|
TcpListener::bind("127.0.0.1:80")?;
|
||||||
|
|
||||||
Ok(true)
|
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.
|
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 typeob::PyTypeObject;
|
||||||
use conversion::{ToPyObject, ToBorrowedObject, IntoPyTuple};
|
use conversion::{ToPyObject, ToBorrowedObject, IntoPyTuple};
|
||||||
|
|
||||||
/**
|
/// Defines a new exception type.
|
||||||
Defines a new exception type.
|
///
|
||||||
|
/// # Syntax
|
||||||
# Syntax
|
/// `py_exception!(module, MyError)`
|
||||||
`py_exception!(module, MyError)`
|
///
|
||||||
|
/// * `module` is the name of the containing module.
|
||||||
* `module` is the name of the containing module.
|
/// * `MyError` is the name of the new exception type.
|
||||||
* `MyError` is the name of the new exception type.
|
///
|
||||||
|
/// # Example
|
||||||
# Example
|
/// ```
|
||||||
```
|
/// #[macro_use]
|
||||||
#[macro_use]
|
/// extern crate pyo3;
|
||||||
extern crate pyo3;
|
///
|
||||||
|
/// use pyo3::{Python, PyDict};
|
||||||
use pyo3::{Python, PyDict};
|
///
|
||||||
|
/// py_exception!(mymodule, CustomError);
|
||||||
py_exception!(mymodule, CustomError);
|
///
|
||||||
|
/// fn main() {
|
||||||
fn main() {
|
/// let gil = Python::acquire_gil();
|
||||||
let gil = Python::acquire_gil();
|
/// let py = gil.python();
|
||||||
let py = gil.python();
|
/// let ctx = PyDict::new(py);
|
||||||
let ctx = PyDict::new(py);
|
///
|
||||||
|
/// ctx.set_item("CustomError", py.get_type::<CustomError>()).unwrap();
|
||||||
ctx.set_item("CustomError", py.get_type::<CustomError>()).unwrap();
|
///
|
||||||
|
/// py.run("assert str(CustomError) == \"<class 'mymodule.CustomError'>\"",
|
||||||
py.run("assert str(CustomError) == \"<class 'mymodule.CustomError'>\"", None, Some(&ctx)).unwrap();
|
/// None, Some(&ctx)).unwrap();
|
||||||
py.run("assert CustomError('oops').args == ('oops',)", None, Some(ctx)).unwrap();
|
/// py.run("assert CustomError('oops').args == ('oops',)", None, Some(ctx)).unwrap();
|
||||||
}
|
/// }
|
||||||
```
|
/// ```
|
||||||
*/
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! py_exception {
|
macro_rules! py_exception {
|
||||||
($module: ident, $name: ident, $base: ty) => {
|
($module: ident, $name: ident, $base: ty) => {
|
||||||
pub struct $name;
|
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 {
|
fn from(_err: $name) -> $crate::PyErr {
|
||||||
$crate::PyErr::new::<$name, _>(())
|
$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
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||||
|
|
||||||
|
#[macro_use] mod exc_impl;
|
||||||
|
|
||||||
pub use self::typeobject::PyType;
|
pub use self::typeobject::PyType;
|
||||||
pub use self::module::PyModule;
|
pub use self::module::PyModule;
|
||||||
pub use self::iterator::PyIterator;
|
pub use self::iterator::PyIterator;
|
||||||
|
@ -13,6 +15,7 @@ pub use self::sequence::PySequence;
|
||||||
pub use self::slice::{PySlice, PySliceIndices};
|
pub use self::slice::{PySlice, PySliceIndices};
|
||||||
pub use self::set::{PySet, PyFrozenSet};
|
pub use self::set::{PySet, PyFrozenSet};
|
||||||
pub use self::stringdata::PyStringData;
|
pub use self::stringdata::PyStringData;
|
||||||
|
pub use self::exc_impl::PyNativeException;
|
||||||
|
|
||||||
#[cfg(Py_3)]
|
#[cfg(Py_3)]
|
||||||
pub use self::string::{PyBytes, PyString};
|
pub use self::string::{PyBytes, PyString};
|
||||||
|
|
|
@ -233,7 +233,7 @@ impl<'p> Python<'p> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import the Python module with the specified name.
|
/// Import the Python module with the specified name.
|
||||||
pub fn import(self, name : &str) -> PyResult<&'p PyModule> {
|
pub fn import(self, name: &str) -> PyResult<&'p PyModule> {
|
||||||
PyModule::import(self, name)
|
PyModule::import(self, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue