Merge pull request #3694 from davidhewitt/set-frozenset
implement `PySetMethods` and `PyFrozenSetMethods`
This commit is contained in:
commit
0344921326
|
@ -206,6 +206,14 @@ impl<'py, T> Bound<'py, T> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Removes the connection for this `Bound<T>` from the GIL, allowing
|
||||
/// it to cross thread boundaries.
|
||||
pub fn unbind(self) -> Py<T> {
|
||||
// Safety: the type T is known to be correct and the ownership of the
|
||||
// pointer is transferred to the new Py<T> instance.
|
||||
unsafe { Py::from_non_null(self.into_non_null()) }
|
||||
}
|
||||
|
||||
/// Casts this `Bound<T>` as the corresponding "GIL Ref" type.
|
||||
///
|
||||
/// This is a helper to be used for migration from the deprecated "GIL Refs" API.
|
||||
|
@ -973,7 +981,7 @@ impl<T> Py<T> {
|
|||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
self.bind(py).as_any().getattr(attr_name).map(Into::into)
|
||||
self.bind(py).as_any().getattr(attr_name).map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Sets an attribute value.
|
||||
|
@ -1017,21 +1025,21 @@ impl<T> Py<T> {
|
|||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<PyObject> {
|
||||
self.bind(py).as_any().call(args, kwargs).map(Into::into)
|
||||
self.bind(py).as_any().call(args, kwargs).map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Calls the object with only positional arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self(*args)`.
|
||||
pub fn call1(&self, py: Python<'_>, args: impl IntoPy<Py<PyTuple>>) -> PyResult<PyObject> {
|
||||
self.bind(py).as_any().call1(args).map(Into::into)
|
||||
self.bind(py).as_any().call1(args).map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Calls the object without arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self()`.
|
||||
pub fn call0(&self, py: Python<'_>) -> PyResult<PyObject> {
|
||||
self.bind(py).as_any().call0().map(Into::into)
|
||||
self.bind(py).as_any().call0().map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Calls a method on the object.
|
||||
|
@ -1054,7 +1062,7 @@ impl<T> Py<T> {
|
|||
self.bind(py)
|
||||
.as_any()
|
||||
.call_method(name, args, kwargs)
|
||||
.map(Into::into)
|
||||
.map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Calls a method on the object with only positional arguments.
|
||||
|
@ -1071,7 +1079,7 @@ impl<T> Py<T> {
|
|||
self.bind(py)
|
||||
.as_any()
|
||||
.call_method1(name, args)
|
||||
.map(Into::into)
|
||||
.map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Calls a method on the object with no arguments.
|
||||
|
@ -1084,7 +1092,7 @@ impl<T> Py<T> {
|
|||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
self.bind(py).as_any().call_method0(name).map(Into::into)
|
||||
self.bind(py).as_any().call_method0(name).map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
|
||||
|
@ -1292,7 +1300,7 @@ where
|
|||
impl<T> std::convert::From<Bound<'_, T>> for Py<T> {
|
||||
#[inline]
|
||||
fn from(other: Bound<'_, T>) -> Self {
|
||||
unsafe { Self::from_non_null(other.into_non_null()) }
|
||||
other.unbind()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1618,7 +1626,7 @@ a = A()
|
|||
.as_borrowed()
|
||||
.to_owned();
|
||||
let ptr = instance.as_ptr();
|
||||
let instance: PyObject = instance.clone().into();
|
||||
let instance: PyObject = instance.clone().unbind();
|
||||
assert_eq!(instance.as_ptr(), ptr);
|
||||
})
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@ pub use crate::types::bytearray::PyByteArrayMethods;
|
|||
pub use crate::types::bytes::PyBytesMethods;
|
||||
pub use crate::types::dict::PyDictMethods;
|
||||
pub use crate::types::float::PyFloatMethods;
|
||||
pub use crate::types::frozenset::PyFrozenSetMethods;
|
||||
pub use crate::types::list::PyListMethods;
|
||||
pub use crate::types::mapping::PyMappingMethods;
|
||||
pub use crate::types::sequence::PySequenceMethods;
|
||||
pub use crate::types::set::PySetMethods;
|
||||
pub use crate::types::string::PyStringMethods;
|
||||
|
|
|
@ -626,15 +626,6 @@ impl<'py> BoundDictIterator<'py> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoIterator for &'_ Bound<'py, PyDict> {
|
||||
type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
|
||||
type IntoIter = BoundDictIterator<'py>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoIterator for Bound<'py, PyDict> {
|
||||
type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
|
||||
type IntoIter = BoundDictIterator<'py>;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::types::PyIterator;
|
||||
use crate::{
|
||||
err::{self, PyErr, PyResult},
|
||||
Py, PyObject,
|
||||
ffi, Bound, Py, PyAny, PyNativeType, PyObject, Python, ToPyObject,
|
||||
};
|
||||
use crate::{ffi, PyAny, Python, ToPyObject};
|
||||
|
||||
use std::ptr;
|
||||
|
||||
/// Allows building a Python `frozenset` one item at a time
|
||||
|
@ -83,12 +83,12 @@ impl PyFrozenSet {
|
|||
/// This is equivalent to len(p) on a set.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { ffi::PySet_Size(self.as_ptr()) as usize }
|
||||
self.as_borrowed().len()
|
||||
}
|
||||
|
||||
/// Check if set is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
self.as_borrowed().is_empty()
|
||||
}
|
||||
|
||||
/// Determine if the set contains the specified key.
|
||||
|
@ -97,7 +97,54 @@ impl PyFrozenSet {
|
|||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(frozenset: &PyFrozenSet, key: PyObject) -> PyResult<bool> {
|
||||
self.as_borrowed().contains(key)
|
||||
}
|
||||
|
||||
/// Returns an iterator of values in this frozen set.
|
||||
pub fn iter(&self) -> PyFrozenSetIterator<'_> {
|
||||
PyFrozenSetIterator(BoundFrozenSetIterator::new(self.as_borrowed().to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of functionality for [`PyFrozenSet`].
|
||||
///
|
||||
/// These methods are defined for the `Bound<'py, PyFrozenSet>` 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 = "PyFrozenSet")]
|
||||
pub trait PyFrozenSetMethods<'py> {
|
||||
/// Returns the number of items in the set.
|
||||
///
|
||||
/// This is equivalent to the Python expression `len(self)`.
|
||||
fn len(&self) -> usize;
|
||||
|
||||
/// Checks if set is empty.
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Determines if the set 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;
|
||||
|
||||
/// Returns an iterator of values in this set.
|
||||
fn iter(&self) -> BoundFrozenSetIterator<'py>;
|
||||
}
|
||||
|
||||
impl<'py> PyFrozenSetMethods<'py> for Bound<'py, PyFrozenSet> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
unsafe { ffi::PySet_Size(self.as_ptr()) as usize }
|
||||
}
|
||||
|
||||
fn contains<K>(&self, key: K) -> PyResult<bool>
|
||||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(frozenset: &Bound<'_, PyFrozenSet>, key: Bound<'_, PyAny>) -> PyResult<bool> {
|
||||
match unsafe { ffi::PySet_Contains(frozenset.as_ptr(), key.as_ptr()) } {
|
||||
1 => Ok(true),
|
||||
0 => Ok(false),
|
||||
|
@ -105,12 +152,48 @@ impl PyFrozenSet {
|
|||
}
|
||||
}
|
||||
|
||||
inner(self, key.to_object(self.py()))
|
||||
let py = self.py();
|
||||
inner(self, key.to_object(py).into_bound(py))
|
||||
}
|
||||
|
||||
/// Returns an iterator of values in this frozen set.
|
||||
pub fn iter(&self) -> PyFrozenSetIterator<'_> {
|
||||
IntoIterator::into_iter(self)
|
||||
fn iter(&self) -> BoundFrozenSetIterator<'py> {
|
||||
BoundFrozenSetIterator::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// PyO3 implementation of an iterator for a Python `frozenset` object.
|
||||
pub struct PyFrozenSetIterator<'py>(BoundFrozenSetIterator<'py>);
|
||||
|
||||
impl<'py> Iterator for PyFrozenSetIterator<'py> {
|
||||
type Item = &'py super::PyAny;
|
||||
|
||||
/// Advances the iterator and returns the next value.
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next().map(Bound::into_gil_ref)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoIterator for &'py PyFrozenSet {
|
||||
type Item = &'py PyAny;
|
||||
type IntoIter = PyFrozenSetIterator<'py>;
|
||||
/// Returns an iterator of values in this set.
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
PyFrozenSetIterator(BoundFrozenSetIterator::new(self.as_borrowed().to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoIterator for Bound<'py, PyFrozenSet> {
|
||||
type Item = Bound<'py, PyAny>;
|
||||
type IntoIter = BoundFrozenSetIterator<'py>;
|
||||
|
||||
/// Returns an iterator of values in this set.
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
BoundFrozenSetIterator::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,25 +201,23 @@ impl PyFrozenSet {
|
|||
mod impl_ {
|
||||
use super::*;
|
||||
|
||||
impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
|
||||
type Item = &'a PyAny;
|
||||
type IntoIter = PyFrozenSetIterator<'a>;
|
||||
/// PyO3 implementation of an iterator for a Python `set` object.
|
||||
pub struct BoundFrozenSetIterator<'p> {
|
||||
it: Bound<'p, PyIterator>,
|
||||
}
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
PyFrozenSetIterator {
|
||||
it: PyIterator::from_object(self).unwrap(),
|
||||
impl<'py> BoundFrozenSetIterator<'py> {
|
||||
pub(super) fn new(frozenset: Bound<'py, PyFrozenSet>) -> Self {
|
||||
Self {
|
||||
it: PyIterator::from_object2(&frozenset).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PyO3 implementation of an iterator for a Python `frozenset` object.
|
||||
pub struct PyFrozenSetIterator<'p> {
|
||||
it: &'p PyIterator,
|
||||
}
|
||||
|
||||
impl<'py> Iterator for PyFrozenSetIterator<'py> {
|
||||
type Item = &'py super::PyAny;
|
||||
impl<'py> Iterator for BoundFrozenSetIterator<'py> {
|
||||
type Item = Bound<'py, super::PyAny>;
|
||||
|
||||
/// Advances the iterator and returns the next value.
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it.next().map(Result::unwrap)
|
||||
|
@ -148,23 +229,20 @@ mod impl_ {
|
|||
mod impl_ {
|
||||
use super::*;
|
||||
|
||||
impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
|
||||
type Item = &'a PyAny;
|
||||
type IntoIter = PyFrozenSetIterator<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
PyFrozenSetIterator { set: self, pos: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// PyO3 implementation of an iterator for a Python `frozenset` object.
|
||||
pub struct PyFrozenSetIterator<'py> {
|
||||
set: &'py PyFrozenSet,
|
||||
pub struct BoundFrozenSetIterator<'py> {
|
||||
set: Bound<'py, PyFrozenSet>,
|
||||
pos: ffi::Py_ssize_t,
|
||||
}
|
||||
|
||||
impl<'py> Iterator for PyFrozenSetIterator<'py> {
|
||||
type Item = &'py PyAny;
|
||||
impl<'py> BoundFrozenSetIterator<'py> {
|
||||
pub(super) fn new(set: Bound<'py, PyFrozenSet>) -> Self {
|
||||
Self { set, pos: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> Iterator for BoundFrozenSetIterator<'py> {
|
||||
type Item = Bound<'py, PyAny>;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -174,7 +252,7 @@ mod impl_ {
|
|||
if ffi::_PySet_NextEntry(self.set.as_ptr(), &mut self.pos, &mut key, &mut hash) != 0
|
||||
{
|
||||
// _PySet_NextEntry returns borrowed object; for safety must make owned (see #890)
|
||||
Some(self.set.py().from_owned_ptr(ffi::_Py_NewRef(key)))
|
||||
Some(key.assume_borrowed(self.set.py()).to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -188,11 +266,17 @@ mod impl_ {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'py> ExactSizeIterator for PyFrozenSetIterator<'py> {
|
||||
impl<'py> ExactSizeIterator for BoundFrozenSetIterator<'py> {
|
||||
fn len(&self) -> usize {
|
||||
self.set.len().saturating_sub(self.pos as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> ExactSizeIterator for PyFrozenSetIterator<'py> {
|
||||
fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use impl_::*;
|
||||
|
@ -243,6 +327,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
let set = PyFrozenSet::empty(py).unwrap();
|
||||
assert_eq!(0, set.len());
|
||||
assert!(set.is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -271,6 +356,34 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_frozenset_iter_size_hint() {
|
||||
Python::with_gil(|py| {
|
||||
let set = PyFrozenSet::new(py, &[1]).unwrap();
|
||||
let mut iter = set.iter();
|
||||
|
||||
// Exact size
|
||||
assert_eq!(iter.len(), 1);
|
||||
assert_eq!(iter.size_hint(), (1, Some(1)));
|
||||
iter.next();
|
||||
assert_eq!(iter.len(), 0);
|
||||
assert_eq!(iter.size_hint(), (0, Some(0)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
fn test_frozenset_iter_size_hint() {
|
||||
Python::with_gil(|py| {
|
||||
let set = PyFrozenSet::new(py, &[1]).unwrap();
|
||||
let iter = set.iter();
|
||||
|
||||
// No known bounds
|
||||
assert_eq!(iter.size_hint(), (0, None));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frozenset_builder() {
|
||||
use super::PyFrozenSetBuilder;
|
||||
|
|
|
@ -657,15 +657,6 @@ impl ExactSizeIterator for BoundListIterator<'_> {
|
|||
|
||||
impl FusedIterator for BoundListIterator<'_> {}
|
||||
|
||||
impl<'a, 'py> IntoIterator for &'a Bound<'py, PyList> {
|
||||
type Item = Bound<'py, PyAny>;
|
||||
type IntoIter = BoundListIterator<'py>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoIterator for Bound<'py, PyList> {
|
||||
type Item = Bound<'py, PyAny>;
|
||||
type IntoIter = BoundListIterator<'py>;
|
||||
|
|
|
@ -77,9 +77,9 @@ pub use self::typeobject::PyType;
|
|||
/// In these cases the iterators are implemented by forwarding to [`PyIterator`].
|
||||
pub mod iter {
|
||||
pub use super::dict::{BoundDictIterator, PyDictIterator};
|
||||
pub use super::frozenset::PyFrozenSetIterator;
|
||||
pub use super::frozenset::{BoundFrozenSetIterator, PyFrozenSetIterator};
|
||||
pub use super::list::{BoundListIterator, PyListIterator};
|
||||
pub use super::set::PySetIterator;
|
||||
pub use super::set::{BoundSetIterator, PySetIterator};
|
||||
pub use super::tuple::PyTupleIterator;
|
||||
}
|
||||
|
||||
|
@ -288,7 +288,7 @@ mod ellipsis;
|
|||
pub(crate) mod float;
|
||||
#[cfg(all(not(Py_LIMITED_API), not(PyPy)))]
|
||||
mod frame;
|
||||
mod frozenset;
|
||||
pub(crate) mod frozenset;
|
||||
mod function;
|
||||
mod iterator;
|
||||
pub(crate) mod list;
|
||||
|
|
311
src/types/set.rs
311
src/types/set.rs
|
@ -2,7 +2,9 @@
|
|||
use crate::types::PyIterator;
|
||||
use crate::{
|
||||
err::{self, PyErr, PyResult},
|
||||
Py,
|
||||
ffi_ptr_ext::FfiPtrExt,
|
||||
instance::Bound,
|
||||
Py, PyNativeType,
|
||||
};
|
||||
use crate::{ffi, PyAny, PyObject, Python, ToPyObject};
|
||||
use std::ptr;
|
||||
|
@ -46,9 +48,7 @@ impl PySet {
|
|||
/// Removes all elements from the set.
|
||||
#[inline]
|
||||
pub fn clear(&self) {
|
||||
unsafe {
|
||||
ffi::PySet_Clear(self.as_ptr());
|
||||
}
|
||||
self.as_borrowed().clear()
|
||||
}
|
||||
|
||||
/// Returns the number of items in the set.
|
||||
|
@ -56,12 +56,12 @@ impl PySet {
|
|||
/// This is equivalent to the Python expression `len(self)`.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { ffi::PySet_Size(self.as_ptr()) as usize }
|
||||
self.as_borrowed().len()
|
||||
}
|
||||
|
||||
/// Checks if set is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
self.as_borrowed().is_empty()
|
||||
}
|
||||
|
||||
/// Determines if the set contains the specified key.
|
||||
|
@ -71,15 +71,7 @@ impl PySet {
|
|||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(set: &PySet, key: PyObject) -> PyResult<bool> {
|
||||
match unsafe { ffi::PySet_Contains(set.as_ptr(), key.as_ptr()) } {
|
||||
1 => Ok(true),
|
||||
0 => Ok(false),
|
||||
_ => Err(PyErr::fetch(set.py())),
|
||||
}
|
||||
}
|
||||
|
||||
inner(self, key.to_object(self.py()))
|
||||
self.as_borrowed().contains(key)
|
||||
}
|
||||
|
||||
/// Removes the element from the set if it is present.
|
||||
|
@ -89,15 +81,7 @@ impl PySet {
|
|||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(set: &PySet, key: PyObject) -> PyResult<bool> {
|
||||
match unsafe { ffi::PySet_Discard(set.as_ptr(), key.as_ptr()) } {
|
||||
1 => Ok(true),
|
||||
0 => Ok(false),
|
||||
_ => Err(PyErr::fetch(set.py())),
|
||||
}
|
||||
}
|
||||
|
||||
inner(self, key.to_object(self.py()))
|
||||
self.as_borrowed().discard(key)
|
||||
}
|
||||
|
||||
/// Adds an element to the set.
|
||||
|
@ -105,23 +89,12 @@ impl PySet {
|
|||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(set: &PySet, key: PyObject) -> PyResult<()> {
|
||||
err::error_on_minusone(set.py(), unsafe {
|
||||
ffi::PySet_Add(set.as_ptr(), key.as_ptr())
|
||||
})
|
||||
}
|
||||
|
||||
inner(self, key.to_object(self.py()))
|
||||
self.as_borrowed().add(key)
|
||||
}
|
||||
|
||||
/// Removes and returns an arbitrary element from the set.
|
||||
pub fn pop(&self) -> Option<PyObject> {
|
||||
let element =
|
||||
unsafe { PyObject::from_owned_ptr_or_err(self.py(), ffi::PySet_Pop(self.as_ptr())) };
|
||||
match element {
|
||||
Ok(e) => Some(e),
|
||||
Err(_) => None,
|
||||
}
|
||||
self.as_borrowed().pop().map(Bound::unbind)
|
||||
}
|
||||
|
||||
/// Returns an iterator of values in this set.
|
||||
|
@ -130,7 +103,177 @@ impl PySet {
|
|||
///
|
||||
/// If PyO3 detects that the set is mutated during iteration, it will panic.
|
||||
pub fn iter(&self) -> PySetIterator<'_> {
|
||||
IntoIterator::into_iter(self)
|
||||
PySetIterator(BoundSetIterator::new(self.as_borrowed().to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of functionality for [`PySet`].
|
||||
///
|
||||
/// These methods are defined for the `Bound<'py, PySet>` 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 = "PySet")]
|
||||
pub trait PySetMethods<'py> {
|
||||
/// Removes all elements from the set.
|
||||
fn clear(&self);
|
||||
|
||||
/// Returns the number of items in the set.
|
||||
///
|
||||
/// This is equivalent to the Python expression `len(self)`.
|
||||
fn len(&self) -> usize;
|
||||
|
||||
/// Checks if set is empty.
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Determines if the set 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;
|
||||
|
||||
/// Removes the element from the set if it is present.
|
||||
///
|
||||
/// Returns `true` if the element was present in the set.
|
||||
fn discard<K>(&self, key: K) -> PyResult<bool>
|
||||
where
|
||||
K: ToPyObject;
|
||||
|
||||
/// Adds an element to the set.
|
||||
fn add<K>(&self, key: K) -> PyResult<()>
|
||||
where
|
||||
K: ToPyObject;
|
||||
|
||||
/// Removes and returns an arbitrary element from the set.
|
||||
fn pop(&self) -> Option<Bound<'py, PyAny>>;
|
||||
|
||||
/// Returns an iterator of values in this set.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If PyO3 detects that the set is mutated during iteration, it will panic.
|
||||
fn iter(&self) -> BoundSetIterator<'py>;
|
||||
}
|
||||
|
||||
impl<'py> PySetMethods<'py> for Bound<'py, PySet> {
|
||||
#[inline]
|
||||
fn clear(&self) {
|
||||
unsafe {
|
||||
ffi::PySet_Clear(self.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
unsafe { ffi::PySet_Size(self.as_ptr()) as usize }
|
||||
}
|
||||
|
||||
fn contains<K>(&self, key: K) -> PyResult<bool>
|
||||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(set: &Bound<'_, PySet>, key: Bound<'_, PyAny>) -> PyResult<bool> {
|
||||
match unsafe { ffi::PySet_Contains(set.as_ptr(), key.as_ptr()) } {
|
||||
1 => Ok(true),
|
||||
0 => Ok(false),
|
||||
_ => Err(PyErr::fetch(set.py())),
|
||||
}
|
||||
}
|
||||
|
||||
let py = self.py();
|
||||
inner(self, key.to_object(py).into_bound(py))
|
||||
}
|
||||
|
||||
fn discard<K>(&self, key: K) -> PyResult<bool>
|
||||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(set: &Bound<'_, PySet>, key: Bound<'_, PyAny>) -> PyResult<bool> {
|
||||
match unsafe { ffi::PySet_Discard(set.as_ptr(), key.as_ptr()) } {
|
||||
1 => Ok(true),
|
||||
0 => Ok(false),
|
||||
_ => Err(PyErr::fetch(set.py())),
|
||||
}
|
||||
}
|
||||
|
||||
let py = self.py();
|
||||
inner(self, key.to_object(py).into_bound(py))
|
||||
}
|
||||
|
||||
fn add<K>(&self, key: K) -> PyResult<()>
|
||||
where
|
||||
K: ToPyObject,
|
||||
{
|
||||
fn inner(set: &Bound<'_, PySet>, key: Bound<'_, PyAny>) -> PyResult<()> {
|
||||
err::error_on_minusone(set.py(), unsafe {
|
||||
ffi::PySet_Add(set.as_ptr(), key.as_ptr())
|
||||
})
|
||||
}
|
||||
|
||||
let py = self.py();
|
||||
inner(self, key.to_object(py).into_bound(py))
|
||||
}
|
||||
|
||||
fn pop(&self) -> Option<Bound<'py, PyAny>> {
|
||||
let element = unsafe { ffi::PySet_Pop(self.as_ptr()).assume_owned_or_err(self.py()) };
|
||||
match element {
|
||||
Ok(e) => Some(e),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn iter(&self) -> BoundSetIterator<'py> {
|
||||
BoundSetIterator::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// PyO3 implementation of an iterator for a Python `set` object.
|
||||
pub struct PySetIterator<'py>(BoundSetIterator<'py>);
|
||||
|
||||
impl<'py> Iterator for PySetIterator<'py> {
|
||||
type Item = &'py super::PyAny;
|
||||
|
||||
/// Advances the iterator and returns the next value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If PyO3 detects that the set is mutated during iteration, it will panic.
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next().map(Bound::into_gil_ref)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoIterator for &'py PySet {
|
||||
type Item = &'py PyAny;
|
||||
type IntoIter = PySetIterator<'py>;
|
||||
/// Returns an iterator of values in this set.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If PyO3 detects that the set is mutated during iteration, it will panic.
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
PySetIterator(BoundSetIterator::new(self.as_borrowed().to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoIterator for Bound<'py, PySet> {
|
||||
type Item = Bound<'py, PyAny>;
|
||||
type IntoIter = BoundSetIterator<'py>;
|
||||
|
||||
/// Returns an iterator of values in this set.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If PyO3 detects that the set is mutated during iteration, it will panic.
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
BoundSetIterator::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,29 +281,21 @@ impl PySet {
|
|||
mod impl_ {
|
||||
use super::*;
|
||||
|
||||
impl<'a> std::iter::IntoIterator for &'a PySet {
|
||||
type Item = &'a PyAny;
|
||||
type IntoIter = PySetIterator<'a>;
|
||||
/// PyO3 implementation of an iterator for a Python `set` object.
|
||||
pub struct BoundSetIterator<'p> {
|
||||
it: Bound<'p, PyIterator>,
|
||||
}
|
||||
|
||||
/// Returns an iterator of values in this set.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If PyO3 detects that the set is mutated during iteration, it will panic.
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
PySetIterator {
|
||||
it: PyIterator::from_object(self).unwrap(),
|
||||
impl<'py> BoundSetIterator<'py> {
|
||||
pub(super) fn new(set: Bound<'py, PySet>) -> Self {
|
||||
Self {
|
||||
it: PyIterator::from_object2(&set).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PyO3 implementation of an iterator for a Python `set` object.
|
||||
pub struct PySetIterator<'p> {
|
||||
it: &'p PyIterator,
|
||||
}
|
||||
|
||||
impl<'py> Iterator for PySetIterator<'py> {
|
||||
type Item = &'py super::PyAny;
|
||||
impl<'py> Iterator for BoundSetIterator<'py> {
|
||||
type Item = Bound<'py, super::PyAny>;
|
||||
|
||||
/// Advances the iterator and returns the next value.
|
||||
///
|
||||
|
@ -179,31 +314,21 @@ mod impl_ {
|
|||
use super::*;
|
||||
|
||||
/// PyO3 implementation of an iterator for a Python `set` object.
|
||||
pub struct PySetIterator<'py> {
|
||||
set: &'py super::PySet,
|
||||
pub struct BoundSetIterator<'py> {
|
||||
set: Bound<'py, super::PySet>,
|
||||
pos: ffi::Py_ssize_t,
|
||||
used: ffi::Py_ssize_t,
|
||||
}
|
||||
|
||||
impl<'a> std::iter::IntoIterator for &'a PySet {
|
||||
type Item = &'a PyAny;
|
||||
type IntoIter = PySetIterator<'a>;
|
||||
/// Returns an iterator of values in this set.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If PyO3 detects that the set is mutated during iteration, it will panic.
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
PySetIterator {
|
||||
set: self,
|
||||
pos: 0,
|
||||
used: unsafe { ffi::PySet_Size(self.as_ptr()) },
|
||||
}
|
||||
impl<'py> BoundSetIterator<'py> {
|
||||
pub(super) fn new(set: Bound<'py, PySet>) -> Self {
|
||||
let used = unsafe { ffi::PySet_Size(set.as_ptr()) };
|
||||
BoundSetIterator { set, pos: 0, used }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> Iterator for PySetIterator<'py> {
|
||||
type Item = &'py super::PyAny;
|
||||
impl<'py> Iterator for BoundSetIterator<'py> {
|
||||
type Item = Bound<'py, super::PyAny>;
|
||||
|
||||
/// Advances the iterator and returns the next value.
|
||||
///
|
||||
|
@ -221,7 +346,7 @@ mod impl_ {
|
|||
if ffi::_PySet_NextEntry(self.set.as_ptr(), &mut self.pos, &mut key, &mut hash) != 0
|
||||
{
|
||||
// _PySet_NextEntry returns borrowed object; for safety must make owned (see #890)
|
||||
Some(self.set.py().from_owned_ptr(ffi::_Py_NewRef(key)))
|
||||
Some(key.assume_borrowed(self.set.py()).to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -235,11 +360,17 @@ mod impl_ {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'py> ExactSizeIterator for PySetIterator<'py> {
|
||||
impl<'py> ExactSizeIterator for BoundSetIterator<'py> {
|
||||
fn len(&self) -> usize {
|
||||
self.set.len().saturating_sub(self.pos as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> ExactSizeIterator for PySetIterator<'py> {
|
||||
fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use impl_::*;
|
||||
|
@ -289,6 +420,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
let set = PySet::empty(py).unwrap();
|
||||
assert_eq!(0, set.len());
|
||||
assert!(set.is_empty());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -406,19 +538,30 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn test_set_iter_size_hint() {
|
||||
Python::with_gil(|py| {
|
||||
let set = PySet::new(py, &[1]).unwrap();
|
||||
|
||||
let mut iter = set.iter();
|
||||
|
||||
if cfg!(Py_LIMITED_API) {
|
||||
assert_eq!(iter.size_hint(), (0, None));
|
||||
} else {
|
||||
assert_eq!(iter.size_hint(), (1, Some(1)));
|
||||
iter.next();
|
||||
assert_eq!(iter.size_hint(), (0, Some(0)));
|
||||
}
|
||||
// Exact size
|
||||
assert_eq!(iter.len(), 1);
|
||||
assert_eq!(iter.size_hint(), (1, Some(1)));
|
||||
iter.next();
|
||||
assert_eq!(iter.len(), 0);
|
||||
assert_eq!(iter.size_hint(), (0, Some(0)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
fn test_set_iter_size_hint() {
|
||||
Python::with_gil(|py| {
|
||||
let set = PySet::new(py, &[1]).unwrap();
|
||||
let iter = set.iter();
|
||||
|
||||
// No known bounds
|
||||
assert_eq!(iter.size_hint(), (0, None));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -435,13 +435,13 @@ impl Py<PyString> {
|
|||
|
||||
impl IntoPy<Py<PyString>> for Bound<'_, PyString> {
|
||||
fn into_py(self, _py: Python<'_>) -> Py<PyString> {
|
||||
self.into()
|
||||
self.unbind()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoPy<Py<PyString>> for &Bound<'_, PyString> {
|
||||
fn into_py(self, _py: Python<'_>) -> Py<PyString> {
|
||||
self.clone().into()
|
||||
self.clone().unbind()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue