Skip to content

Commit

Permalink
Replace IntoInitializer<T> with Into<PyClassInitializer<T>>
Browse files Browse the repository at this point in the history
  • Loading branch information
kngwyu committed Jan 7, 2020
1 parent b602b4b commit 55fe489
Show file tree
Hide file tree
Showing 8 changed files with 23 additions and 48 deletions.
13 changes: 4 additions & 9 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,8 @@ created from Rust, but not from Python.
For arguments, see the `Method arguments` section below.

### Return type
Generally, `#[new]` method have to return `T: IntoInitializer<Self>` or
`PyResult<T> where T: IntoInitializer<Self>`.

If your `T: PyClass` inherits just `PyAny`, you can return just `Self` or `PyResult<Self>`.
But if it inherits other `U: PyClass`, you have to return tuple `(T, U)` or
`PyClassInitializer<T>`.
Generally, `#[new]` method have to return `T: Into<PyClassInitializer<Self>>` or
`PyResult<T> where T: Into<PyClassInitializer<Self>>`.

For constructors that may fail, you should wrap the return type in a PyResult as well.
Consult the table below to determine which type your constructor should return:
Expand All @@ -209,7 +205,7 @@ Consult the table below to determine which type your constructor should return:
By default, `PyAny` is used as the base class. To override this default,
use the `extends` parameter for `pyclass` with the full path to the base class.

For convinience, `(T, U)` implements `IntoInitializer<T>` where `U` is the
For convinience, `(T, U)` implements `Into<PyClassInitializer<T>>` where `U` is the
baseclass of `T`.
But for more deeply nested inheritance, you have to return `PyClassInitializer<T>`
explicitly.
Expand Down Expand Up @@ -261,8 +257,7 @@ struct SubSubClass {
impl SubSubClass {
#[new]
fn new() -> PyClassInitializer<Self> {
SubClass::new()
.into_initializer()
PyClassInitializer::from(SubClass::new())
.add_subclass(SubSubClass{val3: 20})
}

Expand Down
2 changes: 1 addition & 1 deletion pyo3-derive-backend/src/pymethod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream {

#body

match _result.and_then(|init| init.into_initializer().create_shell(_py)) {
match _result.and_then(|init| pyo3::PyClassInitializer::from(init).create_shell(_py)) {
Ok(slf) => slf as _,
Err(e) => e.restore_and_null(_py),
}
Expand Down
8 changes: 4 additions & 4 deletions src/derive_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::exceptions::TypeError;
use crate::init_once;
use crate::instance::PyNativeType;
use crate::pyclass::PyClass;
use crate::pyclass_init::IntoInitializer;
use crate::pyclass_init::PyClassInitializer;
use crate::types::{PyAny, PyDict, PyModule, PyTuple};
use crate::{ffi, GILPool, IntoPy, PyObject, Python};
use std::ptr;
Expand Down Expand Up @@ -184,17 +184,17 @@ impl<T: IntoPy<PyObject>> IntoPyResult<T> for PyResult<T> {

/// 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>> {
pub trait IntoPyNewResult<T: PyClass, I: Into<PyClassInitializer<T>>> {
fn into_pynew_result(self) -> PyResult<I>;
}

impl<T: PyClass, I: IntoInitializer<T>> IntoPyNewResult<T, I> for I {
impl<T: PyClass, I: Into<PyClassInitializer<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> {
impl<T: PyClass, I: Into<PyClassInitializer<T>>> IntoPyNewResult<T, I> for PyResult<I> {
fn into_pynew_result(self) -> PyResult<I> {
self
}
Expand Down
6 changes: 3 additions & 3 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::gil;
use crate::object::PyObject;
use crate::objectprotocol::ObjectProtocol;
use crate::pyclass::{PyClass, PyClassShell};
use crate::pyclass_init::IntoInitializer;
use crate::pyclass_init::PyClassInitializer;
use crate::type_object::{PyObjectLayout, PyTypeInfo};
use crate::types::PyAny;
use crate::{ffi, IntoPy};
Expand Down Expand Up @@ -36,13 +36,13 @@ unsafe impl<T> Sync for Py<T> {}

impl<T> Py<T> {
/// Create new instance of T and move it under python management
pub fn new(py: Python, value: impl IntoInitializer<T>) -> PyResult<Py<T>>
pub fn new(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<Py<T>>
where
T: PyClass,
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
let initializer = value.into_initializer();
let initializer = value.into();
let obj = unsafe { initializer.create_shell(py)? };
let ob = unsafe { Py::from_owned_ptr(obj as _) };
Ok(ob)
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType};
pub use crate::object::PyObject;
pub use crate::objectprotocol::ObjectProtocol;
pub use crate::pyclass::{PyClass, PyClassShell};
pub use crate::pyclass_init::{IntoInitializer, PyClassInitializer};
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::python::{prepare_freethreaded_python, Python};
pub use crate::type_object::{type_flags, PyTypeInfo};

Expand Down
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub use crate::objectprotocol::ObjectProtocol;
pub use crate::python::Python;
pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject};
// This is only part of the prelude because we need it for the pymodule function
pub use crate::pyclass_init::{IntoInitializer, PyClassInitializer};
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::types::PyModule;
pub use pyo3cls::pymodule;
pub use pyo3cls::{pyclass, pyfunction, pymethods, pyproto};
10 changes: 5 additions & 5 deletions src/pyclass.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! An experiment module which has all codes related only to #[pyclass]
use crate::class::methods::{PyMethodDefType, PyMethodsProtocol};
use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
use crate::pyclass_init::IntoInitializer;
use crate::pyclass_init::PyClassInitializer;
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
use crate::type_object::{type_flags, PyObjectLayout, PyObjectSizedLayout, PyTypeObject};
use crate::types::PyAny;
Expand Down Expand Up @@ -130,26 +130,26 @@ pub struct PyClassShell<T: PyClass> {

impl<T: PyClass> PyClassShell<T> {
/// Make new `PyClassShell` on the Python heap and returns the reference of it.
pub fn new_ref(py: Python, value: impl IntoInitializer<T>) -> PyResult<&Self>
pub fn new_ref(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&Self>
where
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
unsafe {
let initializer = value.into_initializer();
let initializer = value.into();
let self_ = initializer.create_shell(py)?;
FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
}
}

/// Make new `PyClassShell` on the Python heap and returns the mutable reference of it.
pub fn new_mut(py: Python, value: impl IntoInitializer<T>) -> PyResult<&mut Self>
pub fn new_mut(py: Python, value: impl Into<PyClassInitializer<T>>) -> PyResult<&mut Self>
where
<T::BaseType as PyTypeInfo>::ConcreteLayout:
crate::type_object::PyObjectSizedLayout<T::BaseType>,
{
unsafe {
let initializer = value.into_initializer();
let initializer = value.into();
let self_ = initializer.create_shell(py)?;
FromPyPointer::from_owned_ptr_or_err(py, self_ as _)
}
Expand Down
28 changes: 4 additions & 24 deletions src/pyclass_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,34 +109,14 @@ where
}
}

/// 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
T: PyClass,
U: Into<PyClassInitializer<T>>,
{
fn into_initializer(self) -> PyClassInitializer<T> {
self.into()
}
}

impl<S, B> IntoInitializer<S> for (S, B)
impl<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = B>,
B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>,
B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
fn into_initializer(self) -> PyClassInitializer<S> {
let (sub, base_init) = self;
base_init.into_initializer().add_subclass(sub)
fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {
let (sub, base) = sub_and_base;
PyClassInitializer::from(base).add_subclass(sub)
}
}

0 comments on commit 55fe489

Please sign in to comment.