diff --git a/guide/src/class.md b/guide/src/class.md index b5bf071d0b2..b346aaebaff 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -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::(); @@ -249,7 +248,6 @@ or by `self_.into_super()` as `PyRef`. ```rust # use pyo3::prelude::*; -use pyo3::PyCell; #[pyclass] struct BaseClass { @@ -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, +} + +#[pymethods] +impl DictWithCounter { + #[new] + fn new() -> Self { + Self::default() + } + fn set(mut self_: PyRefMut, 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 { @@ -337,7 +368,6 @@ impl SubClass { } ``` - ## Object properties Property descriptor methods can be defined in a `#[pymethods]` `impl` block only and have to be @@ -345,14 +375,13 @@ 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 { Ok(self.num) @@ -375,10 +404,8 @@ can be used since Rust 2018). # struct MyClass { # num: i32, # } -# #[pymethods] impl MyClass { - #[getter] fn get_num(&self) -> PyResult { Ok(self.num) @@ -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 { Ok(self.num) @@ -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 { Ok(10) } @@ -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 { @@ -501,7 +523,6 @@ with the `#[classmethod]` attribute. # num: i32, # debug: bool, # } - #[pymethods] impl MyClass { #[classmethod] @@ -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] @@ -555,7 +575,6 @@ use pyo3::types::PyTuple; # num: i32, # debug: bool, # } - #[pymethods] impl MyClass { #[call] @@ -598,7 +617,6 @@ use pyo3::types::{PyDict, PyTuple}; # num: i32, # debug: bool, # } -# #[pymethods] impl MyClass { #[new] @@ -785,7 +803,7 @@ Example: ```rust use pyo3::prelude::*; -use pyo3::{PyIterProtocol, PyCell}; +use pyo3::PyIterProtocol; #[pyclass] struct MyIterator { diff --git a/src/pycell.rs b/src/pycell.rs index 2a5500c8543..e94a22c171e 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -25,9 +25,6 @@ where T::Layout: PySizedLayout, { 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. @@ -67,9 +64,6 @@ unsafe impl PyLayout for PyCellInner { 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)); } @@ -84,6 +78,9 @@ impl PySizedLayout for PyCellInner {} unsafe impl PyBorrowFlagLayout for PyCellInner {} impl PyCellInner { + 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; unsafe { (*base).borrow_flag.get() } @@ -353,9 +350,6 @@ unsafe impl PyLayout for PyCell { 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)); } @@ -446,7 +440,13 @@ pub struct PyRef<'p, T: PyClass> { inner: &'p PyCellInner, } -impl<'p, T: PyClass> AsRef for PyRef<'p, T> { +unsafe impl<'p, T: PyClass> crate::PyNativeType for PyRef<'p, T> {} + +impl<'p, T, U> AsRef for PyRef<'p, T> +where + T: PyClass + PyTypeInfo>, + U: PyClass, +{ fn as_ref(&self) -> &T::BaseType { unsafe { &*self.inner.ob_base.get_ptr() } } @@ -551,13 +551,23 @@ pub struct PyRefMut<'p, T: PyClass> { inner: &'p PyCellInner, } -impl<'p, T: PyClass> AsRef for PyRefMut<'p, T> { +unsafe impl<'p, T: PyClass> crate::PyNativeType for PyRefMut<'p, T> {} + +impl<'p, T, U> AsRef for PyRefMut<'p, T> +where + T: PyClass + PyTypeInfo>, + U: PyClass, +{ fn as_ref(&self) -> &T::BaseType { unsafe { &*self.inner.ob_base.get_ptr() } } } -impl<'p, T: PyClass> AsMut for PyRefMut<'p, T> { +impl<'p, T, U> AsMut for PyRefMut<'p, T> +where + T: PyClass + PyTypeInfo>, + U: PyClass, +{ fn as_mut(&mut self) -> &mut T::BaseType { unsafe { &mut *self.inner.ob_base.get_ptr() } } diff --git a/src/type_object.rs b/src/type_object.rs index 4530b1b0b9a..14509ffcb2a 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -21,7 +21,6 @@ pub unsafe trait PyLayout { } 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` represents `T` is not a instance of diff --git a/src/types/any.rs b/src/types/any.rs index 76280237a96..66e0a668e15 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -23,11 +23,7 @@ use crate::{ffi, PyObject}; /// ``` #[repr(transparent)] pub struct PyAny(PyObject, Unsendable); -unsafe impl crate::type_object::PyLayout for ffi::PyObject { - unsafe fn get_ptr(&self) -> *mut PyAny { - (&self) as *const &Self as *const _ as *mut _ - } -} +unsafe impl crate::type_object::PyLayout for ffi::PyObject {} impl crate::type_object::PySizedLayout for ffi::PyObject {} pyobject_native_type_named!(PyAny); pyobject_native_type_convert!( diff --git a/src/types/mod.rs b/src/types/mod.rs index badd4900a66..79a1bcf7bfa 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -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; @@ -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)*);