Skip to content

Commit

Permalink
pyclass #[new]: allow using custom error type
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 13, 2020
1 parent 2b94da1 commit fa8c93c
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fix FFI definitions for `PyObject_Vectorcall` and `PyVectorcall_Call`. [#1287](https://github.com/PyO3/pyo3/pull/1285)
- Fix building with Anaconda python inside a virtualenv. [#1290](https://github.com/PyO3/pyo3/pull/1290)
- Fix definition of opaque FFI types. [#1312](https://github.com/PyO3/pyo3/pull/1312)
- Fix using custom error type in pyclass `#[new]` methods. [#1319](https://github.com/PyO3/pyo3/pull/1319)

## [0.12.4] - 2020-11-28
### Fixed
Expand Down
3 changes: 2 additions & 1 deletion pyo3-derive-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,15 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
{
use pyo3::type_object::PyTypeInfo;
use pyo3::callback::IntoPyCallbackOutput;
use std::convert::TryFrom;

const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
pyo3::callback_body_without_convert!(_py, {
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);

let initializer = pyo3::PyClassInitializer::try_from(#body)?;
let initializer: pyo3::PyClassInitializer::<#cls> = #body.convert(_py)?;
let cell = initializer.create_cell_from_subtype(_py, subtype)?;
Ok(cell as *mut pyo3::ffi::PyObject)
})
Expand Down
16 changes: 7 additions & 9 deletions src/pyclass_init.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Initialization utilities for `#[pyclass]`.
use crate::callback::IntoPyCallbackOutput;
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
use crate::{PyCell, PyClass, PyErr, PyResult, Python};
use std::convert::TryFrom;
use crate::{PyCell, PyClass, PyResult, Python};
use std::marker::PhantomData;

/// Initializer for Python types.
Expand Down Expand Up @@ -182,16 +182,14 @@ where
}
}

// Implementation which propagates the error from input PyResult. Useful in proc macro
// code where `#[new]` may or may not return PyResult.
impl<T, U> TryFrom<PyResult<U>> for PyClassInitializer<T>
// Implementation used by proc macros to allow anything convertible to PyClassInitializer<T> to be
// the return value of pyclass #[new] method (optionally wrapped in `Result<U, E>`).
impl<T, U> IntoPyCallbackOutput<PyClassInitializer<T>> for U
where
T: PyClass,
U: Into<PyClassInitializer<T>>,
{
type Error = PyErr;

fn try_from(result: PyResult<U>) -> PyResult<Self> {
result.map(Into::into)
fn convert(self, _py: Python) -> PyResult<PyClassInitializer<T>> {
Ok(self.into())
}
}
30 changes: 30 additions & 0 deletions tests/test_class_new.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;

#[pyclass]
Expand Down Expand Up @@ -120,3 +121,32 @@ assert c.from_rust is False
.map_err(|e| e.print(py))
.unwrap();
}

#[pyclass]
#[derive(Debug)]
struct NewWithCustomError {}

struct CustomError;

impl From<CustomError> for PyErr {
fn from(_error: CustomError) -> PyErr {
PyValueError::new_err("custom error")
}
}

#[pymethods]
impl NewWithCustomError {
#[new]
fn new() -> Result<NewWithCustomError, CustomError> {
Err(CustomError)
}
}

#[test]
fn new_with_custom_error() {
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<NewWithCustomError>();
let err = typeobj.call0().unwrap_err();
assert_eq!(err.to_string(), "ValueError: custom error");
}

0 comments on commit fa8c93c

Please sign in to comment.