From b250ad22caabd442cd77a0d73a7c0af7b04bfba8 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 26 Jul 2017 09:16:26 -0700 Subject: [PATCH] update guide related to exception --- guide/src/exception.md | 32 +++++++++++++++++++++++++------- src/err.rs | 32 +++++++++++++++++--------------- src/objects/exc.rs | 24 +++++++++++++----------- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/guide/src/exception.md b/guide/src/exception.md index 07994175..bfa1d746 100644 --- a/guide/src/exception.md +++ b/guide/src/exception.md @@ -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::(), None).restore(py); + PyErr::new::("Error").restore(py); assert!(PyErr::occurred(py)); drop(PyErr::fetch(py)); } @@ -92,11 +92,13 @@ This is an alias for the type `Result`. 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` 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 { - Ok(s.parse::().map_err(|e| e.to_pyerr(py))?) +impl std::convert::From for PyErr { + fn from(err: std::io::Error) -> PyErr { + PyErr::from_value::(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 { + 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. diff --git a/src/err.rs b/src/err.rs index 6a6d0605..93db23be 100644 --- a/src/err.rs +++ b/src/err.rs @@ -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 = Result; 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(exc: Py, 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 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::( + $crate::PyErr::from_value::<$pyexc>( $crate::err::PyErrValue::ToErr(Box::new(err)) ) } @@ -495,10 +499,8 @@ impl std::convert::From 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 std::convert::From ToPyErr for std::io::IntoInnerError { - fn exc_arguments(&self, py: Python) -> PyObject { + fn arguments(&self, py: Python) -> PyObject { self.description().to_object(py) } } diff --git a/src/objects/exc.rs b/src/objects/exc.rs index 7f51ae51..7edf5b0e 100644 --- a/src/objects/exc.rs +++ b/src/objects/exc.rs @@ -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(value: V) -> $crate::PyErr { - $crate::PyErr::new::<$name, V>(value) + pub fn new(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 { unsafe { - $crate::Py::from_borrowed_ptr(ffi::$exc_name) + Py::from_borrowed_ptr(ffi::$exc_name) } } }