update guide related to exception

This commit is contained in:
Nikolay Kim 2017-07-26 09:16:26 -07:00
parent c9572d6e1d
commit b250ad22ca
3 changed files with 55 additions and 33 deletions

View File

@ -44,7 +44,7 @@ use pyo3::{Python, PyErr, exc};
fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None).restore(py);
PyErr::new::<exc::TypeError, _>("Error").restore(py);
assert!(PyErr::occurred(py));
drop(PyErr::fetch(py));
}
@ -92,11 +92,13 @@ This is an alias for the type `Result<T, PyErr>`.
A [`PyErr`](https://pyo3.github.io/PyO3/pyo3/struct.PyErr.html) represents a Python exception.
Errors within the `PyO3` library are also exposed as Python exceptions.
The [`ToPyErr`](https://pyo3.github.io/PyO3/pyo3/trait.ToPyErr.html) trait provides a way to convert Rust errors to Python exceptions.
To handle rust errors, `std::convert::From<T>` and [`ToPyErr`](https://pyo3.github.io/PyO3/pyo3/trait.ToPyErr.html)
traits need to be implemented for `PyErr`. The [`ToPyErr`](https://pyo3.github.io/PyO3/pyo3/trait.ToPyErr.html) trait provides
a way to convert Rust errors arguments to Python exception arguments.
```rust
pub trait ToPyErr {
fn to_pyerr(&self, _: Python) -> PyErr;
fn arguments(&self, _: Python) -> PyObject;
}
```
@ -104,11 +106,27 @@ It's implemented for most of the standard library's error types so that you use
transform errors to Python exceptions as well as taking advantage of `try!` macro or `?` operator.
```rust
use pyo3::{PyResult, ToPyErr};
use std;
use std::net::TcpListener;
use pyo3::{PyErr, PyResult, ToPyErr, exc};
fn parse_int(py: Python, s: String) -> PyResult<usize> {
Ok(s.parse::<usize>().map_err(|e| e.to_pyerr(py))?)
impl std::convert::From<std::io::Error> for PyErr {
fn from(err: std::io::Error) -> PyErr {
PyErr::from_value::<exc::OSError>(PyErrValue::ToErr(Box::new(err)))
}
}
impl ToPyErr for std::io::Error {
fn arguments(&self, py: Python) -> PyObject {
(self.raw_os_error().unwrap_or(0), self.description()).to_object(py)
}
}
fn connect(py: Python, s: String) -> PyResult<bool> {
TcpListener::bind("127.0.0.1:80")?;
Ok(true)
}
```
The code snippet above will raise `ValueError` in Python if `String::parse()` return an error.
The code snippet above will raise `OSError` in Python if `TcpListener::bind()` return an error.

View File

@ -8,7 +8,7 @@ use libc;
use ffi;
use python::{ToPyPointer, IntoPyPointer, Python};
use PyObject;
use object::PyObject;
use objects::{PyObjectRef, PyType, exc};
use instance::Py;
use typeob::PyTypeObject;
@ -132,6 +132,13 @@ pub type PyResult<T> = Result<T, PyErr>;
pub struct PyDowncastError;
/// Conversion trait that allows various errors to be converted into `PyErr`
pub trait ToPyErr {
/// Arguments for exception
fn arguments(&self, Python) -> PyObject;
}
impl PyErr {
/// Creates a new PyErr of type `T`.
///
@ -161,6 +168,8 @@ impl PyErr {
/// `exc` is the exception type; usually one of the standard exceptions
/// like `exc::RuntimeError::type_info()`.
/// `args` is the a tuple of arguments to pass to the exception constructor.
///
/// Panics if `T` is not a python class derived from `BaseException`.
pub fn from_type<A>(exc: Py<PyType>, args: A) -> PyErr
where A: IntoPyTuple + 'static
{
@ -327,7 +336,7 @@ impl PyErr {
let mut pvalue = match pvalue {
PyErrValue::None => std::ptr::null_mut(),
PyErrValue::Value(ob) => ob.into_ptr(),
PyErrValue::ToErr(ob) => ob.exc_arguments(py).into_ptr(),
PyErrValue::ToErr(ob) => ob.arguments(py).into_ptr(),
PyErrValue::ToObject(ob) => ob.to_object(py).into_ptr(),
PyErrValue::ToTuple(ob) => ob.to_tuple(py).into_ptr(),
};
@ -365,7 +374,7 @@ impl PyErr {
let pvalue = match pvalue {
PyErrValue::None => std::ptr::null_mut(),
PyErrValue::Value(ob) => ob.into_ptr(),
PyErrValue::ToErr(ob) => ob.exc_arguments(py).into_ptr(),
PyErrValue::ToErr(ob) => ob.arguments(py).into_ptr(),
PyErrValue::ToObject(ob) => ob.to_object(py).into_ptr(),
PyErrValue::ToTuple(ob) => ob.to_tuple(py).into_ptr(),
};
@ -434,22 +443,17 @@ impl std::convert::From<PyErr> for std::io::Error {
}
}
/// Converts into `PyErr`
pub trait ToPyErr {
fn exc_arguments(&self, Python) -> PyObject;
}
macro_rules! impl_to_pyerr {
($err: ty, $pyexc: ty) => {
impl $crate::ToPyErr for $err {
fn exc_arguments(&self, py: $crate::Python) -> PyObject {
fn arguments(&self, py: $crate::Python) -> PyObject {
self.description().to_object(py)
}
}
impl $crate::std::convert::From<$err> for $crate::PyErr {
fn from(err: $err) -> $crate::PyErr {
$crate::PyErr::from_value::<exc::ValueError>(
$crate::PyErr::from_value::<$pyexc>(
$crate::err::PyErrValue::ToErr(Box::new(err))
)
}
@ -495,10 +499,8 @@ impl std::convert::From<io::Error> for PyErr {
/// Extract `errno` and `errdesc` from from `io::Error`
impl ToPyErr for io::Error {
fn exc_arguments(&self, py: Python) -> PyObject {
let errno = self.raw_os_error().unwrap_or(0);
let errdesc = self.description().to_owned();
(errno, errdesc).to_object(py)
fn arguments(&self, py: Python) -> PyObject {
(self.raw_os_error().unwrap_or(0), self.description()).to_object(py)
}
}
@ -510,7 +512,7 @@ impl<W: 'static + Send + std::fmt::Debug> std::convert::From<std::io::IntoInnerE
}
impl<W: Send + std::fmt::Debug> ToPyErr for std::io::IntoInnerError<W> {
fn exc_arguments(&self, py: Python) -> PyObject {
fn arguments(&self, py: Python) -> PyObject {
self.description().to_object(py)
}
}

View File

@ -9,33 +9,35 @@ use std::{self, mem, ops};
use std::ffi::CStr;
use ffi;
use objects::PyObjectRef;
use instance::Py;
use err::{PyErr, PyResult};
use python::{Python, ToPyPointer};
use err::PyResult;
use super::PyTuple;
use objects::{PyTuple, PyType, PyObjectRef};
use typeob::PyTypeObject;
use conversion::ToPyObject;
macro_rules! exc_type(
($name:ident, $exc_name:ident) => (
pub struct $name;
impl std::convert::From<$name> for $crate::PyErr {
fn from(_err: $name) -> $crate::PyErr {
$crate::PyErr::new::<$name, _>(())
impl std::convert::From<$name> for PyErr {
fn from(_err: $name) -> PyErr {
PyErr::new::<$name, _>(())
}
}
impl $name {
pub fn new<V: $crate::ToPyObject + 'static>(value: V) -> $crate::PyErr {
$crate::PyErr::new::<$name, V>(value)
pub fn new<V: ToPyObject + 'static>(args: V) -> PyErr {
PyErr::new::<$name, V>(args)
}
}
impl $crate::typeob::PyTypeObject for $name {
impl PyTypeObject for $name {
#[inline(always)]
fn init_type() {}
#[inline]
fn type_object() -> $crate::Py<$crate::PyType> {
fn type_object() -> Py<PyType> {
unsafe {
$crate::Py::from_borrowed_ptr(ffi::$exc_name)
Py::from_borrowed_ptr(ffi::$exc_name)
}
}
}