implement `PyMappingMethods`

This commit is contained in:
David Hewitt 2023-11-21 13:01:09 +00:00
parent fd2fc983b1
commit 8e7c90733d
3 changed files with 152 additions and 21 deletions

View File

@ -31,4 +31,5 @@ pub use crate::wrap_pyfunction;
// pub(crate) use crate::types::bytearray::PyByteArrayMethods; // pub(crate) use crate::types::bytearray::PyByteArrayMethods;
// pub(crate) use crate::types::bytes::PyBytesMethods; // pub(crate) use crate::types::bytes::PyBytesMethods;
// pub(crate) use crate::types::float::PyFloatMethods; // 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::sequence::PySequenceMethods;

View File

@ -1,6 +1,10 @@
use crate::err::{PyDowncastError, PyResult}; 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::sync::GILOnceCell;
use crate::type_object::PyTypeInfo; use crate::type_object::PyTypeInfo;
use crate::types::any::PyAnyMethods;
use crate::types::{PyAny, PyDict, PySequence, PyType}; use crate::types::{PyAny, PyDict, PySequence, PyType};
use crate::{ffi, Py, PyNativeType, PyTypeCheck, Python, ToPyObject}; use crate::{ffi, Py, PyNativeType, PyTypeCheck, Python, ToPyObject};
@ -16,15 +20,13 @@ impl PyMapping {
/// This is equivalent to the Python expression `len(self)`. /// This is equivalent to the Python expression `len(self)`.
#[inline] #[inline]
pub fn len(&self) -> PyResult<usize> { pub fn len(&self) -> PyResult<usize> {
let v = unsafe { ffi::PyMapping_Size(self.as_ptr()) }; Py2::borrowed_from_gil_ref(&self).len()
crate::err::error_on_minusone(self.py(), v)?;
Ok(v as usize)
} }
/// Returns whether the mapping is empty. /// Returns whether the mapping is empty.
#[inline] #[inline]
pub fn is_empty(&self) -> PyResult<bool> { pub fn is_empty(&self) -> PyResult<bool> {
self.len().map(|l| l == 0) Py2::borrowed_from_gil_ref(&self).is_empty()
} }
/// Determines if the mapping contains the specified key. /// Determines if the mapping contains the specified key.
@ -34,7 +36,7 @@ impl PyMapping {
where where
K: ToPyObject, K: ToPyObject,
{ {
PyAny::contains(self, key) Py2::borrowed_from_gil_ref(&self).contains(key)
} }
/// Gets the item in self with key `key`. /// Gets the item in self with key `key`.
@ -47,7 +49,9 @@ impl PyMapping {
where where
K: ToPyObject, 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`. /// Sets the item in self with key `key`.
@ -59,7 +63,7 @@ impl PyMapping {
K: ToPyObject, K: ToPyObject,
V: 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`. /// Deletes the item with key `key`.
@ -70,34 +74,31 @@ impl PyMapping {
where where
K: ToPyObject, 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. /// Returns a sequence containing all keys in the mapping.
#[inline] #[inline]
pub fn keys(&self) -> PyResult<&PySequence> { pub fn keys(&self) -> PyResult<&PySequence> {
unsafe { Py2::borrowed_from_gil_ref(&self)
self.py() .keys()
.from_owned_ptr_or_err(ffi::PyMapping_Keys(self.as_ptr())) .map(Py2::into_gil_ref)
}
} }
/// Returns a sequence containing all values in the mapping. /// Returns a sequence containing all values in the mapping.
#[inline] #[inline]
pub fn values(&self) -> PyResult<&PySequence> { pub fn values(&self) -> PyResult<&PySequence> {
unsafe { Py2::borrowed_from_gil_ref(&self)
self.py() .values()
.from_owned_ptr_or_err(ffi::PyMapping_Values(self.as_ptr())) .map(Py2::into_gil_ref)
}
} }
/// Returns a sequence of tuples of all (key, value) pairs in the mapping. /// Returns a sequence of tuples of all (key, value) pairs in the mapping.
#[inline] #[inline]
pub fn items(&self) -> PyResult<&PySequence> { pub fn items(&self) -> PyResult<&PySequence> {
unsafe { Py2::borrowed_from_gil_ref(&self)
self.py() .items()
.from_owned_ptr_or_err(ffi::PyMapping_Items(self.as_ptr())) .map(Py2::into_gil_ref)
}
} }
/// Register a pyclass as a subclass of `collections.abc.Mapping` (from the Python standard /// 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<usize>;
/// Returns whether the mapping is empty.
fn is_empty(&self) -> PyResult<bool>;
/// Determines if the mapping contains the specified key.
///
/// This is equivalent to the Python expression `key in self`.
fn contains<K>(&self, key: K) -> PyResult<bool>
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<K>(&self, key: K) -> PyResult<Py2<'py, PyAny>>
where
K: ToPyObject;
/// Sets the item in self with key `key`.
///
/// This is equivalent to the Python expression `self[key] = value`.
fn set_item<K, V>(&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<K>(&self, key: K) -> PyResult<()>
where
K: ToPyObject;
/// Returns a sequence containing all keys in the mapping.
fn keys(&self) -> PyResult<Py2<'py, PySequence>>;
/// Returns a sequence containing all values in the mapping.
fn values(&self) -> PyResult<Py2<'py, PySequence>>;
/// Returns a sequence of tuples of all (key, value) pairs in the mapping.
fn items(&self) -> PyResult<Py2<'py, PySequence>>;
}
impl<'py> PyMappingMethods<'py> for Py2<'py, PyMapping> {
#[inline]
fn len(&self) -> PyResult<usize> {
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<bool> {
self.len().map(|l| l == 0)
}
fn contains<K>(&self, key: K) -> PyResult<bool>
where
K: ToPyObject,
{
PyAnyMethods::contains(&**self, key)
}
#[inline]
fn get_item<K>(&self, key: K) -> PyResult<Py2<'py, PyAny>>
where
K: ToPyObject,
{
PyAnyMethods::get_item(&**self, key)
}
#[inline]
fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
where
K: ToPyObject,
V: ToPyObject,
{
PyAnyMethods::set_item(&**self, key, value)
}
#[inline]
fn del_item<K>(&self, key: K) -> PyResult<()>
where
K: ToPyObject,
{
PyAnyMethods::del_item(&**self, key)
}
#[inline]
fn keys(&self) -> PyResult<Py2<'py, PySequence>> {
unsafe {
ffi::PyMapping_Keys(self.as_ptr())
.assume_owned_or_err(self.py())
.downcast_into_unchecked()
}
}
#[inline]
fn values(&self) -> PyResult<Py2<'py, PySequence>> {
unsafe {
ffi::PyMapping_Values(self.as_ptr())
.assume_owned_or_err(self.py())
.downcast_into_unchecked()
}
}
#[inline]
fn items(&self) -> PyResult<Py2<'py, PySequence>> {
unsafe {
ffi::PyMapping_Items(self.as_ptr())
.assume_owned_or_err(self.py())
.downcast_into_unchecked()
}
}
}
static MAPPING_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new(); static MAPPING_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new();
fn get_mapping_abc(py: Python<'_>) -> PyResult<&PyType> { fn get_mapping_abc(py: Python<'_>) -> PyResult<&PyType> {

View File

@ -290,7 +290,7 @@ mod frozenset;
mod function; mod function;
mod iterator; mod iterator;
pub(crate) mod list; pub(crate) mod list;
mod mapping; pub(crate) mod mapping;
mod memoryview; mod memoryview;
mod module; mod module;
mod none; mod none;