1use crate::types::PyIterator;
2use crate::{
3 err::{self, PyErr, PyResult},
4 ffi_ptr_ext::FfiPtrExt,
5 instance::Bound,
6 py_result_ext::PyResultExt,
7};
8use crate::{ffi, Borrowed, BoundObject, IntoPyObject, IntoPyObjectExt, PyAny, Python};
9use std::ptr;
10
11#[repr(transparent)]
19pub struct PySet(PyAny);
20
21#[cfg(not(any(PyPy, GraalPy)))]
22pyobject_subclassable_native_type!(PySet, crate::ffi::PySetObject);
23
24#[cfg(not(any(PyPy, GraalPy)))]
25pyobject_native_type!(
26 PySet,
27 ffi::PySetObject,
28 pyobject_native_static_type_object!(ffi::PySet_Type),
29 "builtins",
30 "set",
31 #checkfunction=ffi::PySet_Check
32);
33
34#[cfg(any(PyPy, GraalPy))]
35pyobject_native_type_core!(
36 PySet,
37 pyobject_native_static_type_object!(ffi::PySet_Type),
38 "builtins",
39 "set",
40 #checkfunction=ffi::PySet_Check
41);
42
43impl PySet {
44 #[inline]
48 pub fn new<'py, T>(
49 py: Python<'py>,
50 elements: impl IntoIterator<Item = T>,
51 ) -> PyResult<Bound<'py, PySet>>
52 where
53 T: IntoPyObject<'py>,
54 {
55 try_new_from_iter(py, elements)
56 }
57
58 pub fn empty(py: Python<'_>) -> PyResult<Bound<'_, PySet>> {
60 unsafe {
61 ffi::PySet_New(ptr::null_mut())
62 .assume_owned_or_err(py)
63 .cast_into_unchecked()
64 }
65 }
66}
67
68#[doc(alias = "PySet")]
74pub trait PySetMethods<'py>: crate::sealed::Sealed {
75 fn clear(&self);
77
78 fn len(&self) -> usize;
82
83 fn is_empty(&self) -> bool {
85 self.len() == 0
86 }
87
88 fn contains<K>(&self, key: K) -> PyResult<bool>
92 where
93 K: IntoPyObject<'py>;
94
95 fn discard<K>(&self, key: K) -> PyResult<bool>
99 where
100 K: IntoPyObject<'py>;
101
102 fn add<K>(&self, key: K) -> PyResult<()>
104 where
105 K: IntoPyObject<'py>;
106
107 fn pop(&self) -> Option<Bound<'py, PyAny>>;
109
110 fn iter(&self) -> BoundSetIterator<'py>;
116}
117
118impl<'py> PySetMethods<'py> for Bound<'py, PySet> {
119 #[inline]
120 fn clear(&self) {
121 unsafe {
122 ffi::PySet_Clear(self.as_ptr());
123 }
124 }
125
126 #[inline]
127 fn len(&self) -> usize {
128 unsafe { ffi::PySet_Size(self.as_ptr()) as usize }
129 }
130
131 fn contains<K>(&self, key: K) -> PyResult<bool>
132 where
133 K: IntoPyObject<'py>,
134 {
135 fn inner(set: &Bound<'_, PySet>, key: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
136 match unsafe { ffi::PySet_Contains(set.as_ptr(), key.as_ptr()) } {
137 1 => Ok(true),
138 0 => Ok(false),
139 _ => Err(PyErr::fetch(set.py())),
140 }
141 }
142
143 let py = self.py();
144 inner(
145 self,
146 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
147 )
148 }
149
150 fn discard<K>(&self, key: K) -> PyResult<bool>
151 where
152 K: IntoPyObject<'py>,
153 {
154 fn inner(set: &Bound<'_, PySet>, key: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
155 match unsafe { ffi::PySet_Discard(set.as_ptr(), key.as_ptr()) } {
156 1 => Ok(true),
157 0 => Ok(false),
158 _ => Err(PyErr::fetch(set.py())),
159 }
160 }
161
162 let py = self.py();
163 inner(
164 self,
165 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
166 )
167 }
168
169 fn add<K>(&self, key: K) -> PyResult<()>
170 where
171 K: IntoPyObject<'py>,
172 {
173 fn inner(set: &Bound<'_, PySet>, key: Borrowed<'_, '_, PyAny>) -> PyResult<()> {
174 err::error_on_minusone(set.py(), unsafe {
175 ffi::PySet_Add(set.as_ptr(), key.as_ptr())
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 pop(&self) -> Option<Bound<'py, PyAny>> {
187 let element = unsafe { ffi::PySet_Pop(self.as_ptr()).assume_owned_or_err(self.py()) };
188 element.ok()
189 }
190
191 fn iter(&self) -> BoundSetIterator<'py> {
192 BoundSetIterator::new(self.clone())
193 }
194}
195
196impl<'py> IntoIterator for Bound<'py, PySet> {
197 type Item = Bound<'py, PyAny>;
198 type IntoIter = BoundSetIterator<'py>;
199
200 fn into_iter(self) -> Self::IntoIter {
206 BoundSetIterator::new(self)
207 }
208}
209
210impl<'py> IntoIterator for &Bound<'py, PySet> {
211 type Item = Bound<'py, PyAny>;
212 type IntoIter = BoundSetIterator<'py>;
213
214 fn into_iter(self) -> Self::IntoIter {
220 self.iter()
221 }
222}
223
224pub struct BoundSetIterator<'py>(Bound<'py, PyIterator>);
226
227impl<'py> BoundSetIterator<'py> {
228 pub(super) fn new(set: Bound<'py, PySet>) -> Self {
229 Self(PyIterator::from_object(&set).expect("set should always be iterable"))
230 }
231}
232
233impl<'py> Iterator for BoundSetIterator<'py> {
234 type Item = Bound<'py, super::PyAny>;
235
236 fn next(&mut self) -> Option<Self::Item> {
238 self.0
239 .next()
240 .map(|result| result.expect("set iteration should be infallible"))
241 }
242
243 fn size_hint(&self) -> (usize, Option<usize>) {
244 let len = ExactSizeIterator::len(self);
245 (len, Some(len))
246 }
247
248 #[inline]
249 fn count(self) -> usize
250 where
251 Self: Sized,
252 {
253 self.len()
254 }
255}
256
257impl ExactSizeIterator for BoundSetIterator<'_> {
258 fn len(&self) -> usize {
259 self.0.size_hint().0
260 }
261}
262
263#[inline]
264pub(crate) fn try_new_from_iter<'py, T>(
265 py: Python<'py>,
266 elements: impl IntoIterator<Item = T>,
267) -> PyResult<Bound<'py, PySet>>
268where
269 T: IntoPyObject<'py>,
270{
271 let set = unsafe {
272 ffi::PySet_New(std::ptr::null_mut())
275 .assume_owned_or_err(py)?
276 .cast_into_unchecked()
277 };
278 let ptr = set.as_ptr();
279
280 elements.into_iter().try_for_each(|element| {
281 let obj = element.into_pyobject_or_pyerr(py)?;
282 err::error_on_minusone(py, unsafe { ffi::PySet_Add(ptr, obj.as_ptr()) })
283 })?;
284
285 Ok(set)
286}
287
288#[cfg(test)]
289mod tests {
290 use super::PySet;
291 use crate::{
292 conversion::IntoPyObject,
293 types::{PyAnyMethods, PySetMethods},
294 Python,
295 };
296 use std::collections::HashSet;
297
298 #[test]
299 fn test_set_new() {
300 Python::attach(|py| {
301 let set = PySet::new(py, [1]).unwrap();
302 assert_eq!(1, set.len());
303
304 let v = vec![1];
305 assert!(PySet::new(py, &[v]).is_err());
306 });
307 }
308
309 #[test]
310 fn test_set_empty() {
311 Python::attach(|py| {
312 let set = PySet::empty(py).unwrap();
313 assert_eq!(0, set.len());
314 assert!(set.is_empty());
315 });
316 }
317
318 #[test]
319 fn test_set_len() {
320 Python::attach(|py| {
321 let mut v = HashSet::<i32>::new();
322 let ob = (&v).into_pyobject(py).unwrap();
323 let set = ob.cast::<PySet>().unwrap();
324 assert_eq!(0, set.len());
325 v.insert(7);
326 let ob = v.into_pyobject(py).unwrap();
327 let set2 = ob.cast::<PySet>().unwrap();
328 assert_eq!(1, set2.len());
329 });
330 }
331
332 #[test]
333 fn test_set_clear() {
334 Python::attach(|py| {
335 let set = PySet::new(py, [1]).unwrap();
336 assert_eq!(1, set.len());
337 set.clear();
338 assert_eq!(0, set.len());
339 });
340 }
341
342 #[test]
343 fn test_set_contains() {
344 Python::attach(|py| {
345 let set = PySet::new(py, [1]).unwrap();
346 assert!(set.contains(1).unwrap());
347 });
348 }
349
350 #[test]
351 fn test_set_discard() {
352 Python::attach(|py| {
353 let set = PySet::new(py, [1]).unwrap();
354 assert!(!set.discard(2).unwrap());
355 assert_eq!(1, set.len());
356
357 assert!(set.discard(1).unwrap());
358 assert_eq!(0, set.len());
359 assert!(!set.discard(1).unwrap());
360
361 assert!(set.discard(vec![1, 2]).is_err());
362 });
363 }
364
365 #[test]
366 fn test_set_add() {
367 Python::attach(|py| {
368 let set = PySet::new(py, [1, 2]).unwrap();
369 set.add(1).unwrap(); assert!(set.contains(1).unwrap());
371 });
372 }
373
374 #[test]
375 fn test_set_pop() {
376 Python::attach(|py| {
377 let set = PySet::new(py, [1]).unwrap();
378 let val = set.pop();
379 assert!(val.is_some());
380 let val2 = set.pop();
381 assert!(val2.is_none());
382 assert!(py
383 .eval(c"print('Exception state should not be set.')", None, None)
384 .is_ok());
385 });
386 }
387
388 #[test]
389 fn test_set_iter() {
390 Python::attach(|py| {
391 let set = PySet::new(py, [1]).unwrap();
392
393 for el in set {
394 assert_eq!(1i32, el.extract::<'_, i32>().unwrap());
395 }
396 });
397 }
398
399 #[test]
400 fn test_set_iter_bound() {
401 use crate::types::any::PyAnyMethods;
402
403 Python::attach(|py| {
404 let set = PySet::new(py, [1]).unwrap();
405
406 for el in &set {
407 assert_eq!(1i32, el.extract::<i32>().unwrap());
408 }
409 });
410 }
411
412 #[test]
413 #[should_panic]
414 fn test_set_iter_mutation() {
415 Python::attach(|py| {
416 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
417
418 for _ in &set {
419 let _ = set.add(42);
420 }
421 });
422 }
423
424 #[test]
425 #[should_panic]
426 fn test_set_iter_mutation_same_len() {
427 Python::attach(|py| {
428 let set = PySet::new(py, [1, 2, 3, 4, 5]).unwrap();
429
430 for item in &set {
431 let item: i32 = item.extract().unwrap();
432 let _ = set.del_item(item);
433 let _ = set.add(item + 10);
434 }
435 });
436 }
437
438 #[test]
439 fn test_set_iter_size_hint() {
440 Python::attach(|py| {
441 let set = PySet::new(py, [1]).unwrap();
442 let mut iter = set.iter();
443
444 assert_eq!(iter.len(), 1);
446 assert_eq!(iter.size_hint(), (1, Some(1)));
447 iter.next();
448 assert_eq!(iter.len(), 0);
449 assert_eq!(iter.size_hint(), (0, Some(0)));
450 });
451 }
452
453 #[test]
454 fn test_iter_count() {
455 Python::attach(|py| {
456 let set = PySet::new(py, vec![1, 2, 3]).unwrap();
457 assert_eq!(set.iter().count(), 3);
458 })
459 }
460}