From a2408cacbb031cd382b9912d06e21bddf41844b1 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Mon, 3 Feb 2020 22:25:16 +0900 Subject: [PATCH 01/27] Rename PyClassShell with PyCell --- guide/src/class.md | 44 +++---- guide/src/python_from_rust.md | 4 +- pyo3-derive-backend/src/pyclass.rs | 2 +- pyo3-derive-backend/src/pymethod.rs | 2 +- src/class/iter.rs | 7 +- src/class/macros.rs | 4 +- src/instance.rs | 24 ++-- src/lib.rs | 8 +- src/pycell.rs | 177 ++++++++++++++++++++++++++ src/pyclass.rs | 186 +--------------------------- src/pyclass_init.rs | 15 ++- src/type_object.rs | 2 +- tests/test_dunder.rs | 22 ++-- tests/test_gc.rs | 8 +- tests/test_inheritance.rs | 4 +- tests/test_methods.rs | 8 +- tests/test_pyself.rs | 23 ++-- tests/test_text_signature.rs | 4 +- tests/test_various.rs | 14 +-- 19 files changed, 277 insertions(+), 281 deletions(-) create mode 100644 src/pycell.rs diff --git a/guide/src/class.md b/guide/src/class.md index 9215c6a9eb1..97a841792f1 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -34,7 +34,7 @@ impl pyo3::pyclass::PyClassAlloc for MyClass {} unsafe impl pyo3::PyTypeInfo for MyClass { type Type = MyClass; type BaseType = pyo3::types::PyAny; - type ConcreteLayout = pyo3::PyClassShell; + type ConcreteLayout = pyo3::PyCell; type Initializer = pyo3::PyClassInitializer; const NAME: &'static str = "MyClass"; @@ -109,19 +109,19 @@ fn mymodule(_py: Python, m: &PyModule) -> PyResult<()> { You sometimes need to convert your `pyclass` into a Python object in Rust code (e.g., for testing it). For getting *GIL-bounded* (i.e., with `'py` lifetime) references of `pyclass`, -you can use `PyClassShell`. +you can use `PyCell`. Or you can use `Py` directly, for *not-GIL-bounded* references. -### `PyClassShell` -`PyClassShell` represents the actual layout of `pyclass` on the Python heap. +### `PyCell` +`PyCell` represents the actual layout of `pyclass` on the Python heap. If you want to instantiate `pyclass` in Python and get the reference, -you can use `PyClassShell::new_ref` or `PyClassShell::new_mut`. +you can use `PyCell::new_ref` or `PyCell::new_mut`. ```rust # use pyo3::prelude::*; # use pyo3::types::PyDict; -# use pyo3::PyClassShell; +# use pyo3::PyCell; #[pyclass] struct MyClass { num: i32, @@ -129,15 +129,15 @@ struct MyClass { } let gil = Python::acquire_gil(); let py = gil.python(); -let obj = PyClassShell::new_ref(py, MyClass { num: 3, debug: true }).unwrap(); +let obj = PyCell::new_ref(py, MyClass { num: 3, debug: true }).unwrap(); // You can use deref assert_eq!(obj.num, 3); let dict = PyDict::new(py); -// You can treat a `&PyClassShell` as a normal Python object +// You can treat a `&PyCell` as a normal Python object dict.set_item("obj", obj).unwrap(); -// return &mut PyClassShell -let obj = PyClassShell::new_mut(py, MyClass { num: 3, debug: true }).unwrap(); +// return &mut PyCell +let obj = PyCell::new_mut(py, MyClass { num: 3, debug: true }).unwrap(); obj.num = 5; ``` @@ -230,7 +230,7 @@ explicitly. ```rust # use pyo3::prelude::*; -use pyo3::PyClassShell; +use pyo3::PyCell; #[pyclass] struct BaseClass { @@ -261,7 +261,7 @@ impl SubClass { (SubClass{ val2: 15}, BaseClass::new()) } - fn method2(self_: &PyClassShell) -> PyResult { + fn method2(self_: &PyCell) -> PyResult { self_.get_super().method().map(|x| x * self_.val2) } } @@ -279,7 +279,7 @@ impl SubSubClass { .add_subclass(SubSubClass{val3: 20}) } - fn method3(self_: &PyClassShell) -> PyResult { + fn method3(self_: &PyCell) -> PyResult { let super_ = self_.get_super(); SubClass::method2(super_).map(|x| x * self_.val3) } @@ -288,20 +288,20 @@ impl SubSubClass { # let gil = Python::acquire_gil(); # let py = gil.python(); -# let subsub = pyo3::PyClassShell::new_ref(py, SubSubClass::new()).unwrap(); +# let subsub = pyo3::PyCell::new_ref(py, SubSubClass::new()).unwrap(); # pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000") ``` To access the super class, you can use either of these two ways: -- Use `self_: &PyClassShell` instead of `self`, and call `get_super()` +- Use `self_: &PyCell` instead of `self`, and call `get_super()` - `ObjectProtocol::get_base` -We recommend `PyClassShell` here, since it makes the context much clearer. +We recommend `PyCell` here, since it makes the context much clearer. If `SubClass` does not provide a baseclass initialization, the compilation fails. ```compile_fail # use pyo3::prelude::*; -use pyo3::PyClassShell; +use pyo3::PyCell; #[pyclass] struct BaseClass { @@ -761,8 +761,8 @@ struct GCTracked {} // Fails because it does not implement PyGCProtocol Iterators can be defined using the [`PyIterProtocol`](https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html) trait. It includes two methods `__iter__` and `__next__`: - * `fn __iter__(slf: &mut PyClassShell) -> PyResult>` - * `fn __next__(slf: &mut PyClassShell) -> PyResult>>` + * `fn __iter__(slf: &mut PyCell) -> PyResult>` + * `fn __next__(slf: &mut PyCell) -> PyResult>>` Returning `Ok(None)` from `__next__` indicates that that there are no further items. @@ -770,7 +770,7 @@ Example: ```rust use pyo3::prelude::*; -use pyo3::{PyIterProtocol, PyClassShell}; +use pyo3::{PyIterProtocol, PyCell}; #[pyclass] struct MyIterator { @@ -779,10 +779,10 @@ struct MyIterator { #[pyproto] impl PyIterProtocol for MyIterator { - fn __iter__(slf: &mut PyClassShell) -> PyResult> { + fn __iter__(slf: &mut PyCell) -> PyResult> { Ok(slf.into()) } - fn __next__(slf: &mut PyClassShell) -> PyResult> { + fn __next__(slf: &mut PyCell) -> PyResult> { Ok(slf.iter.next()) } } diff --git a/guide/src/python_from_rust.md b/guide/src/python_from_rust.md index c20a3281853..2250ca70102 100644 --- a/guide/src/python_from_rust.md +++ b/guide/src/python_from_rust.md @@ -36,7 +36,7 @@ your Python extensions quickly. ```rust use pyo3::prelude::*; -use pyo3::{PyClassShell, PyObjectProtocol, py_run}; +use pyo3::{PyCell, PyObjectProtocol, py_run}; # fn main() { #[pyclass] struct UserData { @@ -61,7 +61,7 @@ let userdata = UserData { id: 34, name: "Yu".to_string(), }; -let userdata = PyClassShell::new_ref(py, userdata).unwrap(); +let userdata = PyCell::new_ref(py, userdata).unwrap(); let userdata_as_tuple = (34, "Yu"); py_run!(py, userdata userdata_as_tuple, r#" assert repr(userdata) == "User Yu(id: 34)" diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index 19b01b2502f..bf0f1af3c6e 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -373,7 +373,7 @@ fn impl_class( unsafe impl pyo3::type_object::PyTypeInfo for #cls { type Type = #cls; type BaseType = #base; - type ConcreteLayout = pyo3::pyclass::PyClassShell; + type ConcreteLayout = pyo3::pyclass::PyCell; type Initializer = pyo3::pyclass_init::PyClassInitializer; const NAME: &'static str = #cls_name; diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 97bb1f161f0..7b737057fb6 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -175,7 +175,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { #body - match _result.and_then(|init| pyo3::PyClassInitializer::from(init).create_shell(_py)) { + match _result.and_then(|init| pyo3::PyClassInitializer::from(init).create_cell(_py)) { Ok(slf) => slf as _, Err(e) => e.restore_and_null(_py), } diff --git a/src/class/iter.rs b/src/class/iter.rs index 802bd159e40..aca9c0304dc 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -4,8 +4,7 @@ use crate::callback::{CallbackConverter, PyObjectCallbackConverter}; use crate::err::PyResult; -use crate::{ffi, pyclass::PyClassShell, IntoPy, PyClass, PyObject}; -use crate::{IntoPyPointer, Python}; +use crate::{ffi, IntoPy, IntoPyPointer, PyCell, PyClass, PyObject, Python}; use std::ptr; /// Python Iterator Interface. @@ -14,14 +13,14 @@ use std::ptr; /// `https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter` #[allow(unused_variables)] pub trait PyIterProtocol<'p>: PyClass { - fn __iter__(slf: &mut PyClassShell) -> Self::Result + fn __iter__(slf: &mut PyCell) -> Self::Result where Self: PyIterIterProtocol<'p>, { unimplemented!() } - fn __next__(slf: &mut PyClassShell) -> Self::Result + fn __next__(slf: &mut PyCell) -> Self::Result where Self: PyIterNextProtocol<'p>, { diff --git a/src/class/macros.rs b/src/class/macros.rs index 7fbf2499be9..f67fdd439a6 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -35,10 +35,10 @@ macro_rules! py_unary_pyref_func { where T: for<'p> $trait<'p>, { - use $crate::pyclass::PyClassShell; + use $crate::PyCell; let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf: &mut PyClassShell = &mut *(slf as *mut PyClassShell); + let slf: &mut PyCell = &mut *(slf as *mut PyCell); let res = $class::$f(slf).into(); $crate::callback::cb_convert($conv, py, res) } diff --git a/src/instance.rs b/src/instance.rs index 34f9e7dfbd0..15a74bed7dd 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -3,12 +3,12 @@ use crate::err::{PyErr, PyResult}; use crate::gil; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; -use crate::pyclass::{PyClass, PyClassShell}; -use crate::pyclass_init::PyClassInitializer; use crate::type_object::{PyObjectLayout, PyTypeInfo}; use crate::types::PyAny; -use crate::{ffi, IntoPy}; -use crate::{AsPyPointer, FromPyObject, IntoPyPointer, Python, ToPyObject}; +use crate::{ + ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyCell, PyClass, PyClassInitializer, + Python, ToPyObject, +}; use std::marker::PhantomData; use std::mem; use std::ptr::NonNull; @@ -43,7 +43,7 @@ impl Py { crate::type_object::PyObjectSizedLayout, { let initializer = value.into(); - let obj = unsafe { initializer.create_shell(py)? }; + let obj = unsafe { initializer.create_cell(py)? }; let ob = unsafe { Py::from_owned_ptr(obj as _) }; Ok(ob) } @@ -174,22 +174,22 @@ where } } -// `&PyClassShell` can be converted to `Py` -impl<'a, T> std::convert::From<&PyClassShell> for Py +// `&PyCell` can be converted to `Py` +impl<'a, T> std::convert::From<&PyCell> for Py where T: PyClass, { - fn from(shell: &PyClassShell) -> Self { - unsafe { Py::from_borrowed_ptr(shell.as_ptr()) } + fn from(cell: &PyCell) -> Self { + unsafe { Py::from_borrowed_ptr(cell.as_ptr()) } } } -impl<'a, T> std::convert::From<&mut PyClassShell> for Py +impl<'a, T> std::convert::From<&mut PyCell> for Py where T: PyClass, { - fn from(shell: &mut PyClassShell) -> Self { - unsafe { Py::from_borrowed_ptr(shell.as_ptr()) } + fn from(cell: &mut PyCell) -> Self { + unsafe { Py::from_borrowed_ptr(cell.as_ptr()) } } } diff --git a/src/lib.rs b/src/lib.rs index 14126a5f4de..ce19c20f2e7 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,7 +127,8 @@ pub use crate::gil::{init_once, GILGuard, GILPool}; 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::pycell::PyCell; +pub use crate::pyclass::PyClass; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::{prepare_freethreaded_python, Python}; pub use crate::type_object::{type_flags, PyTypeInfo}; @@ -170,6 +171,7 @@ pub mod marshal; mod object; mod objectprotocol; pub mod prelude; +mod pycell; pub mod pyclass; pub mod pyclass_init; pub mod pyclass_slots; @@ -221,7 +223,7 @@ macro_rules! wrap_pymodule { /// /// # Example /// ``` -/// use pyo3::{prelude::*, py_run, PyClassShell}; +/// use pyo3::{prelude::*, py_run, PyCell}; /// #[pyclass] /// #[derive(Debug)] /// struct Time { @@ -244,7 +246,7 @@ macro_rules! wrap_pymodule { /// } /// let gil = Python::acquire_gil(); /// let py = gil.python(); -/// let time = PyClassShell::new_ref(py, Time {hour: 8, minute: 43, second: 16}).unwrap(); +/// let time = PyCell::new_ref(py, Time {hour: 8, minute: 43, second: 16}).unwrap(); /// let time_as_tuple = (8, 43, 16); /// py_run!(py, time time_as_tuple, r#" /// assert time.hour == 8 diff --git a/src/pycell.rs b/src/pycell.rs new file mode 100644 index 00000000000..710f4300bcc --- /dev/null +++ b/src/pycell.rs @@ -0,0 +1,177 @@ +//! Traits and structs for `#[pyclass]`. +use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; +use crate::pyclass::PyClass; +use crate::pyclass_init::PyClassInitializer; +use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; +use crate::type_object::{PyObjectLayout, PyObjectSizedLayout}; +use crate::types::PyAny; +use crate::{ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python}; +use std::mem::ManuallyDrop; +use std::ptr::NonNull; + +/// `PyCell` represents the concrete layout of `T: PyClass` when it is converted +/// to a Python class. +/// +/// You can use it to test your `#[pyclass]` correctly works. +/// +/// ``` +/// # use pyo3::prelude::*; +/// # use pyo3::{py_run, PyCell}; +/// #[pyclass] +/// struct Book { +/// #[pyo3(get)] +/// name: &'static str, +/// author: &'static str, +/// } +/// let gil = Python::acquire_gil(); +/// let py = gil.python(); +/// let book = Book { +/// name: "The Man in the High Castle", +/// author: "Philip Kindred Dick", +/// }; +/// let book_cell = PyCell::new_ref(py, book).unwrap(); +/// py_run!(py, book_cell, "assert book_cell.name[-6:] == 'Castle'"); +/// ``` +#[repr(C)] +pub struct PyCell { + ob_base: ::ConcreteLayout, + pyclass: ManuallyDrop, + dict: T::Dict, + weakref: T::WeakRef, +} + +impl PyCell { + /// Make new `PyCell` on the Python heap and returns the reference of it. + pub fn new_ref(py: Python, value: impl Into>) -> PyResult<&Self> + where + ::ConcreteLayout: + crate::type_object::PyObjectSizedLayout, + { + unsafe { + let initializer = value.into(); + let self_ = initializer.create_cell(py)?; + FromPyPointer::from_owned_ptr_or_err(py, self_ as _) + } + } + + /// Make new `PyCell` on the Python heap and returns the mutable reference of it. + pub fn new_mut(py: Python, value: impl Into>) -> PyResult<&mut Self> + where + ::ConcreteLayout: + crate::type_object::PyObjectSizedLayout, + { + unsafe { + let initializer = value.into(); + let self_ = initializer.create_cell(py)?; + FromPyPointer::from_owned_ptr_or_err(py, self_ as _) + } + } + + /// Get the reference of base object. + pub fn get_super(&self) -> &::ConcreteLayout { + &self.ob_base + } + + /// Get the mutable reference of base object. + pub fn get_super_mut(&mut self) -> &mut ::ConcreteLayout { + &mut self.ob_base + } + + pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> + where + ::ConcreteLayout: + crate::type_object::PyObjectSizedLayout, + { + let base = T::alloc(py); + if base.is_null() { + return Err(PyErr::fetch(py)); + } + let self_ = base as *mut Self; + (*self_).dict = T::Dict::new(); + (*self_).weakref = T::WeakRef::new(); + Ok(self_) + } +} + +impl PyObjectLayout for PyCell { + const IS_NATIVE_TYPE: bool = false; + fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { + Some(&mut self.ob_base) + } + unsafe fn internal_ref_cast(obj: &PyAny) -> &T { + let cell = obj.as_ptr() as *const Self; + &(*cell).pyclass + } + unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T { + let cell = obj.as_ptr() as *const _ as *mut Self; + &mut (*cell).pyclass + } + unsafe fn py_drop(&mut self, py: Python) { + ManuallyDrop::drop(&mut self.pyclass); + self.dict.clear_dict(py); + self.weakref.clear_weakrefs(self.as_ptr(), py); + self.ob_base.py_drop(py); + } + unsafe fn py_init(&mut self, value: T) { + self.pyclass = ManuallyDrop::new(value); + } +} + +impl PyObjectSizedLayout for PyCell {} + +impl AsPyPointer for PyCell { + fn as_ptr(&self) -> *mut ffi::PyObject { + (self as *const _) as *mut _ + } +} + +impl std::ops::Deref for PyCell { + type Target = T; + fn deref(&self) -> &T { + self.pyclass.deref() + } +} + +impl std::ops::DerefMut for PyCell { + fn deref_mut(&mut self) -> &mut T { + self.pyclass.deref_mut() + } +} + +impl ToPyObject for &PyCell { + fn to_object(&self, py: Python<'_>) -> PyObject { + unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } + } +} + +impl ToPyObject for &mut PyCell { + fn to_object(&self, py: Python<'_>) -> PyObject { + unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } + } +} + +unsafe impl<'p, T> FromPyPointer<'p> for &'p PyCell +where + T: PyClass, +{ + unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { + NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell)) + } + unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { + NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell)) + } +} + +unsafe impl<'p, T> FromPyPointer<'p> for &'p mut PyCell +where + T: PyClass, +{ + unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { + NonNull::new(ptr) + .map(|p| &mut *(gil::register_owned(py, p).as_ptr() as *const _ as *mut PyCell)) + } + unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { + NonNull::new(ptr) + .map(|p| &mut *(gil::register_borrowed(py, p).as_ptr() as *const _ as *mut PyCell)) + } +} diff --git a/src/pyclass.rs b/src/pyclass.rs index 12fb801bd36..9ca0184504f 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -1,15 +1,11 @@ -//! Traits and structs for `#[pyclass]`. +//! `PyClass` trait use crate::class::methods::{PyMethodDefType, PyMethodsProtocol}; -use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; -use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; -use crate::type_object::{type_flags, PyObjectLayout, PyObjectSizedLayout}; -use crate::types::PyAny; -use crate::{class, ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python}; +use crate::type_object::{type_flags, PyObjectLayout}; +use crate::{class, ffi, gil, PyCell, PyErr, PyResult, PyTypeInfo, Python}; use std::ffi::CString; -use std::mem::ManuallyDrop; use std::os::raw::c_void; -use std::ptr::{self, NonNull}; +use std::ptr; #[inline] pub(crate) unsafe fn default_alloc() -> *mut ffi::PyObject { @@ -71,187 +67,17 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) { } /// If `PyClass` is implemented for `T`, then we can use `T` in the Python world, -/// via `PyClassShell`. +/// via `PyCell`. /// /// The `#[pyclass]` attribute automatically implements this trait for your Rust struct, /// so you don't have to use this trait directly. pub trait PyClass: - PyTypeInfo> + Sized + PyClassAlloc + PyMethodsProtocol + PyTypeInfo> + Sized + PyClassAlloc + PyMethodsProtocol { type Dict: PyClassDict; type WeakRef: PyClassWeakRef; } -/// `PyClassShell` represents the concrete layout of `T: PyClass` when it is converted -/// to a Python class. -/// -/// You can use it to test your `#[pyclass]` correctly works. -/// -/// ``` -/// # use pyo3::prelude::*; -/// # use pyo3::{py_run, PyClassShell}; -/// #[pyclass] -/// struct Book { -/// #[pyo3(get)] -/// name: &'static str, -/// author: &'static str, -/// } -/// let gil = Python::acquire_gil(); -/// let py = gil.python(); -/// let book = Book { -/// name: "The Man in the High Castle", -/// author: "Philip Kindred Dick", -/// }; -/// let book_shell = PyClassShell::new_ref(py, book).unwrap(); -/// py_run!(py, book_shell, "assert book_shell.name[-6:] == 'Castle'"); -/// ``` -#[repr(C)] -pub struct PyClassShell { - ob_base: ::ConcreteLayout, - pyclass: ManuallyDrop, - dict: T::Dict, - weakref: T::WeakRef, -} - -impl PyClassShell { - /// Make new `PyClassShell` on the Python heap and returns the reference of it. - pub fn new_ref(py: Python, value: impl Into>) -> PyResult<&Self> - where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, - { - unsafe { - 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 Into>) -> PyResult<&mut Self> - where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, - { - unsafe { - let initializer = value.into(); - let self_ = initializer.create_shell(py)?; - FromPyPointer::from_owned_ptr_or_err(py, self_ as _) - } - } - - /// Get the reference of base object. - pub fn get_super(&self) -> &::ConcreteLayout { - &self.ob_base - } - - /// Get the mutable reference of base object. - pub fn get_super_mut(&mut self) -> &mut ::ConcreteLayout { - &mut self.ob_base - } - - pub(crate) unsafe fn new(py: Python) -> PyResult<*mut Self> - where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, - { - let base = T::alloc(py); - if base.is_null() { - return Err(PyErr::fetch(py)); - } - let self_ = base as *mut Self; - (*self_).dict = T::Dict::new(); - (*self_).weakref = T::WeakRef::new(); - Ok(self_) - } -} - -impl PyObjectLayout for PyClassShell { - const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { - Some(&mut self.ob_base) - } - unsafe fn internal_ref_cast(obj: &PyAny) -> &T { - let shell = obj.as_ptr() as *const Self; - &(*shell).pyclass - } - unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T { - let shell = obj.as_ptr() as *const _ as *mut Self; - &mut (*shell).pyclass - } - unsafe fn py_drop(&mut self, py: Python) { - ManuallyDrop::drop(&mut self.pyclass); - self.dict.clear_dict(py); - self.weakref.clear_weakrefs(self.as_ptr(), py); - self.ob_base.py_drop(py); - } - unsafe fn py_init(&mut self, value: T) { - self.pyclass = ManuallyDrop::new(value); - } -} - -impl PyObjectSizedLayout for PyClassShell {} - -impl AsPyPointer for PyClassShell { - fn as_ptr(&self) -> *mut ffi::PyObject { - (self as *const _) as *mut _ - } -} - -impl std::ops::Deref for PyClassShell { - type Target = T; - fn deref(&self) -> &T { - self.pyclass.deref() - } -} - -impl std::ops::DerefMut for PyClassShell { - fn deref_mut(&mut self) -> &mut T { - self.pyclass.deref_mut() - } -} - -impl ToPyObject for &PyClassShell { - fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } - } -} - -impl ToPyObject for &mut PyClassShell { - fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } - } -} - -unsafe impl<'p, T> FromPyPointer<'p> for &'p PyClassShell -where - T: PyClass, -{ - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyClassShell)) - } - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr) - .map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyClassShell)) - } -} - -unsafe impl<'p, T> FromPyPointer<'p> for &'p mut PyClassShell -where - T: PyClass, -{ - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr).map(|p| { - &mut *(gil::register_owned(py, p).as_ptr() as *const _ as *mut PyClassShell) - }) - } - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr).map(|p| { - &mut *(gil::register_borrowed(py, p).as_ptr() as *const _ as *mut PyClassShell) - }) - } -} - #[cfg(not(Py_LIMITED_API))] pub(crate) fn initialize_type_object( py: Python, diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index bc2904da177..daf59de3826 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -1,7 +1,6 @@ //! Initialization utilities for `#[pyclass]`. -use crate::pyclass::{PyClass, PyClassShell}; use crate::type_object::{PyObjectLayout, PyObjectSizedLayout, PyTypeInfo}; -use crate::{PyResult, Python}; +use crate::{PyCell, PyClass, PyResult, Python}; use std::marker::PhantomData; /// Initializer for Python types. @@ -9,7 +8,7 @@ use std::marker::PhantomData; /// This trait is intended to use internally for distinguishing `#[pyclass]` and /// Python native types. pub trait PyObjectInit: Sized { - fn init_class(self, shell: &mut T::ConcreteLayout); + fn init_class(self, layout: &mut T::ConcreteLayout); private_decl! {} } @@ -17,7 +16,7 @@ pub trait PyObjectInit: Sized { pub struct PyNativeTypeInitializer(PhantomData); impl PyObjectInit for PyNativeTypeInitializer { - fn init_class(self, _shell: &mut T::ConcreteLayout) {} + fn init_class(self, _layout: &mut T::ConcreteLayout) {} private_impl! {} } @@ -115,14 +114,14 @@ impl PyClassInitializer { } #[doc(hidden)] - pub unsafe fn create_shell(self, py: Python) -> PyResult<*mut PyClassShell> + pub unsafe fn create_cell(self, py: Python) -> PyResult<*mut PyCell> where T: PyClass, ::ConcreteLayout: PyObjectSizedLayout, { - let shell = PyClassShell::new(py)?; - self.init_class(&mut *shell); - Ok(shell) + let cell = PyCell::internal_new(py)?; + self.init_class(&mut *cell); + Ok(cell) } } diff --git a/src/type_object.rs b/src/type_object.rs index 6b12542a924..cce36d02d85 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -11,7 +11,7 @@ use std::ptr::NonNull; use std::sync::atomic::{AtomicBool, Ordering}; /// `T: PyObjectLayout` represents that `T` is a concrete representaion of `U` in Python heap. -/// E.g., `PyClassShell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject` +/// E.g., `PyCell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject` /// is of `PyAny`. /// /// This trait is intended to be used internally. diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs index 6fecc4c77cf..3790c73ce95 100755 --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -6,7 +6,7 @@ use pyo3::class::{ use pyo3::exceptions::{IndexError, ValueError}; use pyo3::prelude::*; use pyo3::types::{IntoPyDict, PyAny, PyBytes, PySlice, PyType}; -use pyo3::{ffi, py_run, AsPyPointer, PyClassShell}; +use pyo3::{ffi, py_run, AsPyPointer, PyCell}; use std::convert::TryFrom; use std::{isize, iter}; @@ -53,11 +53,11 @@ struct Iterator { #[pyproto] impl<'p> PyIterProtocol for Iterator { - fn __iter__(slf: &mut PyClassShell) -> PyResult> { + fn __iter__(slf: &mut PyCell) -> PyResult> { Ok(slf.into()) } - fn __next__(slf: &mut PyClassShell) -> PyResult> { + fn __next__(slf: &mut PyCell) -> PyResult> { Ok(slf.iter.next()) } } @@ -250,7 +250,7 @@ fn setitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyClassShell::new_ref(py, SetItem { key: 0, val: 0 }).unwrap(); + let c = PyCell::new_ref(py, SetItem { key: 0, val: 0 }).unwrap(); py_run!(py, c, "c[1] = 2"); assert_eq!(c.key, 1); assert_eq!(c.val, 2); @@ -275,7 +275,7 @@ fn delitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyClassShell::new_ref(py, DelItem { key: 0 }).unwrap(); + let c = PyCell::new_ref(py, DelItem { key: 0 }).unwrap(); py_run!(py, c, "del c[1]"); assert_eq!(c.key, 1); py_expect_exception!(py, c, "c[1] = 2", NotImplementedError); @@ -304,7 +304,7 @@ fn setdelitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyClassShell::new_ref(py, SetDelItem { val: None }).unwrap(); + let c = PyCell::new_ref(py, SetDelItem { val: None }).unwrap(); py_run!(py, c, "c[1] = 2"); assert_eq!(c.val, Some(2)); py_run!(py, c, "del c[1]"); @@ -383,7 +383,7 @@ fn context_manager() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyClassShell::new_mut(py, ContextManager { exit_called: false }).unwrap(); + let c = PyCell::new_mut(py, ContextManager { exit_called: false }).unwrap(); py_run!(py, c, "with c as x: assert x == 42"); assert!(c.exit_called); @@ -455,7 +455,7 @@ struct DunderDictSupport {} fn dunder_dict_support() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyClassShell::new_ref(py, DunderDictSupport {}).unwrap(); + let inst = PyCell::new_ref(py, DunderDictSupport {}).unwrap(); py_run!( py, inst, @@ -470,7 +470,7 @@ fn dunder_dict_support() { fn access_dunder_dict() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyClassShell::new_ref(py, DunderDictSupport {}).unwrap(); + let inst = PyCell::new_ref(py, DunderDictSupport {}).unwrap(); py_run!( py, inst, @@ -488,7 +488,7 @@ struct WeakRefDunderDictSupport {} fn weakref_dunder_dict_support() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyClassShell::new_ref(py, WeakRefDunderDictSupport {}).unwrap(); + let inst = PyCell::new_ref(py, WeakRefDunderDictSupport {}).unwrap(); py_run!( py, inst, @@ -513,7 +513,7 @@ impl PyObjectProtocol for ClassWithGetAttr { fn getattr_doesnt_override_member() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyClassShell::new_ref(py, ClassWithGetAttr { data: 4 }).unwrap(); + let inst = PyCell::new_ref(py, ClassWithGetAttr { data: 4 }).unwrap(); py_assert!(py, inst, "inst.data == 4"); py_assert!(py, inst, "inst.a == 8"); } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 8f2d0923513..1f8de776854 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -3,7 +3,7 @@ use pyo3::class::PyTraverseError; use pyo3::class::PyVisit; use pyo3::prelude::*; use pyo3::types::{PyAny, PyTuple}; -use pyo3::{ffi, py_run, AsPyPointer, PyClassShell}; +use pyo3::{ffi, py_run, AsPyPointer, PyCell}; use std::cell::RefCell; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -153,7 +153,7 @@ fn gc_integration() { { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyClassShell::new_mut( + let inst = PyCell::new_mut( py, GCIntegration { self_ref: RefCell::new(py.None()), @@ -188,7 +188,7 @@ impl PyGCProtocol for GCIntegration2 { fn gc_integration2() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyClassShell::new_ref(py, GCIntegration2 {}).unwrap(); + let inst = PyCell::new_ref(py, GCIntegration2 {}).unwrap(); py_run!(py, inst, "import gc; assert inst in gc.get_objects()"); } @@ -199,7 +199,7 @@ struct WeakRefSupport {} fn weakref_support() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyClassShell::new_ref(py, WeakRefSupport {}).unwrap(); + let inst = PyCell::new_ref(py, WeakRefSupport {}).unwrap(); py_run!( py, inst, diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index f19d0fd13de..49cc537013b 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -126,7 +126,7 @@ impl SetWithName { fn inherit_set() { let gil = Python::acquire_gil(); let py = gil.python(); - let set_sub = pyo3::pyclass::PyClassShell::new_ref(py, SetWithName::new()).unwrap(); + let set_sub = pyo3::pyclass::PyCell::new_ref(py, SetWithName::new()).unwrap(); py_run!( py, set_sub, @@ -152,7 +152,7 @@ impl DictWithName { fn inherit_dict() { let gil = Python::acquire_gil(); let py = gil.python(); - let dict_sub = pyo3::pyclass::PyClassShell::new_ref(py, DictWithName::new()).unwrap(); + let dict_sub = pyo3::pyclass::PyCell::new_ref(py, DictWithName::new()).unwrap(); py_run!( py, dict_sub, diff --git a/tests/test_methods.rs b/tests/test_methods.rs index 153bc2b5fca..3e93e669ed3 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; use pyo3::py_run; use pyo3::types::{IntoPyDict, PyDict, PyList, PySet, PyString, PyTuple, PyType}; -use pyo3::PyClassShell; +use pyo3::PyCell; mod common; @@ -23,7 +23,7 @@ fn instance_method() { let gil = Python::acquire_gil(); let py = gil.python(); - let obj = PyClassShell::new_mut(py, InstanceMethod { member: 42 }).unwrap(); + let obj = PyCell::new_mut(py, InstanceMethod { member: 42 }).unwrap(); assert_eq!(obj.method().unwrap(), 42); let d = [("obj", obj)].into_py_dict(py); py.run("assert obj.method() == 42", None, Some(d)).unwrap(); @@ -49,7 +49,7 @@ fn instance_method_with_args() { let gil = Python::acquire_gil(); let py = gil.python(); - let obj = PyClassShell::new_mut(py, InstanceMethodWithArgs { member: 7 }).unwrap(); + let obj = PyCell::new_mut(py, InstanceMethodWithArgs { member: 7 }).unwrap(); assert_eq!(obj.method(6).unwrap(), 42); let d = [("obj", obj)].into_py_dict(py); py.run("assert obj.method(3) == 21", None, Some(d)).unwrap(); @@ -395,7 +395,7 @@ impl MethodWithLifeTime { fn method_with_lifetime() { let gil = Python::acquire_gil(); let py = gil.python(); - let obj = PyClassShell::new_ref(py, MethodWithLifeTime {}).unwrap(); + let obj = PyCell::new_ref(py, MethodWithLifeTime {}).unwrap(); py_run!( py, obj, diff --git a/tests/test_pyself.rs b/tests/test_pyself.rs index 00eebfb128f..25bf1a895a0 100644 --- a/tests/test_pyself.rs +++ b/tests/test_pyself.rs @@ -2,7 +2,7 @@ use pyo3; use pyo3::prelude::*; use pyo3::types::{PyBytes, PyString}; -use pyo3::{AsPyRef, PyClassShell, PyIterProtocol}; +use pyo3::{AsPyRef, PyCell, PyIterProtocol}; use std::collections::HashMap; mod common; @@ -17,27 +17,20 @@ struct Reader { #[pymethods] impl Reader { - fn clone_ref(slf: &PyClassShell) -> &PyClassShell { + fn clone_ref(slf: &PyCell) -> &PyCell { slf } - fn clone_ref_with_py<'py>( - slf: &'py PyClassShell, - _py: Python<'py>, - ) -> &'py PyClassShell { + fn clone_ref_with_py<'py>(slf: &'py PyCell, _py: Python<'py>) -> &'py PyCell { slf } - fn get_iter(slf: &PyClassShell, keys: Py) -> PyResult { + fn get_iter(slf: &PyCell, keys: Py) -> PyResult { Ok(Iter { reader: slf.into(), keys, idx: 0, }) } - fn get_iter_and_reset( - slf: &mut PyClassShell, - keys: Py, - py: Python, - ) -> PyResult { + fn get_iter_and_reset(slf: &mut PyCell, keys: Py, py: Python) -> PyResult { let reader = Py::new(py, slf.clone())?; slf.inner.clear(); Ok(Iter { @@ -57,12 +50,12 @@ struct Iter { #[pyproto] impl PyIterProtocol for Iter { - fn __iter__(slf: &mut PyClassShell) -> PyResult { + fn __iter__(slf: &mut PyCell) -> PyResult { let py = unsafe { Python::assume_gil_acquired() }; Ok(slf.to_object(py)) } - fn __next__(slf: &mut PyClassShell) -> PyResult> { + fn __next__(slf: &mut PyCell) -> PyResult> { let py = unsafe { Python::assume_gil_acquired() }; let bytes = slf.keys.as_ref(py).as_bytes(); match bytes.get(slf.idx) { @@ -113,7 +106,7 @@ fn test_clone_ref() { fn test_nested_iter_reset() { let gil = Python::acquire_gil(); let py = gil.python(); - let reader = PyClassShell::new_ref(py, reader()).unwrap(); + let reader = PyCell::new_ref(py, reader()).unwrap(); py_assert!( py, reader, diff --git a/tests/test_text_signature.rs b/tests/test_text_signature.rs index 6406cbf590b..85211a34f10 100644 --- a/tests/test_text_signature.rs +++ b/tests/test_text_signature.rs @@ -1,5 +1,5 @@ use pyo3::prelude::*; -use pyo3::{types::PyType, wrap_pyfunction, wrap_pymodule, PyClassShell}; +use pyo3::{types::PyType, wrap_pyfunction, wrap_pymodule, PyCell}; mod common; @@ -144,7 +144,7 @@ fn test_methods() { let _ = a; } #[text_signature = "($self, b)"] - fn pyself_method(_this: &PyClassShell, b: i32) { + fn pyself_method(_this: &PyCell, b: i32) { let _ = b; } #[classmethod] diff --git a/tests/test_various.rs b/tests/test_various.rs index e24db369606..d0bbb7bf4dd 100644 --- a/tests/test_various.rs +++ b/tests/test_various.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; use pyo3::types::IntoPyDict; use pyo3::types::{PyDict, PyTuple}; -use pyo3::{py_run, wrap_pyfunction, AsPyRef, PyClassShell}; +use pyo3::{py_run, wrap_pyfunction, AsPyRef, PyCell}; mod common; @@ -81,8 +81,8 @@ fn intopytuple_pyclass() { let py = gil.python(); let tup = ( - PyClassShell::new_ref(py, SimplePyClass {}).unwrap(), - PyClassShell::new_ref(py, SimplePyClass {}).unwrap(), + PyCell::new_ref(py, SimplePyClass {}).unwrap(), + PyCell::new_ref(py, SimplePyClass {}).unwrap(), ); py_assert!(py, tup, "type(tup[0]).__name__ == 'SimplePyClass'"); py_assert!(py, tup, "type(tup[0]).__name__ == type(tup[1]).__name__"); @@ -106,8 +106,8 @@ fn pytuple_pyclass_iter() { let tup = PyTuple::new( py, [ - PyClassShell::new_ref(py, SimplePyClass {}).unwrap(), - PyClassShell::new_ref(py, SimplePyClass {}).unwrap(), + PyCell::new_ref(py, SimplePyClass {}).unwrap(), + PyCell::new_ref(py, SimplePyClass {}).unwrap(), ] .iter(), ); @@ -127,7 +127,7 @@ impl PickleSupport { } pub fn __reduce__<'py>( - slf: &'py PyClassShell, + slf: &'py PyCell, py: Python<'py>, ) -> PyResult<(PyObject, &'py PyTuple, PyObject)> { let cls = slf.to_object(py).getattr(py, "__class__")?; @@ -152,7 +152,7 @@ fn test_pickle() { let module = PyModule::new(py, "test_module").unwrap(); module.add_class::().unwrap(); add_module(py, module).unwrap(); - let inst = PyClassShell::new_ref(py, PickleSupport {}).unwrap(); + let inst = PyCell::new_ref(py, PickleSupport {}).unwrap(); py_run!( py, inst, From 5d4e7374e93a9f78e9f7c0fc39b3081b7c946ff7 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 9 Feb 2020 16:35:52 +0900 Subject: [PATCH 02/27] Introduce PyDownCastImpl and Change PyTryFrom and FromPyPointer --- src/class/basic.rs | 13 +--- src/class/buffer.rs | 7 +- src/class/context.rs | 5 +- src/class/descr.rs | 6 +- src/class/gc.rs | 11 +-- src/class/macros.rs | 22 +++--- src/class/mapping.rs | 8 +- src/class/number.rs | 6 +- src/class/pyasync.rs | 6 +- src/class/sequence.rs | 14 ++-- src/conversion.rs | 170 +++++++++++------------------------------- src/derive_utils.rs | 5 +- src/instance.rs | 2 +- src/lib.rs | 3 +- src/object.rs | 4 +- src/objectprotocol.rs | 34 +++------ src/prelude.rs | 2 +- src/pycell.rs | 55 ++++---------- src/python.rs | 45 ++++------- src/type_object.rs | 28 ++++--- src/types/any.rs | 13 +--- src/types/module.rs | 2 +- src/types/sequence.rs | 23 ------ src/types/string.rs | 6 +- 24 files changed, 150 insertions(+), 340 deletions(-) diff --git a/src/class/basic.rs b/src/class/basic.rs index 8c670bf8260..ef86ff38ca3 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -11,14 +11,9 @@ use crate::callback::{BoolCallbackConverter, HashConverter, PyObjectCallbackConverter}; use crate::class::methods::PyMethodDef; use crate::err::{PyErr, PyResult}; -use crate::ffi; use crate::objectprotocol::ObjectProtocol; -use crate::type_object::PyTypeInfo; use crate::types::PyAny; -use crate::FromPyObject; -use crate::IntoPyPointer; -use crate::Python; -use crate::{exceptions, IntoPy, PyObject}; +use crate::{exceptions, ffi, FromPyObject, IntoPy, IntoPyPointer, PyClass, PyObject, Python}; use std::os::raw::c_int; use std::ptr; @@ -35,7 +30,7 @@ pub enum CompareOp { /// Basic python class customization #[allow(unused_variables)] -pub trait PyObjectProtocol<'p>: PyTypeInfo { +pub trait PyObjectProtocol<'p>: PyClass { fn __getattr__(&'p self, name: Self::Name) -> Self::Result where Self: PyObjectGetAttrProtocol<'p>, @@ -237,7 +232,7 @@ where return existing; } - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); let arg = py.from_borrowed_ptr::(arg); let result = match arg.extract() { @@ -513,7 +508,7 @@ where { let py = Python::assume_gil_acquired(); let _pool = crate::GILPool::new(py); - let slf = py.from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); let arg = py.from_borrowed_ptr::(arg); let res = match extract_op(op) { diff --git a/src/class/buffer.rs b/src/class/buffer.rs index f0f462db995..c72d7982abd 100644 --- a/src/class/buffer.rs +++ b/src/class/buffer.rs @@ -6,8 +6,7 @@ //! c-api use crate::callback::UnitCallbackConverter; use crate::err::PyResult; -use crate::ffi; -use crate::type_object::PyTypeInfo; +use crate::{ffi, PyClass}; use std::os::raw::c_int; /// Buffer protocol interface @@ -15,7 +14,7 @@ use std::os::raw::c_int; /// For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html) /// c-api #[allow(unused_variables)] -pub trait PyBufferProtocol<'p>: PyTypeInfo { +pub trait PyBufferProtocol<'p>: PyClass { fn bf_getbuffer(&'p self, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result where Self: PyBufferGetBufferProtocol<'p>, @@ -94,7 +93,7 @@ where { let py = crate::Python::assume_gil_acquired(); let _pool = crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); let result = slf.bf_getbuffer(arg1, arg2).into(); crate::callback::cb_convert(UnitCallbackConverter, py, result) diff --git a/src/class/context.rs b/src/class/context.rs index 50aea33e9da..9a9e0e98167 100644 --- a/src/class/context.rs +++ b/src/class/context.rs @@ -6,12 +6,11 @@ use crate::class::methods::PyMethodDef; use crate::err::PyResult; -use crate::type_object::PyTypeInfo; -use crate::PyObject; +use crate::{PyClass, PyObject}; /// Context manager interface #[allow(unused_variables)] -pub trait PyContextProtocol<'p>: PyTypeInfo { +pub trait PyContextProtocol<'p>: PyClass { fn __enter__(&'p mut self) -> Self::Result where Self: PyContextEnterProtocol<'p>, diff --git a/src/class/descr.rs b/src/class/descr.rs index 57d9119caab..80313dfed11 100644 --- a/src/class/descr.rs +++ b/src/class/descr.rs @@ -8,15 +8,13 @@ use crate::callback::{PyObjectCallbackConverter, UnitCallbackConverter}; use crate::class::methods::PyMethodDef; use crate::err::PyResult; -use crate::type_object::PyTypeInfo; use crate::types::{PyAny, PyType}; -use crate::FromPyObject; -use crate::{ffi, IntoPy, PyObject}; +use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject}; use std::os::raw::c_int; /// Descriptor interface #[allow(unused_variables)] -pub trait PyDescrProtocol<'p>: PyTypeInfo { +pub trait PyDescrProtocol<'p>: PyClass { fn __get__(&'p self, instance: &'p PyAny, owner: Option<&'p PyType>) -> Self::Result where Self: PyDescrGetProtocol<'p>, diff --git a/src/class/gc.rs b/src/class/gc.rs index 591b686af5c..18cbcf6a9fd 100644 --- a/src/class/gc.rs +++ b/src/class/gc.rs @@ -3,17 +3,14 @@ //! Python GC support //! -use crate::ffi; -use crate::type_object::PyTypeInfo; -use crate::AsPyPointer; -use crate::Python; +use crate::{ffi, AsPyPointer, PyCell, PyClass, Python}; use std::os::raw::{c_int, c_void}; #[repr(transparent)] pub struct PyTraverseError(c_int); /// GC support -pub trait PyGCProtocol<'p>: PyTypeInfo { +pub trait PyGCProtocol<'p>: PyClass { fn __traverse__(&'p self, visit: PyVisit) -> Result<(), PyTraverseError>; fn __clear__(&'p mut self); } @@ -94,7 +91,7 @@ where { let py = Python::assume_gil_acquired(); let _pool = crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); let visit = PyVisit { visit, @@ -136,7 +133,7 @@ where { let py = Python::assume_gil_acquired(); let _pool = crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); slf.__clear__(); 0 diff --git a/src/class/macros.rs b/src/class/macros.rs index f67fdd439a6..b774ec5512e 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -19,7 +19,7 @@ macro_rules! py_unary_func { { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let res = slf.$f().into(); $crate::callback::cb_convert($conv, py, res.map(|x| x)) } @@ -56,7 +56,7 @@ macro_rules! py_len_func { { let py = Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let result = slf.$f().into(); $crate::callback::cb_convert($conv, py, result) @@ -86,7 +86,7 @@ macro_rules! py_binary_func { use $crate::ObjectProtocol; let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg); let result = match arg.extract() { @@ -146,7 +146,7 @@ macro_rules! py_binary_self_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf1 = py.mut_from_borrowed_ptr::(slf); + let slf1 = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg); let result = match arg.extract() { @@ -182,7 +182,7 @@ macro_rules! py_ssizearg_func { { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let result = slf.$f(arg.into()).into(); $crate::callback::cb_convert($conv, py, result) } @@ -215,7 +215,7 @@ macro_rules! py_ternary_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1); let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2); @@ -286,7 +286,7 @@ macro_rules! py_ternary_self_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf1 = py.mut_from_borrowed_ptr::(slf); + let slf1 = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1); let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2); @@ -325,7 +325,7 @@ macro_rules! py_func_set { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::<$generic>(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf); let result = if value.is_null() { Err($crate::PyErr::new::( @@ -335,7 +335,7 @@ macro_rules! py_func_set { ), )) } else { - let name = py.mut_from_borrowed_ptr::<$crate::types::PyAny>(name); + let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); let value = py.from_borrowed_ptr::<$crate::types::PyAny>(value); match name.extract() { Ok(name) => match value.extract() { @@ -375,7 +375,7 @@ macro_rules! py_func_del { let _pool = $crate::GILPool::new(py); let result = if value.is_null() { - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); match name.extract() { @@ -415,7 +415,7 @@ macro_rules! py_func_set_del { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::<$generic>(slf); + let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf); let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); let result = if value.is_null() { diff --git a/src/class/mapping.rs b/src/class/mapping.rs index f1c4b3ffb0b..cccc6a22969 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -6,15 +6,11 @@ use crate::callback::{LenResultConverter, PyObjectCallbackConverter}; use crate::class::methods::PyMethodDef; use crate::err::{PyErr, PyResult}; -use crate::ffi; -use crate::type_object::PyTypeInfo; -use crate::FromPyObject; -use crate::Python; -use crate::{exceptions, IntoPy, PyObject}; +use crate::{exceptions, ffi, FromPyObject, IntoPy, PyClass, PyObject, Python}; /// Mapping interface #[allow(unused_variables)] -pub trait PyMappingProtocol<'p>: PyTypeInfo { +pub trait PyMappingProtocol<'p>: PyClass { fn __len__(&'p self) -> Self::Result where Self: PyMappingLenProtocol<'p>, diff --git a/src/class/number.rs b/src/class/number.rs index 796b8bad922..f44ef5b854d 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -7,13 +7,11 @@ use crate::callback::PyObjectCallbackConverter; use crate::class::basic::PyObjectProtocolImpl; use crate::class::methods::PyMethodDef; use crate::err::PyResult; -use crate::type_object::PyTypeInfo; -use crate::FromPyObject; -use crate::{ffi, IntoPy, PyObject}; +use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject}; /// Number interface #[allow(unused_variables)] -pub trait PyNumberProtocol<'p>: PyTypeInfo { +pub trait PyNumberProtocol<'p>: PyClass { fn __add__(lhs: Self::Left, rhs: Self::Right) -> Self::Result where Self: PyNumberAddProtocol<'p>, diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs index 2536d4ce2fb..8c1e04e8ca8 100644 --- a/src/class/pyasync.rs +++ b/src/class/pyasync.rs @@ -11,15 +11,13 @@ use crate::callback::PyObjectCallbackConverter; use crate::class::methods::PyMethodDef; use crate::err::PyResult; -use crate::ffi; -use crate::type_object::PyTypeInfo; -use crate::PyObject; +use crate::{ffi, PyClass, PyObject}; /// Python Async/Await support interface. /// /// Each method in this trait corresponds to Python async/await implementation. #[allow(unused_variables)] -pub trait PyAsyncProtocol<'p>: PyTypeInfo { +pub trait PyAsyncProtocol<'p>: PyClass { fn __await__(&'p self) -> Self::Result where Self: PyAsyncAwaitProtocol<'p>, diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 5414704a18f..146ed02dd51 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -5,18 +5,14 @@ use crate::callback::{BoolCallbackConverter, LenResultConverter, PyObjectCallbackConverter}; use crate::err::{PyErr, PyResult}; -use crate::ffi; use crate::objectprotocol::ObjectProtocol; -use crate::type_object::PyTypeInfo; use crate::types::PyAny; -use crate::FromPyObject; -use crate::Python; -use crate::{exceptions, IntoPy, PyObject}; +use crate::{exceptions, ffi, FromPyObject, IntoPy, PyClass, PyObject, Python}; use std::os::raw::c_int; /// Sequence interface #[allow(unused_variables)] -pub trait PySequenceProtocol<'p>: PyTypeInfo + Sized { +pub trait PySequenceProtocol<'p>: PyClass + Sized { fn __len__(&'p self) -> Self::Result where Self: PySequenceLenProtocol<'p>, @@ -267,7 +263,7 @@ mod sq_ass_item_impl { { let py = Python::assume_gil_acquired(); let _pool = crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); let result = if value.is_null() { Err(PyErr::new::(format!( @@ -322,7 +318,7 @@ mod sq_ass_item_impl { { let py = Python::assume_gil_acquired(); let _pool = crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); let result = if value.is_null() { slf.__delitem__(key.into()).into() @@ -373,7 +369,7 @@ mod sq_ass_item_impl { { let py = Python::assume_gil_acquired(); let _pool = crate::GILPool::new(py); - let slf = py.mut_from_borrowed_ptr::(slf); + let slf = py.from_borrowed_ptr::>(slf); let result = if value.is_null() { slf.__delitem__(key.into()).into() diff --git a/src/conversion.rs b/src/conversion.rs index 7a9259ec398..99276714251 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -3,7 +3,7 @@ //! Conversions between various states of rust and python types and their wrappers. use crate::err::{self, PyDowncastError, PyResult}; use crate::object::PyObject; -use crate::type_object::{PyObjectLayout, PyTypeInfo}; +use crate::type_object::{PyDowncastImpl, PyTypeInfo}; use crate::types::PyAny; use crate::types::PyTuple; use crate::{ffi, gil, Py, Python}; @@ -254,11 +254,10 @@ pub mod extract_impl { pub struct Cloned; pub struct Reference; - pub struct MutReference; impl<'a, T: 'a> ExtractImpl<'a, T> for Cloned where - T: Clone, + T: Clone + PyTryFrom<'a>, Reference: ExtractImpl<'a, &'a T>, { fn extract(source: &'a PyAny) -> PyResult { @@ -266,7 +265,7 @@ pub mod extract_impl { } } - impl<'a, T> ExtractImpl<'a, &'a T> for Reference + impl<'a, T: 'a> ExtractImpl<'a, &'a T> for Reference where T: PyTryFrom<'a>, { @@ -274,15 +273,6 @@ pub mod extract_impl { Ok(T::try_from(source)?) } } - - impl<'a, T> ExtractImpl<'a, &'a mut T> for MutReference - where - T: PyTryFrom<'a>, - { - fn extract(source: &'a PyAny) -> PyResult<&'a mut T> { - Ok(T::try_from_mut(source)?) - } - } } use extract_impl::ExtractImpl; @@ -312,8 +302,8 @@ pub trait FromPyObjectImpl { impl<'a, T> FromPyObject<'a> for T where - T: FromPyObjectImpl, - ::Impl: ExtractImpl<'a, Self>, + T: FromPyObjectImpl + 'a, + ::Impl: ExtractImpl<'a, T>, { #[inline] fn extract(ob: &'a PyAny) -> PyResult { @@ -337,108 +327,50 @@ where } } -/// Trait implemented by Python object types that allow a checked downcast. -/// This trait is similar to `std::convert::TryInto` -pub trait PyTryInto: Sized { - /// Cast from PyObject to a concrete Python object type. - fn try_into(&self) -> Result<&T, PyDowncastError>; - - /// Cast from PyObject to a concrete Python object type. With exact type check. - fn try_into_exact(&self) -> Result<&T, PyDowncastError>; - - /// Cast from PyObject to a concrete Python object type. - fn try_into_mut(&self) -> Result<&mut T, PyDowncastError>; - - /// Cast from PyObject to a concrete Python object type. With exact type check. - fn try_into_mut_exact(&self) -> Result<&mut T, PyDowncastError>; -} - /// Trait implemented by Python object types that allow a checked downcast. /// This trait is similar to `std::convert::TryFrom` -pub trait PyTryFrom<'v>: Sized { +pub trait PyTryFrom<'v>: Sized + PyDowncastImpl<'v> { /// Cast from a concrete Python object type to PyObject. fn try_from>(value: V) -> Result<&'v Self, PyDowncastError>; /// Cast from a concrete Python object type to PyObject. With exact type check. fn try_from_exact>(value: V) -> Result<&'v Self, PyDowncastError>; - /// Cast from a concrete Python object type to PyObject. - fn try_from_mut>(value: V) -> Result<&'v mut Self, PyDowncastError>; - - /// Cast from a concrete Python object type to PyObject. With exact type check. - fn try_from_mut_exact>(value: V) -> Result<&'v mut Self, PyDowncastError>; - /// Cast a PyAny to a specific type of PyObject. The caller must /// have already verified the reference is for this type. unsafe fn try_from_unchecked>(value: V) -> &'v Self; - - /// Cast a PyAny to a specific type of PyObject. The caller must - /// have already verified the reference is for this type. - #[allow(clippy::mut_from_ref)] - unsafe fn try_from_mut_unchecked>(value: V) -> &'v mut Self; } -// TryFrom implies TryInto -impl PyTryInto for PyAny -where - U: for<'v> PyTryFrom<'v>, -{ - fn try_into(&self) -> Result<&U, PyDowncastError> { - U::try_from(self) - } - fn try_into_exact(&self) -> Result<&U, PyDowncastError> { - U::try_from_exact(self) - } - fn try_into_mut(&self) -> Result<&mut U, PyDowncastError> { - U::try_from_mut(self) - } - fn try_into_mut_exact(&self) -> Result<&mut U, PyDowncastError> { - U::try_from_mut_exact(self) - } -} +// /// Trait implemented by Python object types that allow a checked downcast. +// /// This trait is similar to `std::convert::TryInto` +// pub trait PyTryInto<'v, T: PyDowncastImpl<'v>>: Sized { +// /// Cast from PyObject to a concrete Python object type. +// fn try_into(&self) -> Result<&T, PyDowncastError>; + +// /// Cast from PyObject to a concrete Python object type. With exact type check. +// fn try_into_exact(&self) -> Result<&T, PyDowncastError>; +// } impl<'v, T> PyTryFrom<'v> for T where - T: PyTypeInfo, + T: PyDowncastImpl<'v> + PyTypeInfo, { - fn try_from>(value: V) -> Result<&'v T, PyDowncastError> { + fn try_from>(value: V) -> Result<&'v Self, PyDowncastError> { let value = value.into(); unsafe { if T::is_instance(value) { - Ok(PyTryFrom::try_from_unchecked(value)) + Ok(Self::try_from_unchecked(value)) } else { Err(PyDowncastError) } } } - fn try_from_exact>(value: V) -> Result<&'v T, PyDowncastError> { + fn try_from_exact>(value: V) -> Result<&'v Self, PyDowncastError> { let value = value.into(); unsafe { if T::is_exact_instance(value) { - Ok(PyTryFrom::try_from_unchecked(value)) - } else { - Err(PyDowncastError) - } - } - } - - fn try_from_mut>(value: V) -> Result<&'v mut T, PyDowncastError> { - let value = value.into(); - unsafe { - if T::is_instance(value) { - Ok(PyTryFrom::try_from_mut_unchecked(value)) - } else { - Err(PyDowncastError) - } - } - } - - fn try_from_mut_exact>(value: V) -> Result<&'v mut T, PyDowncastError> { - let value = value.into(); - unsafe { - if T::is_exact_instance(value) { - Ok(PyTryFrom::try_from_mut_unchecked(value)) + Ok(Self::try_from_unchecked(value)) } else { Err(PyDowncastError) } @@ -446,15 +378,8 @@ where } #[inline] - unsafe fn try_from_unchecked>(value: V) -> &'v T { - let value = value.into(); - T::ConcreteLayout::internal_ref_cast(value) - } - - #[inline] - unsafe fn try_from_mut_unchecked>(value: V) -> &'v mut T { - let value = value.into(); - T::ConcreteLayout::internal_mut_cast(value) + unsafe fn try_from_unchecked>(value: V) -> &'v Self { + Self::unchecked_downcast(value.into()) } } @@ -467,33 +392,37 @@ impl FromPy<()> for Py { /// Raw level conversion between `*mut ffi::PyObject` and PyO3 types. pub unsafe trait FromPyPointer<'p>: Sized { - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option; - unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self { + unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self>; + unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { match Self::from_owned_ptr_or_opt(py, ptr) { Some(s) => s, None => err::panic_after_error(), } } - unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self { + unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { Self::from_owned_ptr_or_panic(py, ptr) } - unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult { + unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<&'p Self> { match Self::from_owned_ptr_or_opt(py, ptr) { Some(s) => Ok(s), None => Err(err::PyErr::fetch(py)), } } - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option; - unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self { + unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) + -> Option<&'p Self>; + unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { match Self::from_borrowed_ptr_or_opt(py, ptr) { Some(s) => s, None => err::panic_after_error(), } } - unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> Self { + unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { Self::from_borrowed_ptr_or_panic(py, ptr) } - unsafe fn from_borrowed_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult { + unsafe fn from_borrowed_ptr_or_err( + py: Python<'p>, + ptr: *mut ffi::PyObject, + ) -> PyResult<&'p Self> { match Self::from_borrowed_ptr_or_opt(py, ptr) { Some(s) => Ok(s), None => Err(err::PyErr::fetch(py)), @@ -501,29 +430,18 @@ pub unsafe trait FromPyPointer<'p>: Sized { } } -unsafe impl<'p, T> FromPyPointer<'p> for &'p T +unsafe impl<'p, T> FromPyPointer<'p> for T where - T: PyTypeInfo, + T: 'p + crate::PyNativeType, { - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr).map(|p| T::ConcreteLayout::internal_ref_cast(gil::register_owned(py, p))) - } - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr) - .map(|p| T::ConcreteLayout::internal_ref_cast(gil::register_borrowed(py, p))) - } -} - -unsafe impl<'p, T> FromPyPointer<'p> for &'p mut T -where - T: PyTypeInfo, -{ - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr).map(|p| T::ConcreteLayout::internal_mut_cast(gil::register_owned(py, p))) - } - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr) - .map(|p| T::ConcreteLayout::internal_mut_cast(gil::register_borrowed(py, p))) + unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { + NonNull::new(ptr).map(|p| Self::unchecked_downcast(gil::register_owned(py, p))) + } + unsafe fn from_borrowed_ptr_or_opt( + py: Python<'p>, + ptr: *mut ffi::PyObject, + ) -> Option<&'p Self> { + NonNull::new(ptr).map(|p| Self::unchecked_downcast(gil::register_borrowed(py, p))) } } diff --git a/src/derive_utils.rs b/src/derive_utils.rs index a5e284b5f9a..fc758ffcd7d 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -11,7 +11,7 @@ use crate::instance::PyNativeType; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::types::{PyAny, PyDict, PyModule, PyTuple}; -use crate::{ffi, GILPool, IntoPy, PyObject, Python}; +use crate::{ffi, AsPyRef, GILPool, IntoPy, PyObject, Python}; use std::ptr; /// Description of a python parameter; used for `parse_args()`. @@ -100,8 +100,7 @@ pub fn parse_fn_args<'p>( // Adjust the remaining args let args = if accept_args { let py = args.py(); - let slice = args.slice(used_args as isize, nargs as isize).into_py(py); - py.checked_cast_as(slice).unwrap() + args.slice(used_args as isize, nargs as isize).as_ref(py) } else { args }; diff --git a/src/instance.rs b/src/instance.rs index 15a74bed7dd..5df15e8e7d3 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -127,7 +127,7 @@ pub trait AsPyRef: Sized { impl AsPyRef for Py { fn as_ref(&self, _py: Python) -> &T { let any = self as *const Py as *const PyAny; - unsafe { T::ConcreteLayout::internal_ref_cast(&*any) } + unimplemented!() } } diff --git a/src/lib.rs b/src/lib.rs index ce19c20f2e7..7e6c017f973 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ pub use crate::class::*; pub use crate::conversion::{ - AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, + AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject, }; pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult}; @@ -165,6 +165,7 @@ pub mod ffi; pub mod freelist; mod gil; mod instance; +mod pyref; #[macro_use] mod internal_tricks; pub mod marshal; diff --git a/src/object.rs b/src/object.rs index c465a8c84a3..44193fa0a39 100644 --- a/src/object.rs +++ b/src/object.rs @@ -149,9 +149,9 @@ impl PyObject { } /// Casts the PyObject to a concrete Python object type. - pub fn cast_as(&self, py: Python) -> Result<&D, PyDowncastError> + pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError> where - D: for<'v> PyTryFrom<'v>, + D: PyTryFrom<'p>, { D::try_from(self.as_ref(py)) } diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index 93451a448ba..55122ad8966 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -3,16 +3,11 @@ use crate::class::basic::CompareOp; use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::exceptions::TypeError; -use crate::ffi; -use crate::instance::PyNativeType; -use crate::object::PyObject; -use crate::type_object::PyTypeInfo; use crate::types::{PyAny, PyDict, PyIterator, PyString, PyTuple, PyType}; -use crate::AsPyPointer; -use crate::IntoPyPointer; -use crate::Py; -use crate::Python; -use crate::{FromPyObject, IntoPy, PyTryFrom, ToBorrowedObject, ToPyObject}; +use crate::{ + ffi, AsPyPointer, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, Py, PyNativeType, + PyObject, PyTryFrom, PyTypeInfo, Python, ToBorrowedObject, ToPyObject, +}; use std::cmp::Ordering; use std::os::raw::c_int; @@ -176,15 +171,10 @@ pub trait ObjectProtocol { fn get_type_ptr(&self) -> *mut ffi::PyTypeObject; /// Gets the Python base object for this object. - fn get_base(&self) -> &::BaseType + fn get_base<'py>(&'py self) -> &'py ::BaseType where - Self: PyTypeInfo; - - /// Gets the Python base object for this object. - - fn get_mut_base(&mut self) -> &mut ::BaseType - where - Self: PyTypeInfo; + Self: PyTypeInfo, + ::BaseType: FromPyPointer<'py>; /// Casts the PyObject to a concrete Python object type. fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError> @@ -455,20 +445,14 @@ where unsafe { (*self.as_ptr()).ob_type } } - fn get_base(&self) -> &::BaseType + fn get_base<'py>(&'py self) -> &'py ::BaseType where Self: PyTypeInfo, + ::BaseType: FromPyPointer<'py>, { unsafe { self.py().from_borrowed_ptr(self.as_ptr()) } } - fn get_mut_base(&mut self) -> &mut ::BaseType - where - Self: PyTypeInfo, - { - unsafe { self.py().mut_from_borrowed_ptr(self.as_ptr()) } - } - fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError> where D: PyTryFrom<'a>, diff --git a/src/prelude.rs b/src/prelude.rs index 4e98df7cfe5..14d3a2b7977 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -17,7 +17,7 @@ pub use crate::object::PyObject; pub use crate::objectprotocol::ObjectProtocol; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::Python; -pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject}; +pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToPyObject}; // This is only part of the prelude because we need it for the pymodule function pub use crate::types::PyModule; pub use pyo3cls::pymodule; diff --git a/src/pycell.rs b/src/pycell.rs index 710f4300bcc..205912a2e85 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -3,7 +3,7 @@ use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; -use crate::type_object::{PyObjectLayout, PyObjectSizedLayout}; +use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout}; use crate::types::PyAny; use crate::{ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python}; use std::mem::ManuallyDrop; @@ -42,20 +42,7 @@ pub struct PyCell { impl PyCell { /// Make new `PyCell` on the Python heap and returns the reference of it. - pub fn new_ref(py: Python, value: impl Into>) -> PyResult<&Self> - where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, - { - unsafe { - let initializer = value.into(); - let self_ = initializer.create_cell(py)?; - FromPyPointer::from_owned_ptr_or_err(py, self_ as _) - } - } - - /// Make new `PyCell` on the Python heap and returns the mutable reference of it. - pub fn new_mut(py: Python, value: impl Into>) -> PyResult<&mut Self> + pub fn new(py: Python, value: impl Into>) -> PyResult<&Self> where ::ConcreteLayout: crate::type_object::PyObjectSizedLayout, @@ -98,14 +85,6 @@ impl PyObjectLayout for PyCell { fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { Some(&mut self.ob_base) } - unsafe fn internal_ref_cast(obj: &PyAny) -> &T { - let cell = obj.as_ptr() as *const Self; - &(*cell).pyclass - } - unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T { - let cell = obj.as_ptr() as *const _ as *mut Self; - &mut (*cell).pyclass - } unsafe fn py_drop(&mut self, py: Python) { ManuallyDrop::drop(&mut self.pyclass); self.dict.clear_dict(py); @@ -117,6 +96,13 @@ impl PyObjectLayout for PyCell { } } +unsafe impl<'py, T: 'py + PyClass> PyDowncastImpl<'py> for PyCell { + unsafe fn unchecked_downcast(obj: &PyAny) -> &'py Self { + &*(obj.as_ptr() as *const Self) + } + private_impl! {} +} + impl PyObjectSizedLayout for PyCell {} impl AsPyPointer for PyCell { @@ -150,28 +136,17 @@ impl ToPyObject for &mut PyCell { } } -unsafe impl<'p, T> FromPyPointer<'p> for &'p PyCell +unsafe impl<'p, T> FromPyPointer<'p> for PyCell where T: PyClass, { - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { + unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell)) } - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { + unsafe fn from_borrowed_ptr_or_opt( + py: Python<'p>, + ptr: *mut ffi::PyObject, + ) -> Option<&'p Self> { NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell)) } } - -unsafe impl<'p, T> FromPyPointer<'p> for &'p mut PyCell -where - T: PyClass, -{ - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr) - .map(|p| &mut *(gil::register_owned(py, p).as_ptr() as *const _ as *mut PyCell)) - } - unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { - NonNull::new(ptr) - .map(|p| &mut *(gil::register_borrowed(py, p).as_ptr() as *const _ as *mut PyCell)) - } -} diff --git a/src/python.rs b/src/python.rs index e5b31ae8f31..a0977b94bc9 100644 --- a/src/python.rs +++ b/src/python.rs @@ -7,7 +7,7 @@ use crate::ffi; use crate::gil::{self, GILGuard}; use crate::instance::AsPyRef; use crate::object::PyObject; -use crate::type_object::{PyObjectLayout, PyTypeInfo, PyTypeObject}; +use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyTypeInfo, PyTypeObject}; use crate::types::{PyAny, PyDict, PyModule, PyType}; use crate::AsPyPointer; use crate::{FromPyPointer, IntoPyPointer, PyTryFrom}; @@ -275,19 +275,19 @@ impl<'p> Python<'p> { /// Register object in release pool, and try to downcast to specific type. pub fn checked_cast_as(self, obj: PyObject) -> Result<&'p T, PyDowncastError> where - T: PyTypeInfo, + T: PyTryFrom<'p>, { - let p = unsafe { gil::register_owned(self, obj.into_nonnull()) }; - ::try_from(p) + let obj = unsafe { gil::register_owned(self, obj.into_nonnull()) }; + ::try_from(obj) } /// Register object in release pool, and do unchecked downcast to specific type. pub unsafe fn cast_as(self, obj: PyObject) -> &'p T where - T: PyTypeInfo, + T: PyDowncastImpl<'p> + PyTypeInfo, { - let p = gil::register_owned(self, obj.into_nonnull()); - T::ConcreteLayout::internal_ref_cast(p) + let obj = gil::register_owned(self, obj.into_nonnull()); + T::unchecked_downcast(obj) } /// Register `ffi::PyObject` pointer in release pool @@ -304,16 +304,7 @@ impl<'p> Python<'p> { #[allow(clippy::wrong_self_convention)] pub unsafe fn from_owned_ptr(self, ptr: *mut ffi::PyObject) -> &'p T where - T: PyTypeInfo, - { - FromPyPointer::from_owned_ptr(self, ptr) - } - - /// Register `ffi::PyObject` pointer in release pool, - /// Do unchecked downcast to specific type. Returns mutable reference. - pub unsafe fn mut_from_owned_ptr(self, ptr: *mut ffi::PyObject) -> &'p mut T - where - T: PyTypeInfo, + T: FromPyPointer<'p>, { FromPyPointer::from_owned_ptr(self, ptr) } @@ -324,7 +315,7 @@ impl<'p> Python<'p> { #[allow(clippy::wrong_self_convention)] pub unsafe fn from_owned_ptr_or_err(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T> where - T: PyTypeInfo, + T: FromPyPointer<'p>, { FromPyPointer::from_owned_ptr_or_err(self, ptr) } @@ -335,7 +326,7 @@ impl<'p> Python<'p> { #[allow(clippy::wrong_self_convention)] pub unsafe fn from_owned_ptr_or_opt(self, ptr: *mut ffi::PyObject) -> Option<&'p T> where - T: PyTypeInfo, + T: FromPyPointer<'p>, { FromPyPointer::from_owned_ptr_or_opt(self, ptr) } @@ -346,17 +337,7 @@ impl<'p> Python<'p> { #[allow(clippy::wrong_self_convention)] pub unsafe fn from_borrowed_ptr(self, ptr: *mut ffi::PyObject) -> &'p T where - T: PyTypeInfo, - { - FromPyPointer::from_borrowed_ptr(self, ptr) - } - - /// Register borrowed `ffi::PyObject` pointer in release pool. - /// Panics if the pointer is `null`. - /// do unchecked downcast to specific type. - pub unsafe fn mut_from_borrowed_ptr(self, ptr: *mut ffi::PyObject) -> &'p mut T - where - T: PyTypeInfo, + T: FromPyPointer<'p>, { FromPyPointer::from_borrowed_ptr(self, ptr) } @@ -367,7 +348,7 @@ impl<'p> Python<'p> { #[allow(clippy::wrong_self_convention)] pub unsafe fn from_borrowed_ptr_or_err(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T> where - T: PyTypeInfo, + T: FromPyPointer<'p>, { FromPyPointer::from_borrowed_ptr_or_err(self, ptr) } @@ -378,7 +359,7 @@ impl<'p> Python<'p> { #[allow(clippy::wrong_self_convention)] pub unsafe fn from_borrowed_ptr_or_opt(self, ptr: *mut ffi::PyObject) -> Option<&'p T> where - T: PyTypeInfo, + T: FromPyPointer<'p>, { FromPyPointer::from_borrowed_ptr_or_opt(self, ptr) } diff --git a/src/type_object.rs b/src/type_object.rs index cce36d02d85..b9ca7bd4545 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -22,15 +22,6 @@ pub trait PyObjectLayout { None } - unsafe fn internal_ref_cast(obj: &PyAny) -> &T { - &*(obj as *const _ as *const T) - } - - #[allow(clippy::mut_from_ref)] - unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T { - &mut *(obj as *const _ as *const T as *mut T) - } - unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} } @@ -61,6 +52,25 @@ pub mod type_flags { pub const EXTENDED: usize = 1 << 4; } +/// Reference abstraction for `PyClass` and `PyNativeType`. +// NOTE: This trait is separated from PyTypeInfo since make it PyTypeInfo<`py> is confusing... +// When GAT and type Ref<'py> becomes stable, let's merge this trait to +// PyConcreteLayout or Py +pub unsafe trait PyDowncastImpl<'py> { + unsafe fn unchecked_downcast(obj: &PyAny) -> &'py Self; + private_decl! {} +} + +unsafe impl<'py, T> PyDowncastImpl<'py> for T +where + T: 'py + crate::PyNativeType, +{ + unsafe fn unchecked_downcast(obj: &PyAny) -> &'py Self { + &*(obj as *const _ as *const Self) + } + private_impl! {} +} + /// Python type information. /// All Python native types(e.g., `PyDict`) and `#[pyclass]` structs implement this trait. /// diff --git a/src/types/any.rs b/src/types/any.rs index a37205f22b2..f0d17649461 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -36,17 +36,10 @@ pyobject_native_type_convert!( pyobject_native_type_extract!(PyAny); impl PyAny { - pub fn downcast_ref(&self) -> Result<&T, PyDowncastError> + pub fn downcast(&self) -> Result<&T, PyDowncastError> where - T: for<'gil> PyTryFrom<'gil>, + for<'py> T: PyTryFrom<'py>, { - T::try_from(self) - } - - pub fn downcast_mut(&self) -> Result<&mut T, PyDowncastError> - where - T: for<'gil> PyTryFrom<'gil>, - { - T::try_from_mut(self) + ::try_from(self) } } diff --git a/src/types/module.rs b/src/types/module.rs index d152c5291ab..5f27ea036bd 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -79,7 +79,7 @@ impl PyModule { /// Return the index (`__all__`) of the module, creating one if needed. pub fn index(&self) -> PyResult<&PyList> { match self.getattr("__all__") { - Ok(idx) => idx.downcast_ref().map_err(PyErr::from), + Ok(idx) => idx.downcast().map_err(PyErr::from), Err(err) => { if err.is_instance::(self.py()) { let l = PyList::empty(self.py()); diff --git a/src/types/sequence.rs b/src/types/sequence.rs index 0de56dfa901..4a9cd9137ae 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -298,34 +298,11 @@ impl<'v> PyTryFrom<'v> for PySequence { ::try_from(value) } - fn try_from_mut>(value: V) -> Result<&'v mut PySequence, PyDowncastError> { - let value = value.into(); - unsafe { - if ffi::PySequence_Check(value.as_ptr()) != 0 { - Ok(::try_from_mut_unchecked(value)) - } else { - Err(PyDowncastError) - } - } - } - - fn try_from_mut_exact>( - value: V, - ) -> Result<&'v mut PySequence, PyDowncastError> { - ::try_from_mut(value) - } - #[inline] unsafe fn try_from_unchecked>(value: V) -> &'v PySequence { let ptr = value.into() as *const _ as *const PySequence; &*ptr } - - #[inline] - unsafe fn try_from_mut_unchecked>(value: V) -> &'v mut PySequence { - let ptr = value.into() as *const _ as *mut PySequence; - &mut *ptr - } } #[cfg(test)] diff --git a/src/types/string.rs b/src/types/string.rs index 51d490f15a5..70b91238f88 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -3,15 +3,11 @@ use crate::conversion::FromPyObject; use crate::conversion::{PyTryFrom, ToPyObject}; use crate::err::{PyErr, PyResult}; -use crate::gil; use crate::instance::PyNativeType; use crate::internal_tricks::Unsendable; use crate::object::PyObject; use crate::types::PyAny; -use crate::AsPyPointer; -use crate::IntoPy; -use crate::Python; -use crate::{ffi, FromPy}; +use crate::{ffi, gil, AsPyPointer, FromPy, IntoPy, Python}; use std::borrow::Cow; use std::ffi::CStr; use std::os::raw::c_char; From c43fb9fcdfc2752642f4ed2b4e8098daeb65d7a5 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 9 Feb 2020 16:36:32 +0900 Subject: [PATCH 03/27] Prototype Implementation of RefCell-like PyCell --- src/class/basic.rs | 11 +-- src/class/buffer.rs | 2 +- src/class/gc.rs | 4 +- src/class/macros.rs | 132 ++++++++++++++------------- src/class/pyasync.rs | 4 +- src/class/sequence.rs | 21 +++-- src/derive_utils.rs | 3 +- src/err.rs | 5 +- src/internal_tricks.rs | 7 ++ src/lib.rs | 1 - src/pycell.rs | 198 +++++++++++++++++++++++++++++++++++------ src/type_object.rs | 8 +- 12 files changed, 286 insertions(+), 110 deletions(-) diff --git a/src/class/basic.rs b/src/class/basic.rs index ef86ff38ca3..2150310f6dc 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -234,11 +234,7 @@ where let slf = py.from_borrowed_ptr::>(slf); let arg = py.from_borrowed_ptr::(arg); - - let result = match arg.extract() { - Ok(arg) => slf.__getattr__(arg).into(), - Err(e) => Err(e), - }; + let result = call_ref!(slf, __getattr__, arg); crate::callback::cb_convert(PyObjectCallbackConverter, py, result) } Some(wrap::) @@ -512,10 +508,7 @@ where let arg = py.from_borrowed_ptr::(arg); let res = match extract_op(op) { - Ok(op) => match arg.extract() { - Ok(arg) => slf.__richcmp__(arg, op).into(), - Err(e) => Err(e), - }, + Ok(op) => call_ref!(slf, __richcmp__, arg ; op), Err(e) => Err(e), }; match res { diff --git a/src/class/buffer.rs b/src/class/buffer.rs index c72d7982abd..64858751fc5 100644 --- a/src/class/buffer.rs +++ b/src/class/buffer.rs @@ -95,7 +95,7 @@ where let _pool = crate::GILPool::new(py); let slf = py.from_borrowed_ptr::>(slf); - let result = slf.bf_getbuffer(arg1, arg2).into(); + let result = slf.borrow().bf_getbuffer(arg1, arg2).into(); crate::callback::cb_convert(UnitCallbackConverter, py, result) } Some(wrap::) diff --git a/src/class/gc.rs b/src/class/gc.rs index 18cbcf6a9fd..b8638808e5a 100644 --- a/src/class/gc.rs +++ b/src/class/gc.rs @@ -98,7 +98,7 @@ where arg, _py: py, }; - match slf.__traverse__(visit) { + match slf.borrow().__traverse__(visit) { Ok(()) => 0, Err(PyTraverseError(code)) => code, } @@ -135,7 +135,7 @@ where let _pool = crate::GILPool::new(py); let slf = py.from_borrowed_ptr::>(slf); - slf.__clear__(); + slf.borrow_mut().__clear__(); 0 } Some(tp_clear::) diff --git a/src/class/macros.rs b/src/class/macros.rs index b774ec5512e..642bc9aa828 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -12,7 +12,17 @@ macro_rules! py_unary_func { *mut $crate::ffi::PyObject ); }; - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $ret_type:ty) => {{ + // Use call_ref! by default + ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $ret_type:ty) => { + py_unary_func!($trait, $class::$f, $res_type, $conv, $ret_type, call_ref); + }; + ($trait:ident, + $class:ident :: $f:ident, + $res_type:ty, + $conv:expr, + $ret_type:ty, + $call: ident + ) => {{ unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> $ret_type where T: for<'p> $trait<'p>, @@ -20,7 +30,7 @@ macro_rules! py_unary_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - let res = slf.$f().into(); + let res = $call!(slf, $f); $crate::callback::cb_convert($conv, py, res.map(|x| x)) } Some(wrap::<$class>) @@ -57,8 +67,7 @@ macro_rules! py_len_func { let py = Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - - let result = slf.$f().into(); + let result = call_ref!(slf, $f); $crate::callback::cb_convert($conv, py, result) } Some(wrap::<$class>) @@ -77,8 +86,11 @@ macro_rules! py_binary_func { *mut $crate::ffi::PyObject ) }; + // Use call_ref! by default ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $return:ty) => {{ - #[allow(unused_mut)] + py_binary_func!($trait, $class::$f, $res_type, $conv, $return, call_ref) + }}; + ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $return:ty, $call:ident) => {{ unsafe extern "C" fn wrap(slf: *mut ffi::PyObject, arg: *mut ffi::PyObject) -> $return where T: for<'p> $trait<'p>, @@ -88,11 +100,7 @@ macro_rules! py_binary_func { let _pool = $crate::GILPool::new(py); let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg); - - let result = match arg.extract() { - Ok(arg) => slf.$f(arg).into(), - Err(e) => Err(e.into()), - }; + let result = $call!(slf, $f, arg); $crate::callback::cb_convert($conv, py, result) } Some(wrap::<$class>) @@ -103,7 +111,6 @@ macro_rules! py_binary_func { #[doc(hidden)] macro_rules! py_binary_num_func { ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ - #[allow(unused_mut)] unsafe extern "C" fn wrap( lhs: *mut ffi::PyObject, rhs: *mut ffi::PyObject, @@ -130,11 +137,12 @@ macro_rules! py_binary_num_func { }}; } +// NOTE(kngwyu): +// Now(2020 2/9) This macro is used only for inplace operation so I used call_refmut here. #[macro_export] #[doc(hidden)] macro_rules! py_binary_self_func { ($trait:ident, $class:ident :: $f:ident) => {{ - #[allow(unused_mut)] unsafe extern "C" fn wrap( slf: *mut ffi::PyObject, arg: *mut ffi::PyObject, @@ -146,13 +154,9 @@ macro_rules! py_binary_self_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf1 = py.from_borrowed_ptr::<$crate::PyCell>(slf); + let slf_ = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg); - - let result = match arg.extract() { - Ok(arg) => slf1.$f(arg).into(), - Err(e) => Err(e.into()), - }; + let result = call_refmut!(slf_, $f, arg); match result { Ok(_) => { ffi::Py_INCREF(slf); @@ -171,8 +175,17 @@ macro_rules! py_binary_self_func { #[macro_export] #[doc(hidden)] macro_rules! py_ssizearg_func { - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ - #[allow(unused_mut)] + // Use call_ref! by default + ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => { + py_ssizearg_func!( + $trait, + $class::$f, + $res_type, + $conv, + call_ref + ) + }; + ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $call:ident) => {{ unsafe extern "C" fn wrap( slf: *mut ffi::PyObject, arg: $crate::ffi::Py_ssize_t, @@ -183,7 +196,7 @@ macro_rules! py_ssizearg_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - let result = slf.$f(arg.into()).into(); + let result = $call!(slf, $f ; arg.into()); $crate::callback::cb_convert($conv, py, result) } Some(wrap::<$class>) @@ -219,13 +232,7 @@ macro_rules! py_ternary_func { let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1); let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2); - let result = match arg1.extract() { - Ok(arg1) => match arg2.extract() { - Ok(arg2) => slf.$f(arg1, arg2).into(), - Err(e) => Err(e.into()), - }, - Err(e) => Err(e.into()), - }; + let result = call_ref!(slf, $f, arg1, arg2); $crate::callback::cb_convert($conv, py, result) } @@ -286,18 +293,10 @@ macro_rules! py_ternary_self_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf1 = py.from_borrowed_ptr::<$crate::PyCell>(slf); + let slf_cell = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1); let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2); - - let result = match arg1.extract() { - Ok(arg1) => match arg2.extract() { - Ok(arg2) => slf1.$f(arg1, arg2).into(), - Err(e) => Err(e.into()), - }, - Err(e) => Err(e.into()), - }; - + let result = call_refmut!(slf_cell, $f, arg1, arg2); match result { Ok(_) => slf, Err(e) => { @@ -337,13 +336,7 @@ macro_rules! py_func_set { } else { let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); let value = py.from_borrowed_ptr::<$crate::types::PyAny>(value); - match name.extract() { - Ok(name) => match value.extract() { - Ok(value) => slf.$fn_set(name, value).into(), - Err(e) => Err(e.into()), - }, - Err(e) => Err(e.into()), - } + call_refmut!(slf, $fn_set, name, value) }; match result { Ok(_) => 0, @@ -378,10 +371,7 @@ macro_rules! py_func_del { let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); - match name.extract() { - Ok(name) => slf.$fn_del(name).into(), - Err(e) => Err(e.into()), - } + call_refmut!(slf, $fn_del, name) } else { Err(PyErr::new::( "Subscript assignment not supported", @@ -419,19 +409,10 @@ macro_rules! py_func_set_del { let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); let result = if value.is_null() { - match name.extract() { - Ok(name) => slf.$fn_del(name).into(), - Err(e) => Err(e.into()), - } + call_refmut!(slf, $fn_del, name) } else { let value = py.from_borrowed_ptr::<$crate::types::PyAny>(value); - match name.extract() { - Ok(name) => match value.extract() { - Ok(value) => slf.$fn_set(name, value).into(), - Err(e) => Err(e.into()), - }, - Err(e) => Err(e.into()), - } + call_refmut!(slf, $fn_set, name, value) }; match result { Ok(_) => 0, @@ -444,3 +425,34 @@ macro_rules! py_func_set_del { Some(wrap::<$generic>) }}; } + +// Utitlities for extract arguments + call method for PyCell +macro_rules! call_ref { + ($slf: expr, $fn: ident $(; $args: expr)*) => { + match $slf.try_borrow_unguarded() { + Ok(slf) => slf.$fn($($args,)*).into(), + Err(e) => Err(e.into()), + } + }; + ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { + match $raw_arg.extract() { + Ok(arg) => call_ref!($slf, $fn $(,$raw_args)* ;arg $(;$args)*), + Err(e) => Err(e.into()), + } + }; +} + +macro_rules! call_refmut { + ($slf: expr, $fn: ident $(; $args: expr)*) => { + match $slf.try_borrow_mut_unguarded() { + Ok(slf) => slf.$fn($($args,)*).into(), + Err(e) => Err(e.into()), + } + }; + ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { + match $raw_arg.extract() { + Ok(arg) => call_refmut!($slf, $fn $(,$raw_args)* ;arg $(;$args)*), + Err(e) => Err(e.into()), + } + }; +} diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs index 8c1e04e8ca8..90a42d83b19 100644 --- a/src/class/pyasync.rs +++ b/src/class/pyasync.rs @@ -242,7 +242,9 @@ mod anext { PyAsyncAnextProtocol, T::__anext__, Option, - IterANextResultConverter + IterANextResultConverter, + *mut crate::ffi::PyObject, + call_refmut ) } } diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 146ed02dd51..643b4d21430 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -273,7 +273,10 @@ mod sq_ass_item_impl { } else { let value = py.from_borrowed_ptr::(value); match value.extract() { - Ok(value) => slf.__setitem__(key.into(), value).into(), + Ok(value) => match slf.try_borrow_mut_unguarded() { + Ok(slf_) => slf_.__setitem__(key.into(), value).into(), + Err(e) => e.into(), + }, Err(e) => Err(e), } }; @@ -321,7 +324,7 @@ mod sq_ass_item_impl { let slf = py.from_borrowed_ptr::>(slf); let result = if value.is_null() { - slf.__delitem__(key.into()).into() + slf.borrow_mut().__delitem__(key.into()).into() } else { Err(PyErr::new::(format!( "Item assignment not supported by {:?}", @@ -372,11 +375,14 @@ mod sq_ass_item_impl { let slf = py.from_borrowed_ptr::>(slf); let result = if value.is_null() { - slf.__delitem__(key.into()).into() + call_refmut!(slf, __delitem__; key.into()) } else { let value = py.from_borrowed_ptr::(value); match value.extract() { - Ok(value) => slf.__setitem__(key.into(), value).into(), + Ok(value) => match slf.try_borrow_mut_unguarded() { + Ok(slf_) => slf_.__setitem__(key.into(), value).into(), + Err(e) => e.into(), + }, Err(e) => Err(e), } }; @@ -497,7 +503,9 @@ where PySequenceInplaceConcatProtocol, T::__inplace_concat__, T, - PyObjectCallbackConverter + PyObjectCallbackConverter, + *mut crate::ffi::PyObject, + call_refmut ) } } @@ -524,7 +532,8 @@ where PySequenceInplaceRepeatProtocol, T::__inplace_repeat__, T, - PyObjectCallbackConverter + PyObjectCallbackConverter, + call_refmut ) } } diff --git a/src/derive_utils.rs b/src/derive_utils.rs index fc758ffcd7d..1dd35dafec2 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -100,7 +100,8 @@ pub fn parse_fn_args<'p>( // Adjust the remaining args let args = if accept_args { let py = args.py(); - args.slice(used_args as isize, nargs as isize).as_ref(py) + // args.slice(used_args as isize, nargs as isize).as_ref(py) + unimplemented!() } else { args }; diff --git a/src/err.rs b/src/err.rs index 337d9a1e718..44ecb4ddac9 100644 --- a/src/err.rs +++ b/src/err.rs @@ -59,6 +59,9 @@ pub type PyResult = Result; /// Marker type that indicates an error while downcasting pub struct PyDowncastError; +/// Marker type for `PyCell`. +pub struct PyBorrowError; + /// Helper conversion trait that allows to use custom arguments for exception constructor. pub trait PyErrArguments { /// Arguments for exception @@ -401,7 +404,7 @@ impl<'a> IntoPy for &'a PyErr { } } -/// Converts `PyDowncastError` to Python `TypeError`. +/// Convert `PyDowncastError` to Python `TypeError`. impl std::convert::From for PyErr { fn from(_err: PyDowncastError) -> PyErr { exceptions::TypeError.into() diff --git a/src/internal_tricks.rs b/src/internal_tricks.rs index 1d6a5eb9f27..0dae9fb838b 100644 --- a/src/internal_tricks.rs +++ b/src/internal_tricks.rs @@ -23,3 +23,10 @@ macro_rules! private_impl { } } } + +macro_rules! pyo3_exception { + ($name: ident, $base: ty) => { + $crate::impl_exception_boilerplate!($name); + $crate::create_exception_type_object!(pyo3_runtime, $name, $base); + }; +} diff --git a/src/lib.rs b/src/lib.rs index 7e6c017f973..df4acc0c73d 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -165,7 +165,6 @@ pub mod ffi; pub mod freelist; mod gil; mod instance; -mod pyref; #[macro_use] mod internal_tricks; pub mod marshal; diff --git a/src/pycell.rs b/src/pycell.rs index 205912a2e85..157dc6ef72f 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -6,10 +6,44 @@ use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout}; use crate::types::PyAny; use crate::{ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python}; +use std::cell::{Cell, UnsafeCell}; +use std::fmt; use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; -/// `PyCell` represents the concrete layout of `T: PyClass` when it is converted +/// Inner type of `PyCell` without dict slots and reference counter. +#[doc(hidden)] +#[repr(C)] +pub struct PyCellBase { + ob_base: ::ConcreteLayout, + value: ManuallyDrop>, +} + +impl PyCellBase { + fn get(&self) -> &T { + unsafe { &*self.value.get() } + } + fn get_mut(&mut self) -> &mut T { + unsafe { &mut *self.value.get() } + } +} + +impl PyObjectLayout for PyCellBase { + const IS_NATIVE_TYPE: bool = false; + fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { + Some(&mut self.ob_base) + } + unsafe fn py_init(&mut self, value: T) { + self.value = ManuallyDrop::new(UnsafeCell::new(value)); + } + unsafe fn py_drop(&mut self, py: Python) { + ManuallyDrop::drop(&mut self.value); + self.ob_base.py_drop(py); + } +} + +/// `Pycell` represents the concrete layout of `T: PyClass` when it is converted /// to a Python class. /// /// You can use it to test your `#[pyclass]` correctly works. @@ -35,7 +69,8 @@ use std::ptr::NonNull; #[repr(C)] pub struct PyCell { ob_base: ::ConcreteLayout, - pyclass: ManuallyDrop, + value: ManuallyDrop>, + borrow_flag: BorrowFlag, dict: T::Dict, weakref: T::WeakRef, } @@ -54,14 +89,40 @@ impl PyCell { } } - /// Get the reference of base object. - pub fn get_super(&self) -> &::ConcreteLayout { - &self.ob_base + pub fn borrow(&self) -> PyRef<'_, T> { + unsafe { + unimplemented!() + // if self.borrow.get() == usize::max_value() { + // borrow_fail(); + // } + // self.borrow.set(self.borrow.get() + 1); + // Ref { + // value: &*self.value.get(), + // borrow: &self.borrow, + // } + } } - /// Get the mutable reference of base object. - pub fn get_super_mut(&mut self) -> &mut ::ConcreteLayout { - &mut self.ob_base + pub fn borrow_mut(&self) -> PyRefMut<'_, T> { + unsafe { + unimplemented!() + // if self.borrow.get() != 0 { + // borrow_fail(); + // } + // self.borrow.set(usize::max_value()); + // RefMut { + // value: &mut *self.value.get(), + // borrow: &self.borrow, + // } + } + } + + pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { + unimplemented!() + } + + pub unsafe fn try_borrow_mut_unguarded(&self) -> Result<&mut T, PyBorrowMutError> { + unimplemented!() } pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> @@ -74,6 +135,7 @@ impl PyCell { return Err(PyErr::fetch(py)); } let self_ = base as *mut Self; + (*self_).borrow_flag = BorrowFlag::UNUSED; (*self_).dict = T::Dict::new(); (*self_).weakref = T::WeakRef::new(); Ok(self_) @@ -85,15 +147,15 @@ impl PyObjectLayout for PyCell { fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { Some(&mut self.ob_base) } + unsafe fn py_init(&mut self, value: T) { + self.value = ManuallyDrop::new(UnsafeCell::new(value)); + } unsafe fn py_drop(&mut self, py: Python) { - ManuallyDrop::drop(&mut self.pyclass); + ManuallyDrop::drop(&mut self.value); self.dict.clear_dict(py); self.weakref.clear_weakrefs(self.as_ptr(), py); self.ob_base.py_drop(py); } - unsafe fn py_init(&mut self, value: T) { - self.pyclass = ManuallyDrop::new(value); - } } unsafe impl<'py, T: 'py + PyClass> PyDowncastImpl<'py> for PyCell { @@ -111,19 +173,6 @@ impl AsPyPointer for PyCell { } } -impl std::ops::Deref for PyCell { - type Target = T; - fn deref(&self) -> &T { - self.pyclass.deref() - } -} - -impl std::ops::DerefMut for PyCell { - fn deref_mut(&mut self) -> &mut T { - self.pyclass.deref_mut() - } -} - impl ToPyObject for &PyCell { fn to_object(&self, py: Python<'_>) -> PyObject { unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } @@ -150,3 +199,102 @@ where NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell)) } } + +pub struct PyRef<'p, T: PyClass> { + value: &'p PyCellBase, + flag: &'p Cell, +} + +impl<'p, T: PyClass> PyRef<'p, T> { + fn get_super(&'p self) -> &'p T::BaseType { + unimplemented!() + } +} + +impl<'p, T: PyClass> Deref for PyRef<'p, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + self.value.get() + } +} + +impl<'p, T: PyClass> Drop for PyRef<'p, T> { + fn drop(&mut self) { + self.flag.set(self.flag.get()); + } +} + +pub struct PyRefMut<'p, T: PyClass> { + value: &'p mut PyCellBase, + flag: &'p Cell, +} + +impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + self.value.get() + } +} + +impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { + #[inline] + fn deref_mut(&mut self) -> &mut T { + self.value.get_mut() + } +} + +impl<'p, T: PyClass> Drop for PyRefMut<'p, T> { + fn drop(&mut self) { + self.flag.set(BorrowFlag::UNUSED); + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +struct BorrowFlag(usize); + +impl BorrowFlag { + const UNUSED: BorrowFlag = BorrowFlag(0); + const HAS_MUTABLE_BORROW: BorrowFlag = BorrowFlag(usize::max_value()); + fn decrement(self) -> Self { + Self(self.0 - 1) + } +} + +pub struct PyBorrowError { + _private: (), +} + +impl fmt::Debug for PyBorrowError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PyBorrowError").finish() + } +} + +impl fmt::Display for PyBorrowError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt("Already mutably borrowed", f) + } +} + +pub struct PyBorrowMutError { + _private: (), +} + +impl fmt::Debug for PyBorrowMutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PyBorrowMutError").finish() + } +} + +impl fmt::Display for PyBorrowMutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt("Already borrowed", f) + } +} + +pyo3_exception!(PyBorrowError, crate::exceptions::Exception); +pyo3_exception!(PyBorrowMutError, crate::exceptions::Exception); diff --git a/src/type_object.rs b/src/type_object.rs index b9ca7bd4545..68a2da19469 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -53,9 +53,11 @@ pub mod type_flags { } /// Reference abstraction for `PyClass` and `PyNativeType`. -// NOTE: This trait is separated from PyTypeInfo since make it PyTypeInfo<`py> is confusing... -// When GAT and type Ref<'py> becomes stable, let's merge this trait to -// PyConcreteLayout or Py +// DEVELOPPER NOTE(kngwyu): +// `&PyCell` is a pointer but `&PyAny` is a pointer of a pointer, so we need +// two different abstraction for them. +// This mismatch eventually should be fixed with https://github.com/PyO3/pyo3/issues/679, +// but now it's not the time. pub unsafe trait PyDowncastImpl<'py> { unsafe fn unchecked_downcast(obj: &PyAny) -> &'py Self; private_decl! {} From 4a5f219cc45d62b6bf652e5e6e53c20380ac0bf9 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Mon, 10 Feb 2020 17:30:48 +0900 Subject: [PATCH 04/27] New AsPyRef --- src/conversion.rs | 8 ++- src/instance.rs | 10 ++-- src/object.rs | 2 +- src/pycell.rs | 130 +++++++++++++++++++++++++++++---------------- src/type_object.rs | 8 ++- src/types/any.rs | 9 +++- src/types/mod.rs | 18 ++++++- 7 files changed, 125 insertions(+), 60 deletions(-) diff --git a/src/conversion.rs b/src/conversion.rs index 99276714251..269a06d4925 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -252,8 +252,12 @@ pub mod extract_impl { fn extract(source: &'a PyAny) -> PyResult; } - pub struct Cloned; - pub struct Reference; + pub struct Cloned { + _private: (), + } + pub struct Reference { + _private: (), + } impl<'a, T: 'a> ExtractImpl<'a, T> for Cloned where diff --git a/src/instance.rs b/src/instance.rs index 5df15e8e7d3..98d592a0242 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -3,7 +3,7 @@ use crate::err::{PyErr, PyResult}; use crate::gil; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; -use crate::type_object::{PyObjectLayout, PyTypeInfo}; +use crate::type_object::PyTypeInfo; use crate::types::PyAny; use crate::{ ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyCell, PyClass, PyClassInitializer, @@ -121,13 +121,13 @@ impl Py { pub trait AsPyRef: Sized { /// Return reference to object. - fn as_ref(&self, py: Python) -> &T; + fn as_ref(&self, py: Python) -> &T::Reference; } impl AsPyRef for Py { - fn as_ref(&self, _py: Python) -> &T { - let any = self as *const Py as *const PyAny; - unimplemented!() + fn as_ref(&self, _py: Python) -> &T::Reference { + let ptr = self as *const Py as *const T::Reference; + unsafe { &*ptr } } } diff --git a/src/object.rs b/src/object.rs index 44193fa0a39..bf02992e23e 100644 --- a/src/object.rs +++ b/src/object.rs @@ -158,7 +158,7 @@ impl PyObject { /// Extracts some type from the Python object. /// This is a wrapper function around `FromPyObject::extract()`. - pub fn extract<'p, D>(&'p self, py: Python) -> PyResult + pub fn extract<'p, D>(&'p self, py: Python<'p>) -> PyResult where D: FromPyObject<'p>, { diff --git a/src/pycell.rs b/src/pycell.rs index 157dc6ef72f..b40fbe93801 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -13,6 +13,9 @@ use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; /// Inner type of `PyCell` without dict slots and reference counter. +/// This struct has two usages: +/// 1. As an inner type of `PyRef` and `PyRefMut`. +/// 2. As a base class when `#[pyclass(Base)]` is specified. #[doc(hidden)] #[repr(C)] pub struct PyCellBase { @@ -20,20 +23,23 @@ pub struct PyCellBase { value: ManuallyDrop>, } -impl PyCellBase { - fn get(&self) -> &T { - unsafe { &*self.value.get() } - } - fn get_mut(&mut self) -> &mut T { - unsafe { &mut *self.value.get() } +impl AsPyPointer for PyCellBase { + fn as_ptr(&self) -> *mut ffi::PyObject { + (self as *const _) as *mut _ } } -impl PyObjectLayout for PyCellBase { +unsafe impl PyObjectLayout for PyCellBase { const IS_NATIVE_TYPE: bool = false; fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { Some(&mut self.ob_base) } + unsafe fn unchecked_ref(&self) -> &T { + &*self.value.get() + } + unsafe fn unchecked_refmut(&mut self) -> &mut T { + &mut *self.value.get() + } unsafe fn py_init(&mut self, value: T) { self.value = ManuallyDrop::new(UnsafeCell::new(value)); } @@ -43,7 +49,7 @@ impl PyObjectLayout for PyCellBase { } } -/// `Pycell` represents the concrete layout of `T: PyClass` when it is converted +/// `PyCell` represents the concrete layout of `T: PyClass` when it is converted /// to a Python class. /// /// You can use it to test your `#[pyclass]` correctly works. @@ -68,9 +74,8 @@ impl PyObjectLayout for PyCellBase { /// ``` #[repr(C)] pub struct PyCell { - ob_base: ::ConcreteLayout, - value: ManuallyDrop>, - borrow_flag: BorrowFlag, + base: PyCellBase, + borrow_flag: Cell, dict: T::Dict, weakref: T::WeakRef, } @@ -90,39 +95,52 @@ impl PyCell { } pub fn borrow(&self) -> PyRef<'_, T> { - unsafe { - unimplemented!() - // if self.borrow.get() == usize::max_value() { - // borrow_fail(); - // } - // self.borrow.set(self.borrow.get() + 1); - // Ref { - // value: &*self.value.get(), - // borrow: &self.borrow, - // } - } + self.try_borrow().expect("Already mutably borrowed") } pub fn borrow_mut(&self) -> PyRefMut<'_, T> { - unsafe { - unimplemented!() - // if self.borrow.get() != 0 { - // borrow_fail(); - // } - // self.borrow.set(usize::max_value()); - // RefMut { - // value: &mut *self.value.get(), - // borrow: &self.borrow, - // } + self.try_borrow_mut().expect("Already borrowed") + } + + pub fn try_borrow(&self) -> Result, PyBorrowError> { + let flag = self.borrow_flag.get(); + if flag != BorrowFlag::HAS_MUTABLE_BORROW { + Err(PyBorrowError { _private: () }) + } else { + self.borrow_flag.set(flag.increment()); + Ok(PyRef { + value: &self.base, + flag: &self.borrow_flag, + }) + } + } + + pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> { + if self.borrow_flag.get() != BorrowFlag::UNUSED { + Err(PyBorrowMutError { _private: () }) + } else { + self.borrow_flag.set(BorrowFlag::HAS_MUTABLE_BORROW); + Ok(PyRefMut { + value: unsafe { &mut *(self.base.as_ptr() as *mut _) }, + flag: &self.borrow_flag, + }) } } pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { - unimplemented!() + if self.borrow_flag.get() != BorrowFlag::HAS_MUTABLE_BORROW { + Err(PyBorrowError { _private: () }) + } else { + Ok(&*self.base.value.get()) + } } pub unsafe fn try_borrow_mut_unguarded(&self) -> Result<&mut T, PyBorrowMutError> { - unimplemented!() + if self.borrow_flag.get() != BorrowFlag::UNUSED { + Err(PyBorrowMutError { _private: () }) + } else { + Ok(&mut *self.base.value.get()) + } } pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> @@ -135,26 +153,32 @@ impl PyCell { return Err(PyErr::fetch(py)); } let self_ = base as *mut Self; - (*self_).borrow_flag = BorrowFlag::UNUSED; + (*self_).borrow_flag = Cell::new(BorrowFlag::UNUSED); (*self_).dict = T::Dict::new(); (*self_).weakref = T::WeakRef::new(); Ok(self_) } } -impl PyObjectLayout for PyCell { +unsafe impl PyObjectLayout for PyCell { const IS_NATIVE_TYPE: bool = false; fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { - Some(&mut self.ob_base) + Some(&mut self.base.ob_base) + } + unsafe fn unchecked_ref(&self) -> &T { + self.base.unchecked_ref() + } + unsafe fn unchecked_refmut(&mut self) -> &mut T { + self.base.unchecked_refmut() } unsafe fn py_init(&mut self, value: T) { - self.value = ManuallyDrop::new(UnsafeCell::new(value)); + self.base.value = ManuallyDrop::new(UnsafeCell::new(value)); } unsafe fn py_drop(&mut self, py: Python) { - ManuallyDrop::drop(&mut self.value); + ManuallyDrop::drop(&mut self.base.value); self.dict.clear_dict(py); self.weakref.clear_weakrefs(self.as_ptr(), py); - self.ob_base.py_drop(py); + self.base.ob_base.py_drop(py); } } @@ -206,8 +230,8 @@ pub struct PyRef<'p, T: PyClass> { } impl<'p, T: PyClass> PyRef<'p, T> { - fn get_super(&'p self) -> &'p T::BaseType { - unimplemented!() + pub fn get_super(&'p self) -> &'p T::BaseType { + unsafe { self.value.ob_base.unchecked_ref() } } } @@ -216,13 +240,13 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> { #[inline] fn deref(&self) -> &T { - self.value.get() + unsafe { self.value.unchecked_ref() } } } impl<'p, T: PyClass> Drop for PyRef<'p, T> { fn drop(&mut self) { - self.flag.set(self.flag.get()); + self.flag.set(self.flag.get().decrement()); } } @@ -231,19 +255,28 @@ pub struct PyRefMut<'p, T: PyClass> { flag: &'p Cell, } +impl<'p, T: PyClass> PyRefMut<'p, T> { + pub fn get_super(&'p self) -> &'p T::BaseType { + unsafe { self.value.ob_base.unchecked_ref() } + } + pub fn get_super_mut(&'p mut self) -> &'p mut T::BaseType { + unsafe { self.value.ob_base.unchecked_refmut() } + } +} + impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { type Target = T; #[inline] fn deref(&self) -> &T { - self.value.get() + unsafe { self.value.unchecked_ref() } } } impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - self.value.get_mut() + unsafe { self.value.unchecked_refmut() } } } @@ -259,7 +292,10 @@ struct BorrowFlag(usize); impl BorrowFlag { const UNUSED: BorrowFlag = BorrowFlag(0); const HAS_MUTABLE_BORROW: BorrowFlag = BorrowFlag(usize::max_value()); - fn decrement(self) -> Self { + const fn increment(self) -> Self { + Self(self.0 + 1) + } + const fn decrement(self) -> Self { Self(self.0 - 1) } } diff --git a/src/type_object.rs b/src/type_object.rs index 68a2da19469..eb9e5299407 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -15,15 +15,16 @@ use std::sync::atomic::{AtomicBool, Ordering}; /// is of `PyAny`. /// /// This trait is intended to be used internally. -pub trait PyObjectLayout { +pub unsafe trait PyObjectLayout { const IS_NATIVE_TYPE: bool = true; fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { None } - unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} + unsafe fn unchecked_ref(&self) -> &T; + unsafe fn unchecked_refmut(&mut self) -> &mut T; } /// `T: PyObjectSizedLayout` represents `T` is not a instance of @@ -101,6 +102,9 @@ pub unsafe trait PyTypeInfo: Sized { /// Layout type ConcreteLayout: PyObjectLayout; + /// This type is an abstraction layer for `AsPyRef`. + type Reference; + /// Initializer for layout type Initializer: PyObjectInit; diff --git a/src/types/any.rs b/src/types/any.rs index f0d17649461..a234e41bbb3 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -23,7 +23,14 @@ use crate::{ffi, PyObject}; /// ``` #[repr(transparent)] pub struct PyAny(PyObject, Unsendable); -impl crate::type_object::PyObjectLayout for ffi::PyObject {} +unsafe impl crate::type_object::PyObjectLayout for ffi::PyObject { + unsafe fn unchecked_ref(&self) -> &PyAny { + &*((&self) as *const &Self as *const _) + } + unsafe fn unchecked_refmut(&mut self) -> &mut PyAny { + &mut *((&self) as *const &mut Self as *const _ as *mut _) + } +} impl crate::type_object::PyObjectSizedLayout 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 cde4a8601cc..79880313729 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -56,10 +56,23 @@ macro_rules! pyobject_native_type_named ( }; ); +macro_rules! impl_layout { + ($name: ty, $layout: path) => { + unsafe impl $crate::type_object::PyObjectLayout<$name> for $layout { + unsafe fn unchecked_ref(&self) -> &$name { + &*((&self) as *const &Self as *const _) + } + unsafe fn unchecked_refmut(&mut self) -> &mut $name { + &mut *((&self) as *const &mut 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 $crate::type_object::PyObjectLayout<$name> for $layout {} + impl_layout!($name, $layout); impl $crate::type_object::PyObjectSizedLayout<$name> for $layout {} pyobject_native_type_named!($name $(,$type_param)*); pyobject_native_type_convert!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*); @@ -81,7 +94,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 $crate::type_object::PyObjectLayout<$name> for $crate::ffi::PyObject {} + impl_layout!($name, $crate::ffi::PyObject); pyobject_native_type_named!($name $(,$type_param)*); pyobject_native_type_convert!($name, $crate::ffi::PyObject, $typeobject, $module, $checkfunction $(,$type_param)*); @@ -118,6 +131,7 @@ macro_rules! pyobject_native_type_convert( type Type = (); type BaseType = $crate::types::PyAny; type ConcreteLayout = $layout; + type Reference = $name; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer; const NAME: &'static str = stringify!($name); From da4ed398bb7ee1861f7d79719c967d8f080591a4 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 15 Feb 2020 13:42:25 +0900 Subject: [PATCH 05/27] Fix PyCell to share BorrowFlag with parents --- pyo3-derive-backend/src/method.rs | 36 +++++- pyo3-derive-backend/src/module.rs | 46 ++++---- pyo3-derive-backend/src/pyclass.rs | 47 ++++---- pyo3-derive-backend/src/pymethod.rs | 33 ++++-- src/class/iter.rs | 10 +- src/class/macros.rs | 7 +- src/conversion.rs | 165 +++++++++++++++------------ src/derive_utils.rs | 43 ++++++- src/err.rs | 3 - src/freelist.rs | 4 +- src/instance.rs | 3 +- src/lib.rs | 6 +- src/prelude.rs | 3 +- src/pycell.rs | 167 +++++++++++++++------------- src/pyclass.rs | 19 ++-- src/pyclass_init.rs | 14 ++- src/type_object.rs | 17 +-- src/types/any.rs | 4 +- src/types/mod.rs | 19 +++- tests/test_class_new.rs | 14 ++- tests/test_dunder.rs | 4 +- tests/test_gc.rs | 22 ++-- tests/test_sequence.rs | 2 +- 23 files changed, 413 insertions(+), 275 deletions(-) mode change 100755 => 100644 tests/test_dunder.rs diff --git a/pyo3-derive-backend/src/method.rs b/pyo3-derive-backend/src/method.rs index ae0186caae2..a4505ac4b79 100644 --- a/pyo3-derive-backend/src/method.rs +++ b/pyo3-derive-backend/src/method.rs @@ -35,6 +35,7 @@ pub enum FnType { #[derive(Clone, PartialEq, Debug)] pub struct FnSpec<'a> { pub tp: FnType, + pub self_: Option, // Rust function name pub name: &'a syn::Ident, // Wrapped python name. This should not have any leading r#. @@ -54,6 +55,28 @@ pub fn get_return_info(output: &syn::ReturnType) -> syn::Type { } impl<'a> FnSpec<'a> { + /// Generate the code for borrowing self + pub(crate) fn borrow_self(&self) -> TokenStream { + let is_mut = self + .self_ + .expect("impl_borrow_self is called for non-self fn"); + if is_mut { + quote! { + match _slf.try_borrow() { + Ok(ref_) => ref_, + Err(e) => return e.into::.restore_and_null(_py), + } + } + } else { + quote! { + match _slf.try_borrow_mut() { + Ok(ref_) => ref_, + Err(e) => return e.into::.restore_and_null(_py), + } + } + } + } + /// Parser function signature and function attributes pub fn parse( sig: &'a syn::Signature, @@ -67,19 +90,19 @@ impl<'a> FnSpec<'a> { mut python_name, } = parse_method_attributes(meth_attrs, allow_custom_name)?; - let mut has_self = false; + let mut self_ = None; let mut arguments = Vec::new(); for input in sig.inputs.iter() { match input { - syn::FnArg::Receiver(_) => { - has_self = true; + syn::FnArg::Receiver(recv) => { + self_ = Some(recv.mutability.is_some()); } syn::FnArg::Typed(syn::PatType { ref pat, ref ty, .. }) => { // skip first argument (cls) - if fn_type == FnType::FnClass && !has_self { - has_self = true; + if fn_type == FnType::FnClass && !self_.is_none() { + self_ = Some(false); continue; } @@ -114,7 +137,7 @@ impl<'a> FnSpec<'a> { let ty = get_return_info(&sig.output); - if fn_type == FnType::Fn && !has_self { + if fn_type == FnType::Fn && self_.is_none() { if arguments.is_empty() { return Err(syn::Error::new_spanned( name, @@ -174,6 +197,7 @@ impl<'a> FnSpec<'a> { Ok(FnSpec { tp: fn_type, + self_, name, python_name, attrs: fn_attrs, diff --git a/pyo3-derive-backend/src/module.rs b/pyo3-derive-backend/src/module.rs index badaf37f0a3..05046fdd0d3 100644 --- a/pyo3-derive-backend/src/module.rs +++ b/pyo3-derive-backend/src/module.rs @@ -54,27 +54,22 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) { } /// Transforms a rust fn arg parsed with syn into a method::FnArg -fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option> { - match input { - syn::FnArg::Receiver(_) => None, - syn::FnArg::Typed(ref cap) => { - let (mutability, by_ref, ident) = match *cap.pat { - syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident), - _ => panic!("unsupported argument: {:?}", cap.pat), - }; +fn wrap_fn_argument<'a>(cap: &'a syn::PatType, name: &'a Ident) -> method::FnArg<'a> { + let (mutability, by_ref, ident) = match *cap.pat { + syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident), + _ => panic!("unsupported argument: {:?}", cap.pat), + }; - let py = crate::utils::if_type_is_python(&cap.ty); - let opt = method::check_arg_ty_and_optional(&name, &cap.ty); - Some(method::FnArg { - name: ident, - mutability, - by_ref, - ty: &cap.ty, - optional: opt, - py, - reference: method::is_ref(&name, &cap.ty), - }) - } + let py = crate::utils::if_type_is_python(&cap.ty); + let opt = method::check_arg_ty_and_optional(&name, &cap.ty); + method::FnArg { + name: ident, + mutability, + by_ref, + ty: &cap.ty, + optional: opt, + py, + reference: method::is_ref(&name, &cap.ty), } } @@ -138,10 +133,16 @@ pub fn add_fn_to_module( pyfn_attrs: Vec, ) -> TokenStream { let mut arguments = Vec::new(); + let mut self_ = None; for input in func.sig.inputs.iter() { - if let Some(fn_arg) = wrap_fn_argument(input, &func.sig.ident) { - arguments.push(fn_arg); + match input { + syn::FnArg::Receiver(recv) => { + self_ = Some(recv.mutability.is_some()); + } + syn::FnArg::Typed(ref cap) => { + arguments.push(wrap_fn_argument(cap, &func.sig.ident)); + } } } @@ -160,6 +161,7 @@ pub fn add_fn_to_module( let spec = method::FnSpec { tp: method::FnType::Fn, + self_, name: &function_wrapper_ident, python_name, attrs: pyfn_attrs, diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index bf0f1af3c6e..fc7eb11e881 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -315,14 +315,18 @@ fn impl_class( } let weakref = if has_weakref { - quote! { type WeakRef = pyo3::pyclass_slots::PyClassWeakRefSlot; } + quote! { pyo3::pyclass_slots::PyClassWeakRefSlot } + } else if attr.has_extends { + quote! { ::WeakRef } } else { - quote! { type WeakRef = pyo3::pyclass_slots::PyClassDummySlot; } + quote! { pyo3::pyclass_slots::PyClassDummySlot } }; let dict = if has_dict { - quote! { type Dict = pyo3::pyclass_slots::PyClassDictSlot; } + quote! { pyo3::pyclass_slots::PyClassDictSlot } + } else if attr.has_extends { + quote! { ::Dict } } else { - quote! { type Dict = pyo3::pyclass_slots::PyClassDummySlot; } + quote! { pyo3::pyclass_slots::PyClassDummySlot } }; let module = if let Some(m) = &attr.module { quote! { Some(#m) } @@ -355,6 +359,16 @@ fn impl_class( } else { quote! { 0 } }; + let base_layout = if attr.has_extends { + quote! { ::Layout } + } else { + quote! { pyo3::pycell::PyCellBase } + }; + let base_nativetype = if attr.has_extends { + quote! { ::BaseNativeType } + } else { + quote! { pyo3::types::PyAny } + }; // If #cls is not extended type, we allow Self->PyObject conversion let into_pyobject = if !attr.has_extends { @@ -373,8 +387,10 @@ fn impl_class( unsafe impl pyo3::type_object::PyTypeInfo for #cls { type Type = #cls; type BaseType = #base; - type ConcreteLayout = pyo3::pyclass::PyCell; + type Layout = pyo3::pycell::PyCell; + type BaseLayout = #base_layout; type Initializer = pyo3::pyclass_init::PyClassInitializer; + type Reference = pyo3::pycell::PyCell; const NAME: &'static str = #cls_name; const MODULE: Option<&'static str> = #module; @@ -390,20 +406,9 @@ fn impl_class( } impl pyo3::PyClass for #cls { - #dict - #weakref - } - - impl pyo3::conversion::FromPyObjectImpl for #cls { - type Impl = pyo3::conversion::extract_impl::Cloned; - } - - impl pyo3::conversion::FromPyObjectImpl for &'_ #cls { - type Impl = pyo3::conversion::extract_impl::Reference; - } - - impl pyo3::conversion::FromPyObjectImpl for &'_ mut #cls { - type Impl = pyo3::conversion::extract_impl::MutReference; + type Dict = #dict; + type WeakRef = #weakref; + type BaseNativeType = #base_nativetype; } #into_pyobject @@ -472,6 +477,8 @@ fn impl_descriptors( FnType::Getter => { let spec = FnSpec { tp: FnType::Getter, + // Assume that the getter has &self receiver + self_: Some(false), name: &name, python_name: name.unraw(), attrs: Vec::new(), @@ -488,6 +495,8 @@ fn impl_descriptors( ); let spec = FnSpec { tp: FnType::Setter, + // Assume that the setter has &mut self receiver + self_: Some(true), name: &setter_name, python_name: name.unraw(), attrs: Vec::new(), diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 7b737057fb6..7cae4497c3b 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -45,7 +45,11 @@ fn check_generic(sig: &syn::Signature) -> syn::Result<()> { /// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords) pub fn impl_wrap(cls: &syn::Type, spec: &FnSpec<'_>, noargs: bool) -> TokenStream { let body = impl_call(cls, &spec); - let slf = impl_self("e! { &mut #cls }); + let borrow_self = spec.borrow_self(); + let slf = quote! { + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; + }; impl_wrap_common(cls, spec, noargs, slf, body) } @@ -60,7 +64,13 @@ pub fn impl_wrap_pyslf( let body = quote! { #cls::#name(_slf, #(#names),*) }; - let slf = impl_self(self_ty); + let slf = quote! { + let _cell = _py.from_borrowed_ptr::>(_slf); + let _slf: #self_ty = match pyo3::derive_utils::PySelf::from_cell(_cell) { + Ok(_slf) => _slf, + Err(e) => return e.restore_and_null(py), + }; + }; impl_wrap_common(cls, spec, noargs, slf, body) } @@ -123,6 +133,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { let python_name = &spec.python_name; let cb = impl_call(cls, &spec); let body = impl_arg_params(&spec, cb); + let borrow_self = spec.borrow_self(); quote! { #[allow(unused_mut)] @@ -134,7 +145,8 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()"); let _py = pyo3::Python::assume_gil_acquired(); let _pool = pyo3::GILPool::new(_py); - let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; let _args = _py.from_borrowed_ptr::(_args); let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); @@ -266,6 +278,8 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result *mut pyo3::ffi::PyObject @@ -274,7 +288,8 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result(_slf); + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; let result = pyo3::derive_utils::IntoPyResult::into_py_result(#fncall); @@ -312,6 +327,7 @@ pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Resul } }; + let borrow_self = spec.borrow_self(); Ok(quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( @@ -321,7 +337,8 @@ pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Resul const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()"); let _py = pyo3::Python::assume_gil_acquired(); let _pool = pyo3::GILPool::new(_py); - let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf); + let _slf = _py.from_borrowed_ptr::>(_slf); + let _slf = #borrow_self; let _value = _py.from_borrowed_ptr(_value); let _result = match <#val_ty as pyo3::FromPyObject>::extract(_value) { @@ -354,12 +371,6 @@ fn impl_call(_cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { quote! { _slf.#fname(#(#names),*) } } -fn impl_self(self_ty: &T) -> TokenStream { - quote! { - let _slf: #self_ty = pyo3::FromPyPointer::from_borrowed_ptr(_py, _slf); - } -} - /// Converts a bool to "true" or "false" fn bool_to_ident(condition: bool) -> syn::Ident { if condition { diff --git a/src/class/iter.rs b/src/class/iter.rs index aca9c0304dc..d347b65df85 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -4,7 +4,7 @@ use crate::callback::{CallbackConverter, PyObjectCallbackConverter}; use crate::err::PyResult; -use crate::{ffi, IntoPy, IntoPyPointer, PyCell, PyClass, PyObject, Python}; +use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, PyRefMut, Python}; use std::ptr; /// Python Iterator Interface. @@ -13,14 +13,14 @@ use std::ptr; /// `https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter` #[allow(unused_variables)] pub trait PyIterProtocol<'p>: PyClass { - fn __iter__(slf: &mut PyCell) -> Self::Result + fn __iter__(slf: PyRefMut) -> Self::Result where Self: PyIterIterProtocol<'p>, { unimplemented!() } - fn __next__(slf: &mut PyCell) -> Self::Result + fn __next__(slf: PyRefMut) -> Self::Result where Self: PyIterNextProtocol<'p>, { @@ -77,7 +77,7 @@ where { #[inline] fn tp_iter() -> Option { - py_unary_pyref_func!( + py_unary_refmut_func!( PyIterIterProtocol, T::__iter__, T::Success, @@ -105,7 +105,7 @@ where { #[inline] fn tp_iternext() -> Option { - py_unary_pyref_func!( + py_unary_refmut_func!( PyIterNextProtocol, T::__next__, Option, diff --git a/src/class/macros.rs b/src/class/macros.rs index 642bc9aa828..74f58029952 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -39,17 +39,16 @@ macro_rules! py_unary_func { #[macro_export] #[doc(hidden)] -macro_rules! py_unary_pyref_func { +macro_rules! py_unary_refmut_func { ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject where T: for<'p> $trait<'p>, { - use $crate::PyCell; let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); - let slf: &mut PyCell = &mut *(slf as *mut PyCell); - let res = $class::$f(slf).into(); + let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); + let res = $class::$f(slf.borrow_mut()).into(); $crate::callback::cb_convert($conv, py, res) } Some(wrap::<$class>) diff --git a/src/conversion.rs b/src/conversion.rs index 269a06d4925..b6790d32243 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -6,7 +6,7 @@ use crate::object::PyObject; use crate::type_object::{PyDowncastImpl, PyTypeInfo}; use crate::types::PyAny; use crate::types::PyTuple; -use crate::{ffi, gil, Py, Python}; +use crate::{ffi, gil, Py, PyCell, PyClass, PyNativeType, PyRef, PyRefMut, Python}; use std::ptr::NonNull; /// This trait represents that, **we can do zero-cost conversion from the object to FFI pointer**. @@ -244,74 +244,43 @@ where } } -#[doc(hidden)] -pub mod extract_impl { - use super::*; - - pub trait ExtractImpl<'a, Target>: Sized { - fn extract(source: &'a PyAny) -> PyResult; - } - - pub struct Cloned { - _private: (), - } - pub struct Reference { - _private: (), - } - - impl<'a, T: 'a> ExtractImpl<'a, T> for Cloned - where - T: Clone + PyTryFrom<'a>, - Reference: ExtractImpl<'a, &'a T>, - { - fn extract(source: &'a PyAny) -> PyResult { - Ok(Reference::extract(source)?.clone()) - } +impl<'a, T> FromPyObject<'a> for &'a PyCell +where + T: PyClass, +{ + fn extract(obj: &'a PyAny) -> PyResult { + PyTryFrom::try_from(obj).map_err(Into::into) } +} - impl<'a, T: 'a> ExtractImpl<'a, &'a T> for Reference - where - T: PyTryFrom<'a>, - { - fn extract(source: &'a PyAny) -> PyResult<&'a T> { - Ok(T::try_from(source)?) - } +impl<'a, T> FromPyObject<'a> for T +where + T: PyClass + Clone, +{ + fn extract(obj: &'a PyAny) -> PyResult { + let cell: &PyCell = PyTryFrom::try_from(obj)?; + let ref_ = unsafe { cell.try_borrow_unguarded()? }; + Ok(ref_.clone()) } } -use extract_impl::ExtractImpl; - -/// This is an internal trait for re-using `FromPyObject` implementations for many pyo3 types. -/// -/// Users should implement `FromPyObject` directly instead of via this trait. -pub trait FromPyObjectImpl { - // Implement this trait with to specify the implementor of `extract_impl::ExtractImpl` to use for - // extracting this type from Python objects. - // - // Example valid implementations are `extract_impl::Cloned`, `extract_impl::Reference`, and - // `extract_impl::MutReference`, which are for extracting `T`, `&T` and `&mut T` respectively via - // PyTryFrom. - // - // We deliberately don't require Impl: ExtractImpl here because we allow #[pyclass] - // to specify an Impl which doesn't satisfy the ExtractImpl constraints. - // - // e.g. non-clone #[pyclass] can still have Impl: Cloned. - // - // We catch invalid Impls in the blanket impl for FromPyObject, which only - // complains when .extract() is actually used. - - /// The type which implements `ExtractImpl`. - type Impl; +impl<'a, T> FromPyObject<'a> for PyRef<'a, T> +where + T: PyClass, +{ + fn extract(obj: &'a PyAny) -> PyResult { + let cell: &PyCell = PyTryFrom::try_from(obj)?; + cell.try_borrow().map_err(Into::into) + } } -impl<'a, T> FromPyObject<'a> for T +impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T> where - T: FromPyObjectImpl + 'a, - ::Impl: ExtractImpl<'a, T>, + T: PyClass, { - #[inline] - fn extract(ob: &'a PyAny) -> PyResult { - ::Impl::extract(ob) + fn extract(obj: &'a PyAny) -> PyResult { + let cell: &PyCell = PyTryFrom::try_from(obj)?; + cell.try_borrow_mut().map_err(Into::into) } } @@ -345,19 +314,32 @@ pub trait PyTryFrom<'v>: Sized + PyDowncastImpl<'v> { unsafe fn try_from_unchecked>(value: V) -> &'v Self; } -// /// Trait implemented by Python object types that allow a checked downcast. -// /// This trait is similar to `std::convert::TryInto` -// pub trait PyTryInto<'v, T: PyDowncastImpl<'v>>: Sized { -// /// Cast from PyObject to a concrete Python object type. -// fn try_into(&self) -> Result<&T, PyDowncastError>; +/// Trait implemented by Python object types that allow a checked downcast. +/// This trait is similar to `std::convert::TryInto` +pub trait PyTryInto: Sized { + /// Cast from PyObject to a concrete Python object type. + fn try_into(&self) -> Result<&T, PyDowncastError>; -// /// Cast from PyObject to a concrete Python object type. With exact type check. -// fn try_into_exact(&self) -> Result<&T, PyDowncastError>; -// } + /// Cast from PyObject to a concrete Python object type. With exact type check. + fn try_into_exact(&self) -> Result<&T, PyDowncastError>; +} + +// TryFrom implies TryInto +impl PyTryInto for PyAny +where + U: for<'v> PyTryFrom<'v>, +{ + fn try_into(&self) -> Result<&U, PyDowncastError> { + U::try_from(self) + } + fn try_into_exact(&self) -> Result<&U, PyDowncastError> { + U::try_from_exact(self) + } +} impl<'v, T> PyTryFrom<'v> for T where - T: PyDowncastImpl<'v> + PyTypeInfo, + T: PyDowncastImpl<'v> + PyTypeInfo + PyNativeType, { fn try_from>(value: V) -> Result<&'v Self, PyDowncastError> { let value = value.into(); @@ -387,6 +369,36 @@ where } } +impl<'v, T> PyTryFrom<'v> for PyCell +where + T: 'v + PyClass, +{ + fn try_from>(value: V) -> Result<&'v Self, PyDowncastError> { + let value = value.into(); + unsafe { + if T::is_instance(value) { + Ok(Self::try_from_unchecked(value)) + } else { + Err(PyDowncastError) + } + } + } + fn try_from_exact>(value: V) -> Result<&'v Self, PyDowncastError> { + let value = value.into(); + unsafe { + if T::is_exact_instance(value) { + Ok(Self::try_from_unchecked(value)) + } else { + Err(PyDowncastError) + } + } + } + #[inline] + unsafe fn try_from_unchecked>(value: V) -> &'v Self { + Self::unchecked_downcast(value.into()) + } +} + /// Converts `()` to an empty Python tuple. impl FromPy<()> for Py { fn from_py(_: (), py: Python) -> Py { @@ -449,6 +461,21 @@ where } } +unsafe impl<'p, T> FromPyPointer<'p> for PyCell +where + T: PyClass, +{ + unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { + NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell)) + } + unsafe fn from_borrowed_ptr_or_opt( + py: Python<'p>, + ptr: *mut ffi::PyObject, + ) -> Option<&'p Self> { + NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell)) + } +} + #[cfg(test)] mod test { use crate::types::PyList; diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 1dd35dafec2..3e6a3d649da 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -11,7 +11,7 @@ use crate::instance::PyNativeType; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::types::{PyAny, PyDict, PyModule, PyTuple}; -use crate::{ffi, AsPyRef, GILPool, IntoPy, PyObject, Python}; +use crate::{ffi, GILPool, IntoPy, PyObject, Python}; use std::ptr; /// Description of a python parameter; used for `parse_args()`. @@ -100,8 +100,8 @@ pub fn parse_fn_args<'p>( // Adjust the remaining args let args = if accept_args { let py = args.py(); - // args.slice(used_args as isize, nargs as isize).as_ref(py) - unimplemented!() + let slice = args.slice(used_args as isize, nargs as isize).into_py(py); + py.checked_cast_as(slice).unwrap() } else { args }; @@ -199,3 +199,40 @@ impl>> IntoPyNewResult for PyRes self } } + +/// Utitlities for basetype +pub trait PyBaseTypeUtils { + type Dict; + type WeakRef; + type Layout; + type BaseNativeType; +} + +impl PyBaseTypeUtils for T { + type Dict = T::Dict; + type WeakRef = T::WeakRef; + type Layout = crate::pycell::PyCellInner; + type BaseNativeType = T::BaseNativeType; +} + +pub trait PySelf<'a, T: PyClass>: Sized { + fn from_cell(cell: &'a crate::PyCell) -> PyResult; +} + +impl<'a, T: PyClass> PySelf<'a, T> for &'a crate::PyCell { + fn from_cell(cell: &'a crate::PyCell) -> PyResult { + Ok(cell) + } +} + +impl<'a, T: PyClass> PySelf<'a, T> for crate::PyRef<'a, T> { + fn from_cell(cell: &'a crate::PyCell) -> PyResult { + cell.try_borrow().map_err(Into::into) + } +} + +impl<'a, T: PyClass> PySelf<'a, T> for crate::PyRefMut<'a, T> { + fn from_cell(cell: &'a crate::PyCell) -> PyResult { + cell.try_borrow_mut().map_err(Into::into) + } +} diff --git a/src/err.rs b/src/err.rs index 44ecb4ddac9..0841a34057d 100644 --- a/src/err.rs +++ b/src/err.rs @@ -59,9 +59,6 @@ pub type PyResult = Result; /// Marker type that indicates an error while downcasting pub struct PyDowncastError; -/// Marker type for `PyCell`. -pub struct PyBorrowError; - /// Helper conversion trait that allows to use custom arguments for exception constructor. pub trait PyErrArguments { /// Arguments for exception diff --git a/src/freelist.rs b/src/freelist.rs index 9dd44edf7a0..e40dfdcc175 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -72,7 +72,7 @@ impl PyClassAlloc for T where T: PyTypeInfo + PyClassWithFreeList, { - unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout { + unsafe fn alloc(_py: Python) -> *mut Self::Layout { if let Some(obj) = ::get_free_list().pop() { ffi::PyObject_Init(obj, ::type_object() as *const _ as _); obj as _ @@ -81,7 +81,7 @@ where } } - unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) { + unsafe fn dealloc(py: Python, self_: *mut Self::Layout) { (*self_).py_drop(py); let obj = self_ as _; diff --git a/src/instance.rs b/src/instance.rs index 98d592a0242..1eec13516ed 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -39,8 +39,7 @@ impl Py { pub fn new(py: Python, value: impl Into>) -> PyResult> where T: PyClass, - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, + T::BaseLayout: crate::type_object::PyObjectSizedLayout, { let initializer = value.into(); let obj = unsafe { initializer.create_cell(py)? }; diff --git a/src/lib.rs b/src/lib.rs index df4acc0c73d..28ec89f4403 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ pub use crate::class::*; pub use crate::conversion::{ - AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, + AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToBorrowedObject, ToPyObject, }; pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult}; @@ -127,7 +127,7 @@ pub use crate::gil::{init_once, GILGuard, GILPool}; pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType}; pub use crate::object::PyObject; pub use crate::objectprotocol::ObjectProtocol; -pub use crate::pycell::PyCell; +pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pyclass::PyClass; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::{prepare_freethreaded_python, Python}; @@ -171,7 +171,7 @@ pub mod marshal; mod object; mod objectprotocol; pub mod prelude; -mod pycell; +pub mod pycell; pub mod pyclass; pub mod pyclass_init; pub mod pyclass_slots; diff --git a/src/prelude.rs b/src/prelude.rs index 14d3a2b7977..d97879a5ef5 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -15,9 +15,10 @@ pub use crate::gil::GILGuard; pub use crate::instance::{AsPyRef, Py}; pub use crate::object::PyObject; pub use crate::objectprotocol::ObjectProtocol; +pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::Python; -pub use crate::{FromPy, FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToPyObject}; +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::types::PyModule; pub use pyo3cls::pymodule; diff --git a/src/pycell.rs b/src/pycell.rs index b40fbe93801..667938b7d55 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -1,43 +1,76 @@ //! Traits and structs for `#[pyclass]`. -use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; +use crate::conversion::{AsPyPointer, FromPyPointer, PyTryFrom, ToPyObject}; +use crate::err::PyDowncastError; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; -use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout}; +use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout, PyTypeInfo}; use crate::types::PyAny; -use crate::{ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python}; +use crate::{ffi, gil, PyErr, PyNativeType, PyObject, PyResult, Python}; use std::cell::{Cell, UnsafeCell}; use std::fmt; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; +#[doc(hidden)] +#[repr(C)] +pub struct PyCellBase { + ob_base: T::Layout, + borrow_flag: Cell, +} + +unsafe impl PyObjectLayout for PyCellBase +where + T: PyTypeInfo + PyNativeType, + T::Layout: PyObjectSizedLayout, +{ + const IS_NATIVE_TYPE: bool = false; + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { + None + } + unsafe fn unchecked_ref(&self) -> &T { + &*((&self) as *const &Self as *const _) + } + unsafe fn unchecked_refmut(&self) -> &mut T { + &mut *((&self) as *const &Self as *const _ as *mut _) + } +} + +// This impl ensures `PyCellBase` can be a base type. +impl PyObjectSizedLayout for PyCellBase +where + T: PyTypeInfo + PyNativeType, + T::Layout: PyObjectSizedLayout, +{ +} + /// Inner type of `PyCell` without dict slots and reference counter. /// This struct has two usages: /// 1. As an inner type of `PyRef` and `PyRefMut`. /// 2. As a base class when `#[pyclass(Base)]` is specified. #[doc(hidden)] #[repr(C)] -pub struct PyCellBase { - ob_base: ::ConcreteLayout, +pub struct PyCellInner { + ob_base: T::BaseLayout, value: ManuallyDrop>, } -impl AsPyPointer for PyCellBase { +impl AsPyPointer for PyCellInner { fn as_ptr(&self) -> *mut ffi::PyObject { (self as *const _) as *mut _ } } -unsafe impl PyObjectLayout for PyCellBase { +unsafe impl PyObjectLayout for PyCellInner { const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.ob_base) } unsafe fn unchecked_ref(&self) -> &T { &*self.value.get() } - unsafe fn unchecked_refmut(&mut self) -> &mut T { + unsafe fn unchecked_refmut(&self) -> &mut T { &mut *self.value.get() } unsafe fn py_init(&mut self, value: T) { @@ -49,6 +82,20 @@ unsafe impl PyObjectLayout for PyCellBase { } } +// This impl ensures `PyCellInner` can be a base type. +impl PyObjectSizedLayout for PyCellInner {} + +impl PyCellInner { + fn get_borrow_flag(&self) -> BorrowFlag { + let base = (&self.ob_base) as *const _ as *const PyCellBase; + unsafe { (*base).borrow_flag.get() } + } + fn set_borrow_flag(&self, flag: BorrowFlag) { + let base = (&self.ob_base) as *const _ as *const PyCellBase; + unsafe { (*base).borrow_flag.set(flag) } + } +} + /// `PyCell` represents the concrete layout of `T: PyClass` when it is converted /// to a Python class. /// @@ -74,8 +121,7 @@ unsafe impl PyObjectLayout for PyCellBase { /// ``` #[repr(C)] pub struct PyCell { - base: PyCellBase, - borrow_flag: Cell, + inner: PyCellInner, dict: T::Dict, weakref: T::WeakRef, } @@ -84,8 +130,7 @@ impl PyCell { /// Make new `PyCell` on the Python heap and returns the reference of it. pub fn new(py: Python, value: impl Into>) -> PyResult<&Self> where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, + T::BaseLayout: crate::type_object::PyObjectSizedLayout, { unsafe { let initializer = value.into(); @@ -103,57 +148,51 @@ impl PyCell { } pub fn try_borrow(&self) -> Result, PyBorrowError> { - let flag = self.borrow_flag.get(); + let flag = self.inner.get_borrow_flag(); if flag != BorrowFlag::HAS_MUTABLE_BORROW { Err(PyBorrowError { _private: () }) } else { - self.borrow_flag.set(flag.increment()); - Ok(PyRef { - value: &self.base, - flag: &self.borrow_flag, - }) + self.inner.set_borrow_flag(flag.increment()); + Ok(PyRef { inner: &self.inner }) } } pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> { - if self.borrow_flag.get() != BorrowFlag::UNUSED { + if self.inner.get_borrow_flag() != BorrowFlag::UNUSED { Err(PyBorrowMutError { _private: () }) } else { - self.borrow_flag.set(BorrowFlag::HAS_MUTABLE_BORROW); - Ok(PyRefMut { - value: unsafe { &mut *(self.base.as_ptr() as *mut _) }, - flag: &self.borrow_flag, - }) + self.inner.set_borrow_flag(BorrowFlag::HAS_MUTABLE_BORROW); + Ok(PyRefMut { inner: &self.inner }) } } pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { - if self.borrow_flag.get() != BorrowFlag::HAS_MUTABLE_BORROW { + if self.inner.get_borrow_flag() != BorrowFlag::HAS_MUTABLE_BORROW { Err(PyBorrowError { _private: () }) } else { - Ok(&*self.base.value.get()) + Ok(&*self.inner.value.get()) } } pub unsafe fn try_borrow_mut_unguarded(&self) -> Result<&mut T, PyBorrowMutError> { - if self.borrow_flag.get() != BorrowFlag::UNUSED { + if self.inner.get_borrow_flag() != BorrowFlag::UNUSED { Err(PyBorrowMutError { _private: () }) } else { - Ok(&mut *self.base.value.get()) + Ok(&mut *self.inner.value.get()) } } pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> where - ::ConcreteLayout: - crate::type_object::PyObjectSizedLayout, + T::BaseLayout: crate::type_object::PyObjectSizedLayout, { let base = T::alloc(py); if base.is_null() { return Err(PyErr::fetch(py)); } + let base = base as *mut PyCellBase; + (*base).borrow_flag = Cell::new(BorrowFlag::UNUSED); let self_ = base as *mut Self; - (*self_).borrow_flag = Cell::new(BorrowFlag::UNUSED); (*self_).dict = T::Dict::new(); (*self_).weakref = T::WeakRef::new(); Ok(self_) @@ -162,23 +201,23 @@ impl PyCell { unsafe impl PyObjectLayout for PyCell { const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { - Some(&mut self.base.ob_base) + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { + Some(&mut self.inner.ob_base) } unsafe fn unchecked_ref(&self) -> &T { - self.base.unchecked_ref() + self.inner.unchecked_ref() } - unsafe fn unchecked_refmut(&mut self) -> &mut T { - self.base.unchecked_refmut() + unsafe fn unchecked_refmut(&self) -> &mut T { + self.inner.unchecked_refmut() } unsafe fn py_init(&mut self, value: T) { - self.base.value = ManuallyDrop::new(UnsafeCell::new(value)); + self.inner.value = ManuallyDrop::new(UnsafeCell::new(value)); } unsafe fn py_drop(&mut self, py: Python) { - ManuallyDrop::drop(&mut self.base.value); + ManuallyDrop::drop(&mut self.inner.value); self.dict.clear_dict(py); self.weakref.clear_weakrefs(self.as_ptr(), py); - self.base.ob_base.py_drop(py); + self.inner.ob_base.py_drop(py); } } @@ -189,8 +228,6 @@ unsafe impl<'py, T: 'py + PyClass> PyDowncastImpl<'py> for PyCell { private_impl! {} } -impl PyObjectSizedLayout for PyCell {} - impl AsPyPointer for PyCell { fn as_ptr(&self) -> *mut ffi::PyObject { (self as *const _) as *mut _ @@ -203,35 +240,13 @@ impl ToPyObject for &PyCell { } } -impl ToPyObject for &mut PyCell { - fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } - } -} - -unsafe impl<'p, T> FromPyPointer<'p> for PyCell -where - T: PyClass, -{ - unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { - NonNull::new(ptr).map(|p| &*(gil::register_owned(py, p).as_ptr() as *const PyCell)) - } - unsafe fn from_borrowed_ptr_or_opt( - py: Python<'p>, - ptr: *mut ffi::PyObject, - ) -> Option<&'p Self> { - NonNull::new(ptr).map(|p| &*(gil::register_borrowed(py, p).as_ptr() as *const PyCell)) - } -} - pub struct PyRef<'p, T: PyClass> { - value: &'p PyCellBase, - flag: &'p Cell, + inner: &'p PyCellInner, } impl<'p, T: PyClass> PyRef<'p, T> { pub fn get_super(&'p self) -> &'p T::BaseType { - unsafe { self.value.ob_base.unchecked_ref() } + unsafe { self.inner.ob_base.unchecked_ref() } } } @@ -240,27 +255,27 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> { #[inline] fn deref(&self) -> &T { - unsafe { self.value.unchecked_ref() } + unsafe { self.inner.unchecked_ref() } } } impl<'p, T: PyClass> Drop for PyRef<'p, T> { fn drop(&mut self) { - self.flag.set(self.flag.get().decrement()); + let flag = self.inner.get_borrow_flag(); + self.inner.set_borrow_flag(flag.decrement()) } } pub struct PyRefMut<'p, T: PyClass> { - value: &'p mut PyCellBase, - flag: &'p Cell, + inner: &'p PyCellInner, } impl<'p, T: PyClass> PyRefMut<'p, T> { pub fn get_super(&'p self) -> &'p T::BaseType { - unsafe { self.value.ob_base.unchecked_ref() } + unsafe { self.inner.ob_base.unchecked_ref() } } - pub fn get_super_mut(&'p mut self) -> &'p mut T::BaseType { - unsafe { self.value.ob_base.unchecked_refmut() } + pub fn get_super_mut(&'p self) -> &'p mut T::BaseType { + unsafe { self.inner.ob_base.unchecked_refmut() } } } @@ -269,20 +284,20 @@ impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { #[inline] fn deref(&self) -> &T { - unsafe { self.value.unchecked_ref() } + unsafe { self.inner.unchecked_ref() } } } impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { self.value.unchecked_refmut() } + unsafe { self.inner.unchecked_refmut() } } } impl<'p, T: PyClass> Drop for PyRefMut<'p, T> { fn drop(&mut self) { - self.flag.set(BorrowFlag::UNUSED); + self.inner.set_borrow_flag(BorrowFlag::UNUSED) } } diff --git a/src/pyclass.rs b/src/pyclass.rs index 9ca0184504f..c9fd8130036 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -2,7 +2,7 @@ use crate::class::methods::{PyMethodDefType, PyMethodsProtocol}; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::type_object::{type_flags, PyObjectLayout}; -use crate::{class, ffi, gil, PyCell, PyErr, PyResult, PyTypeInfo, Python}; +use crate::{class, ffi, gil, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python}; use std::ffi::CString; use std::os::raw::c_void; use std::ptr; @@ -10,9 +10,8 @@ use std::ptr; #[inline] pub(crate) unsafe fn default_alloc() -> *mut ffi::PyObject { let type_obj = T::type_object(); - if T::FLAGS & type_flags::EXTENDED != 0 - && ::ConcreteLayout::IS_NATIVE_TYPE - { + // if the class derives native types(e.g., PyDict), call special new + if T::FLAGS & type_flags::EXTENDED != 0 && T::BaseLayout::IS_NATIVE_TYPE { let base_tp = ::type_object(); if let Some(base_new) = base_tp.tp_new { return base_new(type_obj as *const _ as _, ptr::null_mut(), ptr::null_mut()); @@ -28,7 +27,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized { /// /// # Safety /// This function must return a valid pointer to the Python heap. - unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout { + unsafe fn alloc(_py: Python) -> *mut Self::Layout { default_alloc::() as _ } @@ -36,7 +35,7 @@ pub trait PyClassAlloc: PyTypeInfo + Sized { /// /// # Safety /// `self_` must be a valid pointer to the Python heap. - unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) { + unsafe fn dealloc(py: Python, self_: *mut Self::Layout) { (*self_).py_drop(py); let obj = self_ as _; if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 { @@ -72,10 +71,12 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) { /// The `#[pyclass]` attribute automatically implements this trait for your Rust struct, /// so you don't have to use this trait directly. pub trait PyClass: - PyTypeInfo> + Sized + PyClassAlloc + PyMethodsProtocol + PyTypeInfo> + Sized + PyClassAlloc + PyMethodsProtocol { type Dict: PyClassDict; type WeakRef: PyClassWeakRef; + /// The closest native ancestor. + type BaseNativeType: PyTypeInfo + PyNativeType; } #[cfg(not(Py_LIMITED_API))] @@ -111,12 +112,12 @@ where { let py = Python::assume_gil_acquired(); let _pool = gil::GILPool::new_no_pointers(py); - ::dealloc(py, (obj as *mut T::ConcreteLayout) as _) + ::dealloc(py, (obj as *mut T::Layout) as _) } type_object.tp_dealloc = Some(tp_dealloc_callback::); // type size - type_object.tp_basicsize = std::mem::size_of::() as ffi::Py_ssize_t; + type_object.tp_basicsize = std::mem::size_of::() as ffi::Py_ssize_t; let mut offset = type_object.tp_basicsize; diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index daf59de3826..33654ecf3bf 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -8,7 +8,7 @@ use std::marker::PhantomData; /// This trait is intended to use internally for distinguishing `#[pyclass]` and /// Python native types. pub trait PyObjectInit: Sized { - fn init_class(self, layout: &mut T::ConcreteLayout); + fn init_class>(self, layout: &mut L); private_decl! {} } @@ -16,7 +16,7 @@ pub trait PyObjectInit: Sized { pub struct PyNativeTypeInitializer(PhantomData); impl PyObjectInit for PyNativeTypeInitializer { - fn init_class(self, _layout: &mut T::ConcreteLayout) {} + fn init_class>(self, _layout: &mut L) {} private_impl! {} } @@ -108,6 +108,7 @@ impl PyClassInitializer { pub fn add_subclass(self, subclass_value: S) -> PyClassInitializer where S: PyClass + PyTypeInfo, + S::BaseLayout: PyObjectSizedLayout, S::BaseType: PyTypeInfo, { PyClassInitializer::new(subclass_value, self) @@ -117,7 +118,7 @@ impl PyClassInitializer { pub unsafe fn create_cell(self, py: Python) -> PyResult<*mut PyCell> where T: PyClass, - ::ConcreteLayout: PyObjectSizedLayout, + T::BaseLayout: PyObjectSizedLayout, { let cell = PyCell::internal_new(py)?; self.init_class(&mut *cell); @@ -126,12 +127,12 @@ impl PyClassInitializer { } impl PyObjectInit for PyClassInitializer { - fn init_class(self, obj: &mut T::ConcreteLayout) { + fn init_class>(self, layout: &mut L) { let Self { init, super_init } = self; unsafe { - obj.py_init(init); + layout.py_init(init); } - if let Some(super_obj) = obj.get_super_or() { + if let Some(super_obj) = layout.get_super_or() { super_init.init_class(super_obj); } } @@ -151,6 +152,7 @@ where impl From<(S, B)> for PyClassInitializer where S: PyClass + PyTypeInfo, + S::BaseLayout: PyObjectSizedLayout, B: PyClass + PyTypeInfo>, B::BaseType: PyTypeInfo>, { diff --git a/src/type_object.rs b/src/type_object.rs index eb9e5299407..42ea897a47b 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -1,11 +1,10 @@ // Copyright (c) 2017-present PyO3 Project and Contributors //! Python type object information -use crate::instance::Py; use crate::pyclass::{initialize_type_object, PyClass}; use crate::pyclass_init::PyObjectInit; use crate::types::{PyAny, PyType}; -use crate::{ffi, AsPyPointer, Python}; +use crate::{ffi, AsPyPointer, Py, Python}; use std::cell::UnsafeCell; use std::ptr::NonNull; use std::sync::atomic::{AtomicBool, Ordering}; @@ -17,14 +16,13 @@ use std::sync::atomic::{AtomicBool, Ordering}; /// This trait is intended to be used internally. pub unsafe trait PyObjectLayout { const IS_NATIVE_TYPE: bool = true; - - fn get_super_or(&mut self) -> Option<&mut ::ConcreteLayout> { + fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { None } unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} unsafe fn unchecked_ref(&self) -> &T; - unsafe fn unchecked_refmut(&mut self) -> &mut T; + unsafe fn unchecked_refmut(&self) -> &mut T; } /// `T: PyObjectSizedLayout` represents `T` is not a instance of @@ -100,9 +98,14 @@ pub unsafe trait PyTypeInfo: Sized { type BaseType: PyTypeInfo + PyTypeObject; /// Layout - type ConcreteLayout: PyObjectLayout; + type Layout: PyObjectLayout; + + /// Layout of Basetype + type BaseLayout: PyObjectLayout; - /// This type is an abstraction layer for `AsPyRef`. + /// Abstraction layer for `AsPyRef`. + /// + /// Simply, it's `Self` for native types and `PyCell` for pyclasses. type Reference; /// Initializer for layout diff --git a/src/types/any.rs b/src/types/any.rs index a234e41bbb3..fdfe7c58ec5 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -27,8 +27,8 @@ unsafe impl crate::type_object::PyObjectLayout for ffi::PyObject { unsafe fn unchecked_ref(&self) -> &PyAny { &*((&self) as *const &Self as *const _) } - unsafe fn unchecked_refmut(&mut self) -> &mut PyAny { - &mut *((&self) as *const &mut Self as *const _ as *mut _) + unsafe fn unchecked_refmut(&self) -> &mut PyAny { + &mut *((&self) as *const &Self as *const _ as *mut _) } } impl crate::type_object::PyObjectSizedLayout for ffi::PyObject {} diff --git a/src/types/mod.rs b/src/types/mod.rs index 79880313729..e94306ea43f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -62,8 +62,8 @@ macro_rules! impl_layout { unsafe fn unchecked_ref(&self) -> &$name { &*((&self) as *const &Self as *const _) } - unsafe fn unchecked_refmut(&mut self) -> &mut $name { - &mut *((&self) as *const &mut Self as *const _ as *mut _) + unsafe fn unchecked_refmut(&self) -> &mut $name { + &mut *((&self) as *const &Self as *const _ as *mut _) } } }; @@ -74,6 +74,12 @@ macro_rules! pyobject_native_type { ($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => { impl_layout!($name, $layout); impl $crate::type_object::PyObjectSizedLayout<$name> for $layout {} + impl $crate::derive_utils::PyBaseTypeUtils for $name { + type Dict = $crate::pyclass_slots::PyClassDummySlot; + type WeakRef = $crate::pyclass_slots::PyClassDummySlot; + type Layout = $crate::pycell::PyCellBase<$name>; + type BaseNativeType = $name; + } pyobject_native_type_named!($name $(,$type_param)*); pyobject_native_type_convert!($name, $layout, $typeobject, $module, $checkfunction $(,$type_param)*); pyobject_native_type_extract!($name $(,$type_param)*); @@ -117,8 +123,10 @@ macro_rules! pyobject_native_var_type { // because rust-numpy has a special implementation. macro_rules! pyobject_native_type_extract { ($name: ty $(,$type_param: ident)*) => { - impl<$($type_param,)*> $crate::conversion::FromPyObjectImpl for &'_ $name { - type Impl = $crate::conversion::extract_impl::Reference; + impl<'py, $($type_param,)*> $crate::FromPyObject<'py> for &'py $name { + fn extract(obj: &'py crate::types::PyAny) -> $crate::PyResult { + $crate::PyTryFrom::try_from(obj).map_err(Into::into) + } } } } @@ -130,7 +138,8 @@ macro_rules! pyobject_native_type_convert( unsafe impl<$($type_param,)*> $crate::type_object::PyTypeInfo for $name { type Type = (); type BaseType = $crate::types::PyAny; - type ConcreteLayout = $layout; + type Layout = $layout; + type BaseLayout = ffi::PyObject; type Reference = $name; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer; diff --git a/tests/test_class_new.rs b/tests/test_class_new.rs index ce3107e9f23..c225608f1aa 100644 --- a/tests/test_class_new.rs +++ b/tests/test_class_new.rs @@ -19,7 +19,7 @@ fn empty_class_with_new() { assert!(typeobj .call((), None) .unwrap() - .cast_as::() + .cast_as::>() .is_ok()); } @@ -43,8 +43,9 @@ fn new_with_one_arg() { let py = gil.python(); let typeobj = py.get_type::(); let wrp = typeobj.call((42,), None).unwrap(); - let obj = wrp.cast_as::().unwrap(); - assert_eq!(obj._data, 42); + let obj = wrp.cast_as::>().unwrap(); + let obj_ref = obj.borrow(); + assert_eq!(obj_ref._data, 42); } #[pyclass] @@ -73,7 +74,8 @@ fn new_with_two_args() { .call((10, 20), None) .map_err(|e| e.print(py)) .unwrap(); - let obj = wrp.cast_as::().unwrap(); - assert_eq!(obj._data1, 10); - assert_eq!(obj._data2, 20); + let obj = wrp.cast_as::>().unwrap(); + let obj_ref = obj.borrow(); + assert_eq!(obj_ref._data1, 10); + assert_eq!(obj_ref._data2, 20); } diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs old mode 100755 new mode 100644 index 3790c73ce95..80efb36492a --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -53,11 +53,11 @@ struct Iterator { #[pyproto] impl<'p> PyIterProtocol for Iterator { - fn __iter__(slf: &mut PyCell) -> PyResult> { + fn __iter__(slf: PyRefMut) -> PyResult> { Ok(slf.into()) } - fn __next__(slf: &mut PyCell) -> PyResult> { + fn __next__(slf: PyRefMut) -> PyResult> { Ok(slf.iter.next()) } } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 1f8de776854..6cd11f38dd6 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -3,7 +3,7 @@ use pyo3::class::PyTraverseError; use pyo3::class::PyVisit; use pyo3::prelude::*; use pyo3::types::{PyAny, PyTuple}; -use pyo3::{ffi, py_run, AsPyPointer, PyCell}; +use pyo3::{ffi, py_run, AsPyPointer, PyCell, PyTryInto}; use std::cell::RefCell; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -153,7 +153,7 @@ fn gc_integration() { { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_mut( + let inst = PyCell::new( py, GCIntegration { self_ref: RefCell::new(py.None()), @@ -164,7 +164,8 @@ fn gc_integration() { ) .unwrap(); - *inst.self_ref.borrow_mut() = inst.to_object(py); + let borrow = inst.borrow_mut(); + *borrow.self_ref.borrow_mut() = inst.to_object(py); } let gil = Python::acquire_gil(); @@ -188,7 +189,7 @@ impl PyGCProtocol for GCIntegration2 { fn gc_integration2() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, GCIntegration2 {}).unwrap(); + let inst = PyCell::new(py, GCIntegration2 {}).unwrap(); py_run!(py, inst, "import gc; assert inst in gc.get_objects()"); } @@ -199,7 +200,7 @@ struct WeakRefSupport {} fn weakref_support() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, WeakRefSupport {}).unwrap(); + let inst = PyCell::new(py, WeakRefSupport {}).unwrap(); py_run!( py, inst, @@ -264,12 +265,11 @@ fn inheritance_with_new_methods_with_drop() { let typeobj = py.get_type::(); let inst = typeobj.call((), None).unwrap(); - let obj = SubClassWithDrop::try_from_mut(inst).unwrap(); - obj.data = Some(Arc::clone(&drop_called1)); - - let base: &mut ::BaseType = - unsafe { py.mut_from_borrowed_ptr(inst.as_ptr()) }; - base.data = Some(Arc::clone(&drop_called2)); + let obj: &PyCell = inst.try_into().unwrap(); + let mut obj_ref_mut = obj.borrow_mut(); + obj_ref_mut.data = Some(Arc::clone(&drop_called1)); + let super_ = obj_ref_mut.get_super_mut(); + super_.data = Some(Arc::clone(&drop_called2)); } assert!(drop_called1.load(Ordering::Relaxed)); diff --git a/tests/test_sequence.rs b/tests/test_sequence.rs index 9b97aa6b69b..500bf5b033f 100644 --- a/tests/test_sequence.rs +++ b/tests/test_sequence.rs @@ -64,7 +64,7 @@ impl PySequenceProtocol for ByteSequence { } } - fn __concat__(&self, other: &Self) -> PyResult { + fn __concat__(&self, other: PyRef<'p, Self>) -> PyResult { let mut elements = self.elements.clone(); elements.extend_from_slice(&other.elements); Ok(Self { elements }) From 8f8b42591aa0f6c579a10752278fdc18e45b501f Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 16 Feb 2020 00:24:38 +0900 Subject: [PATCH 06/27] Fix PySelf and AsPyRef --- pyo3-derive-backend/src/method.rs | 66 +++++++++++++------- pyo3-derive-backend/src/pyclass.rs | 1 - pyo3-derive-backend/src/pymethod.rs | 36 ++++++----- src/class/basic.rs | 14 +++-- src/class/macros.rs | 32 +++------- src/conversion.rs | 4 +- src/derive_utils.rs | 22 ------- src/err.rs | 9 ++- src/ffi/setobject.rs | 1 + src/instance.rs | 33 ++++++---- src/lib.rs | 2 +- src/object.rs | 3 +- src/pycell.rs | 94 ++++++++++++++++++++++++----- src/python.rs | 6 +- src/type_object.rs | 15 ++--- src/types/any.rs | 4 +- src/types/mod.rs | 9 ++- tests/test_class_conversion.rs | 12 ++-- tests/test_dunder.rs | 54 +++++++++++------ tests/test_gc.rs | 2 +- tests/test_inheritance.rs | 6 +- tests/test_methods.rs | 12 ++-- tests/test_pyself.rs | 28 +++++---- tests/test_various.rs | 17 +++--- tests/ui/missing_clone.stderr | 1 - 25 files changed, 288 insertions(+), 195 deletions(-) mode change 100644 => 100755 tests/test_dunder.rs diff --git a/pyo3-derive-backend/src/method.rs b/pyo3-derive-backend/src/method.rs index a4505ac4b79..527655b25c7 100644 --- a/pyo3-derive-backend/src/method.rs +++ b/pyo3-derive-backend/src/method.rs @@ -29,7 +29,10 @@ pub enum FnType { FnCall, FnClass, FnStatic, - PySelf(syn::TypeReference), + // self_: &PyCell, + PySelfRef(syn::TypeReference), + // self_: PyRef or PyRefMut + PySelfPath(syn::TypePath), } #[derive(Clone, PartialEq, Debug)] @@ -56,23 +59,28 @@ pub fn get_return_info(output: &syn::ReturnType) -> syn::Type { impl<'a> FnSpec<'a> { /// Generate the code for borrowing self - pub(crate) fn borrow_self(&self) -> TokenStream { + pub(crate) fn borrow_self(&self, return_null: bool) -> TokenStream { let is_mut = self .self_ .expect("impl_borrow_self is called for non-self fn"); + let ret = if return_null { + quote! { restore_and_null } + } else { + quote! { restore_and_minus1 } + }; if is_mut { quote! { - match _slf.try_borrow() { + let mut _slf = match _slf.try_borrow_mut() { Ok(ref_) => ref_, - Err(e) => return e.into::.restore_and_null(_py), - } + Err(e) => return pyo3::PyErr::from(e).#ret(_py), + }; } } else { quote! { - match _slf.try_borrow_mut() { + let _slf = match _slf.try_borrow() { Ok(ref_) => ref_, - Err(e) => return e.into::.restore_and_null(_py), - } + Err(e) => return pyo3::PyErr::from(e).#ret(_py), + }; } } } @@ -101,7 +109,7 @@ impl<'a> FnSpec<'a> { ref pat, ref ty, .. }) => { // skip first argument (cls) - if fn_type == FnType::FnClass && !self_.is_none() { + if fn_type == FnType::FnClass && self_.is_none() { self_ = Some(false); continue; } @@ -144,11 +152,11 @@ impl<'a> FnSpec<'a> { "Static method needs #[staticmethod] attribute", )); } - let tp = match arguments.remove(0).ty { - syn::Type::Reference(r) => replace_self(r)?, + fn_type = match arguments.remove(0).ty { + syn::Type::Reference(r) => FnType::PySelfRef(replace_self_in_ref(r)?), + syn::Type::Path(p) => FnType::PySelfPath(replace_self_in_path(p)), x => return Err(syn::Error::new_spanned(x, "Invalid type as custom self")), }; - fn_type = FnType::PySelf(tp); } // "Tweak" getter / setter names: strip off set_ and get_ if needed @@ -181,9 +189,11 @@ impl<'a> FnSpec<'a> { }; let text_signature = match &fn_type { - FnType::Fn | FnType::PySelf(_) | FnType::FnClass | FnType::FnStatic => { - utils::parse_text_signature_attrs(&mut *meth_attrs, name)? - } + FnType::Fn + | FnType::PySelfRef(_) + | FnType::PySelfPath(_) + | FnType::FnClass + | FnType::FnStatic => utils::parse_text_signature_attrs(&mut *meth_attrs, name)?, FnType::FnNew => parse_erroneous_text_signature( "text_signature not allowed on __new__; if you want to add a signature on \ __new__, put it on the struct definition instead", @@ -538,17 +548,24 @@ fn parse_method_name_attribute( } // Replace &A with &A<_> -fn replace_self(refn: &syn::TypeReference) -> syn::Result { - fn infer(span: proc_macro2::Span) -> syn::GenericArgument { - syn::GenericArgument::Type(syn::Type::Infer(syn::TypeInfer { - underscore_token: syn::token::Underscore { spans: [span] }, - })) - } +fn replace_self_in_ref(refn: &syn::TypeReference) -> syn::Result { let mut res = refn.to_owned(); let tp = match &mut *res.elem { syn::Type::Path(p) => p, _ => return Err(syn::Error::new_spanned(refn, "unsupported argument")), }; + replace_self_impl(tp); + res.lifetime = None; + Ok(res) +} + +fn replace_self_in_path(tp: &syn::TypePath) -> syn::TypePath { + let mut res = tp.to_owned(); + replace_self_impl(&mut res); + res +} + +fn replace_self_impl(tp: &mut syn::TypePath) { for seg in &mut tp.path.segments { if let syn::PathArguments::AngleBracketed(ref mut g) = seg.arguments { let mut args = syn::punctuated::Punctuated::new(); @@ -570,6 +587,9 @@ fn replace_self(refn: &syn::TypeReference) -> syn::Result { g.args = args; } } - res.lifetime = None; - Ok(res) + fn infer(span: proc_macro2::Span) -> syn::GenericArgument { + syn::GenericArgument::Type(syn::Type::Infer(syn::TypeInfer { + underscore_token: syn::token::Underscore { spans: [span] }, + })) + } } diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index fc7eb11e881..7164d791eb0 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -390,7 +390,6 @@ fn impl_class( type Layout = pyo3::pycell::PyCell; type BaseLayout = #base_layout; type Initializer = pyo3::pyclass_init::PyClassInitializer; - type Reference = pyo3::pycell::PyCell; const NAME: &'static str = #cls_name; const MODULE: Option<&'static str> = #module; diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 7cae4497c3b..d549f63ee3b 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -14,7 +14,10 @@ pub fn gen_py_method( Ok(match spec.tp { FnType::Fn => impl_py_method_def(&spec, &impl_wrap(cls, &spec, true)), - FnType::PySelf(ref self_ty) => { + FnType::PySelfRef(ref self_ty) => { + impl_py_method_def(&spec, &impl_wrap_pyslf(cls, &spec, self_ty, true)) + } + FnType::PySelfPath(ref self_ty) => { impl_py_method_def(&spec, &impl_wrap_pyslf(cls, &spec, self_ty, true)) } FnType::FnNew => impl_py_method_def_new(&spec, &impl_wrap_new(cls, &spec)), @@ -45,10 +48,10 @@ fn check_generic(sig: &syn::Signature) -> syn::Result<()> { /// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords) pub fn impl_wrap(cls: &syn::Type, spec: &FnSpec<'_>, noargs: bool) -> TokenStream { let body = impl_call(cls, &spec); - let borrow_self = spec.borrow_self(); + let borrow_self = spec.borrow_self(true); let slf = quote! { let _slf = _py.from_borrowed_ptr::>(_slf); - let _slf = #borrow_self; + #borrow_self }; impl_wrap_common(cls, spec, noargs, slf, body) } @@ -56,7 +59,7 @@ pub fn impl_wrap(cls: &syn::Type, spec: &FnSpec<'_>, noargs: bool) -> TokenStrea pub fn impl_wrap_pyslf( cls: &syn::Type, spec: &FnSpec<'_>, - self_ty: &syn::TypeReference, + self_ty: impl quote::ToTokens, noargs: bool, ) -> TokenStream { let names = get_arg_names(spec); @@ -66,9 +69,9 @@ pub fn impl_wrap_pyslf( }; let slf = quote! { let _cell = _py.from_borrowed_ptr::>(_slf); - let _slf: #self_ty = match pyo3::derive_utils::PySelf::from_cell(_cell) { + let _slf: #self_ty = match std::convert::TryFrom::try_from(_cell) { Ok(_slf) => _slf, - Err(e) => return e.restore_and_null(py), + Err(e) => return pyo3::PyErr::from(e).restore_and_null(_py), }; }; impl_wrap_common(cls, spec, noargs, slf, body) @@ -133,7 +136,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { let python_name = &spec.python_name; let cb = impl_call(cls, &spec); let body = impl_arg_params(&spec, cb); - let borrow_self = spec.borrow_self(); + let borrow_self = spec.borrow_self(true); quote! { #[allow(unused_mut)] @@ -146,7 +149,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { let _py = pyo3::Python::assume_gil_acquired(); let _pool = pyo3::GILPool::new(_py); let _slf = _py.from_borrowed_ptr::>(_slf); - let _slf = #borrow_self; + #borrow_self let _args = _py.from_borrowed_ptr::(_args); let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs); @@ -278,7 +281,7 @@ pub(crate) fn impl_wrap_getter(cls: &syn::Type, spec: &FnSpec) -> syn::Result syn::Result>(_slf); - let _slf = #borrow_self; + #borrow_self let result = pyo3::derive_utils::IntoPyResult::into_py_result(#fncall); match result { - Ok(val) => { - pyo3::IntoPyPointer::into_ptr(pyo3::IntoPy::::into_py(val, _py)) - } - Err(e) => { - e.restore(_py); - ::std::ptr::null_mut() - } + Ok(val) => pyo3::IntoPyPointer::into_ptr(pyo3::IntoPy::::into_py(val, _py)), + Err(e) => e.restore_and_null(_py), } } }) @@ -327,7 +325,7 @@ pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Resul } }; - let borrow_self = spec.borrow_self(); + let borrow_self = spec.borrow_self(false); Ok(quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( @@ -338,7 +336,7 @@ pub(crate) fn impl_wrap_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Resul let _py = pyo3::Python::assume_gil_acquired(); let _pool = pyo3::GILPool::new(_py); let _slf = _py.from_borrowed_ptr::>(_slf); - let _slf = #borrow_self; + #borrow_self let _value = _py.from_borrowed_ptr(_value); let _result = match <#val_ty as pyo3::FromPyObject>::extract(_value) { diff --git a/src/class/basic.rs b/src/class/basic.rs index 2150310f6dc..5d8e60e5f47 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -15,7 +15,6 @@ use crate::objectprotocol::ObjectProtocol; use crate::types::PyAny; use crate::{exceptions, ffi, FromPyObject, IntoPy, IntoPyPointer, PyClass, PyObject, Python}; use std::os::raw::c_int; -use std::ptr; /// Operators for the __richcmp__ method #[derive(Debug)] @@ -508,15 +507,18 @@ where let arg = py.from_borrowed_ptr::(arg); let res = match extract_op(op) { - Ok(op) => call_ref!(slf, __richcmp__, arg ; op), + Ok(op) => match arg.extract() { + Ok(arg) => match slf.try_borrow_unguarded() { + Ok(borrow) => borrow.__richcmp__(arg, op).into(), + Err(e) => Err(e.into()), + }, + Err(e) => Err(e), + }, Err(e) => Err(e), }; match res { Ok(val) => val.into_py(py).into_ptr(), - Err(e) => { - e.restore(py); - ptr::null_mut() - } + Err(e) => e.restore_and_null(py), } } Some(wrap::) diff --git a/src/class/macros.rs b/src/class/macros.rs index 74f58029952..3ba318801fb 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -161,10 +161,7 @@ macro_rules! py_binary_self_func { ffi::Py_INCREF(slf); slf } - Err(e) => { - e.restore(py); - std::ptr::null_mut() - } + Err(e) => e.restore_and_null(py), } } Some(wrap::<$class>) @@ -298,17 +295,13 @@ macro_rules! py_ternary_self_func { let result = call_refmut!(slf_cell, $f, arg1, arg2); match result { Ok(_) => slf, - Err(e) => { - e.restore(py); - std::ptr::null_mut() - } + Err(e) => e.restore_and_null(py), } } Some(wrap::) }}; } -#[doc(hidden)] macro_rules! py_func_set { ($trait_name:ident, $generic:ident, $fn_set:ident) => {{ unsafe extern "C" fn wrap<$generic>( @@ -339,10 +332,7 @@ macro_rules! py_func_set { }; match result { Ok(_) => 0, - Err(e) => { - e.restore(py); - -1 - } + Err(e) => e.restore_and_minus1(py), } } @@ -350,7 +340,6 @@ macro_rules! py_func_set { }}; } -#[doc(hidden)] macro_rules! py_func_del { ($trait_name:ident, $generic:ident, $fn_del:ident) => {{ unsafe extern "C" fn wrap( @@ -378,10 +367,7 @@ macro_rules! py_func_del { }; match result { Ok(_) => 0, - Err(e) => { - e.restore(py); - -1 - } + Err(e) => e.restore_and_minus1(py), } } @@ -389,7 +375,6 @@ macro_rules! py_func_del { }}; } -#[doc(hidden)] macro_rules! py_func_set_del { ($trait1:ident, $trait2:ident, $generic:ident, $fn_set:ident, $fn_del:ident) => {{ unsafe extern "C" fn wrap<$generic>( @@ -415,10 +400,7 @@ macro_rules! py_func_set_del { }; match result { Ok(_) => 0, - Err(e) => { - e.restore(py); - -1 - } + Err(e) => e.restore_and_minus1(py), } } Some(wrap::<$generic>) @@ -435,7 +417,7 @@ macro_rules! call_ref { }; ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { match $raw_arg.extract() { - Ok(arg) => call_ref!($slf, $fn $(,$raw_args)* ;arg $(;$args)*), + Ok(arg) => call_ref!($slf, $fn $(,$raw_args)* $(;$args)* ;arg), Err(e) => Err(e.into()), } }; @@ -450,7 +432,7 @@ macro_rules! call_refmut { }; ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { match $raw_arg.extract() { - Ok(arg) => call_refmut!($slf, $fn $(,$raw_args)* ;arg $(;$args)*), + Ok(arg) => call_refmut!($slf, $fn $(,$raw_args)* $(;$args)* ;arg), Err(e) => Err(e.into()), } }; diff --git a/src/conversion.rs b/src/conversion.rs index b6790d32243..3c10e6a2495 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -302,7 +302,7 @@ where /// Trait implemented by Python object types that allow a checked downcast. /// This trait is similar to `std::convert::TryFrom` -pub trait PyTryFrom<'v>: Sized + PyDowncastImpl<'v> { +pub trait PyTryFrom<'v>: Sized + PyDowncastImpl { /// Cast from a concrete Python object type to PyObject. fn try_from>(value: V) -> Result<&'v Self, PyDowncastError>; @@ -339,7 +339,7 @@ where impl<'v, T> PyTryFrom<'v> for T where - T: PyDowncastImpl<'v> + PyTypeInfo + PyNativeType, + T: PyDowncastImpl + PyTypeInfo + PyNativeType, { fn try_from>(value: V) -> Result<&'v Self, PyDowncastError> { let value = value.into(); diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 3e6a3d649da..ee019a8b08c 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -214,25 +214,3 @@ impl PyBaseTypeUtils for T { type Layout = crate::pycell::PyCellInner; type BaseNativeType = T::BaseNativeType; } - -pub trait PySelf<'a, T: PyClass>: Sized { - fn from_cell(cell: &'a crate::PyCell) -> PyResult; -} - -impl<'a, T: PyClass> PySelf<'a, T> for &'a crate::PyCell { - fn from_cell(cell: &'a crate::PyCell) -> PyResult { - Ok(cell) - } -} - -impl<'a, T: PyClass> PySelf<'a, T> for crate::PyRef<'a, T> { - fn from_cell(cell: &'a crate::PyCell) -> PyResult { - cell.try_borrow().map_err(Into::into) - } -} - -impl<'a, T: PyClass> PySelf<'a, T> for crate::PyRefMut<'a, T> { - fn from_cell(cell: &'a crate::PyCell) -> PyResult { - cell.try_borrow_mut().map_err(Into::into) - } -} diff --git a/src/err.rs b/src/err.rs index 0841a34057d..9076529a0a0 100644 --- a/src/err.rs +++ b/src/err.rs @@ -331,13 +331,20 @@ impl PyErr { unsafe { ffi::PyErr_Restore(ptype.into_ptr(), pvalue, ptraceback.into_ptr()) } } - #[doc(hidden)] /// Utility method for proc-macro code + #[doc(hidden)] pub fn restore_and_null(self, py: Python) -> *mut T { self.restore(py); std::ptr::null_mut() } + /// Utility method for proc-macro code + #[doc(hidden)] + pub fn restore_and_minus1(self, py: Python) -> crate::libc::c_int { + self.restore(py); + -1 + } + /// Issue a warning message. /// May return a PyErr if warnings-as-errors is enabled. pub fn warn(py: Python, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> { diff --git a/src/ffi/setobject.rs b/src/ffi/setobject.rs index 9f6282801eb..6f9c832b4a6 100644 --- a/src/ffi/setobject.rs +++ b/src/ffi/setobject.rs @@ -21,6 +21,7 @@ pub struct setentry { } #[repr(C)] +#[derive(Debug)] pub struct PySetObject { pub ob_base: PyObject, pub fill: Py_ssize_t, diff --git a/src/instance.rs b/src/instance.rs index 1eec13516ed..dda3a56afb0 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -3,11 +3,11 @@ use crate::err::{PyErr, PyResult}; use crate::gil; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; -use crate::type_object::PyTypeInfo; +use crate::type_object::PyDowncastImpl; use crate::types::PyAny; use crate::{ ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyCell, PyClass, PyClassInitializer, - Python, ToPyObject, + PyRef, PyRefMut, Python, ToPyObject, }; use std::marker::PhantomData; use std::mem; @@ -118,15 +118,17 @@ impl Py { } } -pub trait AsPyRef: Sized { +pub trait AsPyRef: Sized { + type Target; /// Return reference to object. - fn as_ref(&self, py: Python) -> &T::Reference; + fn as_ref(&self, py: Python<'_>) -> &Self::Target; } -impl AsPyRef for Py { - fn as_ref(&self, _py: Python) -> &T::Reference { - let ptr = self as *const Py as *const T::Reference; - unsafe { &*ptr } +impl<'p, T: PyClass> AsPyRef for Py { + type Target = PyCell; + fn as_ref(&self, _py: Python) -> &PyCell { + let any = self as *const Py as *const PyAny; + unsafe { PyDowncastImpl::unchecked_downcast(&*any) } } } @@ -183,12 +185,21 @@ where } } -impl<'a, T> std::convert::From<&mut PyCell> for Py +impl<'a, T> std::convert::From> for Py where T: PyClass, { - fn from(cell: &mut PyCell) -> Self { - unsafe { Py::from_borrowed_ptr(cell.as_ptr()) } + fn from(pyref: PyRef<'a, T>) -> Self { + unsafe { Py::from_borrowed_ptr(pyref.as_ptr()) } + } +} + +impl<'a, T> std::convert::From> for Py +where + T: PyClass, +{ + fn from(pyref: PyRefMut<'a, T>) -> Self { + unsafe { Py::from_borrowed_ptr(pyref.as_ptr()) } } } diff --git a/src/lib.rs b/src/lib.rs index 28ec89f4403..a88a7ea4603 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -246,7 +246,7 @@ macro_rules! wrap_pymodule { /// } /// let gil = Python::acquire_gil(); /// let py = gil.python(); -/// let time = PyCell::new_ref(py, Time {hour: 8, minute: 43, second: 16}).unwrap(); +/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap(); /// let time_as_tuple = (8, 43, 16); /// py_run!(py, time time_as_tuple, r#" /// assert time.hour == 8 diff --git a/src/object.rs b/src/object.rs index bf02992e23e..83c726ee73f 100644 --- a/src/object.rs +++ b/src/object.rs @@ -250,7 +250,8 @@ impl PyObject { } } -impl AsPyRef for PyObject { +impl AsPyRef for PyObject { + type Target = PyAny; fn as_ref(&self, _py: Python) -> &PyAny { unsafe { &*(self as *const _ as *const PyAny) } } diff --git a/src/pycell.rs b/src/pycell.rs index 667938b7d55..856c757b9b5 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -1,17 +1,14 @@ //! Traits and structs for `#[pyclass]`. -use crate::conversion::{AsPyPointer, FromPyPointer, PyTryFrom, ToPyObject}; -use crate::err::PyDowncastError; -use crate::pyclass::PyClass; +use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout, PyTypeInfo}; use crate::types::PyAny; -use crate::{ffi, gil, PyErr, PyNativeType, PyObject, PyResult, Python}; +use crate::{ffi, FromPy, PyClass, PyErr, PyNativeType, PyObject, PyResult, Python}; use std::cell::{Cell, UnsafeCell}; use std::fmt; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; -use std::ptr::NonNull; #[doc(hidden)] #[repr(C)] @@ -25,10 +22,7 @@ where T: PyTypeInfo + PyNativeType, T::Layout: PyObjectSizedLayout, { - const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { - None - } + const IS_NATIVE_TYPE: bool = true; unsafe fn unchecked_ref(&self) -> &T { &*((&self) as *const &Self as *const _) } @@ -96,6 +90,7 @@ impl PyCellInner { } } +// TODO(kngwyu): Mutability example /// `PyCell` represents the concrete layout of `T: PyClass` when it is converted /// to a Python class. /// @@ -116,7 +111,7 @@ impl PyCellInner { /// name: "The Man in the High Castle", /// author: "Philip Kindred Dick", /// }; -/// let book_cell = PyCell::new_ref(py, book).unwrap(); +/// let book_cell = PyCell::new(py, book).unwrap(); /// py_run!(py, book_cell, "assert book_cell.name[-6:] == 'Castle'"); /// ``` #[repr(C)] @@ -149,7 +144,7 @@ impl PyCell { pub fn try_borrow(&self) -> Result, PyBorrowError> { let flag = self.inner.get_borrow_flag(); - if flag != BorrowFlag::HAS_MUTABLE_BORROW { + if flag == BorrowFlag::HAS_MUTABLE_BORROW { Err(PyBorrowError { _private: () }) } else { self.inner.set_borrow_flag(flag.increment()); @@ -167,7 +162,7 @@ impl PyCell { } pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { - if self.inner.get_borrow_flag() != BorrowFlag::HAS_MUTABLE_BORROW { + if self.inner.get_borrow_flag() == BorrowFlag::HAS_MUTABLE_BORROW { Err(PyBorrowError { _private: () }) } else { Ok(&*self.inner.value.get()) @@ -221,8 +216,8 @@ unsafe impl PyObjectLayout for PyCell { } } -unsafe impl<'py, T: 'py + PyClass> PyDowncastImpl<'py> for PyCell { - unsafe fn unchecked_downcast(obj: &PyAny) -> &'py Self { +unsafe impl PyDowncastImpl for PyCell { + unsafe fn unchecked_downcast(obj: &PyAny) -> &Self { &*(obj.as_ptr() as *const Self) } private_impl! {} @@ -230,7 +225,7 @@ unsafe impl<'py, T: 'py + PyClass> PyDowncastImpl<'py> for PyCell { impl AsPyPointer for PyCell { fn as_ptr(&self) -> *mut ffi::PyObject { - (self as *const _) as *mut _ + self.inner.as_ptr() } } @@ -240,6 +235,25 @@ impl ToPyObject for &PyCell { } } +impl fmt::Debug for PyCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.try_borrow() { + Ok(borrow) => f.debug_struct("RefCell").field("value", &borrow).finish(), + Err(_) => { + struct BorrowedPlaceholder; + impl fmt::Debug for BorrowedPlaceholder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + f.debug_struct("RefCell") + .field("value", &BorrowedPlaceholder) + .finish() + } + } + } +} + pub struct PyRef<'p, T: PyClass> { inner: &'p PyCellInner, } @@ -266,6 +280,31 @@ impl<'p, T: PyClass> Drop for PyRef<'p, T> { } } +impl<'p, T: PyClass> FromPy> for PyObject { + fn from_py(pyref: PyRef<'p, T>, py: Python<'_>) -> PyObject { + unsafe { PyObject::from_borrowed_ptr(py, pyref.inner.as_ptr()) } + } +} + +impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell> for crate::PyRef<'a, T> { + type Error = PyBorrowError; + fn try_from(cell: &'a crate::PyCell) -> Result { + cell.try_borrow() + } +} + +impl<'a, T: PyClass> AsPyPointer for PyRef<'a, T> { + fn as_ptr(&self) -> *mut ffi::PyObject { + self.inner.as_ptr() + } +} + +impl fmt::Debug for PyRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + pub struct PyRefMut<'p, T: PyClass> { inner: &'p PyCellInner, } @@ -301,6 +340,31 @@ impl<'p, T: PyClass> Drop for PyRefMut<'p, T> { } } +impl<'p, T: PyClass> FromPy> for PyObject { + fn from_py(pyref: PyRefMut<'p, T>, py: Python<'_>) -> PyObject { + unsafe { PyObject::from_borrowed_ptr(py, pyref.inner.as_ptr()) } + } +} + +impl<'a, T: PyClass> AsPyPointer for PyRefMut<'a, T> { + fn as_ptr(&self) -> *mut ffi::PyObject { + self.inner.as_ptr() + } +} + +impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell> for crate::PyRefMut<'a, T> { + type Error = PyBorrowMutError; + fn try_from(cell: &'a crate::PyCell) -> Result { + cell.try_borrow_mut() + } +} + +impl fmt::Debug for PyRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&*(self.deref()), f) + } +} + #[derive(Copy, Clone, Eq, PartialEq)] struct BorrowFlag(usize); diff --git a/src/python.rs b/src/python.rs index a0977b94bc9..2944e648176 100644 --- a/src/python.rs +++ b/src/python.rs @@ -7,7 +7,7 @@ use crate::ffi; use crate::gil::{self, GILGuard}; use crate::instance::AsPyRef; use crate::object::PyObject; -use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyTypeInfo, PyTypeObject}; +use crate::type_object::{PyDowncastImpl, PyTypeInfo, PyTypeObject}; use crate::types::{PyAny, PyDict, PyModule, PyType}; use crate::AsPyPointer; use crate::{FromPyPointer, IntoPyPointer, PyTryFrom}; @@ -177,7 +177,7 @@ impl<'p> Python<'p> { /// Some(locals), /// ).unwrap(); /// let ret = locals.get_item("ret").unwrap(); - /// let b64: &PyBytes = ret.downcast_ref().unwrap(); + /// let b64: &PyBytes = ret.downcast().unwrap(); /// assert_eq!(b64.as_bytes(), b"SGVsbG8gUnVzdCE="); /// ``` pub fn run( @@ -284,7 +284,7 @@ impl<'p> Python<'p> { /// Register object in release pool, and do unchecked downcast to specific type. pub unsafe fn cast_as(self, obj: PyObject) -> &'p T where - T: PyDowncastImpl<'p> + PyTypeInfo, + T: PyDowncastImpl + PyTypeInfo, { let obj = gil::register_owned(self, obj.into_nonnull()); T::unchecked_downcast(obj) diff --git a/src/type_object.rs b/src/type_object.rs index 42ea897a47b..90df093d06d 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -57,16 +57,16 @@ pub mod type_flags { // two different abstraction for them. // This mismatch eventually should be fixed with https://github.com/PyO3/pyo3/issues/679, // but now it's not the time. -pub unsafe trait PyDowncastImpl<'py> { - unsafe fn unchecked_downcast(obj: &PyAny) -> &'py Self; +pub unsafe trait PyDowncastImpl { + unsafe fn unchecked_downcast(obj: &PyAny) -> &Self; private_decl! {} } -unsafe impl<'py, T> PyDowncastImpl<'py> for T +unsafe impl<'py, T> PyDowncastImpl for T where T: 'py + crate::PyNativeType, { - unsafe fn unchecked_downcast(obj: &PyAny) -> &'py Self { + unsafe fn unchecked_downcast(obj: &PyAny) -> &Self { &*(obj as *const _ as *const Self) } private_impl! {} @@ -100,14 +100,9 @@ pub unsafe trait PyTypeInfo: Sized { /// Layout type Layout: PyObjectLayout; - /// Layout of Basetype + /// Layout of Basetype. type BaseLayout: PyObjectLayout; - /// Abstraction layer for `AsPyRef`. - /// - /// Simply, it's `Self` for native types and `PyCell` for pyclasses. - type Reference; - /// Initializer for layout type Initializer: PyObjectInit; diff --git a/src/types/any.rs b/src/types/any.rs index fdfe7c58ec5..cdda72fdd14 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -18,8 +18,8 @@ use crate::{ffi, PyObject}; /// let dict = PyDict::new(gil.python()); /// assert!(gil.python().is_instance::(dict).unwrap()); /// let any = dict.as_ref(); -/// assert!(any.downcast_ref::().is_ok()); -/// assert!(any.downcast_ref::().is_err()); +/// assert!(any.downcast::().is_ok()); +/// assert!(any.downcast::().is_err()); /// ``` #[repr(transparent)] pub struct PyAny(PyObject, Unsendable); diff --git a/src/types/mod.rs b/src/types/mod.rs index e94306ea43f..5e82f85a616 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -140,7 +140,6 @@ macro_rules! pyobject_native_type_convert( type BaseType = $crate::types::PyAny; type Layout = $layout; type BaseLayout = ffi::PyObject; - type Reference = $name; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer; const NAME: &'static str = stringify!($name); @@ -167,6 +166,14 @@ macro_rules! pyobject_native_type_convert( } } + impl $crate::AsPyRef for $crate::Py<$name> { + type Target = $name; + fn as_ref(&self, _py: $crate::Python) -> &$name { + let any = self as *const $crate::Py<$name> as *const $crate::types::PyAny; + unsafe { $crate::type_object::PyDowncastImpl::unchecked_downcast(&*any) } + } + } + impl<$($type_param,)*> ::std::fmt::Debug for $name { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> diff --git a/tests/test_class_conversion.rs b/tests/test_class_conversion.rs index ee3678ddd48..e04e7375d72 100644 --- a/tests/test_class_conversion.rs +++ b/tests/test_class_conversion.rs @@ -16,10 +16,12 @@ fn test_cloneable_pyclass() { let py_c = Py::new(py, c.clone()).unwrap().to_object(py); let c2: Cloneable = py_c.extract(py).unwrap(); - let rc: &Cloneable = py_c.extract(py).unwrap(); - let mrc: &mut Cloneable = py_c.extract(py).unwrap(); - assert_eq!(c, c2); - assert_eq!(&c, rc); - assert_eq!(&c, mrc); + { + let rc: PyRef = py_c.extract(py).unwrap(); + assert_eq!(&c, &*rc); + // Drops PyRef before taking PyRefMut + } + let mrc: PyRefMut = py_c.extract(py).unwrap(); + assert_eq!(&c, &*mrc); } diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs old mode 100644 new mode 100755 index 80efb36492a..61de8b323bf --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -57,7 +57,7 @@ impl<'p> PyIterProtocol for Iterator { Ok(slf.into()) } - fn __next__(slf: PyRefMut) -> PyResult> { + fn __next__(mut slf: PyRefMut) -> PyResult> { Ok(slf.iter.next()) } } @@ -231,6 +231,7 @@ fn callable() { } #[pyclass] +#[derive(Debug)] struct SetItem { key: i32, val: i32, @@ -250,10 +251,13 @@ fn setitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyCell::new_ref(py, SetItem { key: 0, val: 0 }).unwrap(); + let c = PyCell::new(py, SetItem { key: 0, val: 0 }).unwrap(); py_run!(py, c, "c[1] = 2"); - assert_eq!(c.key, 1); - assert_eq!(c.val, 2); + { + let c = c.borrow(); + assert_eq!(c.key, 1); + assert_eq!(c.val, 2); + } py_expect_exception!(py, c, "del c[1]", NotImplementedError); } @@ -275,9 +279,12 @@ fn delitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyCell::new_ref(py, DelItem { key: 0 }).unwrap(); + let c = PyCell::new(py, DelItem { key: 0 }).unwrap(); py_run!(py, c, "del c[1]"); - assert_eq!(c.key, 1); + { + let c = c.borrow(); + assert_eq!(c.key, 1); + } py_expect_exception!(py, c, "c[1] = 2", NotImplementedError); } @@ -304,10 +311,14 @@ fn setdelitem() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyCell::new_ref(py, SetDelItem { val: None }).unwrap(); + let c = PyCell::new(py, SetDelItem { val: None }).unwrap(); py_run!(py, c, "c[1] = 2"); - assert_eq!(c.val, Some(2)); + { + let c = c.borrow(); + assert_eq!(c.val, Some(2)); + } py_run!(py, c, "del c[1]"); + let c = c.borrow(); assert_eq!(c.val, None); } @@ -383,21 +394,26 @@ fn context_manager() { let gil = Python::acquire_gil(); let py = gil.python(); - let c = PyCell::new_mut(py, ContextManager { exit_called: false }).unwrap(); + let c = PyCell::new(py, ContextManager { exit_called: false }).unwrap(); py_run!(py, c, "with c as x: assert x == 42"); - assert!(c.exit_called); - - c.exit_called = false; + { + let mut c = c.borrow_mut(); + assert!(c.exit_called); + c.exit_called = false; + } py_run!(py, c, "with c as x: raise ValueError"); - assert!(c.exit_called); - - c.exit_called = false; + { + let mut c = c.borrow_mut(); + assert!(c.exit_called); + c.exit_called = false; + } py_expect_exception!( py, c, "with c as x: raise NotImplementedError", NotImplementedError ); + let c = c.borrow(); assert!(c.exit_called); } @@ -455,7 +471,7 @@ struct DunderDictSupport {} fn dunder_dict_support() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, DunderDictSupport {}).unwrap(); + let inst = PyCell::new(py, DunderDictSupport {}).unwrap(); py_run!( py, inst, @@ -470,7 +486,7 @@ fn dunder_dict_support() { fn access_dunder_dict() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, DunderDictSupport {}).unwrap(); + let inst = PyCell::new(py, DunderDictSupport {}).unwrap(); py_run!( py, inst, @@ -488,7 +504,7 @@ struct WeakRefDunderDictSupport {} fn weakref_dunder_dict_support() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, WeakRefDunderDictSupport {}).unwrap(); + let inst = PyCell::new(py, WeakRefDunderDictSupport {}).unwrap(); py_run!( py, inst, @@ -513,7 +529,7 @@ impl PyObjectProtocol for ClassWithGetAttr { fn getattr_doesnt_override_member() { let gil = Python::acquire_gil(); let py = gil.python(); - let inst = PyCell::new_ref(py, ClassWithGetAttr { data: 4 }).unwrap(); + let inst = PyCell::new(py, ClassWithGetAttr { data: 4 }).unwrap(); py_assert!(py, inst, "inst.data == 4"); py_assert!(py, inst, "inst.a == 8"); } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 6cd11f38dd6..6a86d3e2ec0 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -164,7 +164,7 @@ fn gc_integration() { ) .unwrap(); - let borrow = inst.borrow_mut(); + let mut borrow = inst.borrow_mut(); *borrow.self_ref.borrow_mut() = inst.to_object(py); } diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index 49cc537013b..5ceccb716fe 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -109,6 +109,7 @@ except Exception as e: } #[pyclass(extends=PySet)] +#[derive(Debug)] struct SetWithName { #[pyo3(get(name))] _name: &'static str, @@ -126,7 +127,7 @@ impl SetWithName { fn inherit_set() { let gil = Python::acquire_gil(); let py = gil.python(); - let set_sub = pyo3::pyclass::PyCell::new_ref(py, SetWithName::new()).unwrap(); + let set_sub = pyo3::PyCell::new(py, SetWithName::new()).unwrap(); py_run!( py, set_sub, @@ -135,6 +136,7 @@ fn inherit_set() { } #[pyclass(extends=PyDict)] +#[derive(Debug)] struct DictWithName { #[pyo3(get(name))] _name: &'static str, @@ -152,7 +154,7 @@ impl DictWithName { fn inherit_dict() { let gil = Python::acquire_gil(); let py = gil.python(); - let dict_sub = pyo3::pyclass::PyCell::new_ref(py, DictWithName::new()).unwrap(); + let dict_sub = pyo3::PyCell::new(py, DictWithName::new()).unwrap(); py_run!( py, dict_sub, diff --git a/tests/test_methods.rs b/tests/test_methods.rs index 3e93e669ed3..9b1bceacbe5 100644 --- a/tests/test_methods.rs +++ b/tests/test_methods.rs @@ -23,8 +23,9 @@ fn instance_method() { let gil = Python::acquire_gil(); let py = gil.python(); - let obj = PyCell::new_mut(py, InstanceMethod { member: 42 }).unwrap(); - assert_eq!(obj.method().unwrap(), 42); + let obj = PyCell::new(py, InstanceMethod { member: 42 }).unwrap(); + let obj_ref = obj.borrow(); + assert_eq!(obj_ref.method().unwrap(), 42); let d = [("obj", obj)].into_py_dict(py); py.run("assert obj.method() == 42", None, Some(d)).unwrap(); py.run("assert obj.method.__doc__ == 'Test method'", None, Some(d)) @@ -49,8 +50,9 @@ fn instance_method_with_args() { let gil = Python::acquire_gil(); let py = gil.python(); - let obj = PyCell::new_mut(py, InstanceMethodWithArgs { member: 7 }).unwrap(); - assert_eq!(obj.method(6).unwrap(), 42); + let obj = PyCell::new(py, InstanceMethodWithArgs { member: 7 }).unwrap(); + let obj_ref = obj.borrow(); + assert_eq!(obj_ref.method(6).unwrap(), 42); let d = [("obj", obj)].into_py_dict(py); py.run("assert obj.method(3) == 21", None, Some(d)).unwrap(); py.run("assert obj.method(multiplier=6) == 42", None, Some(d)) @@ -395,7 +397,7 @@ impl MethodWithLifeTime { fn method_with_lifetime() { let gil = Python::acquire_gil(); let py = gil.python(); - let obj = PyCell::new_ref(py, MethodWithLifeTime {}).unwrap(); + let obj = PyCell::new(py, MethodWithLifeTime {}).unwrap(); py_run!( py, obj, diff --git a/tests/test_pyself.rs b/tests/test_pyself.rs index 25bf1a895a0..3aee62a33ce 100644 --- a/tests/test_pyself.rs +++ b/tests/test_pyself.rs @@ -30,7 +30,11 @@ impl Reader { idx: 0, }) } - fn get_iter_and_reset(slf: &mut PyCell, keys: Py, py: Python) -> PyResult { + fn get_iter_and_reset( + mut slf: PyRefMut, + keys: Py, + py: Python, + ) -> PyResult { let reader = Py::new(py, slf.clone())?; slf.inner.clear(); Ok(Iter { @@ -42,6 +46,7 @@ impl Reader { } #[pyclass] +#[derive(Debug)] struct Iter { reader: Py, keys: Py, @@ -50,23 +55,23 @@ struct Iter { #[pyproto] impl PyIterProtocol for Iter { - fn __iter__(slf: &mut PyCell) -> PyResult { + fn __iter__(slf: PyRefMut) -> PyResult { let py = unsafe { Python::assume_gil_acquired() }; - Ok(slf.to_object(py)) + Ok(slf.into_py(py)) } - fn __next__(slf: &mut PyCell) -> PyResult> { + fn __next__(mut slf: PyRefMut) -> PyResult> { let py = unsafe { Python::assume_gil_acquired() }; let bytes = slf.keys.as_ref(py).as_bytes(); match bytes.get(slf.idx) { Some(&b) => { - let res = slf - .reader - .as_ref(py) + slf.idx += 1; + let reader = slf.reader.as_ref(py); + let reader_ref = reader.try_borrow()?; + let res = reader_ref .inner .get(&b) .map(|s| PyString::new(py, s).into()); - slf.idx += 1; Ok(res) } None => Ok(None), @@ -82,7 +87,7 @@ fn reader() -> Reader { } #[test] -fn test_nested_iter1() { +fn test_nested_iter() { let gil = Python::acquire_gil(); let py = gil.python(); let reader: PyObject = reader().into_py(py); @@ -106,11 +111,12 @@ fn test_clone_ref() { fn test_nested_iter_reset() { let gil = Python::acquire_gil(); let py = gil.python(); - let reader = PyCell::new_ref(py, reader()).unwrap(); + let reader = PyCell::new(py, reader()).unwrap(); py_assert!( py, reader, "list(reader.get_iter_and_reset(bytes([3, 5, 2]))) == ['c', 'e', 'b']" ); - assert!(reader.inner.is_empty()); + let reader_ref = reader.borrow(); + assert!(reader_ref.inner.is_empty()); } diff --git a/tests/test_various.rs b/tests/test_various.rs index d0bbb7bf4dd..99708e0eb95 100644 --- a/tests/test_various.rs +++ b/tests/test_various.rs @@ -15,7 +15,7 @@ impl MutRefArg { fn get(&self) -> PyResult { Ok(self.n) } - fn set_other(&self, other: &mut MutRefArg) -> PyResult<()> { + fn set_other(&self, mut other: PyRefMut) -> PyResult<()> { other.n = 100; Ok(()) } @@ -31,7 +31,8 @@ fn mut_ref_arg() { let d = [("inst1", &inst1), ("inst2", &inst2)].into_py_dict(py); py.run("inst1.set_other(inst2)", None, Some(d)).unwrap(); - assert_eq!(inst2.as_ref(py).n, 100); + let inst2 = inst2.as_ref(py).borrow(); + assert_eq!(inst2.n, 100); } #[pyclass] @@ -81,8 +82,8 @@ fn intopytuple_pyclass() { let py = gil.python(); let tup = ( - PyCell::new_ref(py, SimplePyClass {}).unwrap(), - PyCell::new_ref(py, SimplePyClass {}).unwrap(), + PyCell::new(py, SimplePyClass {}).unwrap(), + PyCell::new(py, SimplePyClass {}).unwrap(), ); py_assert!(py, tup, "type(tup[0]).__name__ == 'SimplePyClass'"); py_assert!(py, tup, "type(tup[0]).__name__ == type(tup[1]).__name__"); @@ -106,8 +107,8 @@ fn pytuple_pyclass_iter() { let tup = PyTuple::new( py, [ - PyCell::new_ref(py, SimplePyClass {}).unwrap(), - PyCell::new_ref(py, SimplePyClass {}).unwrap(), + PyCell::new(py, SimplePyClass {}).unwrap(), + PyCell::new(py, SimplePyClass {}).unwrap(), ] .iter(), ); @@ -141,7 +142,7 @@ fn add_module(py: Python, module: &PyModule) -> PyResult<()> { .dict() .get_item("modules") .unwrap() - .downcast_mut::()? + .downcast::()? .set_item(module.name()?, module) } @@ -152,7 +153,7 @@ fn test_pickle() { let module = PyModule::new(py, "test_module").unwrap(); module.add_class::().unwrap(); add_module(py, module).unwrap(); - let inst = PyCell::new_ref(py, PickleSupport {}).unwrap(); + let inst = PyCell::new(py, PickleSupport {}).unwrap(); py_run!( py, inst, diff --git a/tests/ui/missing_clone.stderr b/tests/ui/missing_clone.stderr index 54452e43bc2..bb243766f3d 100644 --- a/tests/ui/missing_clone.stderr +++ b/tests/ui/missing_clone.stderr @@ -4,5 +4,4 @@ error[E0277]: the trait bound `TestClass: std::clone::Clone` is not satisfied 15 | let t: TestClass = pyvalue.extract(py).unwrap(); | ^^^^^^^ the trait `std::clone::Clone` is not implemented for `TestClass` | - = note: required because of the requirements on the impl of `pyo3::conversion::extract_impl::ExtractImpl<'_, TestClass>` for `pyo3::conversion::extract_impl::Cloned` = note: required because of the requirements on the impl of `pyo3::conversion::FromPyObject<'_>` for `TestClass` From daca04e5f34725ad19912661f621790289a0036b Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 18 Feb 2020 00:16:11 +0900 Subject: [PATCH 07/27] Update class.md and Change super API --- guide/src/class.md | 97 +++++++++++++++++++++-------------- guide/src/python_from_rust.md | 2 +- src/pycell.rs | 39 ++++++++++++-- 3 files changed, 95 insertions(+), 43 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 97a841792f1..3d0d40af5d1 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -22,6 +22,7 @@ Specifically, the following implementation is generated: ```rust use pyo3::prelude::*; +use pyo3::types::PyAny; /// Class for demonstration struct MyClass { @@ -33,9 +34,10 @@ impl pyo3::pyclass::PyClassAlloc for MyClass {} unsafe impl pyo3::PyTypeInfo for MyClass { type Type = MyClass; - type BaseType = pyo3::types::PyAny; - type ConcreteLayout = pyo3::PyCell; - type Initializer = pyo3::PyClassInitializer; + type BaseType = PyAny; + type BaseLayout = pyo3::pycell::PyCellBase; + type Layout = PyCell; + type Initializer = PyClassInitializer; const NAME: &'static str = "MyClass"; const MODULE: Option<&'static str> = None; @@ -53,6 +55,7 @@ unsafe impl pyo3::PyTypeInfo for MyClass { impl pyo3::pyclass::PyClass for MyClass { type Dict = pyo3::pyclass_slots::PyClassDummySlot; type WeakRef = pyo3::pyclass_slots::PyClassDummySlot; + type BaseNativeType = PyAny; } impl pyo3::IntoPy for MyClass { @@ -105,18 +108,26 @@ fn mymodule(_py: Python, m: &PyModule) -> PyResult<()> { } ``` -## Get Python objects from `pyclass` -You sometimes need to convert your `pyclass` into a Python object in Rust code (e.g., for testing it). +## PyCell and interior mutability +You sometimes need to convert your `pyclass` into a Python object and access it +from Rust code (e.g., for testing it). +`PyCell` is our primary interface for that. -For getting *GIL-bounded* (i.e., with `'py` lifetime) references of `pyclass`, -you can use `PyCell`. -Or you can use `Py` directly, for *not-GIL-bounded* references. +`PyCell` is always allocated in the Python heap, so we don't have the ownership of it. +We can get `&PyCell`, not `PyCell`. -### `PyCell` -`PyCell` represents the actual layout of `pyclass` on the Python heap. +Thus, to mutate data behind `&PyCell` safely, we employs +[Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) +like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html). -If you want to instantiate `pyclass` in Python and get the reference, -you can use `PyCell::new_ref` or `PyCell::new_mut`. +Users who are familiar with `RefCell` can use `PyCell` just like `RefCell`. + +For users who doesn't know `RefCell` well, we repeat the Rust's borrowing rule here: +- At any given time, you can have either (but not both of) one mutable reference or any number of immutable references. +- References must always be valid. +`PyCell` ensures these borrowing rules by managing a reference counter. + +TODO: link to the API document ```rust # use pyo3::prelude::*; @@ -124,28 +135,31 @@ you can use `PyCell::new_ref` or `PyCell::new_mut`. # use pyo3::PyCell; #[pyclass] struct MyClass { + #[pyo3(get)] num: i32, debug: bool, } let gil = Python::acquire_gil(); let py = gil.python(); -let obj = PyCell::new_ref(py, MyClass { num: 3, debug: true }).unwrap(); -// You can use deref -assert_eq!(obj.num, 3); -let dict = PyDict::new(py); -// You can treat a `&PyCell` as a normal Python object -dict.set_item("obj", obj).unwrap(); - -// return &mut PyCell -let obj = PyCell::new_mut(py, MyClass { num: 3, debug: true }).unwrap(); -obj.num = 5; +let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap(); +{ + let obj_ref = obj.borrow(); // Get PyRef + assert_eq!(obj_ref.num, 3); + // You cannot get PyRefMut unless all PyRef drop + assert!(obj.try_borrow_mut().is_err()); +} +{ + let mut obj_mut = obj.borrow_mut(); // Get PyRefMut + obj_mut.num = 5; + // You cannot get PyRef unless all PyRefMut drop + assert!(obj.try_borrow().is_err()); +} +// You can convert `&PyCell` to Python object +pyo3::py_run!(py, obj, "assert obj.num == 5") ``` -### `Py` - -`Py` is an object wrapper which stores an object longer than the GIL lifetime. - -You can use it to avoid lifetime problems. +`&PyCell` is bouded by the same lifetime as `GILGuard`. +To avoid this you can use `Py`, which stores an object longer than the GIL lifetime. ```rust # use pyo3::prelude::*; #[pyclass] @@ -159,7 +173,9 @@ fn return_myclass() -> Py { } let gil = Python::acquire_gil(); let obj = return_myclass(); -assert_eq!(obj.as_ref(gil.python()).num, 1); +let cell = obj.as_ref(gil.python()); // AsPyRef::as_ref returns &PyCell +let obj_ref = cell.borrow(); // Get PyRef +assert_eq!(obj_ref.num, 1); ``` ## Customizing the class @@ -228,6 +244,9 @@ baseclass of `T`. But for more deeply nested inheritance, you have to return `PyClassInitializer` explicitly. +To get a parent class from child, use `PyRef` instead of `&self`, +or `PyRefMut` instead of `&mut self`. + ```rust # use pyo3::prelude::*; use pyo3::PyCell; @@ -261,8 +280,9 @@ impl SubClass { (SubClass{ val2: 15}, BaseClass::new()) } - fn method2(self_: &PyCell) -> PyResult { - self_.get_super().method().map(|x| x * self_.val2) + fn method2(self_: PyRef) -> PyResult { + let super_ = self_.as_super(); // Get &BaseClass + super_.method().map(|x| x * self_.val2) } } @@ -279,16 +299,17 @@ impl SubSubClass { .add_subclass(SubSubClass{val3: 20}) } - fn method3(self_: &PyCell) -> PyResult { - let super_ = self_.get_super(); - SubClass::method2(super_).map(|x| x * self_.val3) + fn method3(self_: PyRef) -> PyResult { + let v = self_.val3; + let super_ = self_.into_super(); // Get PyRef + SubClass::method2(super_).map(|x| x * v) } } # let gil = Python::acquire_gil(); # let py = gil.python(); -# let subsub = pyo3::PyCell::new_ref(py, SubSubClass::new()).unwrap(); +# let subsub = pyo3::PyCell::new(py, SubSubClass::new()).unwrap(); # pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000") ``` @@ -761,8 +782,8 @@ struct GCTracked {} // Fails because it does not implement PyGCProtocol Iterators can be defined using the [`PyIterProtocol`](https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html) trait. It includes two methods `__iter__` and `__next__`: - * `fn __iter__(slf: &mut PyCell) -> PyResult>` - * `fn __next__(slf: &mut PyCell) -> PyResult>>` + * `fn __iter__(slf: PyRefMut) -> PyResult>` + * `fn __next__(slf: PyRefMut) -> PyResult>>` Returning `Ok(None)` from `__next__` indicates that that there are no further items. @@ -779,10 +800,10 @@ struct MyIterator { #[pyproto] impl PyIterProtocol for MyIterator { - fn __iter__(slf: &mut PyCell) -> PyResult> { + fn __iter__(mut slf: PyRefMut) -> PyResult> { Ok(slf.into()) } - fn __next__(slf: &mut PyCell) -> PyResult> { + fn __next__(mut slf: PyRefMut) -> PyResult> { Ok(slf.iter.next()) } } diff --git a/guide/src/python_from_rust.md b/guide/src/python_from_rust.md index 2250ca70102..1582a6b7779 100644 --- a/guide/src/python_from_rust.md +++ b/guide/src/python_from_rust.md @@ -61,7 +61,7 @@ let userdata = UserData { id: 34, name: "Yu".to_string(), }; -let userdata = PyCell::new_ref(py, userdata).unwrap(); +let userdata = PyCell::new(py, userdata).unwrap(); let userdata_as_tuple = (34, "Yu"); py_run!(py, userdata userdata_as_tuple, r#" assert repr(userdata) == "User Yu(id: 34)" diff --git a/src/pycell.rs b/src/pycell.rs index 856c757b9b5..8a4212fe9f0 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -258,12 +258,29 @@ pub struct PyRef<'p, T: PyClass> { inner: &'p PyCellInner, } -impl<'p, T: PyClass> PyRef<'p, T> { - pub fn get_super(&'p self) -> &'p T::BaseType { +impl<'p, T> PyRef<'p, T> +where + T: PyClass, +{ + pub fn as_super(&'p self) -> &'p T::BaseType { unsafe { self.inner.ob_base.unchecked_ref() } } } +impl<'p, T, U> PyRef<'p, T> +where + T: PyClass + PyTypeInfo>, + U: PyClass, +{ + pub fn into_super(self) -> PyRef<'p, U> { + let res = PyRef { + inner: &self.inner.ob_base, + }; + std::mem::forget(self); // Avoid drop + res + } +} + impl<'p, T: PyClass> Deref for PyRef<'p, T> { type Target = T; @@ -310,14 +327,28 @@ pub struct PyRefMut<'p, T: PyClass> { } impl<'p, T: PyClass> PyRefMut<'p, T> { - pub fn get_super(&'p self) -> &'p T::BaseType { + pub fn as_super(&'p self) -> &'p T::BaseType { unsafe { self.inner.ob_base.unchecked_ref() } } - pub fn get_super_mut(&'p self) -> &'p mut T::BaseType { + pub fn as_super_mut(&'p mut self) -> &'p mut T::BaseType { unsafe { self.inner.ob_base.unchecked_refmut() } } } +impl<'p, T, U> PyRefMut<'p, T> +where + T: PyClass + PyTypeInfo>, + U: PyClass, +{ + pub fn into_super(self) -> PyRefMut<'p, U> { + let res = PyRefMut { + inner: &self.inner.ob_base, + }; + std::mem::forget(self); // Avoid drop + res + } +} + impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { type Target = T; From 98d810e662856a3da90e907a31f073f755b9e11b Mon Sep 17 00:00:00 2001 From: Yuji Kanagawa Date: Tue, 18 Feb 2020 12:31:45 +0900 Subject: [PATCH 08/27] Apply suggestions from davidhewitt's review Co-Authored-By: David Hewitt <1939362+davidhewitt@users.noreply.github.com> --- guide/src/class.md | 6 ++--- pyo3-derive-backend/src/module.rs | 33 +++++++++++---------------- pyo3-derive-backend/src/pyfunction.rs | 2 +- pyo3cls/src/lib.rs | 4 +++- src/derive_utils.rs | 2 +- src/objectprotocol.rs | 8 +++---- 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 3d0d40af5d1..02a6abc38f9 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -116,7 +116,7 @@ from Rust code (e.g., for testing it). `PyCell` is always allocated in the Python heap, so we don't have the ownership of it. We can get `&PyCell`, not `PyCell`. -Thus, to mutate data behind `&PyCell` safely, we employs +Thus, to mutate data behind `&PyCell` safely, we employ the [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html). @@ -145,13 +145,13 @@ let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap(); { let obj_ref = obj.borrow(); // Get PyRef assert_eq!(obj_ref.num, 3); - // You cannot get PyRefMut unless all PyRef drop + // You cannot get PyRefMut unless all PyRefs are dropped assert!(obj.try_borrow_mut().is_err()); } { let mut obj_mut = obj.borrow_mut(); // Get PyRefMut obj_mut.num = 5; - // You cannot get PyRef unless all PyRefMut drop + // You cannot get any other refs until the PyRefMut is dropped assert!(obj.try_borrow().is_err()); } // You can convert `&PyCell` to Python object diff --git a/pyo3-derive-backend/src/module.rs b/pyo3-derive-backend/src/module.rs index 05046fdd0d3..b282c3aa41c 100644 --- a/pyo3-derive-backend/src/module.rs +++ b/pyo3-derive-backend/src/module.rs @@ -28,7 +28,7 @@ pub fn py_init(fnname: &Ident, name: &Ident, doc: syn::LitStr) -> TokenStream { } /// Finds and takes care of the #[pyfn(...)] in `#[pymodule]` -pub fn process_functions_in_module(func: &mut syn::ItemFn) { +pub fn process_functions_in_module(func: &mut syn::ItemFn) -> syn::Result<()> { let mut stmts: Vec = Vec::new(); for stmt in func.block.stmts.iter_mut() { @@ -36,7 +36,7 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) { if let Some((module_name, python_name, pyfn_attrs)) = extract_pyfn_attrs(&mut func.attrs) { - let function_to_python = add_fn_to_module(func, python_name, pyfn_attrs); + let function_to_python = add_fn_to_module(func, python_name, pyfn_attrs)?; let function_wrapper_ident = function_wrapper_ident(&func.sig.ident); let item: syn::ItemFn = syn::parse_quote! { fn block_wrapper() { @@ -51,18 +51,19 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) { } func.block.stmts = stmts; + Ok(()) } /// Transforms a rust fn arg parsed with syn into a method::FnArg -fn wrap_fn_argument<'a>(cap: &'a syn::PatType, name: &'a Ident) -> method::FnArg<'a> { +fn wrap_fn_argument<'a>(cap: &'a syn::PatType, name: &'a Ident) -> syn::Result> { let (mutability, by_ref, ident) = match *cap.pat { syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident), - _ => panic!("unsupported argument: {:?}", cap.pat), + _ => return Err(syn::Error::new_spanned(&cap.pat, "Unsupported argument")), }; let py = crate::utils::if_type_is_python(&cap.ty); let opt = method::check_arg_ty_and_optional(&name, &cap.ty); - method::FnArg { + Ok(method::FnArg { name: ident, mutability, by_ref, @@ -70,7 +71,7 @@ fn wrap_fn_argument<'a>(cap: &'a syn::PatType, name: &'a Ident) -> method::FnArg optional: opt, py, reference: method::is_ref(&name, &cap.ty), - } + }) } /// Extracts the data from the #[pyfn(...)] attribute of a function @@ -131,7 +132,7 @@ pub fn add_fn_to_module( func: &mut syn::ItemFn, python_name: Ident, pyfn_attrs: Vec, -) -> TokenStream { +) -> syn::Result { let mut arguments = Vec::new(); let mut self_ = None; @@ -141,21 +142,15 @@ pub fn add_fn_to_module( self_ = Some(recv.mutability.is_some()); } syn::FnArg::Typed(ref cap) => { - arguments.push(wrap_fn_argument(cap, &func.sig.ident)); + arguments.push(wrap_fn_argument(cap, &func.sig.ident)?); } } } let ty = method::get_return_info(&func.sig.output); - let text_signature = match utils::parse_text_signature_attrs(&mut func.attrs, &python_name) { - Ok(text_signature) => text_signature, - Err(err) => return err.to_compile_error(), - }; - let doc = match utils::get_doc(&func.attrs, text_signature, true) { - Ok(doc) => doc, - Err(err) => return err.to_compile_error(), - }; + let text_signature = utils::parse_text_signature_attrs(&mut func.attrs, &python_name)?; + let doc = utils::get_doc(&func.attrs, text_signature, true)?; let function_wrapper_ident = function_wrapper_ident(&func.sig.ident); @@ -176,7 +171,7 @@ pub fn add_fn_to_module( let wrapper = function_c_wrapper(&func.sig.ident, &spec); - let tokens = quote! { + Ok(quote! { fn #function_wrapper_ident(py: pyo3::Python) -> pyo3::PyObject { #wrapper @@ -199,9 +194,7 @@ pub fn add_fn_to_module( function } - }; - - tokens + }) } /// Generate static function wrapper (PyCFunction, PyCFunctionWithKeywords) diff --git a/pyo3-derive-backend/src/pyfunction.rs b/pyo3-derive-backend/src/pyfunction.rs index 588deb6a9de..c7bcacd3214 100644 --- a/pyo3-derive-backend/src/pyfunction.rs +++ b/pyo3-derive-backend/src/pyfunction.rs @@ -234,7 +234,7 @@ pub fn parse_name_attribute(attrs: &mut Vec) -> syn::Result syn::Result { let python_name = parse_name_attribute(&mut ast.attrs)?.unwrap_or_else(|| ast.sig.ident.unraw()); - Ok(add_fn_to_module(ast, python_name, args.arguments)) + add_fn_to_module(ast, python_name, args.arguments) } #[cfg(test)] diff --git a/pyo3cls/src/lib.rs b/pyo3cls/src/lib.rs index 0ba93c998ce..795423cbff3 100644 --- a/pyo3cls/src/lib.rs +++ b/pyo3cls/src/lib.rs @@ -23,7 +23,9 @@ pub fn pymodule(attr: TokenStream, input: TokenStream) -> TokenStream { parse_macro_input!(attr as syn::Ident) }; - process_functions_in_module(&mut ast); + if let Err(err) = process_functions_in_module(&mut ast) { + return err.to_compile_error().into(); + } let doc = match get_doc(&ast.attrs, None, false) { Ok(doc) => doc, diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 1901f76a572..da8cf01cb44 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -219,7 +219,7 @@ impl GetPropertyValue for PyObject { } } -/// Utitlities for basetype +/// Utilities for basetype pub trait PyBaseTypeUtils { type Dict; type WeakRef; diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index 55122ad8966..d777ee632d0 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -171,10 +171,10 @@ pub trait ObjectProtocol { fn get_type_ptr(&self) -> *mut ffi::PyTypeObject; /// Gets the Python base object for this object. - fn get_base<'py>(&'py self) -> &'py ::BaseType + fn get_base(&self) -> &::BaseType where Self: PyTypeInfo, - ::BaseType: FromPyPointer<'py>; + ::BaseType: for<'py> FromPyPointer<'py>; /// Casts the PyObject to a concrete Python object type. fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError> @@ -445,10 +445,10 @@ where unsafe { (*self.as_ptr()).ob_type } } - fn get_base<'py>(&'py self) -> &'py ::BaseType + fn get_base(&self) -> &::BaseType where Self: PyTypeInfo, - ::BaseType: FromPyPointer<'py>, + ::BaseType: for<'py> FromPyPointer<'py>, { unsafe { self.py().from_borrowed_ptr(self.as_ptr()) } } From 4b746af11b0995676d209422f23dd07a4fbe5db4 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 18 Feb 2020 12:52:41 +0900 Subject: [PATCH 09/27] Rename unchecked_refmut -> unchecked_mut --- src/pycell.rs | 38 +++++++++++++++++++------------------- src/type_object.rs | 2 +- src/types/any.rs | 2 +- src/types/mod.rs | 2 +- tests/test_gc.rs | 3 +-- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/pycell.rs b/src/pycell.rs index 8a4212fe9f0..40a07ec303f 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -26,7 +26,7 @@ where unsafe fn unchecked_ref(&self) -> &T { &*((&self) as *const &Self as *const _) } - unsafe fn unchecked_refmut(&self) -> &mut T { + unsafe fn unchecked_mut(&self) -> &mut T { &mut *((&self) as *const &Self as *const _ as *mut _) } } @@ -64,7 +64,7 @@ unsafe impl PyObjectLayout for PyCellInner { unsafe fn unchecked_ref(&self) -> &T { &*self.value.get() } - unsafe fn unchecked_refmut(&self) -> &mut T { + unsafe fn unchecked_mut(&self) -> &mut T { &mut *self.value.get() } unsafe fn py_init(&mut self, value: T) { @@ -202,8 +202,8 @@ unsafe impl PyObjectLayout for PyCell { unsafe fn unchecked_ref(&self) -> &T { self.inner.unchecked_ref() } - unsafe fn unchecked_refmut(&self) -> &mut T { - self.inner.unchecked_refmut() + unsafe fn unchecked_mut(&self) -> &mut T { + self.inner.unchecked_mut() } unsafe fn py_init(&mut self, value: T) { self.inner.value = ManuallyDrop::new(UnsafeCell::new(value)); @@ -262,7 +262,7 @@ impl<'p, T> PyRef<'p, T> where T: PyClass, { - pub fn as_super(&'p self) -> &'p T::BaseType { + pub fn as_super(&self) -> &T::BaseType { unsafe { self.inner.ob_base.unchecked_ref() } } } @@ -273,11 +273,11 @@ where U: PyClass, { pub fn into_super(self) -> PyRef<'p, U> { - let res = PyRef { - inner: &self.inner.ob_base, - }; - std::mem::forget(self); // Avoid drop - res + let PyRef { inner } = self; + std::mem::forget(self); + PyRef { + inner: &inner.ob_base, + } } } @@ -327,11 +327,11 @@ pub struct PyRefMut<'p, T: PyClass> { } impl<'p, T: PyClass> PyRefMut<'p, T> { - pub fn as_super(&'p self) -> &'p T::BaseType { + pub fn as_super(&self) -> &T::BaseType { unsafe { self.inner.ob_base.unchecked_ref() } } - pub fn as_super_mut(&'p mut self) -> &'p mut T::BaseType { - unsafe { self.inner.ob_base.unchecked_refmut() } + pub fn as_super_mut(&mut self) -> &mut T::BaseType { + unsafe { self.inner.ob_base.unchecked_mut() } } } @@ -341,11 +341,11 @@ where U: PyClass, { pub fn into_super(self) -> PyRefMut<'p, U> { - let res = PyRefMut { - inner: &self.inner.ob_base, - }; - std::mem::forget(self); // Avoid drop - res + let PyRefMut { inner } = self; + std::mem::forget(self); + PyRefMut { + inner: &inner.ob_base, + } } } @@ -361,7 +361,7 @@ impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { self.inner.unchecked_refmut() } + unsafe { self.inner.unchecked_mut() } } } diff --git a/src/type_object.rs b/src/type_object.rs index 90df093d06d..e694f3635d0 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -22,7 +22,7 @@ pub unsafe trait PyObjectLayout { unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} unsafe fn unchecked_ref(&self) -> &T; - unsafe fn unchecked_refmut(&self) -> &mut T; + unsafe fn unchecked_mut(&self) -> &mut T; } /// `T: PyObjectSizedLayout` represents `T` is not a instance of diff --git a/src/types/any.rs b/src/types/any.rs index cdda72fdd14..9a1cd113277 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -27,7 +27,7 @@ unsafe impl crate::type_object::PyObjectLayout for ffi::PyObject { unsafe fn unchecked_ref(&self) -> &PyAny { &*((&self) as *const &Self as *const _) } - unsafe fn unchecked_refmut(&self) -> &mut PyAny { + unsafe fn unchecked_mut(&self) -> &mut PyAny { &mut *((&self) as *const &Self as *const _ as *mut _) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 5e82f85a616..1e7c247df35 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -62,7 +62,7 @@ macro_rules! impl_layout { unsafe fn unchecked_ref(&self) -> &$name { &*((&self) as *const &Self as *const _) } - unsafe fn unchecked_refmut(&self) -> &mut $name { + unsafe fn unchecked_mut(&self) -> &mut $name { &mut *((&self) as *const &Self as *const _ as *mut _) } } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 6a86d3e2ec0..67a74215857 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -268,8 +268,7 @@ fn inheritance_with_new_methods_with_drop() { let obj: &PyCell = inst.try_into().unwrap(); let mut obj_ref_mut = obj.borrow_mut(); obj_ref_mut.data = Some(Arc::clone(&drop_called1)); - let super_ = obj_ref_mut.get_super_mut(); - super_.data = Some(Arc::clone(&drop_called2)); + obj_ref_mut.as_super_mut().data = Some(Arc::clone(&drop_called2)); } assert!(drop_called1.load(Ordering::Relaxed)); From 1f5cb83ef8ad8a332b5928bd26dcce1dd52824e2 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 18 Feb 2020 14:20:39 +0900 Subject: [PATCH 10/27] Add tests for mutability checking --- src/type_object.rs | 1 + tests/test_inheritance.rs | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/type_object.rs b/src/type_object.rs index e694f3635d0..1570c526150 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -22,6 +22,7 @@ pub unsafe trait PyObjectLayout { unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} unsafe fn unchecked_ref(&self) -> &T; + #[allow(clippy::mut_from_ref)] unsafe fn unchecked_mut(&self) -> &mut T; } diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index 5ceccb716fe..f4bb4dfd702 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -36,6 +36,14 @@ impl BaseClass { fn new() -> Self { BaseClass { val1: 10 } } + fn base_method(&self, x: usize) -> usize { + x * self.val1 + } + fn base_set(&mut self, fn_: &pyo3::types::PyAny) -> PyResult<()> { + let value: usize = fn_.call0()?.extract()?; + self.val1 = value; + Ok(()) + } } #[pyclass(extends=BaseClass)] @@ -50,6 +58,13 @@ impl SubClass { fn new() -> (Self, BaseClass) { (SubClass { val2: 5 }, BaseClass { val1: 10 }) } + fn sub_method(&self, x: usize) -> usize { + x * self.val2 + } + fn sub_set_and_ret(&mut self, x: usize) -> usize { + self.val2 = x; + x + } } #[test] @@ -61,6 +76,33 @@ fn inheritance_with_new_methods() { py_run!(py, inst, "assert inst.val1 == 10; assert inst.val2 == 5"); } +#[test] +fn call_base_and_sub_methods() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let obj = PyCell::new(py, SubClass::new()).unwrap(); + py_run!( + py, + obj, + r#" + assert obj.base_method(10) == 100 + assert obj.sub_method(10) == 50 +"# + ); +} + +#[test] +fn mutation_fails() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let obj = PyCell::new(py, SubClass::new()).unwrap(); + let global = Some([("obj", obj)].into_py_dict(py)); + let e = py + .run("obj.base_set(lambda: obj.sub_set_and_ret(1))", global, None) + .unwrap_err(); + assert!(e.is_instance::(py)) +} + #[pyclass] struct BaseClassWithResult { _val: usize, From 3d0ee2a28d7c471123218094645b3745a379edcd Mon Sep 17 00:00:00 2001 From: kngwyu Date: Fri, 21 Feb 2020 20:37:35 +0900 Subject: [PATCH 11/27] Use AsRef/AsMut instead of as_super/as_super_mut --- guide/src/class.md | 2 +- src/pycell.rs | 16 ++++++++-------- tests/test_gc.rs | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 02a6abc38f9..92721a848be 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -281,7 +281,7 @@ impl SubClass { } fn method2(self_: PyRef) -> PyResult { - let super_ = self_.as_super(); // Get &BaseClass + let super_ = self_.as_ref(); // Get &BaseClass super_.method().map(|x| x * self_.val2) } } diff --git a/src/pycell.rs b/src/pycell.rs index 40a07ec303f..3d28a1c2020 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -258,11 +258,8 @@ pub struct PyRef<'p, T: PyClass> { inner: &'p PyCellInner, } -impl<'p, T> PyRef<'p, T> -where - T: PyClass, -{ - pub fn as_super(&self) -> &T::BaseType { +impl<'p, T: PyClass> AsRef for PyRef<'p, T> { + fn as_ref(&self) -> &T::BaseType { unsafe { self.inner.ob_base.unchecked_ref() } } } @@ -326,11 +323,14 @@ pub struct PyRefMut<'p, T: PyClass> { inner: &'p PyCellInner, } -impl<'p, T: PyClass> PyRefMut<'p, T> { - pub fn as_super(&self) -> &T::BaseType { +impl<'p, T: PyClass> AsRef for PyRefMut<'p, T> { + fn as_ref(&self) -> &T::BaseType { unsafe { self.inner.ob_base.unchecked_ref() } } - pub fn as_super_mut(&mut self) -> &mut T::BaseType { +} + +impl<'p, T: PyClass> AsMut for PyRefMut<'p, T> { + fn as_mut(&mut self) -> &mut T::BaseType { unsafe { self.inner.ob_base.unchecked_mut() } } } diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 67a74215857..04016a2338f 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -268,7 +268,7 @@ fn inheritance_with_new_methods_with_drop() { let obj: &PyCell = inst.try_into().unwrap(); let mut obj_ref_mut = obj.borrow_mut(); obj_ref_mut.data = Some(Arc::clone(&drop_called1)); - obj_ref_mut.as_super_mut().data = Some(Arc::clone(&drop_called2)); + obj_ref_mut.as_mut().data = Some(Arc::clone(&drop_called2)); } assert!(drop_called1.load(Ordering::Relaxed)); From 043b13046a47dfecea3dd08ca2f6e71f20b83c1f Mon Sep 17 00:00:00 2001 From: kngwyu Date: Fri, 21 Feb 2020 21:12:32 +0900 Subject: [PATCH 12/27] Write docs for PyCell, PyRef, PyRefMut --- guide/src/class.md | 4 +-- src/derive_utils.rs | 4 +-- src/pycell.rs | 87 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 84 insertions(+), 11 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 92721a848be..5ccafb4f036 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -122,10 +122,10 @@ like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html Users who are familiar with `RefCell` can use `PyCell` just like `RefCell`. -For users who doesn't know `RefCell` well, we repeat the Rust's borrowing rule here: +For users who doesn't know `RefCell` well, here we repeat the Rust's borrowing rule: - At any given time, you can have either (but not both of) one mutable reference or any number of immutable references. - References must always be valid. -`PyCell` ensures these borrowing rules by managing a reference counter. +`PyCell` ensures these borrowing rules by tracking references at runtime. TODO: link to the API document diff --git a/src/derive_utils.rs b/src/derive_utils.rs index da8cf01cb44..1f0387f91d6 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -182,8 +182,8 @@ impl> IntoPyResult for PyResult { } } -/// 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. +/// 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. pub trait IntoPyNewResult>> { fn into_pynew_result(self) -> PyResult; } diff --git a/src/pycell.rs b/src/pycell.rs index 3d28a1c2020..b2927ff1d54 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -1,4 +1,4 @@ -//! Traits and structs for `#[pyclass]`. +//! Includes `PyCell` implementation. use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; @@ -90,15 +90,21 @@ impl PyCellInner { } } -// TODO(kngwyu): Mutability example -/// `PyCell` represents the concrete layout of `T: PyClass` when it is converted -/// to a Python class. +/// `PyCell` is the container type for [`PyClass`](../pyclass/trait.PyClass.html). /// -/// You can use it to test your `#[pyclass]` correctly works. +/// From Python side, `PyCell` is the concrete layout of `T: PyClass` in the Python heap, +/// which means we can convert `*const PyClass` to `*mut ffi::PyObject`. /// +/// From Rust side, `PyCell` is the mutable container of `T`. +/// Since `PyCell` is always on the Python heap, we don't have the ownership of it. +/// Thus, to mutate the data behind `&PyCell` safely, we employ the +/// [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) +/// like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html). +/// +/// In most cases, `PyCell` is hidden behind `#[pymethods]`. +/// However, you can construct `&PyCell` directly to test your pyclass in Rust code. /// ``` /// # use pyo3::prelude::*; -/// # use pyo3::{py_run, PyCell}; /// #[pyclass] /// struct Book { /// #[pyo3(get)] @@ -112,7 +118,34 @@ impl PyCellInner { /// author: "Philip Kindred Dick", /// }; /// let book_cell = PyCell::new(py, book).unwrap(); -/// py_run!(py, book_cell, "assert book_cell.name[-6:] == 'Castle'"); +/// // you can expose PyCell to Python snippets +/// pyo3::py_run!(py, book_cell, "assert book_cell.name[-6:] == 'Castle'"); +/// ``` +/// You can also use `slf: &PyCell` as an alternative `self` receiver of `#[pymethod]`. +/// ``` +/// # use pyo3::prelude::*; +/// use std::collections::HashMap; +/// #[pyclass] +/// #[derive(Default)] +/// struct Counter { +/// data: HashMap, +/// } +/// #[pymethods] +/// impl Counter { +/// fn increment(slf: &PyCell, name: String) -> PyResult { +/// let mut slf_mut = slf.try_borrow_mut()?; +/// // Now a mutable reference exists so we cannot another one +/// assert!(slf.try_borrow().is_err()); +/// assert!(slf.try_borrow_mut().is_err()); +/// let counter = slf_mut.data.entry(name).or_insert(0); +/// *counter += 1; +/// Ok(*counter) +/// } +/// } +/// # let gil = Python::acquire_gil(); +/// # let py = gil.python(); +/// # let counter = PyCell::new(py, Counter::default()).unwrap(); +/// # pyo3::py_run!(py, counter, "assert counter.increment('cat') == 1"); /// ``` #[repr(C)] pub struct PyCell { @@ -254,6 +287,43 @@ impl fmt::Debug for PyCell { } } +/// Wraps a borrowed reference to a value in a `PyCell`. +/// +/// See the [`PyCell`](struct.PyCell.html) documentation for more. +/// # Example +/// You can use `PyRef` as an alternative of `&self` receiver when +/// - You need to access the pointer of `PyCell`. +/// - You want to get super class. +/// ``` +/// # use pyo3::prelude::*; +/// #[pyclass] +/// struct Parent { +/// basename: &'static str, +/// } +/// #[pyclass(extends=Parent)] +/// struct Child { +/// name: &'static str, +/// } +/// #[pymethods] +/// impl Child { +/// #[new] +/// fn new() -> (Self, Parent) { +/// (Child { name: "Caterpillar" }, Parent { basename: "Butterfly" }) +/// } +/// fn format(slf: PyRef) -> String { +/// // We can get *mut ffi::PyObject from PyRef +/// use pyo3::AsPyPointer; +/// let refcnt = unsafe { pyo3::ffi::Py_REFCNT(slf.as_ptr()) }; +/// // We can get &Self::BaseType by as_ref +/// let basename = slf.as_ref().basename; +/// format!("{}(base: {}, cnt: {})", slf.name, basename, refcnt) +/// } +/// } +/// # let gil = Python::acquire_gil(); +/// # let py = gil.python(); +/// # let sub = PyCell::new(py, Child::new()).unwrap(); +/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 4)'"); +/// ``` pub struct PyRef<'p, T: PyClass> { inner: &'p PyCellInner, } @@ -319,6 +389,9 @@ impl fmt::Debug for PyRef<'_, T> { } } +/// Wraps a mutable borrowed reference to a value in a `PyCell`. +/// +/// See the [`PyCell`](struct.PyCell.html) and [`PyRef`](struct.PyRef.html) documentations for more. pub struct PyRefMut<'p, T: PyClass> { inner: &'p PyCellInner, } From c2a40fbf700482b40213b7a955f6593a8cb9dd62 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 22 Feb 2020 20:01:08 +0900 Subject: [PATCH 13/27] Modify CallbackConverter so that it can deal with try_borrow well --- pyo3-derive-backend/src/module.rs | 3 +- pyo3-derive-backend/src/pymethod.rs | 15 ++-- src/callback.rs | 122 +++++++++++++------------ src/class/basic.rs | 19 ++-- src/class/descr.rs | 11 +-- src/class/iter.rs | 21 ++--- src/class/macros.rs | 135 ++++++++++++++-------------- src/class/mapping.rs | 3 +- src/class/number.rs | 63 +++++-------- src/class/pyasync.rs | 27 +++--- src/class/sequence.rs | 37 +++----- 11 files changed, 202 insertions(+), 254 deletions(-) diff --git a/pyo3-derive-backend/src/module.rs b/pyo3-derive-backend/src/module.rs index b282c3aa41c..975b057436f 100644 --- a/pyo3-derive-backend/src/module.rs +++ b/pyo3-derive-backend/src/module.rs @@ -221,8 +221,7 @@ fn function_c_wrapper(name: &Ident, spec: &method::FnSpec<'_>) -> TokenStream { #body - pyo3::callback::cb_convert( - pyo3::callback::PyObjectCallbackConverter, _py, _result) + pyo3::callback::cb_obj_convert(_py, _result) } } } diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index 1eade4e5a34..0449c204395 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -115,8 +115,7 @@ fn impl_wrap_common( pyo3::derive_utils::IntoPyResult::into_py_result(#body) }; - pyo3::callback::cb_convert( - pyo3::callback::PyObjectCallbackConverter, _py, _result) + pyo3::callback::cb_obj_convert(_py, _result) } } } else { @@ -138,8 +137,7 @@ fn impl_wrap_common( #body - pyo3::callback::cb_convert( - pyo3::callback::PyObjectCallbackConverter, _py, _result) + pyo3::callback::cb_obj_convert(_py, _result) } } } @@ -169,8 +167,7 @@ pub fn impl_proto_wrap(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { #body - pyo3::callback::cb_convert( - pyo3::callback::PyObjectCallbackConverter, _py, _result) + pyo3::callback::cb_obj_convert(_py, _result) } } } @@ -237,8 +234,7 @@ pub fn impl_wrap_class(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { #body - pyo3::callback::cb_convert( - pyo3::callback::PyObjectCallbackConverter, _py, _result) + pyo3::callback::cb_obj_convert(_py, _result) } } } @@ -267,8 +263,7 @@ pub fn impl_wrap_static(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { #body - pyo3::callback::cb_convert( - pyo3::callback::PyObjectCallbackConverter, _py, _result) + pyo3::callback::cb_obj_convert(_py, _result) } } } diff --git a/src/callback.rs b/src/callback.rs index 8061aeea23c..5abec052815 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -10,53 +10,62 @@ use crate::{IntoPy, PyObject, Python}; use std::os::raw::c_int; use std::{isize, ptr}; -pub trait CallbackConverter { - type R; +/// Convert the result of callback function into the appropriate return value. +pub trait CallbackConverter { + type Source; + type Result; + const ERR_VALUE: Self::Result; - fn convert(s: S, p: Python) -> Self::R; - fn error_value() -> Self::R; + fn convert(s: Self::Source, py: Python) -> Self::Result; + + #[inline] + fn convert_result(py: Python, value: PyResult) -> Self::Result { + match value { + Ok(val) => Self::convert(val, py), + Err(e) => { + e.restore(py); + Self::ERR_VALUE + } + } + } } -pub struct PyObjectCallbackConverter; +pub struct PyObjectCallbackConverter(pub std::marker::PhantomData); -impl CallbackConverter for PyObjectCallbackConverter +impl CallbackConverter for PyObjectCallbackConverter where - S: IntoPy, + T: IntoPy, { - type R = *mut ffi::PyObject; - - fn convert(val: S, py: Python) -> *mut ffi::PyObject { - val.into_py(py).into_ptr() - } + type Source = T; + type Result = *mut ffi::PyObject; + const ERR_VALUE: Self::Result = ptr::null_mut(); - #[inline] - fn error_value() -> *mut ffi::PyObject { - ptr::null_mut() + fn convert(s: Self::Source, py: Python) -> Self::Result { + s.into_py(py).into_ptr() } } pub struct BoolCallbackConverter; -impl CallbackConverter for BoolCallbackConverter { - type R = c_int; - - #[inline] - fn convert(val: bool, _py: Python) -> c_int { - val as c_int - } +impl CallbackConverter for BoolCallbackConverter { + type Source = bool; + type Result = c_int; + const ERR_VALUE: Self::Result = -1; #[inline] - fn error_value() -> c_int { - -1 + fn convert(s: Self::Source, _py: Python) -> Self::Result { + s as c_int } } pub struct LenResultConverter; -impl CallbackConverter for LenResultConverter { - type R = isize; +impl CallbackConverter for LenResultConverter { + type Source = usize; + type Result = isize; + const ERR_VALUE: Self::Result = -1; - fn convert(val: usize, py: Python) -> isize { + fn convert(val: Self::Source, py: Python) -> Self::Result { if val <= (isize::MAX as usize) { val as isize } else { @@ -64,27 +73,19 @@ impl CallbackConverter for LenResultConverter { -1 } } - - #[inline] - fn error_value() -> isize { - -1 - } } pub struct UnitCallbackConverter; -impl CallbackConverter<()> for UnitCallbackConverter { - type R = c_int; +impl CallbackConverter for UnitCallbackConverter { + type Source = (); + type Result = c_int; + const ERR_VALUE: Self::Result = -1; #[inline] - fn convert(_: (), _: Python) -> c_int { + fn convert(_s: Self::Source, _py: Python) -> Self::Result { 0 } - - #[inline] - fn error_value() -> c_int { - -1 - } } pub trait WrappingCastTo { @@ -112,13 +113,15 @@ wrapping_cast!(i32, Py_hash_t); wrapping_cast!(isize, Py_hash_t); wrapping_cast!(i64, Py_hash_t); -pub struct HashConverter; +pub struct HashConverter(pub std::marker::PhantomData); -impl CallbackConverter for HashConverter +impl CallbackConverter for HashConverter where T: WrappingCastTo, { - type R = Py_hash_t; + type Source = T; + type Result = Py_hash_t; + const ERR_VALUE: Self::Result = -1; #[inline] fn convert(val: T, _py: Python) -> Py_hash_t { @@ -129,23 +132,30 @@ where hash } } +} - #[inline] - fn error_value() -> Py_hash_t { - -1 - } +// Short hands methods for macros +#[inline] +pub fn cb_convert(_c: C, py: Python, value: PyResult) -> C::Result +where + C: CallbackConverter, +{ + C::convert_result(py, value) } #[inline] -pub unsafe fn cb_convert(_c: C, py: Python, value: PyResult) -> C::R +pub fn cb_obj_convert>( + py: Python, + value: PyResult, +) -> as CallbackConverter>::Result { + PyObjectCallbackConverter::::convert_result(py, value) +} + +#[inline] +pub unsafe fn cb_err(_c: C, py: Python, err: impl Into) -> C::Result where - C: CallbackConverter, + C: CallbackConverter, { - match value { - Ok(val) => C::convert(val, py), - Err(e) => { - e.restore(py); - C::error_value() - } - } + err.into().restore(py); + C::ERR_VALUE } diff --git a/src/class/basic.rs b/src/class/basic.rs index 5d8e60e5f47..cb920aeeb11 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -233,8 +233,13 @@ where let slf = py.from_borrowed_ptr::>(slf); let arg = py.from_borrowed_ptr::(arg); - let result = call_ref!(slf, __getattr__, arg); - crate::callback::cb_convert(PyObjectCallbackConverter, py, result) + call_ref_with_converter!( + slf, + PyObjectCallbackConverter::(std::marker::PhantomData), + py, + __getattr__, + arg + ) } Some(wrap::) } @@ -355,8 +360,7 @@ where py_unary_func!( PyObjectStrProtocol, T::__str__, - ::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -380,8 +384,7 @@ where py_unary_func!( PyObjectReprProtocol, T::__repr__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -444,8 +447,7 @@ where py_unary_func!( PyObjectHashProtocol, T::__hash__, - isize, - HashConverter, + HashConverter::(std::marker::PhantomData), ffi::Py_hash_t ) } @@ -470,7 +472,6 @@ where py_unary_func!( PyObjectBoolProtocol, T::__bool__, - bool, BoolCallbackConverter, c_int ) diff --git a/src/class/descr.rs b/src/class/descr.rs index 80313dfed11..61c2e51c0d8 100644 --- a/src/class/descr.rs +++ b/src/class/descr.rs @@ -87,8 +87,7 @@ where py_ternary_func!( PyDescrGetProtocol, T::__get__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -109,13 +108,7 @@ where T: for<'p> PyDescrSetProtocol<'p>, { fn tp_descr_set() -> Option { - py_ternary_func!( - PyDescrSetProtocol, - T::__set__, - (), - UnitCallbackConverter, - c_int - ) + py_ternary_func!(PyDescrSetProtocol, T::__set__, UnitCallbackConverter, c_int) } } diff --git a/src/class/iter.rs b/src/class/iter.rs index d347b65df85..c6d693617da 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -80,8 +80,7 @@ where py_unary_refmut_func!( PyIterIterProtocol, T::__iter__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -108,21 +107,22 @@ where py_unary_refmut_func!( PyIterNextProtocol, T::__next__, - Option, - IterNextConverter + IterNextConverter::(std::marker::PhantomData) ) } } -struct IterNextConverter; +struct IterNextConverter(std::marker::PhantomData); -impl CallbackConverter> for IterNextConverter +impl CallbackConverter for IterNextConverter where T: IntoPy, { - type R = *mut ffi::PyObject; + type Source = Option; + type Result = *mut ffi::PyObject; + const ERR_VALUE: Self::Result = ptr::null_mut(); - fn convert(val: Option, py: Python) -> *mut ffi::PyObject { + fn convert(val: Self::Source, py: Python) -> Self::Result { match val { Some(val) => val.into_py(py).into_ptr(), None => unsafe { @@ -131,9 +131,4 @@ where }, } } - - #[inline] - fn error_value() -> *mut ffi::PyObject { - ptr::null_mut() - } } diff --git a/src/class/macros.rs b/src/class/macros.rs index 3ba318801fb..124860d0cc6 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -3,24 +3,23 @@ #[macro_export] #[doc(hidden)] macro_rules! py_unary_func { - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => { + ($trait:ident, $class:ident :: $f:ident, $conv: expr) => { + py_unary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject); + }; + // Use call_ref! by default + ($trait:ident, $class:ident :: $f:ident, $conv: expr, $ret_type:ty) => { py_unary_func!( $trait, $class::$f, - $res_type, $conv, - *mut $crate::ffi::PyObject + $ret_type, + call_ref_with_converter ); }; - // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $ret_type:ty) => { - py_unary_func!($trait, $class::$f, $res_type, $conv, $ret_type, call_ref); - }; - ($trait:ident, + ($trait: ident, $class:ident :: $f:ident, - $res_type:ty, - $conv:expr, - $ret_type:ty, + $conv: expr, + $ret_type: ty, $call: ident ) => {{ unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> $ret_type @@ -30,8 +29,7 @@ macro_rules! py_unary_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - let res = $call!(slf, $f); - $crate::callback::cb_convert($conv, py, res.map(|x| x)) + $call!(slf, $conv, py, $f) } Some(wrap::<$class>) }}; @@ -40,7 +38,7 @@ macro_rules! py_unary_func { #[macro_export] #[doc(hidden)] macro_rules! py_unary_refmut_func { - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ + ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{ unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject where T: for<'p> $trait<'p>, @@ -76,20 +74,14 @@ macro_rules! py_len_func { #[macro_export] #[doc(hidden)] macro_rules! py_binary_func { - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => { - py_binary_func!( - $trait, - $class::$f, - $res_type, - $conv, - *mut $crate::ffi::PyObject - ) + ($trait:ident, $class:ident :: $f:ident, $conv:expr) => { + py_binary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject) }; // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $return:ty) => {{ - py_binary_func!($trait, $class::$f, $res_type, $conv, $return, call_ref) + ($trait:ident, $class:ident :: $f:ident, $conv:expr, $return:ty) => {{ + py_binary_func!($trait, $class::$f, $conv, $return, call_ref_with_converter) }}; - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $return:ty, $call:ident) => {{ + ($trait:ident, $class:ident :: $f:ident, $conv:expr, $return:ty, $call:ident) => {{ unsafe extern "C" fn wrap(slf: *mut ffi::PyObject, arg: *mut ffi::PyObject) -> $return where T: for<'p> $trait<'p>, @@ -99,8 +91,7 @@ macro_rules! py_binary_func { let _pool = $crate::GILPool::new(py); let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg); - let result = $call!(slf, $f, arg); - $crate::callback::cb_convert($conv, py, result) + $call!(slf, $conv, py, $f, arg) } Some(wrap::<$class>) }}; @@ -109,7 +100,7 @@ macro_rules! py_binary_func { #[macro_export] #[doc(hidden)] macro_rules! py_binary_num_func { - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ + ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{ unsafe extern "C" fn wrap( lhs: *mut ffi::PyObject, rhs: *mut ffi::PyObject, @@ -137,7 +128,7 @@ macro_rules! py_binary_num_func { } // NOTE(kngwyu): -// Now(2020 2/9) This macro is used only for inplace operation so I used call_refmut here. +// Now(2020 2/9) This macro is used only for inplace operation so I used call_mut here. #[macro_export] #[doc(hidden)] macro_rules! py_binary_self_func { @@ -155,7 +146,7 @@ macro_rules! py_binary_self_func { let _pool = $crate::GILPool::new(py); let slf_ = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg = py.from_borrowed_ptr::<$crate::types::PyAny>(arg); - let result = call_refmut!(slf_, $f, arg); + let result = call_mut!(slf_, $f, arg); match result { Ok(_) => { ffi::Py_INCREF(slf); @@ -172,16 +163,15 @@ macro_rules! py_binary_self_func { #[doc(hidden)] macro_rules! py_ssizearg_func { // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => { + ($trait:ident, $class:ident :: $f:ident, $conv:expr) => { py_ssizearg_func!( $trait, $class::$f, - $res_type, $conv, - call_ref + call_ref_with_converter ) }; - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $call:ident) => {{ + ($trait:ident, $class:ident :: $f:ident, $conv:expr, $call:ident) => {{ unsafe extern "C" fn wrap( slf: *mut ffi::PyObject, arg: $crate::ffi::Py_ssize_t, @@ -192,8 +182,7 @@ macro_rules! py_ssizearg_func { let py = $crate::Python::assume_gil_acquired(); let _pool = $crate::GILPool::new(py); let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - let result = $call!(slf, $f ; arg.into()); - $crate::callback::cb_convert($conv, py, result) + $call!(slf, $conv, py, $f ;arg.into()) } Some(wrap::<$class>) }}; @@ -202,16 +191,10 @@ macro_rules! py_ssizearg_func { #[macro_export] #[doc(hidden)] macro_rules! py_ternary_func { - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => { - py_ternary_func!( - $trait, - $class::$f, - $res_type, - $conv, - *mut $crate::ffi::PyObject - ); + ($trait:ident, $class:ident :: $f:ident, $conv:expr) => { + py_ternary_func!($trait, $class::$f, $conv, *mut $crate::ffi::PyObject); }; - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr, $return_type:ty) => {{ + ($trait:ident, $class:ident :: $f:ident, $conv:expr, $return_type:ty) => {{ unsafe extern "C" fn wrap( slf: *mut $crate::ffi::PyObject, arg1: *mut $crate::ffi::PyObject, @@ -228,8 +211,7 @@ macro_rules! py_ternary_func { let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1); let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2); - let result = call_ref!(slf, $f, arg1, arg2); - $crate::callback::cb_convert($conv, py, result) + call_ref_with_converter!(slf, $conv, py, $f, arg1, arg2) } Some(wrap::) @@ -239,7 +221,7 @@ macro_rules! py_ternary_func { #[macro_export] #[doc(hidden)] macro_rules! py_ternary_num_func { - ($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{ + ($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{ unsafe extern "C" fn wrap( arg1: *mut $crate::ffi::PyObject, arg2: *mut $crate::ffi::PyObject, @@ -292,7 +274,7 @@ macro_rules! py_ternary_self_func { let slf_cell = py.from_borrowed_ptr::<$crate::PyCell>(slf); let arg1 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg1); let arg2 = py.from_borrowed_ptr::<$crate::types::PyAny>(arg2); - let result = call_refmut!(slf_cell, $f, arg1, arg2); + let result = call_mut!(slf_cell, $f, arg1, arg2); match result { Ok(_) => slf, Err(e) => e.restore_and_null(py), @@ -328,7 +310,7 @@ macro_rules! py_func_set { } else { let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); let value = py.from_borrowed_ptr::<$crate::types::PyAny>(value); - call_refmut!(slf, $fn_set, name, value) + call_mut!(slf, $fn_set, name, value) }; match result { Ok(_) => 0, @@ -359,7 +341,7 @@ macro_rules! py_func_del { let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); - call_refmut!(slf, $fn_del, name) + call_mut!(slf, $fn_del, name) } else { Err(PyErr::new::( "Subscript assignment not supported", @@ -393,10 +375,10 @@ macro_rules! py_func_set_del { let name = py.from_borrowed_ptr::<$crate::types::PyAny>(name); let result = if value.is_null() { - call_refmut!(slf, $fn_del, name) + call_mut!(slf, $fn_del, name) } else { let value = py.from_borrowed_ptr::<$crate::types::PyAny>(value); - call_refmut!(slf, $fn_set, name, value) + call_mut!(slf, $fn_set, name, value) }; match result { Ok(_) => 0, @@ -407,33 +389,48 @@ macro_rules! py_func_set_del { }}; } -// Utitlities for extract arguments + call method for PyCell -macro_rules! call_ref { - ($slf: expr, $fn: ident $(; $args: expr)*) => { - match $slf.try_borrow_unguarded() { - Ok(slf) => slf.$fn($($args,)*).into(), +macro_rules! _call_impl { + ($slf: ident, $fn: ident $(; $args: expr)*) => { $slf.$fn($($args,)*).into() }; + ($slf: ident, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { + match $raw_arg.extract() { + Ok(arg) => _call_impl!($slf, $fn $(,$raw_args)* $(;$args)* ;arg), Err(e) => Err(e.into()), } }; - ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { - match $raw_arg.extract() { - Ok(arg) => call_ref!($slf, $fn $(,$raw_args)* $(;$args)* ;arg), +} + +macro_rules! call_ref { + ($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { + match $slf.try_borrow() { + Ok(slf) => _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*), Err(e) => Err(e.into()), } }; } -macro_rules! call_refmut { - ($slf: expr, $fn: ident $(; $args: expr)*) => { - match $slf.try_borrow_mut_unguarded() { - Ok(slf) => slf.$fn($($args,)*).into(), - Err(e) => Err(e.into()), +macro_rules! call_ref_with_converter { + ($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { + match $slf.try_borrow() { + Ok(slf) => $crate::callback::cb_convert($conv, $py, _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*)), + Err(e) => $crate::callback::cb_err($conv, $py, e) } }; - ($slf: expr, $fn: ident, $raw_arg: expr $(,$raw_args: expr)* $(; $args: expr)*) => { - match $raw_arg.extract() { - Ok(arg) => call_refmut!($slf, $fn $(,$raw_args)* $(;$args)* ;arg), +} + +macro_rules! call_mut { + ($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { + match $slf.try_borrow_mut() { + Ok(mut slf) => _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*), Err(e) => Err(e.into()), } }; } + +macro_rules! call_mut_with_converter { + ($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { + match $slf.try_borrow_mut() { + Ok(mut slf) => $crate::callback::cb_convert($conv, $py, _call_impl!(slf, $fn $(,$raw_args)* $(;$args)*)), + Err(e) => $crate::callback::cb_err($conv, $py, e) + } + }; +} diff --git a/src/class/mapping.rs b/src/class/mapping.rs index cccc6a22969..3937313461d 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -167,8 +167,7 @@ where py_binary_func!( PyMappingGetItemProtocol, T::__getitem__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } diff --git a/src/class/number.rs b/src/class/number.rs index f44ef5b854d..1be54f5425b 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -764,8 +764,7 @@ where py_binary_num_func!( PyNumberAddProtocol, T::__add__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -791,8 +790,7 @@ where py_binary_num_func!( PyNumberSubProtocol, T::__sub__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -818,8 +816,7 @@ where py_binary_num_func!( PyNumberMulProtocol, T::__mul__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -845,8 +842,7 @@ where py_binary_num_func!( PyNumberMatmulProtocol, T::__matmul__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -872,8 +868,7 @@ where py_binary_num_func!( PyNumberTruedivProtocol, T::__truediv__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -899,8 +894,7 @@ where py_binary_num_func!( PyNumberFloordivProtocol, T::__floordiv__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -926,8 +920,7 @@ where py_binary_num_func!( PyNumberModProtocol, T::__mod__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -953,8 +946,7 @@ where py_binary_num_func!( PyNumberDivmodProtocol, T::__divmod__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -980,8 +972,7 @@ where py_ternary_num_func!( PyNumberPowProtocol, T::__pow__, - ::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1007,8 +998,7 @@ where py_binary_num_func!( PyNumberLShiftProtocol, T::__lshift__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1034,8 +1024,7 @@ where py_binary_num_func!( PyNumberRShiftProtocol, T::__rshift__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1061,8 +1050,7 @@ where py_binary_num_func!( PyNumberAndProtocol, T::__and__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1088,8 +1076,7 @@ where py_binary_num_func!( PyNumberXorProtocol, T::__xor__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1115,8 +1102,7 @@ where py_binary_num_func!( PyNumberOrProtocol, T::__or__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1620,8 +1606,7 @@ where py_unary_func!( PyNumberNegProtocol, T::__neg__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1647,8 +1632,7 @@ where py_unary_func!( PyNumberPosProtocol, T::__pos__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1674,8 +1658,7 @@ where py_unary_func!( PyNumberAbsProtocol, T::__abs__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1701,8 +1684,7 @@ where py_unary_func!( PyNumberInvertProtocol, T::__invert__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1728,8 +1710,7 @@ where py_unary_func!( PyNumberIntProtocol, T::__int__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1755,8 +1736,7 @@ where py_unary_func!( PyNumberFloatProtocol, T::__float__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -1782,8 +1762,7 @@ where py_unary_func!( PyNumberIndexProtocol, T::__index__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs index 90a42d83b19..842219cab43 100644 --- a/src/class/pyasync.rs +++ b/src/class/pyasync.rs @@ -153,8 +153,7 @@ where py_unary_func!( PyAsyncAwaitProtocol, T::__await__, - ::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -181,8 +180,7 @@ where py_unary_func!( PyAsyncAiterProtocol, T::__aiter__, - ::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -206,17 +204,20 @@ mod anext { use crate::IntoPyPointer; use crate::Python; use crate::{ffi, IntoPy, PyObject}; + use std::marker::PhantomData; use std::ptr; - pub struct IterANextResultConverter; + struct IterANextResultConverter(PhantomData); - impl CallbackConverter> for IterANextResultConverter + impl CallbackConverter for IterANextResultConverter where T: IntoPy, { - type R = *mut ffi::PyObject; + type Source = Option; + type Result = *mut ffi::PyObject; + const ERR_VALUE: Self::Result = ptr::null_mut(); - fn convert(val: Option, py: Python) -> *mut ffi::PyObject { + fn convert(val: Self::Source, py: Python) -> Self::Result { match val { Some(val) => val.into_py(py).into_ptr(), None => unsafe { @@ -225,11 +226,6 @@ mod anext { }, } } - - #[inline] - fn error_value() -> *mut ffi::PyObject { - ptr::null_mut() - } } impl PyAsyncAnextProtocolImpl for T @@ -241,10 +237,9 @@ mod anext { py_unary_func!( PyAsyncAnextProtocol, T::__anext__, - Option, - IterANextResultConverter, + IterANextResultConverter::(std::marker::PhantomData), *mut crate::ffi::PyObject, - call_refmut + call_mut_with_converter ) } } diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 643b4d21430..79716e8449e 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -202,8 +202,7 @@ where py_ssizearg_func!( PySequenceGetItemProtocol, T::__getitem__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -283,10 +282,7 @@ mod sq_ass_item_impl { match result { Ok(_) => 0, - Err(e) => { - e.restore(py); - -1 - } + Err(e) => e.restore_and_minus1(py), } } Some(wrap::) @@ -334,10 +330,7 @@ mod sq_ass_item_impl { match result { Ok(_) => 0, - Err(e) => { - e.restore(py); - -1 - } + Err(e) => e.restore_and_minus1(py), } } Some(wrap::) @@ -375,7 +368,7 @@ mod sq_ass_item_impl { let slf = py.from_borrowed_ptr::>(slf); let result = if value.is_null() { - call_refmut!(slf, __delitem__; key.into()) + call_mut!(slf, __delitem__; key.into()) } else { let value = py.from_borrowed_ptr::(value); match value.extract() { @@ -388,10 +381,7 @@ mod sq_ass_item_impl { }; match result { Ok(_) => 0, - Err(e) => { - e.restore(py); - -1 - } + Err(e) => e.restore_and_minus1(py), } } Some(wrap::) @@ -420,7 +410,6 @@ where py_binary_func!( PySequenceContainsProtocol, T::__contains__, - bool, BoolCallbackConverter, c_int ) @@ -448,8 +437,7 @@ where py_binary_func!( PySequenceConcatProtocol, T::__concat__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -475,8 +463,7 @@ where py_ssizearg_func!( PySequenceRepeatProtocol, T::__repeat__, - T::Success, - PyObjectCallbackConverter + PyObjectCallbackConverter::(std::marker::PhantomData) ) } } @@ -502,10 +489,9 @@ where py_binary_func!( PySequenceInplaceConcatProtocol, T::__inplace_concat__, - T, - PyObjectCallbackConverter, + PyObjectCallbackConverter::(std::marker::PhantomData), *mut crate::ffi::PyObject, - call_refmut + call_mut_with_converter ) } } @@ -531,9 +517,8 @@ where py_ssizearg_func!( PySequenceInplaceRepeatProtocol, T::__inplace_repeat__, - T, - PyObjectCallbackConverter, - call_refmut + PyObjectCallbackConverter::(std::marker::PhantomData), + call_mut_with_converter ) } } From d3d61c6ad3a4f1eaade65fa86e894c69906e33e3 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 22 Feb 2020 20:26:11 +0900 Subject: [PATCH 14/27] Remove all usages of unguarded --- src/class/basic.rs | 28 +++++++++++++++------------- src/class/sequence.rs | 42 ++++++++++++++++++++++-------------------- src/conversion.rs | 3 +-- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/class/basic.rs b/src/class/basic.rs index cb920aeeb11..d6be61f2181 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -507,19 +507,21 @@ where let slf = py.from_borrowed_ptr::>(slf); let arg = py.from_borrowed_ptr::(arg); - let res = match extract_op(op) { - Ok(op) => match arg.extract() { - Ok(arg) => match slf.try_borrow_unguarded() { - Ok(borrow) => borrow.__richcmp__(arg, op).into(), - Err(e) => Err(e.into()), - }, - Err(e) => Err(e), - }, - Err(e) => Err(e), - }; - match res { - Ok(val) => val.into_py(py).into_ptr(), - Err(e) => e.restore_and_null(py), + match slf.try_borrow() { + Ok(borrowed_slf) => { + let res = match extract_op(op) { + Ok(op) => match arg.extract() { + Ok(arg) => borrowed_slf.__richcmp__(arg, op).into(), + Err(e) => Err(e), + }, + Err(e) => Err(e), + }; + match res { + Ok(val) => val.into_py(py).into_ptr(), + Err(e) => e.restore_and_null(py), + } + } + Err(e) => PyErr::from(e).restore_and_null(py), } } Some(wrap::) diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 79716e8449e..4798a24ec50 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -264,25 +264,27 @@ mod sq_ass_item_impl { let _pool = crate::GILPool::new(py); let slf = py.from_borrowed_ptr::>(slf); - let result = if value.is_null() { - Err(PyErr::new::(format!( + if value.is_null() { + return PyErr::new::(format!( "Item deletion is not supported by {:?}", stringify!(T) - ))) - } else { - let value = py.from_borrowed_ptr::(value); - match value.extract() { - Ok(value) => match slf.try_borrow_mut_unguarded() { - Ok(slf_) => slf_.__setitem__(key.into(), value).into(), + )) + .restore_and_minus1(py); + } + + match slf.try_borrow_mut() { + Ok(mut slf) => { + let value = py.from_borrowed_ptr::(value); + let result = match value.extract() { + Ok(value) => slf.__setitem__(key.into(), value).into(), Err(e) => e.into(), - }, - Err(e) => Err(e), + }; + match result { + Ok(_) => 0, + Err(e) => e.restore_and_minus1(py), + } } - }; - - match result { - Ok(_) => 0, - Err(e) => e.restore_and_minus1(py), + Err(e) => PyErr::from(e).restore_and_minus1(py), } } Some(wrap::) @@ -371,12 +373,12 @@ mod sq_ass_item_impl { call_mut!(slf, __delitem__; key.into()) } else { let value = py.from_borrowed_ptr::(value); - match value.extract() { - Ok(value) => match slf.try_borrow_mut_unguarded() { - Ok(slf_) => slf_.__setitem__(key.into(), value).into(), - Err(e) => e.into(), + match slf.try_borrow_mut() { + Ok(mut slf_) => match value.extract() { + Ok(value) => slf_.__setitem__(key.into(), value).into(), + Err(e) => Err(e), }, - Err(e) => Err(e), + Err(e) => Err(e.into()), } }; match result { diff --git a/src/conversion.rs b/src/conversion.rs index 3c10e6a2495..469eb749e4d 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -259,8 +259,7 @@ where { fn extract(obj: &'a PyAny) -> PyResult { let cell: &PyCell = PyTryFrom::try_from(obj)?; - let ref_ = unsafe { cell.try_borrow_unguarded()? }; - Ok(ref_.clone()) + Ok(unsafe { cell.try_borrow_unguarded()?.clone() }) } } From 0e3f7cbc30c46afcc69407f44aedb280395a43d7 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sat, 22 Feb 2020 23:38:01 +0900 Subject: [PATCH 15/27] More documents for PyCell --- src/pycell.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 125 insertions(+), 10 deletions(-) diff --git a/src/pycell.rs b/src/pycell.rs index b2927ff1d54..7d03bb917e5 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -101,8 +101,11 @@ impl PyCellInner { /// [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) /// like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html). /// +/// # Examples +/// /// In most cases, `PyCell` is hidden behind `#[pymethods]`. /// However, you can construct `&PyCell` directly to test your pyclass in Rust code. +/// /// ``` /// # use pyo3::prelude::*; /// #[pyclass] @@ -121,23 +124,25 @@ impl PyCellInner { /// // you can expose PyCell to Python snippets /// pyo3::py_run!(py, book_cell, "assert book_cell.name[-6:] == 'Castle'"); /// ``` -/// You can also use `slf: &PyCell` as an alternative `self` receiver of `#[pymethod]`. +/// You can use `slf: &PyCell` as an alternative `self` receiver of `#[pymethod]`, +/// though you rarely need it. /// ``` /// # use pyo3::prelude::*; /// use std::collections::HashMap; /// #[pyclass] /// #[derive(Default)] /// struct Counter { -/// data: HashMap, +/// counter: HashMap /// } /// #[pymethods] /// impl Counter { +/// // You can use &mut self here, but now we use &PyCell for demonstration /// fn increment(slf: &PyCell, name: String) -> PyResult { /// let mut slf_mut = slf.try_borrow_mut()?; -/// // Now a mutable reference exists so we cannot another one +/// // Now a mutable reference exists so we cannot get another one /// assert!(slf.try_borrow().is_err()); /// assert!(slf.try_borrow_mut().is_err()); -/// let counter = slf_mut.data.entry(name).or_insert(0); +/// let counter = slf_mut.counter.entry(name).or_insert(0); /// *counter += 1; /// Ok(*counter) /// } @@ -156,6 +161,7 @@ pub struct PyCell { impl PyCell { /// Make new `PyCell` on the Python heap and returns the reference of it. + /// pub fn new(py: Python, value: impl Into>) -> PyResult<&Self> where T::BaseLayout: crate::type_object::PyObjectSizedLayout, @@ -167,14 +173,50 @@ impl PyCell { } } + /// Immutably borrows the value `T`. This borrow lasts untill the returned `PyRef` exists. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use + /// [`try_borrow`](#method.try_borrow). pub fn borrow(&self) -> PyRef<'_, T> { self.try_borrow().expect("Already mutably borrowed") } + /// Mutably borrows the value `T`. This borrow lasts untill the returned `PyRefMut` exists. + /// + /// # Panics + /// + /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use + /// [`try_borrow_mut`](#method.try_borrow_mut). pub fn borrow_mut(&self) -> PyRefMut<'_, T> { self.try_borrow_mut().expect("Already borrowed") } + /// Immutably borrows the value `T`, returning an error if the value is currently + /// mutably borrowed. This borrow lasts untill the returned `PyRef` exists. + /// + /// This is the non-panicking variant of [`borrow`](#method.borrow). + /// + /// # Examples + /// + /// ``` + /// # use pyo3::prelude::*; + /// #[pyclass] + /// struct Class {} + /// let gil = Python::acquire_gil(); + /// let py = gil.python(); + /// let c = PyCell::new(py, Class {}).unwrap(); + /// { + /// let m = c.borrow_mut(); + /// assert!(c.try_borrow().is_err()); + /// } + /// + /// { + /// let m = c.borrow(); + /// assert!(c.try_borrow().is_ok()); + /// } + /// ``` pub fn try_borrow(&self) -> Result, PyBorrowError> { let flag = self.inner.get_borrow_flag(); if flag == BorrowFlag::HAS_MUTABLE_BORROW { @@ -185,6 +227,27 @@ impl PyCell { } } + /// Mutably borrows the value `T`, returning an error if the value is currently borrowed. + /// This borrow lasts untill the returned `PyRefMut` exists. + /// + /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut). + /// + /// # Examples + /// + /// ``` + /// # use pyo3::prelude::*; + /// #[pyclass] + /// struct Class {} + /// let gil = Python::acquire_gil(); + /// let py = gil.python(); + /// let c = PyCell::new(py, Class {}).unwrap(); + /// { + /// let m = c.borrow(); + /// assert!(c.try_borrow_mut().is_err()); + /// } + /// + /// assert!(c.try_borrow_mut().is_ok()); + /// ``` pub fn try_borrow_mut(&self) -> Result, PyBorrowMutError> { if self.inner.get_borrow_flag() != BorrowFlag::UNUSED { Err(PyBorrowMutError { _private: () }) @@ -194,6 +257,35 @@ impl PyCell { } } + /// Immutably borrows the value `T`, returning an error if the value is + /// currently mutably borrowed. + /// + /// # Safety + /// + /// This method is unsafe because it does not return a `PyRef`, + /// thus leaving the borrow flag untouched. Mutably borrowing the `PyCell` + /// while the reference returned by this method is alive is undefined behaviour. + /// + /// # Examples + /// + /// ``` + /// # use pyo3::prelude::*; + /// #[pyclass] + /// struct Class {} + /// let gil = Python::acquire_gil(); + /// let py = gil.python(); + /// let c = PyCell::new(py, Class {}).unwrap(); + /// + /// { + /// let m = c.borrow_mut(); + /// assert!(unsafe { c.try_borrow_unguarded() }.is_err()); + /// } + /// + /// { + /// let m = c.borrow(); + /// assert!(unsafe { c.try_borrow_unguarded() }.is_ok()); + /// } + /// ``` pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, PyBorrowError> { if self.inner.get_borrow_flag() == BorrowFlag::HAS_MUTABLE_BORROW { Err(PyBorrowError { _private: () }) @@ -202,12 +294,35 @@ impl PyCell { } } - pub unsafe fn try_borrow_mut_unguarded(&self) -> Result<&mut T, PyBorrowMutError> { - if self.inner.get_borrow_flag() != BorrowFlag::UNUSED { - Err(PyBorrowMutError { _private: () }) - } else { - Ok(&mut *self.inner.value.get()) - } + /// Replaces the wrapped value with a new one, returning the old value, + /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + #[inline] + pub fn replace(&self, t: T) -> T { + std::mem::replace(&mut *self.borrow_mut(), t) + } + + /// Replaces the wrapped value with a new one computed from `f`, returning the old value. + /// + /// # Panics + /// + /// Panics if the value is currently borrowed. + pub fn replace_with T>(&self, f: F) -> T { + let mut_borrow = &mut *self.borrow_mut(); + let replacement = f(mut_borrow); + std::mem::replace(mut_borrow, replacement) + } + + /// Swaps the wrapped value of `self` with the wrapped value of `other`. + /// + /// # Panics + /// + /// Panics if the value in either `PyCell` is currently borrowed. + #[inline] + pub fn swap(&self, other: &Self) { + std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) } pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> From 68a3b15943f376c19dd8eca849ea3c89025db2ab Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 23 Feb 2020 00:56:58 +0900 Subject: [PATCH 16/27] Use PyBorrowFlagLayout to ensure the baseclass has a borrow flag --- pyo3-derive-backend/src/pyclass.rs | 2 +- src/derive_utils.rs | 4 ++-- src/freelist.rs | 2 +- src/instance.rs | 4 ++-- src/pycell.rs | 37 ++++++++++++++++++++---------- src/pyclass.rs | 2 +- src/pyclass_init.rs | 15 ++++++------ src/type_object.rs | 18 ++++++++------- src/types/any.rs | 4 ++-- src/types/mod.rs | 6 ++--- 10 files changed, 55 insertions(+), 39 deletions(-) diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index 0cf99099d43..a36bcd2f0b0 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -362,7 +362,7 @@ fn impl_class( quote! { 0 } }; let base_layout = if attr.has_extends { - quote! { ::Layout } + quote! { ::LayoutAsBase } } else { quote! { pyo3::pycell::PyCellBase } }; diff --git a/src/derive_utils.rs b/src/derive_utils.rs index 1f0387f91d6..7b66675402e 100644 --- a/src/derive_utils.rs +++ b/src/derive_utils.rs @@ -223,13 +223,13 @@ impl GetPropertyValue for PyObject { pub trait PyBaseTypeUtils { type Dict; type WeakRef; - type Layout; + type LayoutAsBase; type BaseNativeType; } impl PyBaseTypeUtils for T { type Dict = T::Dict; type WeakRef = T::WeakRef; - type Layout = crate::pycell::PyCellInner; + type LayoutAsBase = crate::pycell::PyCellInner; type BaseNativeType = T::BaseNativeType; } diff --git a/src/freelist.rs b/src/freelist.rs index e40dfdcc175..d3a4ae4e413 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -4,7 +4,7 @@ use crate::ffi; use crate::pyclass::{tp_free_fallback, PyClassAlloc}; -use crate::type_object::{PyObjectLayout, PyTypeInfo}; +use crate::type_object::{PyLayout, PyTypeInfo}; use crate::Python; use std::mem; use std::os::raw::c_void; diff --git a/src/instance.rs b/src/instance.rs index dda3a56afb0..8a541710570 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -3,7 +3,7 @@ use crate::err::{PyErr, PyResult}; use crate::gil; use crate::object::PyObject; use crate::objectprotocol::ObjectProtocol; -use crate::type_object::PyDowncastImpl; +use crate::type_object::{PyBorrowFlagLayout, PyDowncastImpl}; use crate::types::PyAny; use crate::{ ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyCell, PyClass, PyClassInitializer, @@ -39,7 +39,7 @@ impl Py { pub fn new(py: Python, value: impl Into>) -> PyResult> where T: PyClass, - T::BaseLayout: crate::type_object::PyObjectSizedLayout, + T::BaseLayout: PyBorrowFlagLayout, { let initializer = value.into(); let obj = unsafe { initializer.create_cell(py)? }; diff --git a/src/pycell.rs b/src/pycell.rs index 7d03bb917e5..bf6d4b5883e 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -2,7 +2,7 @@ use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject}; use crate::pyclass_init::PyClassInitializer; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; -use crate::type_object::{PyDowncastImpl, PyObjectLayout, PyObjectSizedLayout, PyTypeInfo}; +use crate::type_object::{PyBorrowFlagLayout, PyDowncastImpl, PyLayout, PySizedLayout, PyTypeInfo}; use crate::types::PyAny; use crate::{ffi, FromPy, PyClass, PyErr, PyNativeType, PyObject, PyResult, Python}; use std::cell::{Cell, UnsafeCell}; @@ -17,10 +17,10 @@ pub struct PyCellBase { borrow_flag: Cell, } -unsafe impl PyObjectLayout for PyCellBase +unsafe impl PyLayout for PyCellBase where T: PyTypeInfo + PyNativeType, - T::Layout: PyObjectSizedLayout, + T::Layout: PySizedLayout, { const IS_NATIVE_TYPE: bool = true; unsafe fn unchecked_ref(&self) -> &T { @@ -31,11 +31,18 @@ where } } -// This impl ensures `PyCellBase` can be a base type. -impl PyObjectSizedLayout for PyCellBase +// Thes impls ensures `PyCellBase` can be a base type. +impl PySizedLayout for PyCellBase where T: PyTypeInfo + PyNativeType, - T::Layout: PyObjectSizedLayout, + T::Layout: PySizedLayout, +{ +} + +unsafe impl PyBorrowFlagLayout for PyCellBase +where + T: PyTypeInfo + PyNativeType, + T::Layout: PySizedLayout, { } @@ -56,7 +63,7 @@ impl AsPyPointer for PyCellInner { } } -unsafe impl PyObjectLayout for PyCellInner { +unsafe impl PyLayout for PyCellInner { const IS_NATIVE_TYPE: bool = false; fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.ob_base) @@ -76,8 +83,9 @@ unsafe impl PyObjectLayout for PyCellInner { } } -// This impl ensures `PyCellInner` can be a base type. -impl PyObjectSizedLayout for PyCellInner {} +// Thes impls ensures `PyCellInner` can be a base type. +impl PySizedLayout for PyCellInner {} +unsafe impl PyBorrowFlagLayout for PyCellInner {} impl PyCellInner { fn get_borrow_flag(&self) -> BorrowFlag { @@ -164,7 +172,7 @@ impl PyCell { /// pub fn new(py: Python, value: impl Into>) -> PyResult<&Self> where - T::BaseLayout: crate::type_object::PyObjectSizedLayout, + T::BaseLayout: PyBorrowFlagLayout, { unsafe { let initializer = value.into(); @@ -325,9 +333,12 @@ impl PyCell { std::mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) } + /// Allocates new PyCell without initilizing value. + /// Requires `T::BaseLayout: PyBorrowFlagLayout` to ensure that + /// this layout has a borrow flag. pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> where - T::BaseLayout: crate::type_object::PyObjectSizedLayout, + T::BaseLayout: PyBorrowFlagLayout, { let base = T::alloc(py); if base.is_null() { @@ -342,7 +353,7 @@ impl PyCell { } } -unsafe impl PyObjectLayout for PyCell { +unsafe impl PyLayout for PyCell { const IS_NATIVE_TYPE: bool = false; fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.inner.ob_base) @@ -598,6 +609,7 @@ impl BorrowFlag { } } +/// An error returned by [`PyCell::try_borrow`](struct.PyCell.html#method.try_borrow). pub struct PyBorrowError { _private: (), } @@ -614,6 +626,7 @@ impl fmt::Display for PyBorrowError { } } +/// An error returned by [`PyCell::try_borrow_mut`](struct.PyCell.html#method.try_borrow_mut). pub struct PyBorrowMutError { _private: (), } diff --git a/src/pyclass.rs b/src/pyclass.rs index c9fd8130036..fa06336c253 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -1,7 +1,7 @@ //! `PyClass` trait use crate::class::methods::{PyMethodDefType, PyMethodsProtocol}; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; -use crate::type_object::{type_flags, PyObjectLayout}; +use crate::type_object::{type_flags, PyLayout}; use crate::{class, ffi, gil, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python}; use std::ffi::CString; use std::os::raw::c_void; diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index 33654ecf3bf..14ced26c588 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -1,5 +1,5 @@ //! Initialization utilities for `#[pyclass]`. -use crate::type_object::{PyObjectLayout, PyObjectSizedLayout, PyTypeInfo}; +use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo}; use crate::{PyCell, PyClass, PyResult, Python}; use std::marker::PhantomData; @@ -8,7 +8,7 @@ use std::marker::PhantomData; /// This trait is intended to use internally for distinguishing `#[pyclass]` and /// Python native types. pub trait PyObjectInit: Sized { - fn init_class>(self, layout: &mut L); + fn init_class>(self, layout: &mut L); private_decl! {} } @@ -16,7 +16,7 @@ pub trait PyObjectInit: Sized { pub struct PyNativeTypeInitializer(PhantomData); impl PyObjectInit for PyNativeTypeInitializer { - fn init_class>(self, _layout: &mut L) {} + fn init_class>(self, _layout: &mut L) {} private_impl! {} } @@ -108,17 +108,18 @@ impl PyClassInitializer { pub fn add_subclass(self, subclass_value: S) -> PyClassInitializer where S: PyClass + PyTypeInfo, - S::BaseLayout: PyObjectSizedLayout, + S::BaseLayout: PySizedLayout, S::BaseType: PyTypeInfo, { PyClassInitializer::new(subclass_value, self) } + // Create a new PyCell + initialize it #[doc(hidden)] pub unsafe fn create_cell(self, py: Python) -> PyResult<*mut PyCell> where T: PyClass, - T::BaseLayout: PyObjectSizedLayout, + T::BaseLayout: PyBorrowFlagLayout, { let cell = PyCell::internal_new(py)?; self.init_class(&mut *cell); @@ -127,7 +128,7 @@ impl PyClassInitializer { } impl PyObjectInit for PyClassInitializer { - fn init_class>(self, layout: &mut L) { + fn init_class>(self, layout: &mut L) { let Self { init, super_init } = self; unsafe { layout.py_init(init); @@ -152,7 +153,7 @@ where impl From<(S, B)> for PyClassInitializer where S: PyClass + PyTypeInfo, - S::BaseLayout: PyObjectSizedLayout, + S::BaseLayout: PySizedLayout, B: PyClass + PyTypeInfo>, B::BaseType: PyTypeInfo>, { diff --git a/src/type_object.rs b/src/type_object.rs index 1570c526150..59d90c1f8d7 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -9,12 +9,12 @@ use std::cell::UnsafeCell; use std::ptr::NonNull; use std::sync::atomic::{AtomicBool, Ordering}; -/// `T: PyObjectLayout` represents that `T` is a concrete representaion of `U` in Python heap. +/// `T: PyLayout` represents that `T` is a concrete representaion of `U` in Python heap. /// E.g., `PyCell` is a concrete representaion of all `pyclass`es, and `ffi::PyObject` /// is of `PyAny`. /// /// This trait is intended to be used internally. -pub unsafe trait PyObjectLayout { +pub unsafe trait PyLayout { const IS_NATIVE_TYPE: bool = true; fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { None @@ -26,12 +26,14 @@ pub unsafe trait PyObjectLayout { unsafe fn unchecked_mut(&self) -> &mut T; } -/// `T: PyObjectSizedLayout` represents `T` is not a instance of +/// `T: PySizedLayout` represents `T` is not a instance of /// [`PyVarObject`](https://docs.python.org/3.8/c-api/structures.html?highlight=pyvarobject#c.PyVarObject). /// , in addition that `T` is a concrete representaion of `U`. -/// -/// `pyclass`es need this trait for their base class. -pub trait PyObjectSizedLayout: PyObjectLayout + Sized {} +pub trait PySizedLayout: PyLayout + Sized {} + +/// Marker type indicates that `Self` can be a baselayout of PyClass. +/// This trait assumes a certain layout and thus is unsafe. +pub unsafe trait PyBorrowFlagLayout: PyLayout + Sized {} /// Our custom type flags #[doc(hidden)] @@ -99,10 +101,10 @@ pub unsafe trait PyTypeInfo: Sized { type BaseType: PyTypeInfo + PyTypeObject; /// Layout - type Layout: PyObjectLayout; + type Layout: PyLayout; /// Layout of Basetype. - type BaseLayout: PyObjectLayout; + type BaseLayout: PySizedLayout; /// Initializer for layout type Initializer: PyObjectInit; diff --git a/src/types/any.rs b/src/types/any.rs index 9a1cd113277..250e1f9f063 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -23,7 +23,7 @@ use crate::{ffi, PyObject}; /// ``` #[repr(transparent)] pub struct PyAny(PyObject, Unsendable); -unsafe impl crate::type_object::PyObjectLayout for ffi::PyObject { +unsafe impl crate::type_object::PyLayout for ffi::PyObject { unsafe fn unchecked_ref(&self) -> &PyAny { &*((&self) as *const &Self as *const _) } @@ -31,7 +31,7 @@ unsafe impl crate::type_object::PyObjectLayout for ffi::PyObject { &mut *((&self) as *const &Self as *const _ as *mut _) } } -impl crate::type_object::PyObjectSizedLayout for ffi::PyObject {} +impl crate::type_object::PySizedLayout for ffi::PyObject {} pyobject_native_type_named!(PyAny); pyobject_native_type_convert!( PyAny, diff --git a/src/types/mod.rs b/src/types/mod.rs index 1e7c247df35..7484132f120 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -58,7 +58,7 @@ macro_rules! pyobject_native_type_named ( macro_rules! impl_layout { ($name: ty, $layout: path) => { - unsafe impl $crate::type_object::PyObjectLayout<$name> for $layout { + unsafe impl $crate::type_object::PyLayout<$name> for $layout { unsafe fn unchecked_ref(&self) -> &$name { &*((&self) as *const &Self as *const _) } @@ -73,11 +73,11 @@ macro_rules! impl_layout { macro_rules! pyobject_native_type { ($name: ty, $layout: path, $typeobject: expr, $module: expr, $checkfunction: path $(,$type_param: ident)*) => { impl_layout!($name, $layout); - impl $crate::type_object::PyObjectSizedLayout<$name> for $layout {} + impl $crate::type_object::PySizedLayout<$name> for $layout {} impl $crate::derive_utils::PyBaseTypeUtils for $name { type Dict = $crate::pyclass_slots::PyClassDummySlot; type WeakRef = $crate::pyclass_slots::PyClassDummySlot; - type Layout = $crate::pycell::PyCellBase<$name>; + type LayoutAsBase = $crate::pycell::PyCellBase<$name>; type BaseNativeType = $name; } pyobject_native_type_named!($name $(,$type_param)*); From 9bc41923be1fe99f9e694414e73368fb3ffe40ab Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 25 Feb 2020 19:56:58 +0900 Subject: [PATCH 17/27] More documents for AsPyRef and PyRef --- src/conversion.rs | 2 ++ src/instance.rs | 36 +++++++++++++++++++++++++++++++++++- src/pycell.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/type_object.rs | 3 ++- 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/conversion.rs b/src/conversion.rs index 469eb749e4d..9c2e802de4e 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -300,6 +300,8 @@ where } /// Trait implemented by Python object types that allow a checked downcast. +/// If `T` implements `PyTryFrom`, we can convert `&PyAny` to `&T`. +/// /// This trait is similar to `std::convert::TryFrom` pub trait PyTryFrom<'v>: Sized + PyDowncastImpl { /// Cast from a concrete Python object type to PyObject. diff --git a/src/instance.rs b/src/instance.rs index 8a541710570..3c7db293019 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -35,7 +35,11 @@ unsafe impl Send for Py {} unsafe impl Sync for Py {} impl Py { - /// Create new instance of T and move it under python management + /// Create a new instance `Py`. + /// + /// This method is **soft-duplicated** since PyO3 0.9.0. + /// Use [`PyCell::new`](../pycell/struct.PyCell.html#method.new) and + /// `Py::from` instead. pub fn new(py: Python, value: impl Into>) -> PyResult> where T: PyClass, @@ -118,6 +122,36 @@ impl Py { } } +/// Retrives `&'py` types from `Py` or `PyObject`. +/// +/// # Examples +/// `PyObject::as_ref` returns `&PyAny`. +/// ``` +/// # use pyo3::prelude::*; +/// use pyo3::ObjectProtocol; +/// let obj: PyObject = { +/// let gil = Python::acquire_gil(); +/// let py = gil.python(); +/// py.eval("[]", None, None).unwrap().to_object(py) +/// }; +/// let gil = Python::acquire_gil(); +/// let py = gil.python(); +/// assert_eq!(obj.as_ref(py).len().unwrap(), 0); // PyAny implements ObjectProtocol +/// ``` +/// `Py::as_ref` returns `&PyDict`, `&PyList` or so for native types, and `&PyCell` +/// for `#[pyclass]`. +/// ``` +/// # use pyo3::prelude::*; +/// use pyo3::ObjectProtocol; +/// let obj: PyObject = { +/// let gil = Python::acquire_gil(); +/// let py = gil.python(); +/// py.eval("[]", None, None).unwrap().to_object(py) +/// }; +/// let gil = Python::acquire_gil(); +/// let py = gil.python(); +/// assert_eq!(obj.as_ref(py).len().unwrap(), 0); // PyAny implements ObjectProtocol +/// ``` pub trait AsPyRef: Sized { type Target; /// Return reference to object. diff --git a/src/pycell.rs b/src/pycell.rs index bf6d4b5883e..d2902a7be60 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -465,6 +465,43 @@ where T: PyClass + PyTypeInfo>, U: PyClass, { + /// Get `PyRef`. + /// You can use this method to get super class of super class. + /// + /// # Examples + /// ``` + /// # use pyo3::prelude::*; + /// #[pyclass] + /// struct Base1 { + /// name1: &'static str, + /// } + /// #[pyclass(extends=Base1)] + /// struct Base2 { + /// name2: &'static str, + /// } + /// #[pyclass(extends=Base2)] + /// struct Sub { + /// name3: &'static str, + /// } + /// #[pymethods] + /// impl Sub { + /// #[new] + /// fn new() -> PyClassInitializer { + /// PyClassInitializer::from(Base1{ name1: "base1" }) + /// .add_subclass(Base2 { name2: "base2" }) + /// .add_subclass(Self { name3: "sub" }) + /// } + /// fn name(slf: PyRef) -> String { + /// let subname = slf.name3; + /// let super_ = slf.into_super(); + /// format!("{} {} {}", super_.as_ref().name1, super_.name2, subname) + /// } + /// } + /// # let gil = Python::acquire_gil(); + /// # let py = gil.python(); + /// # let sub = PyCell::new(py, Sub::new()).unwrap(); + /// # pyo3::py_run!(py, sub, "assert sub.name() == 'base1 base2 sub'") + /// ``` pub fn into_super(self) -> PyRef<'p, U> { let PyRef { inner } = self; std::mem::forget(self); @@ -539,6 +576,8 @@ where T: PyClass + PyTypeInfo>, U: PyClass, { + /// Get `PyRef`. + /// See [`PyRef::into_super`](struct.PyRef.html#method.into_super) for more. pub fn into_super(self) -> PyRefMut<'p, U> { let PyRefMut { inner } = self; std::mem::forget(self); diff --git a/src/type_object.rs b/src/type_object.rs index 59d90c1f8d7..b23899052df 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -54,13 +54,14 @@ pub mod type_flags { pub const EXTENDED: usize = 1 << 4; } -/// Reference abstraction for `PyClass` and `PyNativeType`. +/// Reference abstraction for `PyClass` and `PyNativeType`. Used internaly. // DEVELOPPER NOTE(kngwyu): // `&PyCell` is a pointer but `&PyAny` is a pointer of a pointer, so we need // two different abstraction for them. // This mismatch eventually should be fixed with https://github.com/PyO3/pyo3/issues/679, // but now it's not the time. pub unsafe trait PyDowncastImpl { + /// Cast `&PyAny` to `&Self` without no type checking. unsafe fn unchecked_downcast(obj: &PyAny) -> &Self; private_decl! {} } From 18957159f4469f5122d0a49e2391e6a711c71698 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 25 Feb 2020 20:02:03 +0900 Subject: [PATCH 18/27] Add tests for inheriting class with dict or weakref --- tests/test_dunder.rs | 21 +++++++++++++++++++++ tests/test_gc.rs | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs index 6fbafb63423..5d5d8dfce24 100755 --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -497,6 +497,27 @@ fn access_dunder_dict() { ); } +// If the base class has dict support, child class also has dict +#[pyclass(extends=DunderDictSupport)] +struct InheritDict { + _value: usize, +} + +#[test] +fn inherited_dict() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = PyCell::new(py, (InheritDict { _value: 0 }, DunderDictSupport {})).unwrap(); + py_run!( + py, + inst, + r#" + inst.a = 1 + assert inst.__dict__ == {'a': 1} + "# + ); +} + #[pyclass(weakref, dict)] struct WeakRefDunderDictSupport {} diff --git a/tests/test_gc.rs b/tests/test_gc.rs index 04016a2338f..7225aa4933a 100644 --- a/tests/test_gc.rs +++ b/tests/test_gc.rs @@ -208,6 +208,24 @@ fn weakref_support() { ); } +// If the base class has weakref support, child class also has weakref. +#[pyclass(extends=WeakRefSupport)] +struct InheritWeakRef { + _value: usize, +} + +#[test] +fn inherited_weakref() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = PyCell::new(py, (InheritWeakRef { _value: 0 }, WeakRefSupport {})).unwrap(); + py_run!( + py, + inst, + "import weakref; assert weakref.ref(inst)() is inst" + ); +} + #[pyclass] struct BaseClassWithDrop { data: Option>, From bab146a580458f19298a73d219f4bf3f2edfd504 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 25 Feb 2020 20:16:30 +0900 Subject: [PATCH 19/27] Refactor set_item --- src/class/sequence.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 4798a24ec50..b753a059aa3 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -272,19 +272,19 @@ mod sq_ass_item_impl { .restore_and_minus1(py); } - match slf.try_borrow_mut() { + let result = match slf.try_borrow_mut() { Ok(mut slf) => { let value = py.from_borrowed_ptr::(value); - let result = match value.extract() { + match value.extract() { Ok(value) => slf.__setitem__(key.into(), value).into(), Err(e) => e.into(), - }; - match result { - Ok(_) => 0, - Err(e) => e.restore_and_minus1(py), } } - Err(e) => PyErr::from(e).restore_and_minus1(py), + Err(e) => Err(PyErr::from(e)), + }; + match result { + Ok(_) => 0, + Err(e) => e.restore_and_minus1(py), } } Some(wrap::) From ff1ae983265e925c11c97f12c36a3bd5a66347d5 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 25 Feb 2020 21:56:23 +0900 Subject: [PATCH 20/27] Fix class.md --- guide/src/class.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/guide/src/class.md b/guide/src/class.md index 5ccafb4f036..1a191931431 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -111,7 +111,7 @@ fn mymodule(_py: Python, m: &PyModule) -> PyResult<()> { ## PyCell and interior mutability You sometimes need to convert your `pyclass` into a Python object and access it from Rust code (e.g., for testing it). -`PyCell` is our primary interface for that. +[`PyCell`](https://pyo3.rs/master/doc/pyo3/pycell/struct.PyCell.html) is our primary interface for that. `PyCell` is always allocated in the Python heap, so we don't have the ownership of it. We can get `&PyCell`, not `PyCell`. @@ -127,12 +127,9 @@ For users who doesn't know `RefCell` well, here we repeat the Rust's borrowing r - References must always be valid. `PyCell` ensures these borrowing rules by tracking references at runtime. -TODO: link to the API document - ```rust # use pyo3::prelude::*; # use pyo3::types::PyDict; -# use pyo3::PyCell; #[pyclass] struct MyClass { #[pyo3(get)] @@ -153,6 +150,7 @@ let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap(); obj_mut.num = 5; // You cannot get any other refs until the PyRefMut is dropped assert!(obj.try_borrow().is_err()); + assert!(obj.try_borrow_mut().is_err()); } // You can convert `&PyCell` to Python object pyo3::py_run!(py, obj, "assert obj.num == 5") @@ -809,10 +807,6 @@ impl PyIterProtocol for MyIterator { } ``` -## Manually implementing pyclass - -TODO: Which traits to implement (basically `PyTypeCreate: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol + Sized`) and what they mean. - ## How methods are implemented Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a From 05159417a85c1d01bef8abfe580f3dfa33353e47 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Tue, 25 Feb 2020 22:09:39 +0900 Subject: [PATCH 21/27] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6929e41acda..331af07ff87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * The implementation for `IntoPy for T` where `U: FromPy` is no longer specializable. Control the behavior of this via the implementation of `FromPy`. [#713](https://github.com/PyO3/pyo3/pull/713) * Use `parking_lot::Mutex` instead of `spin::Mutex`. [#734](https://github.com/PyO3/pyo3/pull/734) * Bumped minimum Rust version to `1.42.0-nightly 2020-01-21`. [#761](https://github.com/PyO3/pyo3/pull/761) +* `PyClassShell` is renamed `PyCell` and has RefCell-like features. [#770](https://github.com/PyO3/pyo3/pull/770) ### Added @@ -24,6 +25,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * `FromPyObject` is now automatically implemented for `T: Clone` pyclasses. [#730](https://github.com/PyO3/pyo3/pull/730) * `#[pyo3(get)]` and `#[pyo3(set)]` will now use the Rust doc-comment from the field for the Python property. [#755](https://github.com/PyO3/pyo3/pull/755) * `#[setter]` functions may now take an argument of `Pyo3::Python`. [#760](https://github.com/PyO3/pyo3/pull/760) +* New `PyRef` and `PyRefMut`. [#770](https://github.com/PyO3/pyo3/pull/770) +* `PyTypeInfo::BaseLayout` and `PyClass::BaseNativeType`. [#770](https://github.com/PyO3/pyo3/pull/770) +* `PyDowncastImpl`. [#770](https://github.com/PyO3/pyo3/pull/770) ### Fixed @@ -38,6 +42,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * `PyRef`, `PyRefMut`, `PyRawObject`. [#683](https://github.com/PyO3/pyo3/pull/683) * `PyNoArgsFunction`. [#741](https://github.com/PyO3/pyo3/pull/741) * `initialize_type()`. To set the module name for a `#[pyclass]`, use the `module` argument to the macro. #[751](https://github.com/PyO3/pyo3/pull/751) +* `AsPyRef::as_mut/with/with_mut/into_py/into_mut_py`. [#770](https://github.com/PyO3/pyo3/pull/770) +* `PyTryFrom::try_from_mut/try_from_mut_exact/try_from_mut_unchecked`. [#770](https://github.com/PyO3/pyo3/pull/770) +* `Python::mut_from_owned_ptr/mut_from_borrowed_ptr`. [#770](https://github.com/PyO3/pyo3/pull/770) +* `ObjectProtocol::get_mut_base`. [#770](https://github.com/PyO3/pyo3/pull/770) ## [0.8.5] From 6a64806d9063b3e3a92dca2353e17d13d8500a79 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Wed, 26 Feb 2020 17:10:46 +0900 Subject: [PATCH 22/27] Address clippy warnings --- src/callback.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/callback.rs b/src/callback.rs index 5abec052815..ab8e17eea9b 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -13,7 +13,7 @@ use std::{isize, ptr}; /// Convert the result of callback function into the appropriate return value. pub trait CallbackConverter { type Source; - type Result; + type Result: Copy; const ERR_VALUE: Self::Result; fn convert(s: Self::Source, py: Python) -> Self::Result; @@ -143,6 +143,7 @@ where C::convert_result(py, value) } +// Same as cb_convert(PyObjectCallbackConverter, py, value) #[inline] pub fn cb_obj_convert>( py: Python, From dcac5ad200433f081099a8d0b64c129be3cce4a1 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Wed, 26 Feb 2020 17:19:33 +0900 Subject: [PATCH 23/27] Simplify PyLayout --- src/pycell.rs | 33 ++++++++++++--------------------- src/type_object.rs | 4 +--- src/types/any.rs | 7 ++----- src/types/mod.rs | 7 ++----- 4 files changed, 17 insertions(+), 34 deletions(-) diff --git a/src/pycell.rs b/src/pycell.rs index d2902a7be60..0e7761c512f 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -23,11 +23,8 @@ where T::Layout: PySizedLayout, { const IS_NATIVE_TYPE: bool = true; - unsafe fn unchecked_ref(&self) -> &T { - &*((&self) as *const &Self as *const _) - } - unsafe fn unchecked_mut(&self) -> &mut T { - &mut *((&self) as *const &Self as *const _ as *mut _) + unsafe fn get_ptr(&self) -> *mut T { + (&self) as *const &Self as *const _ as *mut _ } } @@ -68,11 +65,8 @@ unsafe impl PyLayout for PyCellInner { fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.ob_base) } - unsafe fn unchecked_ref(&self) -> &T { - &*self.value.get() - } - unsafe fn unchecked_mut(&self) -> &mut T { - &mut *self.value.get() + 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)); @@ -358,11 +352,8 @@ unsafe impl PyLayout for PyCell { fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.inner.ob_base) } - unsafe fn unchecked_ref(&self) -> &T { - self.inner.unchecked_ref() - } - unsafe fn unchecked_mut(&self) -> &mut T { - self.inner.unchecked_mut() + 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)); @@ -456,7 +447,7 @@ pub struct PyRef<'p, T: PyClass> { impl<'p, T: PyClass> AsRef for PyRef<'p, T> { fn as_ref(&self) -> &T::BaseType { - unsafe { self.inner.ob_base.unchecked_ref() } + unsafe { &*self.inner.ob_base.get_ptr() } } } @@ -516,7 +507,7 @@ impl<'p, T: PyClass> Deref for PyRef<'p, T> { #[inline] fn deref(&self) -> &T { - unsafe { self.inner.unchecked_ref() } + unsafe { &*self.inner.get_ptr() } } } @@ -561,13 +552,13 @@ pub struct PyRefMut<'p, T: PyClass> { impl<'p, T: PyClass> AsRef for PyRefMut<'p, T> { fn as_ref(&self) -> &T::BaseType { - unsafe { self.inner.ob_base.unchecked_ref() } + unsafe { &*self.inner.ob_base.get_ptr() } } } impl<'p, T: PyClass> AsMut for PyRefMut<'p, T> { fn as_mut(&mut self) -> &mut T::BaseType { - unsafe { self.inner.ob_base.unchecked_mut() } + unsafe { &mut *self.inner.ob_base.get_ptr() } } } @@ -592,14 +583,14 @@ impl<'p, T: PyClass> Deref for PyRefMut<'p, T> { #[inline] fn deref(&self) -> &T { - unsafe { self.inner.unchecked_ref() } + unsafe { &*self.inner.get_ptr() } } } impl<'p, T: PyClass> DerefMut for PyRefMut<'p, T> { #[inline] fn deref_mut(&mut self) -> &mut T { - unsafe { self.inner.unchecked_mut() } + unsafe { &mut *self.inner.get_ptr() } } } diff --git a/src/type_object.rs b/src/type_object.rs index b23899052df..9aac8ed3926 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -21,9 +21,7 @@ pub unsafe trait PyLayout { } unsafe fn py_init(&mut self, _value: T) {} unsafe fn py_drop(&mut self, _py: Python) {} - unsafe fn unchecked_ref(&self) -> &T; - #[allow(clippy::mut_from_ref)] - unsafe fn unchecked_mut(&self) -> &mut T; + 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 250e1f9f063..76280237a96 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -24,11 +24,8 @@ use crate::{ffi, PyObject}; #[repr(transparent)] pub struct PyAny(PyObject, Unsendable); unsafe impl crate::type_object::PyLayout for ffi::PyObject { - unsafe fn unchecked_ref(&self) -> &PyAny { - &*((&self) as *const &Self as *const _) - } - unsafe fn unchecked_mut(&self) -> &mut PyAny { - &mut *((&self) as *const &Self as *const _ as *mut _) + unsafe fn get_ptr(&self) -> *mut PyAny { + (&self) as *const &Self as *const _ as *mut _ } } impl crate::type_object::PySizedLayout for ffi::PyObject {} diff --git a/src/types/mod.rs b/src/types/mod.rs index 7484132f120..badd4900a66 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -59,11 +59,8 @@ 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 unchecked_ref(&self) -> &$name { - &*((&self) as *const &Self as *const _) - } - unsafe fn unchecked_mut(&self) -> &mut $name { - &mut *((&self) as *const &Self as *const _ as *mut _) + unsafe fn get_ptr(&self) -> *mut $name { + (&self) as *const &Self as *const _ as *mut _ } } }; From 461b32a4320387e08d8ae9e2669ee542a5e550ce Mon Sep 17 00:00:00 2001 From: kngwyu Date: Fri, 28 Feb 2020 16:49:25 +0900 Subject: [PATCH 24/27] More docs --- src/callback.rs | 2 ++ src/class/iter.rs | 4 ++-- src/class/macros.rs | 7 +++++-- src/pycell.rs | 9 +++++---- src/pyclass.rs | 5 ++++- src/type_object.rs | 30 +++++++++++++++++++++++------- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/callback.rs b/src/callback.rs index ab8e17eea9b..edeca66c2bd 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -11,6 +11,8 @@ use std::os::raw::c_int; use std::{isize, ptr}; /// Convert the result of callback function into the appropriate return value. +/// +/// Used by PyO3 macros. pub trait CallbackConverter { type Source; type Result: Copy; diff --git a/src/class/iter.rs b/src/class/iter.rs index c6d693617da..8d7522e3f6d 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -9,8 +9,8 @@ use std::ptr; /// Python Iterator Interface. /// -/// more information -/// `https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter` +/// Check [CPython doc](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter) +/// for more. #[allow(unused_variables)] pub trait PyIterProtocol<'p>: PyClass { fn __iter__(slf: PyRefMut) -> Self::Result diff --git a/src/class/macros.rs b/src/class/macros.rs index 124860d0cc6..7cb9ae7ed79 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -127,8 +127,7 @@ macro_rules! py_binary_num_func { }}; } -// NOTE(kngwyu): -// Now(2020 2/9) This macro is used only for inplace operation so I used call_mut here. +// NOTE(kngwyu): This macro is used only for inplace operations, so I used call_mut here. #[macro_export] #[doc(hidden)] macro_rules! py_binary_self_func { @@ -399,6 +398,7 @@ macro_rules! _call_impl { }; } +/// Call `slf.try_borrow()?.$fn(...)` macro_rules! call_ref { ($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { match $slf.try_borrow() { @@ -408,6 +408,7 @@ macro_rules! call_ref { }; } +/// Call `slf.try_borrow()?.$fn(...)` and returns the result using the given CallbackConverter macro_rules! call_ref_with_converter { ($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { match $slf.try_borrow() { @@ -417,6 +418,7 @@ macro_rules! call_ref_with_converter { }; } +/// Call `slf.try_borrow_mut()?.$fn(...)` macro_rules! call_mut { ($slf: expr, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { match $slf.try_borrow_mut() { @@ -426,6 +428,7 @@ macro_rules! call_mut { }; } +/// Call `slf.try_borrow_mut()?.$fn(...)` and returns the result using the given CallbackConverter macro_rules! call_mut_with_converter { ($slf: expr, $conv: expr, $py: ident, $fn: ident $(,$raw_args: expr)* $(; $args: expr)*) => { match $slf.try_borrow_mut() { diff --git a/src/pycell.rs b/src/pycell.rs index 0e7761c512f..8dba97d70e2 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -10,6 +10,8 @@ use std::fmt; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; +/// Base layout of PyCell. +/// This is necessary for sharing BorrowFlag between parents and children. #[doc(hidden)] #[repr(C)] pub struct PyCellBase { @@ -46,7 +48,7 @@ where /// Inner type of `PyCell` without dict slots and reference counter. /// This struct has two usages: /// 1. As an inner type of `PyRef` and `PyRefMut`. -/// 2. As a base class when `#[pyclass(Base)]` is specified. +/// 2. When `#[pyclass(extends=Base)]` is specified, `PyCellInner` is used as a base layout. #[doc(hidden)] #[repr(C)] pub struct PyCellInner { @@ -77,7 +79,7 @@ unsafe impl PyLayout for PyCellInner { } } -// Thes impls ensures `PyCellInner` can be a base type. +// These impls ensures `PyCellInner` can be a base type. impl PySizedLayout for PyCellInner {} unsafe impl PyBorrowFlagLayout for PyCellInner {} @@ -328,8 +330,7 @@ impl PyCell { } /// Allocates new PyCell without initilizing value. - /// Requires `T::BaseLayout: PyBorrowFlagLayout` to ensure that - /// this layout has a borrow flag. + /// Requires `T::BaseLayout: PyBorrowFlagLayout` to ensure `self` has a borrow flag. pub(crate) unsafe fn internal_new(py: Python) -> PyResult<*mut Self> where T::BaseLayout: PyBorrowFlagLayout, diff --git a/src/pyclass.rs b/src/pyclass.rs index fa06336c253..a3e0f375079 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -73,9 +73,12 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) { pub trait PyClass: PyTypeInfo> + Sized + PyClassAlloc + PyMethodsProtocol { + /// Specify this class has `#[pyclass(dict)]` or not. type Dict: PyClassDict; + /// Specify this class has `#[pyclass(weakref)]` or not. type WeakRef: PyClassWeakRef; - /// The closest native ancestor. + /// The closest native ancestor. This is `PyAny` by default, and when you declare + /// `#[pyclass(extends=PyList)]`, it's `PyList`. type BaseNativeType: PyTypeInfo + PyNativeType; } diff --git a/src/type_object.rs b/src/type_object.rs index 9aac8ed3926..799cd7238ee 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -29,8 +29,20 @@ pub unsafe trait PyLayout { /// , in addition that `T` is a concrete representaion of `U`. pub trait PySizedLayout: PyLayout + Sized {} -/// Marker type indicates that `Self` can be a baselayout of PyClass. -/// This trait assumes a certain layout and thus is unsafe. +/// Marker type indicates that `Self` can be a base layout of `PyClass`. +/// +/// # Safety +/// +/// Self should be layouted as follows: +/// ```ignore +/// #[repr(C)] +/// struct Self { +/// obj: ffi::PyObject, +/// borrow_flag: u64, +/// ... +/// } +/// ``` +/// Otherwise, implementing this trait is undefined behavior. pub unsafe trait PyBorrowFlagLayout: PyLayout + Sized {} /// Our custom type flags @@ -53,13 +65,17 @@ pub mod type_flags { } /// Reference abstraction for `PyClass` and `PyNativeType`. Used internaly. -// DEVELOPPER NOTE(kngwyu): -// `&PyCell` is a pointer but `&PyAny` is a pointer of a pointer, so we need -// two different abstraction for them. -// This mismatch eventually should be fixed with https://github.com/PyO3/pyo3/issues/679, -// but now it's not the time. +// NOTE(kngwyu): +// `&PyCell` is a pointer of `ffi::PyObject` but `&PyAny` is a pointer of a pointer, +// so we need abstraction. +// This mismatch eventually should be fixed(e.g., https://github.com/PyO3/pyo3/issues/679). pub unsafe trait PyDowncastImpl { /// Cast `&PyAny` to `&Self` without no type checking. + /// + /// # Safety + /// + /// Unless obj is not an instance of a type corresponding to Self, + /// this method causes undefined behavior. unsafe fn unchecked_downcast(obj: &PyAny) -> &Self; private_decl! {} } From 399e4bf9b1f7b7ab371da5eeef36d45c1446a1bf Mon Sep 17 00:00:00 2001 From: Yuji Kanagawa Date: Sun, 1 Mar 2020 12:43:04 +0900 Subject: [PATCH 25/27] Apply suggestions from code review Co-Authored-By: David Hewitt <1939362+davidhewitt@users.noreply.github.com> --- CHANGELOG.md | 3 +-- guide/src/class.md | 4 ++-- src/type_object.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 331af07ff87..f36e2d060f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * The implementation for `IntoPy for T` where `U: FromPy` is no longer specializable. Control the behavior of this via the implementation of `FromPy`. [#713](https://github.com/PyO3/pyo3/pull/713) * Use `parking_lot::Mutex` instead of `spin::Mutex`. [#734](https://github.com/PyO3/pyo3/pull/734) * Bumped minimum Rust version to `1.42.0-nightly 2020-01-21`. [#761](https://github.com/PyO3/pyo3/pull/761) -* `PyClassShell` is renamed `PyCell` and has RefCell-like features. [#770](https://github.com/PyO3/pyo3/pull/770) ### Added - +* `PyCell`, which has RefCell-like features. [#770](https://github.com/PyO3/pyo3/pull/770) * `PyClass`, `PyClassShell`, `PyObjectLayout`, `PyClassInitializer`. [#683](https://github.com/PyO3/pyo3/pull/683) * Implemented `IntoIterator` for `PySet` and `PyFrozenSet`. [#716](https://github.com/PyO3/pyo3/pull/716) * `FromPyObject` is now automatically implemented for `T: Clone` pyclasses. [#730](https://github.com/PyO3/pyo3/pull/730) diff --git a/guide/src/class.md b/guide/src/class.md index 1a191931431..d1b3d02f389 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -122,7 +122,7 @@ like [std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html Users who are familiar with `RefCell` can use `PyCell` just like `RefCell`. -For users who doesn't know `RefCell` well, here we repeat the Rust's borrowing rule: +For users who are not very familiar with `RefCell`, here is a reminder of Rust's rules of borrowing: - At any given time, you can have either (but not both of) one mutable reference or any number of immutable references. - References must always be valid. `PyCell` ensures these borrowing rules by tracking references at runtime. @@ -156,7 +156,7 @@ let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap(); pyo3::py_run!(py, obj, "assert obj.num == 5") ``` -`&PyCell` is bouded by the same lifetime as `GILGuard`. +`&PyCell` is bounded by the same lifetime as `GILGuard`. To avoid this you can use `Py`, which stores an object longer than the GIL lifetime. ```rust # use pyo3::prelude::*; diff --git a/src/type_object.rs b/src/type_object.rs index 799cd7238ee..939f26d2fb9 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -33,7 +33,7 @@ pub trait PySizedLayout: PyLayout + Sized {} /// /// # Safety /// -/// Self should be layouted as follows: +/// Self should be laid out as follows: /// ```ignore /// #[repr(C)] /// struct Self { From ca6227c739ab005e6649e9c49e1b5b41a950a21a Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 1 Mar 2020 12:58:28 +0900 Subject: [PATCH 26/27] Address review comments --- CHANGELOG.md | 6 +++--- src/pycell.rs | 4 ++-- src/pyclass.rs | 2 +- src/pyclass_init.rs | 2 +- src/type_object.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f36e2d060f3..fe0ce5e0db1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,15 +16,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * The implementation for `IntoPy for T` where `U: FromPy` is no longer specializable. Control the behavior of this via the implementation of `FromPy`. [#713](https://github.com/PyO3/pyo3/pull/713) * Use `parking_lot::Mutex` instead of `spin::Mutex`. [#734](https://github.com/PyO3/pyo3/pull/734) * Bumped minimum Rust version to `1.42.0-nightly 2020-01-21`. [#761](https://github.com/PyO3/pyo3/pull/761) +* `PyRef` and `PyRefMut` are renewed for `PyCell`. [#770](https://github.com/PyO3/pyo3/pull/770) ### Added * `PyCell`, which has RefCell-like features. [#770](https://github.com/PyO3/pyo3/pull/770) -* `PyClass`, `PyClassShell`, `PyObjectLayout`, `PyClassInitializer`. [#683](https://github.com/PyO3/pyo3/pull/683) +* `PyClass`, `PyLayout`, `PyClassInitializer`. [#683](https://github.com/PyO3/pyo3/pull/683) * Implemented `IntoIterator` for `PySet` and `PyFrozenSet`. [#716](https://github.com/PyO3/pyo3/pull/716) * `FromPyObject` is now automatically implemented for `T: Clone` pyclasses. [#730](https://github.com/PyO3/pyo3/pull/730) * `#[pyo3(get)]` and `#[pyo3(set)]` will now use the Rust doc-comment from the field for the Python property. [#755](https://github.com/PyO3/pyo3/pull/755) * `#[setter]` functions may now take an argument of `Pyo3::Python`. [#760](https://github.com/PyO3/pyo3/pull/760) -* New `PyRef` and `PyRefMut`. [#770](https://github.com/PyO3/pyo3/pull/770) * `PyTypeInfo::BaseLayout` and `PyClass::BaseNativeType`. [#770](https://github.com/PyO3/pyo3/pull/770) * `PyDowncastImpl`. [#770](https://github.com/PyO3/pyo3/pull/770) @@ -38,7 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Removed -* `PyRef`, `PyRefMut`, `PyRawObject`. [#683](https://github.com/PyO3/pyo3/pull/683) +* `PyRawObject`. [#683](https://github.com/PyO3/pyo3/pull/683) * `PyNoArgsFunction`. [#741](https://github.com/PyO3/pyo3/pull/741) * `initialize_type()`. To set the module name for a `#[pyclass]`, use the `module` argument to the macro. #[751](https://github.com/PyO3/pyo3/pull/751) * `AsPyRef::as_mut/with/with_mut/into_py/into_mut_py`. [#770](https://github.com/PyO3/pyo3/pull/770) diff --git a/src/pycell.rs b/src/pycell.rs index 8dba97d70e2..2a5500c8543 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -64,7 +64,7 @@ impl AsPyPointer for PyCellInner { unsafe impl PyLayout for PyCellInner { const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { + fn get_super(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.ob_base) } unsafe fn get_ptr(&self) -> *mut T { @@ -350,7 +350,7 @@ impl PyCell { unsafe impl PyLayout for PyCell { const IS_NATIVE_TYPE: bool = false; - fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { + fn get_super(&mut self) -> Option<&mut T::BaseLayout> { Some(&mut self.inner.ob_base) } unsafe fn get_ptr(&self) -> *mut T { diff --git a/src/pyclass.rs b/src/pyclass.rs index a3e0f375079..f9d1441c4ca 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -78,7 +78,7 @@ pub trait PyClass: /// Specify this class has `#[pyclass(weakref)]` or not. type WeakRef: PyClassWeakRef; /// The closest native ancestor. This is `PyAny` by default, and when you declare - /// `#[pyclass(extends=PyList)]`, it's `PyList`. + /// `#[pyclass(extends=PyDict)]`, it's `PyDict`. type BaseNativeType: PyTypeInfo + PyNativeType; } diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index 14ced26c588..3be018f241c 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -133,7 +133,7 @@ impl PyObjectInit for PyClassInitializer { unsafe { layout.py_init(init); } - if let Some(super_obj) = layout.get_super_or() { + if let Some(super_obj) = layout.get_super() { super_init.init_class(super_obj); } } diff --git a/src/type_object.rs b/src/type_object.rs index 939f26d2fb9..4530b1b0b9a 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -16,7 +16,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; /// This trait is intended to be used internally. pub unsafe trait PyLayout { const IS_NATIVE_TYPE: bool = true; - fn get_super_or(&mut self) -> Option<&mut T::BaseLayout> { + fn get_super(&mut self) -> Option<&mut T::BaseLayout> { None } unsafe fn py_init(&mut self, _value: T) {} From ee0c178fedbeab680618b8f802a7eb4b08f6de34 Mon Sep 17 00:00:00 2001 From: kngwyu Date: Mon, 2 Mar 2020 13:05:27 +0900 Subject: [PATCH 27/27] Remove ObjectProtocol::get_base and fix class.md --- CHANGELOG.md | 2 +- guide/src/class.md | 12 ++++-------- pyo3-derive-backend/src/method.rs | 4 ++-- src/objectprotocol.rs | 18 ++---------------- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe0ce5e0db1..774c850730d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * `AsPyRef::as_mut/with/with_mut/into_py/into_mut_py`. [#770](https://github.com/PyO3/pyo3/pull/770) * `PyTryFrom::try_from_mut/try_from_mut_exact/try_from_mut_unchecked`. [#770](https://github.com/PyO3/pyo3/pull/770) * `Python::mut_from_owned_ptr/mut_from_borrowed_ptr`. [#770](https://github.com/PyO3/pyo3/pull/770) -* `ObjectProtocol::get_mut_base`. [#770](https://github.com/PyO3/pyo3/pull/770) +* `ObjectProtocol::get_base/get_mut_base`. [#770](https://github.com/PyO3/pyo3/pull/770) ## [0.8.5] diff --git a/guide/src/class.md b/guide/src/class.md index d1b3d02f389..b5bf071d0b2 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -114,7 +114,7 @@ from Rust code (e.g., for testing it). [`PyCell`](https://pyo3.rs/master/doc/pyo3/pycell/struct.PyCell.html) is our primary interface for that. `PyCell` is always allocated in the Python heap, so we don't have the ownership of it. -We can get `&PyCell`, not `PyCell`. +We can only get `&PyCell`, not `PyCell`. Thus, to mutate data behind `&PyCell` safely, we employ the [Interior Mutability Pattern](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) @@ -242,8 +242,10 @@ baseclass of `T`. But for more deeply nested inheritance, you have to return `PyClassInitializer` explicitly. -To get a parent class from child, use `PyRef` instead of `&self`, +To get a parent class from a child, use `PyRef` instead of `&self`, or `PyRefMut` instead of `&mut self`. +Then you can access a parent class by `self_.as_ref()` as `&Self::BaseClass`, +or by `self_.into_super()` as `PyRef`. ```rust # use pyo3::prelude::*; @@ -311,12 +313,6 @@ impl SubSubClass { # pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000") ``` -To access the super class, you can use either of these two ways: -- Use `self_: &PyCell` instead of `self`, and call `get_super()` -- `ObjectProtocol::get_base` -We recommend `PyCell` here, since it makes the context much clearer. - - If `SubClass` does not provide a baseclass initialization, the compilation fails. ```compile_fail # use pyo3::prelude::*; diff --git a/pyo3-derive-backend/src/method.rs b/pyo3-derive-backend/src/method.rs index 4543f341cba..8042ce1169d 100644 --- a/pyo3-derive-backend/src/method.rs +++ b/pyo3-derive-backend/src/method.rs @@ -29,9 +29,9 @@ pub enum FnType { FnCall, FnClass, FnStatic, - // self_: &PyCell, + /// For methods taht have `self_: &PyCell` instead of self receiver PySelfRef(syn::TypeReference), - // self_: PyRef or PyRefMut + /// For methods taht have `self_: PyRef` or `PyRefMut` instead of self receiver PySelfPath(syn::TypePath), } diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index d777ee632d0..a2822a27c65 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -5,8 +5,8 @@ use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::exceptions::TypeError; use crate::types::{PyAny, PyDict, PyIterator, PyString, PyTuple, PyType}; use crate::{ - ffi, AsPyPointer, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, Py, PyNativeType, - PyObject, PyTryFrom, PyTypeInfo, Python, ToBorrowedObject, ToPyObject, + ffi, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyNativeType, PyObject, PyTryFrom, + Python, ToBorrowedObject, ToPyObject, }; use std::cmp::Ordering; use std::os::raw::c_int; @@ -170,12 +170,6 @@ pub trait ObjectProtocol { /// Gets the Python type pointer for this object. fn get_type_ptr(&self) -> *mut ffi::PyTypeObject; - /// Gets the Python base object for this object. - fn get_base(&self) -> &::BaseType - where - Self: PyTypeInfo, - ::BaseType: for<'py> FromPyPointer<'py>; - /// Casts the PyObject to a concrete Python object type. fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError> where @@ -445,14 +439,6 @@ where unsafe { (*self.as_ptr()).ob_type } } - fn get_base(&self) -> &::BaseType - where - Self: PyTypeInfo, - ::BaseType: for<'py> FromPyPointer<'py>, - { - unsafe { self.py().from_borrowed_ptr(self.as_ptr()) } - } - fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError> where D: PyTryFrom<'a>,