1use crate::types::PyIterator;
2#[allow(deprecated)]
3use crate::ToPyObject;
4use crate::{
5 err::{self, PyErr, PyResult},
6 ffi_ptr_ext::FfiPtrExt,
7 instance::Bound,
8 py_result_ext::PyResultExt,
9 types::any::PyAnyMethods,
10};
11use crate::{ffi, Borrowed, BoundObject, IntoPyObject, IntoPyObjectExt, PyAny, Python};
12use std::ptr;
13
14#[repr(transparent)]
22pub struct PySet(PyAny);
23
24#[cfg(not(any(PyPy, GraalPy)))]
25pyobject_subclassable_native_type!(PySet, crate::ffi::PySetObject);
26
27#[cfg(not(any(PyPy, GraalPy)))]
28pyobject_native_type!(
29 PySet,
30 ffi::PySetObject,
31 pyobject_native_static_type_object!(ffi::PySet_Type),
32 #checkfunction=ffi::PySet_Check
33);
34
35#[cfg(any(PyPy, GraalPy))]
36pyobject_native_type_core!(
37 PySet,
38 pyobject_native_static_type_object!(ffi::PySet_Type),
39 #checkfunction=ffi::PySet_Check
40);
41
42impl PySet {
43 #[inline]
47 pub fn new<'py, T>(
48 py: Python<'py>,
49 elements: impl IntoIterator<Item = T>,
50 ) -> PyResult<Bound<'py, PySet>>
51 where
52 T: IntoPyObject<'py>,
53 {
54 try_new_from_iter(py, elements)
55 }
56
57 #[deprecated(since = "0.23.0", note = "renamed to `PySet::new`")]
59 #[allow(deprecated)]
60 #[inline]
61 pub fn new_bound<'a, 'p, T: ToPyObject + 'a>(
62 py: Python<'p>,
63 elements: impl IntoIterator<Item = &'a T>,
64 ) -> PyResult<Bound<'p, PySet>> {
65 Self::new(py, elements.into_iter().map(|e| e.to_object(py)))
66 }
67
68 pub fn empty(py: Python<'_>) -> PyResult<Bound<'_, PySet>> {
70 unsafe {
71 ffi::PySet_New(ptr::null_mut())
72 .assume_owned_or_err(py)
73 .downcast_into_unchecked()
74 }
75 }
76
77 #[deprecated(since = "0.23.0", note = "renamed to `PySet::empty`")]
79 #[inline]
80 pub fn empty_bound(py: Python<'_>) -> PyResult<Bound<'_, PySet>> {
81 Self::empty(py)
82 }
83}
84
85#[doc(alias = "PySet")]
91pub trait PySetMethods<'py>: crate::sealed::Sealed {
92 fn clear(&self);
94
95 fn len(&self) -> usize;
99
100 fn is_empty(&self) -> bool {
102 self.len() == 0
103 }
104
105 fn contains<K>(&self, key: K) -> PyResult<bool>
109 where
110 K: IntoPyObject<'py>;
111
112 fn discard<K>(&self, key: K) -> PyResult<bool>
116 where
117 K: IntoPyObject<'py>;
118
119 fn add<K>(&self, key: K) -> PyResult<()>
121 where
122 K: IntoPyObject<'py>;
123
124 fn pop(&self) -> Option<Bound<'py, PyAny>>;
126
127 fn iter(&self) -> BoundSetIterator<'py>;
133}
134
135impl<'py> PySetMethods<'py> for Bound<'py, PySet> {
136 #[inline]
137 fn clear(&self) {
138 unsafe {
139 ffi::PySet_Clear(self.as_ptr());
140 }
141 }
142
143 #[inline]
144 fn len(&self) -> usize {
145 unsafe { ffi::PySet_Size(self.as_ptr()) as usize }
146 }
147
148 fn contains<K>(&self, key: K) -> PyResult<bool>
149 where
150 K: IntoPyObject<'py>,
151 {
152 fn inner(set: &Bound<'_, PySet>, key: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
153 match unsafe { ffi::PySet_Contains(set.as_ptr(), key.as_ptr()) } {
154 1 => Ok(true),
155 0 => Ok(false),
156 _ => Err(PyErr::fetch(set.py())),
157 }
158 }
159
160 let py = self.py();
161 inner(
162 self,
163 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
164 )
165 }
166
167 fn discard<K>(&self, key: K) -> PyResult<bool>
168 where
169 K: IntoPyObject<'py>,
170 {
171 fn inner(set: &Bound<'_, PySet>, key: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
172 match unsafe { ffi::PySet_Discard(set.as_ptr(), key.as_ptr()) } {
173 1 => Ok(true),
174 0 => Ok(false),
175 _ => Err(PyErr::fetch(set.py())),
176 }
177 }
178
179 let py = self.py();
180 inner(
181 self,
182 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
183 )
184 }
185
186 fn add<K>(&self, key: K) -> PyResult<()>
187 where
188 K: IntoPyObject<'py>,
189 {
190 fn inner(set: &Bound<'_, PySet>, key: Borrowed<'_, '_, PyAny>) -> PyResult<()> {
191 err::error_on_minusone(set.py(), unsafe {
192 ffi::PySet_Add(set.as_ptr(), key.as_ptr())
193 })
194 }
195
196 let py = self.py();
197 inner(
198 self,
199 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
200 )
201 }
202
203 fn pop(&self) -> Option<Bound<'py, PyAny>> {
204 let element = unsafe { ffi::PySet_Pop(self.as_ptr()).assume_owned_or_err(self.py()) };
205 element.ok()
206 }
207
208 fn iter(&self) -> BoundSetIterator<'py> {
209 BoundSetIterator::new(self.clone())
210 }
211}
212
213impl<'py> IntoIterator for Bound<'py, PySet> {
214 type Item = Bound<'py, PyAny>;
215 type IntoIter = BoundSetIterator<'py>;
216
217 fn into_iter(self) -> Self::IntoIter {
223 BoundSetIterator::new(self)
224 }
225}
226
227impl<'py> IntoIterator for &Bound<'py, PySet> {
228 type Item = Bound<'py, PyAny>;
229 type IntoIter = BoundSetIterator<'py>;
230
231 fn into_iter(self) -> Self::IntoIter {
237 self.iter()
238 }
239}
240
241pub struct BoundSetIterator<'p> {
243 it: Bound<'p, PyIterator>,
244 remaining: usize,
247}
248
249impl<'py> BoundSetIterator<'py> {
250 pub(super) fn new(set: Bound<'py, PySet>) -> Self {
251 Self {
252 it: PyIterator::from_object(&set).unwrap(),
253 remaining: set.len(),
254 }
255 }
256}
257
258impl<'py> Iterator for BoundSetIterator<'py> {
259 type Item = Bound<'py, super::PyAny>;
260
261 fn next(&mut self) -> Option<Self::Item> {
263 self.remaining = self.remaining.saturating_sub(1);
264 self.it.next().map(Result::unwrap)
265 }
266
267 fn size_hint(&self) -> (usize, Option<usize>) {
268 (self.remaining, Some(self.remaining))
269 }
270
271 #[inline]
272 fn count(self) -> usize
273 where
274 Self: Sized,
275 {
276 self.len()
277 }
278}
279
280impl ExactSizeIterator for BoundSetIterator<'_> {
281 fn len(&self) -> usize {
282 self.remaining
283 }
284}
285
286#[allow(deprecated)]
287#[inline]
288pub(crate) fn new_from_iter<T: ToPyObject>(
289 py: Python<'_>,
290 elements: impl IntoIterator<Item = T>,
291) -> PyResult<Bound<'_, PySet>> {
292 let mut iter = elements.into_iter().map(|e| e.to_object(py));
293 try_new_from_iter(py, &mut iter)
294}
295
296#[inline]
297pub(crate) fn try_new_from_iter<'py, T>(
298 py: Python<'py>,
299 elements: impl IntoIterator<Item = T>,
300) -> PyResult<Bound<'py, PySet>>
301where
302 T: IntoPyObject<'py>,
303{
304 let set = unsafe {
305 ffi::PySet_New(std::ptr::null_mut())
308 .assume_owned_or_err(py)?
309 .downcast_into_unchecked()
310 };
311 let ptr = set.as_ptr();
312
313 elements.into_iter().try_for_each(|element| {
314 let obj = element.into_pyobject_or_pyerr(py)?;
315 err::error_on_minusone(py, unsafe { ffi::PySet_Add(ptr, obj.as_ptr()) })
316 })?;
317
318 Ok(set)
319}
320
321#[cfg(test)]
322mod tests {
323 use super::PySet;
324 use crate::{
325 conversion::IntoPyObject,
326 ffi,
327 types::{PyAnyMethods, PySetMethods},
328 Python,
329 };
330 use std::collections::HashSet;
331
332 #[test]
333 fn test_set_new() {
334 Python::with_gil(|py| {
335 let set = PySet::new(py, [1]).unwrap();
336 assert_eq!(1, set.len());
337
338 let v = vec![1];
339 assert!(PySet::new(py, &[v]).is_err());
340 });
341 }
342
343 #[test]
344 fn test_set_empty() {
345 Python::with_gil(|py| {
346 let set = PySet::empty(py).unwrap();
347 assert_eq!(0, set.len());
348 assert!(set.is_empty());
349 });
350 }
351
352 #[test]
353 fn test_set_len() {
354 Python::with_gil(|py| {
355 let mut v = HashSet::<i32>::new();
356 let ob = (&v).into_pyobject(py).unwrap();
357 let set = ob.downcast::<PySet>().unwrap();
358 assert_eq!(0, set.len());
359 v.insert(7);
360 let ob = v.into_pyobject(py).unwrap();
361 let set2 = ob.downcast::<PySet>().unwrap();
362 assert_eq!(1, set2.len());
363 });
364 }
365
366 #[test]
367 fn test_set_clear() {
368 Python::with_gil(|py| {
369 let set = PySet::new(py, [1]).unwrap();
370 assert_eq!(1, set.len());
371 set.clear();
372 assert_eq!(0, set.len());
373 });
374 }
375
376 #[test]
377 fn test_set_contains() {
378 Python::with_gil(|py| {
379 let set = PySet::new(py, [1]).unwrap();
380 assert!(set.contains(1).unwrap());
381 });
382 }
383
384 #[test]
385 fn test_set_discard() {
386 Python::with_gil(|py| {
387 let set = PySet::new(py, [1]).unwrap();
388 assert!(!set.discard(2).unwrap());
389 assert_eq!(1, set.len());
390
391 assert!(set.discard(1).unwrap());
392 assert_eq!(0, set.len());
393 assert!(!set.discard(1).unwrap());
394
395 assert!(set.discard(vec![1, 2]).is_err());
396 });
397 }
398
399 #[test]
400 fn test_set_add() {
401 Python::with_gil(|py| {
402 let set = PySet::new(py, [1, 2]).unwrap();
403 set.add(1).unwrap(); assert!(set.contains(1).unwrap());
405 });
406 }
407
408 #[test]
409 fn test_set_pop() {
410 Python::with_gil(|py| {
411 let set = PySet::new(py, [1]).unwrap();
412 let val = set.pop();
413 assert!(val.is_some());
414 let val2 = set.pop();
415 assert!(val2.is_none());
416 assert!(py
417 .eval(
418 ffi::c_str!("print('Exception state should not be set.')"),
419 None,
420 None
421 )
422 .is_ok());
423 });
424 }
425
426 #[test]
427 fn test_set_iter() {
428 Python::with_gil(|py| {
429 let set = PySet::new(py, [1]).unwrap();
430
431 for el in set {
432 assert_eq!(1i32, el.extract::<'_, i32>().unwrap());
433 }
434 });
435 }
436
437 #[test]
438 fn test_set_iter_bound() {
439 use crate::types::any::PyAnyMethods;
440
441 Python::with_gil(|py| {
442 let set = PySet::new(py, [1]).unwrap();
443
444 for el in &set {
445 assert_eq!(1i32, el.extract::<i32>().unwrap());
446 }
447 });
448 }
449
450 #[test]
451 #[should_panic]
452 fn test_set_iter_mutation() {
453 Python::with_gil(|py| {
454 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
455
456 for _ in &set {
457 let _ = set.add(42);
458 }
459 });
460 }
461
462 #[test]
463 #[should_panic]
464 fn test_set_iter_mutation_same_len() {
465 Python::with_gil(|py| {
466 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
467
468 for item in &set {
469 let item: i32 = item.extract().unwrap();
470 let _ = set.del_item(item);
471 let _ = set.add(item + 10);
472 }
473 });
474 }
475
476 #[test]
477 fn test_set_iter_size_hint() {
478 Python::with_gil(|py| {
479 let set = PySet::new(py, [1]).unwrap();
480 let mut iter = set.iter();
481
482 assert_eq!(iter.len(), 1);
484 assert_eq!(iter.size_hint(), (1, Some(1)));
485 iter.next();
486 assert_eq!(iter.len(), 0);
487 assert_eq!(iter.size_hint(), (0, Some(0)));
488 });
489 }
490
491 #[test]
492 fn test_iter_count() {
493 Python::with_gil(|py| {
494 let set = PySet::new(py, vec![1, 2, 3]).unwrap();
495 assert_eq!(set.iter().count(), 3);
496 })
497 }
498}