diff --git a/src/prelude.rs b/src/prelude.rs index 0af287e4..301e62b3 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -32,5 +32,6 @@ pub use crate::wrap_pyfunction; // pub(crate) use crate::types::bytes::PyBytesMethods; // pub(crate) use crate::types::dict::PyDictMethods; // pub(crate) use crate::types::float::PyFloatMethods; +// pub(crate) use crate::types::mapping::PyMappingMethods; // pub(crate) use crate::types::sequence::PySequenceMethods; // pub(crate) use crate::types::string::PyStringMethods; diff --git a/src/types/mapping.rs b/src/types/mapping.rs index 798b7268..bf6a6d49 100644 --- a/src/types/mapping.rs +++ b/src/types/mapping.rs @@ -1,6 +1,10 @@ use crate::err::{PyDowncastError, PyResult}; +use crate::ffi_ptr_ext::FfiPtrExt; +use crate::instance::Py2; +use crate::py_result_ext::PyResultExt; use crate::sync::GILOnceCell; use crate::type_object::PyTypeInfo; +use crate::types::any::PyAnyMethods; use crate::types::{PyAny, PyDict, PySequence, PyType}; use crate::{ffi, Py, PyNativeType, PyTypeCheck, Python, ToPyObject}; @@ -16,15 +20,13 @@ impl PyMapping { /// This is equivalent to the Python expression `len(self)`. #[inline] pub fn len(&self) -> PyResult { - let v = unsafe { ffi::PyMapping_Size(self.as_ptr()) }; - crate::err::error_on_minusone(self.py(), v)?; - Ok(v as usize) + Py2::borrowed_from_gil_ref(&self).len() } /// Returns whether the mapping is empty. #[inline] pub fn is_empty(&self) -> PyResult { - self.len().map(|l| l == 0) + Py2::borrowed_from_gil_ref(&self).is_empty() } /// Determines if the mapping contains the specified key. @@ -34,7 +36,7 @@ impl PyMapping { where K: ToPyObject, { - PyAny::contains(self, key) + Py2::borrowed_from_gil_ref(&self).contains(key) } /// Gets the item in self with key `key`. @@ -47,7 +49,9 @@ impl PyMapping { where K: ToPyObject, { - PyAny::get_item(self, key) + Py2::borrowed_from_gil_ref(&self) + .get_item(key) + .map(Py2::into_gil_ref) } /// Sets the item in self with key `key`. @@ -59,7 +63,7 @@ impl PyMapping { K: ToPyObject, V: ToPyObject, { - PyAny::set_item(self, key, value) + Py2::borrowed_from_gil_ref(&self).set_item(key, value) } /// Deletes the item with key `key`. @@ -70,34 +74,31 @@ impl PyMapping { where K: ToPyObject, { - PyAny::del_item(self, key) + Py2::borrowed_from_gil_ref(&self).del_item(key) } /// Returns a sequence containing all keys in the mapping. #[inline] pub fn keys(&self) -> PyResult<&PySequence> { - unsafe { - self.py() - .from_owned_ptr_or_err(ffi::PyMapping_Keys(self.as_ptr())) - } + Py2::borrowed_from_gil_ref(&self) + .keys() + .map(Py2::into_gil_ref) } /// Returns a sequence containing all values in the mapping. #[inline] pub fn values(&self) -> PyResult<&PySequence> { - unsafe { - self.py() - .from_owned_ptr_or_err(ffi::PyMapping_Values(self.as_ptr())) - } + Py2::borrowed_from_gil_ref(&self) + .values() + .map(Py2::into_gil_ref) } /// Returns a sequence of tuples of all (key, value) pairs in the mapping. #[inline] pub fn items(&self) -> PyResult<&PySequence> { - unsafe { - self.py() - .from_owned_ptr_or_err(ffi::PyMapping_Items(self.as_ptr())) - } + Py2::borrowed_from_gil_ref(&self) + .items() + .map(Py2::into_gil_ref) } /// Register a pyclass as a subclass of `collections.abc.Mapping` (from the Python standard @@ -110,6 +111,135 @@ impl PyMapping { } } +/// Implementation of functionality for [`PyMapping`]. +/// +/// These methods are defined for the `Py2<'py, PyMapping>` smart pointer, so to use method call +/// syntax these methods are separated into a trait, because stable Rust does not yet support +/// `arbitrary_self_types`. +#[doc(alias = "PyMapping")] +pub(crate) trait PyMappingMethods<'py> { + /// Returns the number of objects in the mapping. + /// + /// This is equivalent to the Python expression `len(self)`. + fn len(&self) -> PyResult; + + /// Returns whether the mapping is empty. + fn is_empty(&self) -> PyResult; + + /// Determines if the mapping contains the specified key. + /// + /// This is equivalent to the Python expression `key in self`. + fn contains(&self, key: K) -> PyResult + where + K: ToPyObject; + + /// Gets the item in self with key `key`. + /// + /// Returns an `Err` if the item with specified key is not found, usually `KeyError`. + /// + /// This is equivalent to the Python expression `self[key]`. + fn get_item(&self, key: K) -> PyResult> + where + K: ToPyObject; + + /// Sets the item in self with key `key`. + /// + /// This is equivalent to the Python expression `self[key] = value`. + fn set_item(&self, key: K, value: V) -> PyResult<()> + where + K: ToPyObject, + V: ToPyObject; + + /// Deletes the item with key `key`. + /// + /// This is equivalent to the Python statement `del self[key]`. + fn del_item(&self, key: K) -> PyResult<()> + where + K: ToPyObject; + + /// Returns a sequence containing all keys in the mapping. + fn keys(&self) -> PyResult>; + + /// Returns a sequence containing all values in the mapping. + fn values(&self) -> PyResult>; + + /// Returns a sequence of tuples of all (key, value) pairs in the mapping. + fn items(&self) -> PyResult>; +} + +impl<'py> PyMappingMethods<'py> for Py2<'py, PyMapping> { + #[inline] + fn len(&self) -> PyResult { + let v = unsafe { ffi::PyMapping_Size(self.as_ptr()) }; + crate::err::error_on_minusone(self.py(), v)?; + Ok(v as usize) + } + + #[inline] + fn is_empty(&self) -> PyResult { + self.len().map(|l| l == 0) + } + + fn contains(&self, key: K) -> PyResult + where + K: ToPyObject, + { + PyAnyMethods::contains(&**self, key) + } + + #[inline] + fn get_item(&self, key: K) -> PyResult> + where + K: ToPyObject, + { + PyAnyMethods::get_item(&**self, key) + } + + #[inline] + fn set_item(&self, key: K, value: V) -> PyResult<()> + where + K: ToPyObject, + V: ToPyObject, + { + PyAnyMethods::set_item(&**self, key, value) + } + + #[inline] + fn del_item(&self, key: K) -> PyResult<()> + where + K: ToPyObject, + { + PyAnyMethods::del_item(&**self, key) + } + + #[inline] + fn keys(&self) -> PyResult> { + unsafe { + ffi::PyMapping_Keys(self.as_ptr()) + .assume_owned_or_err(self.py()) + .downcast_into_unchecked() + } + } + + #[inline] + fn values(&self) -> PyResult> { + unsafe { + ffi::PyMapping_Values(self.as_ptr()) + .assume_owned_or_err(self.py()) + .downcast_into_unchecked() + } + } + + #[inline] + fn items(&self) -> PyResult> { + unsafe { + ffi::PyMapping_Items(self.as_ptr()) + .assume_owned_or_err(self.py()) + .downcast_into_unchecked() + } + } +} + static MAPPING_ABC: GILOnceCell> = GILOnceCell::new(); fn get_mapping_abc(py: Python<'_>) -> PyResult<&PyType> { diff --git a/src/types/mod.rs b/src/types/mod.rs index 9d88b191..5adddf50 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -290,7 +290,7 @@ mod frozenset; mod function; mod iterator; pub(crate) mod list; -mod mapping; +pub(crate) mod mapping; mod memoryview; mod module; mod none;