rename ToPyErr to PyErrArguments

This commit is contained in:
Nikolay Kim 2017-07-26 10:47:17 -07:00
parent 3b6d784bfb
commit e45eb6e878
3 changed files with 38 additions and 37 deletions

View File

@ -92,18 +92,16 @@ 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.
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.
PyO3 library handles python exception in two stages. During first stage `PyErr` instance get
created. At this stage python gil is not required. During second stage, actual python
exception instance get crated and set to python interpreter.
```rust
pub trait ToPyErr {
fn arguments(&self, _: Python) -> PyObject;
}
```
It's implemented for most of the standard library's error types so that you use [`Result::map_err()`](https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err) to
transform errors to Python exceptions as well as taking advantage of `try!` macro or `?` operator.
In simple case, for custom errors support implementation of `std::convert::From<T>` trait
for this custom error is enough. `PyErr::new` accepts arguments in form
of `ToPyObject + 'static`. In case if `'static` constraint can not be satisfied or
more complex arguments are required [`PyErrArgument`](https://pyo3.github.io/PyO3/pyo3/trait.PyErrArguments.html)
trait can be implemented. In that case actual exception arguments creation get delayed
until `Python` object is available.
```rust
use std;
@ -112,13 +110,7 @@ use pyo3::{PyErr, PyResult, ToPyErr, exc};
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)
exc::OSError.into()
}
}
@ -130,3 +122,16 @@ fn connect(py: Python, s: String) -> PyResult<bool> {
```
The code snippet above will raise `OSError` in Python if `TcpListener::bind()` return an error.
`std::convert::From<T>` trait is implemented for most of the standard library's error
types so `try!` macro or `?` operator can be used.
```rust
use pyo3::{PyResult, ToPyErr};
fn parse_int(py: Python, s: String) -> PyResult<usize> {
Ok(s.parse::<usize>()?)
}
```
The code snippet above will raise `ValueError` in Python if `String::parse()` return an error.

View File

@ -103,7 +103,7 @@ macro_rules! py_exception {
pub enum PyErrValue {
None,
Value(PyObject),
ToErr(Box<ToPyErr>),
ToErr(Box<PyErrArguments>),
ToObject(Box<ToPyObject>),
ToTuple(Box<IntoPyTuple>),
}
@ -112,33 +112,31 @@ pub enum PyErrValue {
pub struct PyErr {
/// The type of the exception. This should be either a `PyClass` or a `PyType`.
pub ptype: Py<PyType>,
/// The value of the exception.
///
/// This can be either an instance of `ptype`,
/// This can be either an instance of `PyObject`,
/// a tuple of arguments to be passed to `ptype`'s constructor,
/// or a single argument to be passed to `ptype`'s constructor.
/// Call `PyErr::instance()` to get the exception instance in all cases.
pub pvalue: PyErrValue,
/// The `PyTraceBack` object associated with the error.
pub ptraceback: Option<PyObject>,
}
/// Represents the result of a Python call.
pub type PyResult<T> = Result<T, PyErr>;
/// Marker type that indicates an error while downcasting
pub struct PyDowncastError;
/// Conversion trait that allows various errors to be converted into `PyErr`
pub trait ToPyErr {
/// Helper conversion trait that allows to use custom arguments for exception constructor.
pub trait PyErrArguments {
/// Arguments for exception
fn arguments(&self, Python) -> PyObject;
}
impl PyErr {
/// Creates a new PyErr of type `T`.
///
@ -425,7 +423,7 @@ impl std::fmt::Debug for PyErr {
/// Converts `PyDowncastError` to Python `TypeError`.
impl std::convert::From<PyDowncastError> for PyErr {
fn from(_err: PyDowncastError) -> PyErr {
PyErr::new::<exc::TypeError, _>(())
exc::TypeError.into()
}
}
@ -445,17 +443,15 @@ impl std::convert::From<PyErr> for std::io::Error {
macro_rules! impl_to_pyerr {
($err: ty, $pyexc: ty) => {
impl $crate::ToPyErr for $err {
fn arguments(&self, py: $crate::Python) -> PyObject {
impl PyErrArguments for $err {
fn arguments(&self, py: 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::<$pyexc>(
$crate::err::PyErrValue::ToErr(Box::new(err))
)
impl std::convert::From<$err> for PyErr {
fn from(err: $err) -> PyErr {
PyErr::from_value::<$pyexc>(PyErrValue::ToErr(Box::new(err)))
}
}
}
@ -498,7 +494,7 @@ impl std::convert::From<io::Error> for PyErr {
/// Extract `errno` and `errdesc` from from `io::Error`
impl ToPyErr for io::Error {
impl PyErrArguments for io::Error {
fn arguments(&self, py: Python) -> PyObject {
(self.raw_os_error().unwrap_or(0), self.description()).to_object(py)
}
@ -511,7 +507,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> {
impl<W: Send + std::fmt::Debug> PyErrArguments for std::io::IntoInnerError<W> {
fn arguments(&self, py: Python) -> PyObject {
self.description().to_object(py)
}

View File

@ -150,7 +150,7 @@ pub mod ffi {
pub use ffi3::*;
}
pub use err::{PyErr, PyErrValue, PyResult, PyDowncastError, ToPyErr};
pub use err::{PyErr, PyErrValue, PyResult, PyDowncastError, PyErrArguments};
pub use objects::*;
pub use objectprotocol::ObjectProtocol;
pub use object::PyObject;