pyclass #[new]: allow using custom error type
This commit is contained in:
parent
2b94da1cbd
commit
fa8c93cfd1
|
@ -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 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 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 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
|
## [0.12.4] - 2020-11-28
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -176,6 +176,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
||||||
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
||||||
{
|
{
|
||||||
use pyo3::type_object::PyTypeInfo;
|
use pyo3::type_object::PyTypeInfo;
|
||||||
|
use pyo3::callback::IntoPyCallbackOutput;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
||||||
|
@ -183,7 +184,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
|
||||||
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
|
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
|
||||||
let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
|
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)?;
|
let cell = initializer.create_cell_from_subtype(_py, subtype)?;
|
||||||
Ok(cell as *mut pyo3::ffi::PyObject)
|
Ok(cell as *mut pyo3::ffi::PyObject)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Initialization utilities for `#[pyclass]`.
|
//! Initialization utilities for `#[pyclass]`.
|
||||||
|
use crate::callback::IntoPyCallbackOutput;
|
||||||
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
|
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
|
||||||
use crate::{PyCell, PyClass, PyErr, PyResult, Python};
|
use crate::{PyCell, PyClass, PyResult, Python};
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
/// Initializer for Python types.
|
/// Initializer for Python types.
|
||||||
|
@ -182,16 +182,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation which propagates the error from input PyResult. Useful in proc macro
|
// Implementation used by proc macros to allow anything convertible to PyClassInitializer<T> to be
|
||||||
// code where `#[new]` may or may not return PyResult.
|
// the return value of pyclass #[new] method (optionally wrapped in `Result<U, E>`).
|
||||||
impl<T, U> TryFrom<PyResult<U>> for PyClassInitializer<T>
|
impl<T, U> IntoPyCallbackOutput<PyClassInitializer<T>> for U
|
||||||
where
|
where
|
||||||
T: PyClass,
|
T: PyClass,
|
||||||
U: Into<PyClassInitializer<T>>,
|
U: Into<PyClassInitializer<T>>,
|
||||||
{
|
{
|
||||||
type Error = PyErr;
|
fn convert(self, _py: Python) -> PyResult<PyClassInitializer<T>> {
|
||||||
|
Ok(self.into())
|
||||||
fn try_from(result: PyResult<U>) -> PyResult<Self> {
|
|
||||||
result.map(Into::into)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use pyo3::exceptions::PyValueError;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -120,3 +121,32 @@ assert c.from_rust is False
|
||||||
.map_err(|e| e.print(py))
|
.map_err(|e| e.print(py))
|
||||||
.unwrap();
|
.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");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue