Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 42 additions & 24 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ impl pyo3::class::methods::PyMethodsInventoryDispatch for MyClass {
}

pyo3::inventory::collect!(MyClassGeneratedPyo3Inventory);

# let gil = Python::acquire_gil();
# let py = gil.python();
# let cls = py.get_type::<MyClass>();
Expand Down Expand Up @@ -249,7 +248,6 @@ or by `self_.into_super()` as `PyRef<Self::BaseClass>`.

```rust
# use pyo3::prelude::*;
use pyo3::PyCell;

#[pyclass]
struct BaseClass {
Expand Down Expand Up @@ -305,18 +303,51 @@ impl SubSubClass {
SubClass::method2(super_).map(|x| x * v)
}
}


# let gil = Python::acquire_gil();
# let py = gil.python();
# let subsub = pyo3::PyCell::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000")
```
You can also inherit native types such as `PyDict`, if it implements
[`PySizedLayout`](https://pyo3.rs/master/doc/pyo3/type_object/trait.PySizedLayout.html).

However, because of some technical problems, now we don't provide safe upcast methods for types
that inherit native types. Even in such cases, you can get a base class by raw pointer conversion.

```rust
# use pyo3::prelude::*;
use pyo3::types::{PyAny, PyDict};
use pyo3::{AsPyPointer, PyNativeType};
use std::collections::HashMap;

#[pyclass(extends=PyDict)]
#[derive(Default)]
struct DictWithCounter {
counter: HashMap<String, usize>,
}

#[pymethods]
impl DictWithCounter {
#[new]
fn new() -> Self {
Self::default()
}
fn set(mut self_: PyRefMut<Self>, key: String, value: &PyAny) -> PyResult<()> {
self_.counter.entry(key.clone()).or_insert(0);
let py = self_.py();
let dict: &PyDict = unsafe { py.from_borrowed_ptr_or_err(self_.as_ptr())? };
dict.set_item(key, value)
}
}
# let gil = Python::acquire_gil();
# let py = gil.python();
# let cnt = pyo3::PyCell::new(py, DictWithCounter::new()).unwrap();
# pyo3::py_run!(py, cnt, "cnt.set('abc', 10); assert cnt['abc'] == 10")
```

If `SubClass` does not provide a baseclass initialization, the compilation fails.
```compile_fail
# use pyo3::prelude::*;
use pyo3::PyCell;

#[pyclass]
struct BaseClass {
Expand All @@ -337,22 +368,20 @@ impl SubClass {
}
```


## Object properties

Property descriptor methods can be defined in a `#[pymethods]` `impl` block only and have to be
annotated with `#[getter]` and `#[setter]` attributes. For example:

```rust
# use pyo3::prelude::*;
# #[pyclass]
# struct MyClass {
# num: i32,
# }
#
#[pyclass]
struct MyClass {
num: i32,
}

#[pymethods]
impl MyClass {

#[getter]
fn num(&self) -> PyResult<i32> {
Ok(self.num)
Expand All @@ -375,10 +404,8 @@ can be used since Rust 2018).
# struct MyClass {
# num: i32,
# }
#
#[pymethods]
impl MyClass {

#[getter]
fn get_num(&self) -> PyResult<i32> {
Ok(self.num)
Expand All @@ -403,10 +430,8 @@ If this parameter is specified, it is used as the property name, i.e.
# struct MyClass {
# num: i32,
# }
#
#[pymethods]
impl MyClass {

#[getter(number)]
fn num(&self) -> PyResult<i32> {
Ok(self.num)
Expand Down Expand Up @@ -448,10 +473,8 @@ block with some variations, like descriptors, class method static methods, etc.
# struct MyClass {
# num: i32,
# }
#
#[pymethods]
impl MyClass {

fn method1(&self) -> PyResult<i32> {
Ok(10)
}
Expand All @@ -477,7 +500,6 @@ gets injected by the method wrapper, e.g.
# num: i32,
# debug: bool,
# }

#[pymethods]
impl MyClass {
fn method2(&self, py: Python) -> PyResult<i32> {
Expand All @@ -501,7 +523,6 @@ with the `#[classmethod]` attribute.
# num: i32,
# debug: bool,
# }

#[pymethods]
impl MyClass {
#[classmethod]
Expand Down Expand Up @@ -532,7 +553,6 @@ To create a static method for a custom class, the method needs to be annotated w
# num: i32,
# debug: bool,
# }

#[pymethods]
impl MyClass {
#[staticmethod]
Expand All @@ -555,7 +575,6 @@ use pyo3::types::PyTuple;
# num: i32,
# debug: bool,
# }

#[pymethods]
impl MyClass {
#[call]
Expand Down Expand Up @@ -598,7 +617,6 @@ use pyo3::types::{PyDict, PyTuple};
# num: i32,
# debug: bool,
# }
#
#[pymethods]
impl MyClass {
#[new]
Expand Down Expand Up @@ -785,7 +803,7 @@ Example:

```rust
use pyo3::prelude::*;
use pyo3::{PyIterProtocol, PyCell};
use pyo3::PyIterProtocol;

#[pyclass]
struct MyIterator {
Expand Down
34 changes: 22 additions & 12 deletions src/pycell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ where
T::Layout: PySizedLayout<T>,
{
const IS_NATIVE_TYPE: bool = true;
unsafe fn get_ptr(&self) -> *mut T {
(&self) as *const &Self as *const _ as *mut _
}
}

// Thes impls ensures `PyCellBase` can be a base type.
Expand Down Expand Up @@ -67,9 +64,6 @@ unsafe impl<T: PyClass> PyLayout<T> for PyCellInner<T> {
fn get_super(&mut self) -> Option<&mut T::BaseLayout> {
Some(&mut self.ob_base)
}
unsafe fn get_ptr(&self) -> *mut T {
self.value.get()
}
unsafe fn py_init(&mut self, value: T) {
self.value = ManuallyDrop::new(UnsafeCell::new(value));
}
Expand All @@ -84,6 +78,9 @@ impl<T: PyClass> PySizedLayout<T> for PyCellInner<T> {}
unsafe impl<T: PyClass> PyBorrowFlagLayout<T> for PyCellInner<T> {}

impl<T: PyClass> PyCellInner<T> {
unsafe fn get_ptr(&self) -> *mut T {
self.value.get()
}
fn get_borrow_flag(&self) -> BorrowFlag {
let base = (&self.ob_base) as *const _ as *const PyCellBase<T::BaseNativeType>;
unsafe { (*base).borrow_flag.get() }
Expand Down Expand Up @@ -353,9 +350,6 @@ unsafe impl<T: PyClass> PyLayout<T> for PyCell<T> {
fn get_super(&mut self) -> Option<&mut T::BaseLayout> {
Some(&mut self.inner.ob_base)
}
unsafe fn get_ptr(&self) -> *mut T {
self.inner.get_ptr()
}
unsafe fn py_init(&mut self, value: T) {
self.inner.value = ManuallyDrop::new(UnsafeCell::new(value));
}
Expand Down Expand Up @@ -446,7 +440,13 @@ pub struct PyRef<'p, T: PyClass> {
inner: &'p PyCellInner<T>,
}

impl<'p, T: PyClass> AsRef<T::BaseType> for PyRef<'p, T> {
unsafe impl<'p, T: PyClass> crate::PyNativeType for PyRef<'p, T> {}

impl<'p, T, U> AsRef<U> for PyRef<'p, T>
where
T: PyClass + PyTypeInfo<BaseType = U, BaseLayout = PyCellInner<U>>,
U: PyClass,
{
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
}
Expand Down Expand Up @@ -551,13 +551,23 @@ pub struct PyRefMut<'p, T: PyClass> {
inner: &'p PyCellInner<T>,
}

impl<'p, T: PyClass> AsRef<T::BaseType> for PyRefMut<'p, T> {
unsafe impl<'p, T: PyClass> crate::PyNativeType for PyRefMut<'p, T> {}

impl<'p, T, U> AsRef<U> for PyRefMut<'p, T>
where
T: PyClass + PyTypeInfo<BaseType = U, BaseLayout = PyCellInner<U>>,
U: PyClass,
{
fn as_ref(&self) -> &T::BaseType {
unsafe { &*self.inner.ob_base.get_ptr() }
}
}

impl<'p, T: PyClass> AsMut<T::BaseType> for PyRefMut<'p, T> {
impl<'p, T, U> AsMut<U> for PyRefMut<'p, T>
where
T: PyClass + PyTypeInfo<BaseType = U, BaseLayout = PyCellInner<U>>,
U: PyClass,
{
fn as_mut(&mut self) -> &mut T::BaseType {
unsafe { &mut *self.inner.ob_base.get_ptr() }
}
Expand Down
1 change: 0 additions & 1 deletion src/type_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub unsafe trait PyLayout<T: PyTypeInfo> {
}
unsafe fn py_init(&mut self, _value: T) {}
unsafe fn py_drop(&mut self, _py: Python) {}
unsafe fn get_ptr(&self) -> *mut T;
}

/// `T: PySizedLayout<U>` represents `T` is not a instance of
Expand Down
6 changes: 1 addition & 5 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ use crate::{ffi, PyObject};
/// ```
#[repr(transparent)]
pub struct PyAny(PyObject, Unsendable);
unsafe impl crate::type_object::PyLayout<PyAny> for ffi::PyObject {
unsafe fn get_ptr(&self) -> *mut PyAny {
(&self) as *const &Self as *const _ as *mut _
}
}
unsafe impl crate::type_object::PyLayout<PyAny> for ffi::PyObject {}
impl crate::type_object::PySizedLayout<PyAny> for ffi::PyObject {}
pyobject_native_type_named!(PyAny);
pyobject_native_type_convert!(
Expand Down
14 changes: 2 additions & 12 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,10 @@ macro_rules! pyobject_native_type_named (
};
);

macro_rules! impl_layout {
($name: ty, $layout: path) => {
unsafe impl $crate::type_object::PyLayout<$name> for $layout {
unsafe fn get_ptr(&self) -> *mut $name {
(&self) as *const &Self as *const _ as *mut _
}
}
};
}

#[macro_export]
macro_rules! pyobject_native_type {
($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
impl_layout!($name, $layout);
unsafe impl $crate::type_object::PyLayout<$name> for $layout {}
impl $crate::type_object::PySizedLayout<$name> for $layout {}
impl $crate::derive_utils::PyBaseTypeUtils for $name {
type Dict = $crate::pyclass_slots::PyClassDummySlot;
Expand Down Expand Up @@ -97,7 +87,7 @@ macro_rules! pyobject_native_type {
#[macro_export]
macro_rules! pyobject_native_var_type {
($name: ty, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => {
impl_layout!($name, $crate::ffi::PyObject);
unsafe impl $crate::type_object::PyLayout<$name> for $crate::ffi::PyObject {}
pyobject_native_type_named!($name $(,$type_param)*);
pyobject_native_type_convert!($name, $crate::ffi::PyObject,
$typeobject, $module, $checkfunction $(,$type_param)*);
Expand Down