Simplify IntoInitializer

This commit is contained in:
David Hewitt 2020-01-06 13:14:41 +00:00
parent 18e565fab5
commit 60edeb889e
6 changed files with 50 additions and 49 deletions

View file

@ -257,10 +257,10 @@ struct SubSubClass {
#[pymethods] #[pymethods]
impl SubSubClass { impl SubSubClass {
#[new] #[new]
fn new() -> impl IntoInitializer<Self> { fn new() -> PyClassInitializer<Self> {
Ok(SubClass::new() SubClass::new()
.into_initializer()? .into_initializer()
.add_subclass(SubSubClass{val3: 20})) .add_subclass(SubSubClass{val3: 20})
} }
fn method3(self_: &PyClassShell<Self>) -> PyResult<usize> { fn method3(self_: &PyClassShell<Self>) -> PyResult<usize> {

View file

@ -154,7 +154,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
let body = impl_arg_params_( let body = impl_arg_params_(
spec, spec,
cb, cb,
quote! { pyo3::pyclass_init::IntoInitializer::into_initializer }, quote! { pyo3::derive_utils::IntoPyNewResult::into_pynew_result },
); );
quote! { quote! {
@ -174,7 +174,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {
#body #body
match _result.and_then(|init| init.create_shell(_py)) { match _result.and_then(|init| init.into_initializer().create_shell(_py)) {
Ok(slf) => slf as _, Ok(slf) => slf as _,
Err(e) => e.restore_and_null(_py), Err(e) => e.restore_and_null(_py),
} }

View file

@ -10,6 +10,7 @@ use crate::init_once;
use crate::instance::PyNativeType; use crate::instance::PyNativeType;
use crate::types::{PyAny, PyDict, PyModule, PyTuple}; use crate::types::{PyAny, PyDict, PyModule, PyTuple};
use crate::{ffi, GILPool, IntoPy, PyObject, Python}; use crate::{ffi, GILPool, IntoPy, PyObject, Python};
use crate::pyclass_init::IntoInitializer;
use std::ptr; use std::ptr;
/// Description of a python parameter; used for `parse_args()`. /// Description of a python parameter; used for `parse_args()`.
@ -179,3 +180,23 @@ impl<T: IntoPy<PyObject>> IntoPyResult<T> for PyResult<T> {
self self
} }
} }
use crate::pyclass::PyClass;
/// Variant of IntoPyResult for the specific case of #[new]. In the case of returning (Sub, Base)
/// from #[new], IntoPyResult can't apply because (Sub, Base) doesn't implement IntoPy<PyObject>.
pub trait IntoPyNewResult<T: PyClass, I: IntoInitializer<T>> {
fn into_pynew_result(self) -> PyResult<I>;
}
impl<T: PyClass, I: IntoInitializer<T>> IntoPyNewResult<T, I> for I {
fn into_pynew_result(self) -> PyResult<I> {
Ok(self)
}
}
impl<T: PyClass, I: IntoInitializer<T>> IntoPyNewResult<T, I> for PyResult<I> {
fn into_pynew_result(self) -> PyResult<I> {
self
}
}

View file

@ -42,7 +42,7 @@ impl<T> Py<T> {
<T::BaseType as PyTypeInfo>::ConcreteLayout: <T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>, crate::type_object::PyObjectSizedLayout<T::BaseType>,
{ {
let initializer = value.into_initializer()?; let initializer = value.into_initializer();
let obj = unsafe { initializer.create_shell(py)? }; let obj = unsafe { initializer.create_shell(py)? };
let ob = unsafe { Py::from_owned_ptr(obj as _) }; let ob = unsafe { Py::from_owned_ptr(obj as _) };
Ok(ob) Ok(ob)

View file

@ -136,7 +136,7 @@ impl<T: PyClass> PyClassShell<T> {
crate::type_object::PyObjectSizedLayout<T::BaseType>, crate::type_object::PyObjectSizedLayout<T::BaseType>,
{ {
unsafe { unsafe {
let initializer = value.into_initializer()?; let initializer = value.into_initializer();
let self_ = initializer.create_shell(py)?; let self_ = initializer.create_shell(py)?;
FromPyPointer::from_owned_ptr_or_err(py, self_ as _) FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
} }
@ -149,7 +149,7 @@ impl<T: PyClass> PyClassShell<T> {
crate::type_object::PyObjectSizedLayout<T::BaseType>, crate::type_object::PyObjectSizedLayout<T::BaseType>,
{ {
unsafe { unsafe {
let initializer = value.into_initializer()?; let initializer = value.into_initializer();
let self_ = initializer.create_shell(py)?; let self_ = initializer.create_shell(py)?;
FromPyPointer::from_owned_ptr_or_err(py, self_ as _) FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
} }

View file

@ -109,30 +109,33 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
} }
} }
/// Represets that we can convert the type to `PyClassInitializer`. impl<T> From<T> for PyClassInitializer<T>
///
/// It is mainly used in our proc-macro code.
pub trait IntoInitializer<T: PyClass> {
fn into_initializer(self) -> PyResult<PyClassInitializer<T>>;
}
impl<T> IntoInitializer<T> for T
where where
T: PyClass, T: PyClass,
T::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<T::BaseType>>, T::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<T::BaseType>>,
{ {
fn into_initializer(self) -> PyResult<PyClassInitializer<T>> { fn from(value: T) -> PyClassInitializer<T> {
Ok(PyClassInitializer::from_value(self)) PyClassInitializer::from_value(value)
} }
} }
impl<T> IntoInitializer<T> for PyResult<T> /// An extension of Into which extends the range of possible types from `#[pyclass]`'s `#[new]`.
///
/// In particular it permits for the return type of `#[new]` to be a (SubType, BaseType) pair
/// which will also be initialized.
///
/// It is mainly used in our proc-macro code.
pub trait IntoInitializer<T: PyClass> {
fn into_initializer(self) -> PyClassInitializer<T>;
}
impl<T, U> IntoInitializer<T> for U
where where
T: PyClass, T: PyClass,
T::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<T::BaseType>>, U: Into<PyClassInitializer<T>>
{ {
fn into_initializer(self) -> PyResult<PyClassInitializer<T>> { fn into_initializer(self) -> PyClassInitializer<T> {
self.map(PyClassInitializer::from_value) self.into()
} }
} }
@ -142,31 +145,8 @@ where
B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>, B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>,
B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>, B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{ {
fn into_initializer(self) -> PyResult<PyClassInitializer<S>> { fn into_initializer(self) -> PyClassInitializer<S> {
let (sub, base) = self; let (sub, base_init) = self;
Ok(PyClassInitializer::from_value(base).add_subclass(sub)) base_init.into_initializer().add_subclass(sub)
}
}
impl<S, B> IntoInitializer<S> for PyResult<(S, B)>
where
S: PyClass + PyTypeInfo<BaseType = B>,
B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>,
B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
fn into_initializer(self) -> PyResult<PyClassInitializer<S>> {
self.map(|(sub, base)| PyClassInitializer::from_value(base).add_subclass(sub))
}
}
impl<T: PyClass> IntoInitializer<T> for PyClassInitializer<T> {
fn into_initializer(self) -> PyResult<PyClassInitializer<T>> {
Ok(self)
}
}
impl<T: PyClass> IntoInitializer<T> for PyResult<PyClassInitializer<T>> {
fn into_initializer(self) -> PyResult<PyClassInitializer<T>> {
self
} }
} }