1use crate::err::{self, PyErr, PyResult};
2use crate::ffi::Py_ssize_t;
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::instance::{Borrowed, Bound};
5use crate::py_result_ext::PyResultExt;
6use crate::types::{PyAny, PyList, PyMapping};
7use crate::{ffi, BoundObject, IntoPyObject, IntoPyObjectExt, Python};
8#[cfg(RustPython)]
9use crate::{
10 sync::PyOnceLock,
11 types::{PyType, PyTypeMethods},
12 Py,
13};
14
15#[repr(transparent)]
23pub struct PyDict(PyAny);
24
25#[cfg(not(GraalPy))]
26pyobject_subclassable_native_type!(PyDict, crate::ffi::PyDictObject);
27
28#[cfg(not(RustPython))]
29pyobject_native_type!(
30 PyDict,
31 ffi::PyDictObject,
32 pyobject_native_static_type_object!(ffi::PyDict_Type),
33 "builtins",
34 "dict",
35 #checkfunction=ffi::PyDict_Check
36);
37
38#[cfg(RustPython)]
39pyobject_native_type_core!(
40 PyDict,
41 |py| {
42 static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
43 TYPE.import(py, "builtins", "dict").unwrap().as_type_ptr()
44 },
45 "builtins",
46 "dict",
47 #checkfunction=ffi::PyDict_Check
48);
49
50#[cfg(not(any(PyPy, GraalPy, RustPython)))]
52#[repr(transparent)]
53pub struct PyDictKeys(PyAny);
54
55#[cfg(not(any(PyPy, GraalPy, RustPython)))]
56pyobject_native_type_core!(
57 PyDictKeys,
58 pyobject_native_static_type_object!(ffi::PyDictKeys_Type),
59 "builtins",
60 "dict_keys",
61 #checkfunction=ffi::PyDictKeys_Check
62);
63
64#[cfg(not(any(PyPy, GraalPy, RustPython)))]
66#[repr(transparent)]
67pub struct PyDictValues(PyAny);
68
69#[cfg(not(any(PyPy, GraalPy, RustPython)))]
70pyobject_native_type_core!(
71 PyDictValues,
72 pyobject_native_static_type_object!(ffi::PyDictValues_Type),
73 "builtins",
74 "dict_values",
75 #checkfunction=ffi::PyDictValues_Check
76);
77
78#[cfg(not(any(PyPy, GraalPy, RustPython)))]
80#[repr(transparent)]
81pub struct PyDictItems(PyAny);
82
83#[cfg(not(any(PyPy, GraalPy, RustPython)))]
84pyobject_native_type_core!(
85 PyDictItems,
86 pyobject_native_static_type_object!(ffi::PyDictItems_Type),
87 "builtins",
88 "dict_items",
89 #checkfunction=ffi::PyDictItems_Check
90);
91
92impl PyDict {
93 pub fn new(py: Python<'_>) -> Bound<'_, PyDict> {
95 unsafe { ffi::PyDict_New().assume_owned(py).cast_into_unchecked() }
96 }
97
98 #[cfg(not(any(PyPy, GraalPy)))]
106 pub fn from_sequence<'py>(seq: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyDict>> {
107 let py = seq.py();
108 let dict = Self::new(py);
109 err::error_on_minusone(py, unsafe {
110 ffi::PyDict_MergeFromSeq2(dict.as_ptr(), seq.as_ptr(), 1)
111 })?;
112 Ok(dict)
113 }
114}
115
116#[doc(alias = "PyDict")]
122pub trait PyDictMethods<'py>: crate::sealed::Sealed {
123 fn copy(&self) -> PyResult<Bound<'py, PyDict>>;
127
128 fn clear(&self);
130
131 fn len(&self) -> usize;
135
136 fn is_empty(&self) -> bool;
138
139 fn contains<K>(&self, key: K) -> PyResult<bool>
143 where
144 K: IntoPyObject<'py>;
145
146 fn get_item<K>(&self, key: K) -> PyResult<Option<Bound<'py, PyAny>>>
152 where
153 K: IntoPyObject<'py>;
154
155 fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
159 where
160 K: IntoPyObject<'py>,
161 V: IntoPyObject<'py>;
162
163 fn del_item<K>(&self, key: K) -> PyResult<()>
167 where
168 K: IntoPyObject<'py>;
169
170 fn keys(&self) -> Bound<'py, PyList>;
174
175 fn values(&self) -> Bound<'py, PyList>;
179
180 fn items(&self) -> Bound<'py, PyList>;
184
185 fn iter(&self) -> BoundDictIterator<'py>;
193
194 fn locked_for_each<F>(&self, closure: F) -> PyResult<()>
205 where
206 F: Fn(Bound<'py, PyAny>, Bound<'py, PyAny>) -> PyResult<()>;
207
208 fn as_mapping(&self) -> &Bound<'py, PyMapping>;
210
211 fn into_mapping(self) -> Bound<'py, PyMapping>;
213
214 fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()>;
219
220 fn update_if_missing(&self, other: &Bound<'_, PyMapping>) -> PyResult<()>;
229
230 fn set_default<K, V>(&self, key: K, default_value: V) -> PyResult<bool>
235 where
236 K: IntoPyObject<'py>,
237 V: IntoPyObject<'py>;
238
239 fn set_default_with_result<K, V>(
245 &self,
246 key: K,
247 default_value: V,
248 ) -> PyResult<(bool, Bound<'py, PyAny>)>
249 where
250 K: IntoPyObject<'py>,
251 V: IntoPyObject<'py>;
252}
253
254impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> {
255 fn copy(&self) -> PyResult<Bound<'py, PyDict>> {
256 unsafe {
257 ffi::PyDict_Copy(self.as_ptr())
258 .assume_owned_or_err(self.py())
259 .cast_into_unchecked()
260 }
261 }
262
263 fn clear(&self) {
264 unsafe { ffi::PyDict_Clear(self.as_ptr()) }
265 }
266
267 fn len(&self) -> usize {
268 dict_len(self) as usize
269 }
270
271 fn is_empty(&self) -> bool {
272 self.len() == 0
273 }
274
275 fn contains<K>(&self, key: K) -> PyResult<bool>
276 where
277 K: IntoPyObject<'py>,
278 {
279 fn inner(dict: &Bound<'_, PyDict>, key: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
280 match unsafe { ffi::PyDict_Contains(dict.as_ptr(), key.as_ptr()) } {
281 1 => Ok(true),
282 0 => Ok(false),
283 _ => Err(PyErr::fetch(dict.py())),
284 }
285 }
286
287 let py = self.py();
288 inner(
289 self,
290 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
291 )
292 }
293
294 fn get_item<K>(&self, key: K) -> PyResult<Option<Bound<'py, PyAny>>>
295 where
296 K: IntoPyObject<'py>,
297 {
298 fn inner<'py>(
299 dict: &Bound<'py, PyDict>,
300 key: Borrowed<'_, '_, PyAny>,
301 ) -> PyResult<Option<Bound<'py, PyAny>>> {
302 let py = dict.py();
303 let mut result: *mut ffi::PyObject = core::ptr::null_mut();
304 match unsafe {
305 ffi::compat::PyDict_GetItemRef(dict.as_ptr(), key.as_ptr(), &mut result)
306 } {
307 core::ffi::c_int::MIN..=-1 => Err(PyErr::fetch(py)),
308 0 => Ok(None),
309 1..=core::ffi::c_int::MAX => {
310 Ok(Some(unsafe { result.assume_owned_unchecked(py) }))
313 }
314 }
315 }
316
317 let py = self.py();
318 inner(
319 self,
320 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
321 )
322 }
323
324 fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
325 where
326 K: IntoPyObject<'py>,
327 V: IntoPyObject<'py>,
328 {
329 fn inner(
330 dict: &Bound<'_, PyDict>,
331 key: Borrowed<'_, '_, PyAny>,
332 value: Borrowed<'_, '_, PyAny>,
333 ) -> PyResult<()> {
334 err::error_on_minusone(dict.py(), unsafe {
335 ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr())
336 })
337 }
338
339 let py = self.py();
340 inner(
341 self,
342 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
343 value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
344 )
345 }
346
347 fn del_item<K>(&self, key: K) -> PyResult<()>
348 where
349 K: IntoPyObject<'py>,
350 {
351 fn inner(dict: &Bound<'_, PyDict>, key: Borrowed<'_, '_, PyAny>) -> PyResult<()> {
352 err::error_on_minusone(dict.py(), unsafe {
353 ffi::PyDict_DelItem(dict.as_ptr(), key.as_ptr())
354 })
355 }
356
357 let py = self.py();
358 inner(
359 self,
360 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
361 )
362 }
363
364 fn keys(&self) -> Bound<'py, PyList> {
365 unsafe {
366 ffi::PyDict_Keys(self.as_ptr())
367 .assume_owned(self.py())
368 .cast_into_unchecked()
369 }
370 }
371
372 fn values(&self) -> Bound<'py, PyList> {
373 unsafe {
374 ffi::PyDict_Values(self.as_ptr())
375 .assume_owned(self.py())
376 .cast_into_unchecked()
377 }
378 }
379
380 fn items(&self) -> Bound<'py, PyList> {
381 unsafe {
382 ffi::PyDict_Items(self.as_ptr())
383 .assume_owned(self.py())
384 .cast_into_unchecked()
385 }
386 }
387
388 fn iter(&self) -> BoundDictIterator<'py> {
389 BoundDictIterator::new(self.clone())
390 }
391
392 fn locked_for_each<F>(&self, f: F) -> PyResult<()>
393 where
394 F: Fn(Bound<'py, PyAny>, Bound<'py, PyAny>) -> PyResult<()>,
395 {
396 #[cfg(feature = "nightly")]
397 {
398 self.iter().try_for_each(|(key, value)| f(key, value))
401 }
402
403 #[cfg(not(feature = "nightly"))]
404 {
405 crate::sync::critical_section::with_critical_section(self, || {
406 self.iter().try_for_each(|(key, value)| f(key, value))
407 })
408 }
409 }
410
411 fn as_mapping(&self) -> &Bound<'py, PyMapping> {
412 unsafe { self.cast_unchecked() }
413 }
414
415 fn into_mapping(self) -> Bound<'py, PyMapping> {
416 unsafe { self.cast_into_unchecked() }
417 }
418
419 fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()> {
420 err::error_on_minusone(self.py(), unsafe {
421 ffi::PyDict_Update(self.as_ptr(), other.as_ptr())
422 })
423 }
424
425 fn update_if_missing(&self, other: &Bound<'_, PyMapping>) -> PyResult<()> {
426 err::error_on_minusone(self.py(), unsafe {
427 ffi::PyDict_Merge(self.as_ptr(), other.as_ptr(), 0)
428 })
429 }
430
431 fn set_default<K, V>(&self, key: K, default_value: V) -> PyResult<bool>
432 where
433 K: IntoPyObject<'py>,
434 V: IntoPyObject<'py>,
435 {
436 fn inner(
437 dict: &Bound<'_, PyDict>,
438 key: Borrowed<'_, '_, PyAny>,
439 value: Borrowed<'_, '_, PyAny>,
440 ) -> PyResult<bool> {
441 setdefault_result_from_nonerror_return_code(err::error_on_minusone_with_result(
442 dict.py(),
443 unsafe {
444 ffi::compat::PyDict_SetDefaultRef(
445 dict.as_ptr(),
446 key.as_ptr(),
447 value.as_ptr(),
448 core::ptr::null_mut(),
449 )
450 },
451 ))
452 }
453 let py = self.py();
454
455 inner(
456 self,
457 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
458 default_value
459 .into_pyobject_or_pyerr(py)?
460 .into_any()
461 .as_borrowed(),
462 )
463 }
464
465 fn set_default_with_result<K, V>(
466 &self,
467 key: K,
468 default_value: V,
469 ) -> PyResult<(bool, Bound<'py, PyAny>)>
470 where
471 K: IntoPyObject<'py>,
472 V: IntoPyObject<'py>,
473 {
474 fn inner<'py>(
475 dict: &Bound<'_, PyDict>,
476 key: Borrowed<'_, '_, PyAny>,
477 value: Borrowed<'_, '_, PyAny>,
478 py: Python<'py>,
479 ) -> PyResult<(bool, Bound<'py, PyAny>)> {
480 let mut result = core::ptr::NonNull::dangling().as_ptr();
481 let code = setdefault_result_from_nonerror_return_code(
482 err::error_on_minusone_with_result(dict.py(), unsafe {
483 ffi::compat::PyDict_SetDefaultRef(
484 dict.as_ptr(),
485 key.as_ptr(),
486 value.as_ptr(),
487 &mut result,
488 )
489 }),
490 )?;
491 let out_result = unsafe { result.assume_owned_unchecked(py) };
493 Ok((code, out_result))
494 }
495 let py = self.py();
496 inner(
497 self,
498 key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
499 default_value
500 .into_pyobject_or_pyerr(py)?
501 .into_any()
502 .as_borrowed(),
503 py,
504 )
505 }
506}
507
508fn setdefault_result_from_nonerror_return_code(code: PyResult<core::ffi::c_int>) -> PyResult<bool> {
509 match code? {
510 0 => Ok(true),
512 1 => Ok(false),
514 x => panic!("Unknown return value from PyDict_SetDefaultRef: {x}"),
515 }
516}
517
518impl<'a, 'py> Borrowed<'a, 'py, PyDict> {
519 pub(crate) unsafe fn iter_borrowed(self) -> BorrowedDictIter<'a, 'py> {
525 BorrowedDictIter::new(self)
526 }
527}
528
529fn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t {
530 #[cfg(any(PyPy, GraalPy, Py_LIMITED_API, Py_GIL_DISABLED))]
531 unsafe {
532 ffi::PyDict_Size(dict.as_ptr())
533 }
534
535 #[cfg(not(any(PyPy, GraalPy, Py_LIMITED_API, Py_GIL_DISABLED)))]
536 unsafe {
537 (*dict.as_ptr().cast::<ffi::PyDictObject>()).ma_used
538 }
539}
540
541pub struct BoundDictIterator<'py> {
543 dict: Bound<'py, PyDict>,
544 inner: DictIterImpl,
545}
546
547enum DictIterImpl {
548 DictIter {
549 ppos: ffi::Py_ssize_t,
550 di_used: ffi::Py_ssize_t,
551 remaining: ffi::Py_ssize_t,
552 },
553}
554
555impl DictIterImpl {
556 #[inline]
557 unsafe fn next_unchecked<'py>(
560 &mut self,
561 dict: &Bound<'py, PyDict>,
562 ) -> Option<(Bound<'py, PyAny>, Bound<'py, PyAny>)> {
563 match self {
564 Self::DictIter {
565 di_used,
566 remaining,
567 ppos,
568 ..
569 } => {
570 let ma_used = dict_len(dict);
571
572 if *di_used != ma_used {
577 *di_used = -1;
578 panic!("dictionary changed size during iteration");
579 };
580
581 if *remaining == -1 {
592 *di_used = -1;
593 panic!("dictionary keys changed during iteration");
594 };
595
596 let mut key: *mut ffi::PyObject = core::ptr::null_mut();
597 let mut value: *mut ffi::PyObject = core::ptr::null_mut();
598
599 if unsafe { ffi::PyDict_Next(dict.as_ptr(), ppos, &mut key, &mut value) != 0 } {
600 *remaining -= 1;
601 let py = dict.py();
602 Some((
606 unsafe { key.assume_borrowed_unchecked(py).to_owned() },
607 unsafe { value.assume_borrowed_unchecked(py).to_owned() },
608 ))
609 } else {
610 None
611 }
612 }
613 }
614 }
615
616 #[cfg(Py_GIL_DISABLED)]
617 #[inline]
618 fn with_critical_section<F, R>(&mut self, dict: &Bound<'_, PyDict>, f: F) -> R
619 where
620 F: FnOnce(&mut Self) -> R,
621 {
622 match self {
623 Self::DictIter { .. } => {
624 crate::sync::critical_section::with_critical_section(dict, || f(self))
625 }
626 }
627 }
628}
629
630impl<'py> Iterator for BoundDictIterator<'py> {
631 type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
632
633 #[inline]
634 fn next(&mut self) -> Option<Self::Item> {
635 #[cfg(Py_GIL_DISABLED)]
636 {
637 self.inner
638 .with_critical_section(&self.dict, |inner| unsafe {
639 inner.next_unchecked(&self.dict)
640 })
641 }
642 #[cfg(not(Py_GIL_DISABLED))]
643 {
644 unsafe { self.inner.next_unchecked(&self.dict) }
645 }
646 }
647
648 #[inline]
649 fn size_hint(&self) -> (usize, Option<usize>) {
650 let len = self.len();
651 (len, Some(len))
652 }
653
654 #[inline]
655 fn count(self) -> usize
656 where
657 Self: Sized,
658 {
659 self.len()
660 }
661
662 #[inline]
663 #[cfg(Py_GIL_DISABLED)]
664 fn fold<B, F>(mut self, init: B, mut f: F) -> B
665 where
666 Self: Sized,
667 F: FnMut(B, Self::Item) -> B,
668 {
669 self.inner.with_critical_section(&self.dict, |inner| {
670 let mut accum = init;
671 while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
672 accum = f(accum, x);
673 }
674 accum
675 })
676 }
677
678 #[inline]
679 #[cfg(all(Py_GIL_DISABLED, feature = "nightly"))]
680 fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
681 where
682 Self: Sized,
683 F: FnMut(B, Self::Item) -> R,
684 R: core::ops::Try<Output = B>,
685 {
686 self.inner.with_critical_section(&self.dict, |inner| {
687 let mut accum = init;
688 while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
689 accum = f(accum, x)?
690 }
691 R::from_output(accum)
692 })
693 }
694
695 #[inline]
696 #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
697 fn all<F>(&mut self, mut f: F) -> bool
698 where
699 Self: Sized,
700 F: FnMut(Self::Item) -> bool,
701 {
702 self.inner.with_critical_section(&self.dict, |inner| {
703 while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
704 if !f(x) {
705 return false;
706 }
707 }
708 true
709 })
710 }
711
712 #[inline]
713 #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
714 fn any<F>(&mut self, mut f: F) -> bool
715 where
716 Self: Sized,
717 F: FnMut(Self::Item) -> bool,
718 {
719 self.inner.with_critical_section(&self.dict, |inner| {
720 while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
721 if f(x) {
722 return true;
723 }
724 }
725 false
726 })
727 }
728
729 #[inline]
730 #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
731 fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
732 where
733 Self: Sized,
734 P: FnMut(&Self::Item) -> bool,
735 {
736 self.inner.with_critical_section(&self.dict, |inner| {
737 while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
738 if predicate(&x) {
739 return Some(x);
740 }
741 }
742 None
743 })
744 }
745
746 #[inline]
747 #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
748 fn find_map<B, F>(&mut self, mut f: F) -> Option<B>
749 where
750 Self: Sized,
751 F: FnMut(Self::Item) -> Option<B>,
752 {
753 self.inner.with_critical_section(&self.dict, |inner| {
754 while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
755 if let found @ Some(_) = f(x) {
756 return found;
757 }
758 }
759 None
760 })
761 }
762
763 #[inline]
764 #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
765 fn position<P>(&mut self, mut predicate: P) -> Option<usize>
766 where
767 Self: Sized,
768 P: FnMut(Self::Item) -> bool,
769 {
770 self.inner.with_critical_section(&self.dict, |inner| {
771 let mut acc = 0;
772 while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
773 if predicate(x) {
774 return Some(acc);
775 }
776 acc += 1;
777 }
778 None
779 })
780 }
781}
782
783impl ExactSizeIterator for BoundDictIterator<'_> {
784 fn len(&self) -> usize {
785 match self.inner {
786 DictIterImpl::DictIter { remaining, .. } => remaining as usize,
787 }
788 }
789}
790
791impl<'py> BoundDictIterator<'py> {
792 fn new(dict: Bound<'py, PyDict>) -> Self {
793 let remaining = dict_len(&dict);
794
795 Self {
796 dict,
797 inner: DictIterImpl::DictIter {
798 ppos: 0,
799 di_used: remaining,
800 remaining,
801 },
802 }
803 }
804}
805
806impl<'py> IntoIterator for Bound<'py, PyDict> {
807 type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
808 type IntoIter = BoundDictIterator<'py>;
809
810 fn into_iter(self) -> Self::IntoIter {
811 BoundDictIterator::new(self)
812 }
813}
814
815impl<'py> IntoIterator for &Bound<'py, PyDict> {
816 type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
817 type IntoIter = BoundDictIterator<'py>;
818
819 fn into_iter(self) -> Self::IntoIter {
820 self.iter()
821 }
822}
823
824mod borrowed_iter {
825 use super::*;
826
827 pub struct BorrowedDictIter<'a, 'py> {
831 dict: Borrowed<'a, 'py, PyDict>,
832 ppos: ffi::Py_ssize_t,
833 len: ffi::Py_ssize_t,
834 }
835
836 impl<'a, 'py> Iterator for BorrowedDictIter<'a, 'py> {
837 type Item = (Borrowed<'a, 'py, PyAny>, Borrowed<'a, 'py, PyAny>);
838
839 #[inline]
840 fn next(&mut self) -> Option<Self::Item> {
841 let mut key: *mut ffi::PyObject = core::ptr::null_mut();
842 let mut value: *mut ffi::PyObject = core::ptr::null_mut();
843
844 if unsafe { ffi::PyDict_Next(self.dict.as_ptr(), &mut self.ppos, &mut key, &mut value) }
846 != 0
847 {
848 let py = self.dict.py();
849 self.len -= 1;
850 Some(unsafe {
854 (
855 key.assume_borrowed_unchecked(py),
856 value.assume_borrowed_unchecked(py),
857 )
858 })
859 } else {
860 None
861 }
862 }
863
864 #[inline]
865 fn size_hint(&self) -> (usize, Option<usize>) {
866 let len = self.len();
867 (len, Some(len))
868 }
869
870 #[inline]
871 fn count(self) -> usize
872 where
873 Self: Sized,
874 {
875 self.len()
876 }
877 }
878
879 impl ExactSizeIterator for BorrowedDictIter<'_, '_> {
880 fn len(&self) -> usize {
881 self.len as usize
882 }
883 }
884
885 impl<'a, 'py> BorrowedDictIter<'a, 'py> {
886 pub(super) fn new(dict: Borrowed<'a, 'py, PyDict>) -> Self {
887 let len = dict_len(&dict);
888 BorrowedDictIter { dict, ppos: 0, len }
889 }
890 }
891}
892
893pub(crate) use borrowed_iter::BorrowedDictIter;
894
895pub trait IntoPyDict<'py>: Sized {
898 fn into_py_dict(self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>>;
901}
902
903impl<'py, T, I> IntoPyDict<'py> for I
904where
905 T: PyDictItem<'py>,
906 I: IntoIterator<Item = T>,
907{
908 fn into_py_dict(self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
909 let dict = PyDict::new(py);
910 self.into_iter().try_for_each(|item| {
911 let (key, value) = item.unpack();
912 dict.set_item(key, value)
913 })?;
914 Ok(dict)
915 }
916}
917
918trait PyDictItem<'py> {
920 type K: IntoPyObject<'py>;
921 type V: IntoPyObject<'py>;
922 fn unpack(self) -> (Self::K, Self::V);
923}
924
925impl<'py, K, V> PyDictItem<'py> for (K, V)
926where
927 K: IntoPyObject<'py>,
928 V: IntoPyObject<'py>,
929{
930 type K = K;
931 type V = V;
932
933 fn unpack(self) -> (Self::K, Self::V) {
934 (self.0, self.1)
935 }
936}
937
938impl<'a, 'py, K, V> PyDictItem<'py> for &'a (K, V)
939where
940 &'a K: IntoPyObject<'py>,
941 &'a V: IntoPyObject<'py>,
942{
943 type K = &'a K;
944 type V = &'a V;
945
946 fn unpack(self) -> (Self::K, Self::V) {
947 (&self.0, &self.1)
948 }
949}
950
951#[cfg(test)]
952mod tests {
953 use super::*;
954 use crate::platform::prelude::*;
955 use crate::platform::HashMap;
956 use crate::types::{PyAnyMethods as _, PyTuple};
957 use alloc::collections::BTreeMap;
958
959 #[test]
960 fn test_new() {
961 Python::attach(|py| {
962 let dict = [(7, 32)].into_py_dict(py).unwrap();
963 assert_eq!(
964 32,
965 dict.get_item(7i32)
966 .unwrap()
967 .unwrap()
968 .extract::<i32>()
969 .unwrap()
970 );
971 assert!(dict.get_item(8i32).unwrap().is_none());
972 let map: HashMap<i32, i32> = [(7, 32)].iter().cloned().collect();
973 assert_eq!(map, dict.extract().unwrap());
974 let map: BTreeMap<i32, i32> = [(7, 32)].iter().cloned().collect();
975 assert_eq!(map, dict.extract().unwrap());
976 });
977 }
978
979 #[test]
980 #[cfg(not(any(PyPy, GraalPy)))]
981 fn test_from_sequence() {
982 Python::attach(|py| {
983 let items = PyList::new(py, vec![("a", 1), ("b", 2)]).unwrap();
984 let dict = PyDict::from_sequence(&items).unwrap();
985 assert_eq!(
986 1,
987 dict.get_item("a")
988 .unwrap()
989 .unwrap()
990 .extract::<i32>()
991 .unwrap()
992 );
993 assert_eq!(
994 2,
995 dict.get_item("b")
996 .unwrap()
997 .unwrap()
998 .extract::<i32>()
999 .unwrap()
1000 );
1001 let map: HashMap<String, i32> =
1002 [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
1003 assert_eq!(map, dict.extract().unwrap());
1004 let map: BTreeMap<String, i32> =
1005 [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
1006 assert_eq!(map, dict.extract().unwrap());
1007 });
1008 }
1009
1010 #[test]
1011 #[cfg(not(any(PyPy, GraalPy)))]
1012 fn test_from_sequence_err() {
1013 Python::attach(|py| {
1014 let items = PyList::new(py, vec!["a", "b"]).unwrap();
1015 assert!(PyDict::from_sequence(&items).is_err());
1016 });
1017 }
1018
1019 #[test]
1020 fn test_copy() {
1021 Python::attach(|py| {
1022 let dict = [(7, 32)].into_py_dict(py).unwrap();
1023
1024 let ndict = dict.copy().unwrap();
1025 assert_eq!(
1026 32,
1027 ndict
1028 .get_item(7i32)
1029 .unwrap()
1030 .unwrap()
1031 .extract::<i32>()
1032 .unwrap()
1033 );
1034 assert!(ndict.get_item(8i32).unwrap().is_none());
1035 });
1036 }
1037
1038 #[test]
1039 fn test_len() {
1040 Python::attach(|py| {
1041 let mut v = HashMap::<i32, i32>::new();
1042 let dict = (&v).into_pyobject(py).unwrap();
1043 assert_eq!(0, dict.len());
1044 v.insert(7, 32);
1045 let dict2 = v.into_pyobject(py).unwrap();
1046 assert_eq!(1, dict2.len());
1047 });
1048 }
1049
1050 #[test]
1051 fn test_contains() {
1052 Python::attach(|py| {
1053 let mut v = HashMap::new();
1054 v.insert(7, 32);
1055 let dict = v.into_pyobject(py).unwrap();
1056 assert!(dict.contains(7i32).unwrap());
1057 assert!(!dict.contains(8i32).unwrap());
1058 });
1059 }
1060
1061 #[test]
1062 fn test_get_item() {
1063 Python::attach(|py| {
1064 let mut v = HashMap::new();
1065 v.insert(7, 32);
1066 let dict = v.into_pyobject(py).unwrap();
1067 assert_eq!(
1068 32,
1069 dict.get_item(7i32)
1070 .unwrap()
1071 .unwrap()
1072 .extract::<i32>()
1073 .unwrap()
1074 );
1075 assert!(dict.get_item(8i32).unwrap().is_none());
1076 });
1077 }
1078
1079 #[cfg(feature = "macros")]
1080 #[test]
1081 fn test_get_item_error_path() {
1082 use crate::exceptions::PyTypeError;
1083
1084 #[crate::pyclass(crate = "crate")]
1085 struct HashErrors;
1086
1087 #[crate::pymethods(crate = "crate")]
1088 impl HashErrors {
1089 #[new]
1090 fn new() -> Self {
1091 HashErrors {}
1092 }
1093
1094 fn __hash__(&self) -> PyResult<isize> {
1095 Err(PyTypeError::new_err("Error from __hash__"))
1096 }
1097 }
1098
1099 Python::attach(|py| {
1100 let class = py.get_type::<HashErrors>();
1101 let instance = class.call0().unwrap();
1102 let d = PyDict::new(py);
1103 match d.get_item(instance) {
1104 Ok(_) => {
1105 panic!("this get_item call should always error")
1106 }
1107 Err(err) => {
1108 assert!(err.is_instance_of::<PyTypeError>(py));
1109 assert!(err.value(py).to_string().contains("Error from __hash__"));
1110 }
1111 }
1112 })
1113 }
1114
1115 #[test]
1116 fn test_set_item() {
1117 Python::attach(|py| {
1118 let mut v = HashMap::new();
1119 v.insert(7, 32);
1120 let dict = v.into_pyobject(py).unwrap();
1121 assert!(dict.set_item(7i32, 42i32).is_ok()); assert!(dict.set_item(8i32, 123i32).is_ok()); assert_eq!(
1124 42i32,
1125 dict.get_item(7i32)
1126 .unwrap()
1127 .unwrap()
1128 .extract::<i32>()
1129 .unwrap()
1130 );
1131 assert_eq!(
1132 123i32,
1133 dict.get_item(8i32)
1134 .unwrap()
1135 .unwrap()
1136 .extract::<i32>()
1137 .unwrap()
1138 );
1139 });
1140 }
1141
1142 #[test]
1143 fn test_set_item_refcnt() {
1144 Python::attach(|py| {
1145 let cnt;
1146 let obj = py.eval(c"object()", None, None).unwrap();
1147 {
1148 cnt = obj._get_refcnt();
1149 let _dict = [(10, &obj)].into_py_dict(py);
1150 }
1151 {
1152 assert_eq!(cnt, obj._get_refcnt());
1153 }
1154 });
1155 }
1156
1157 #[test]
1158 fn test_set_item_does_not_update_original_object() {
1159 Python::attach(|py| {
1160 let mut v = HashMap::new();
1161 v.insert(7, 32);
1162 let dict = (&v).into_pyobject(py).unwrap();
1163 assert!(dict.set_item(7i32, 42i32).is_ok()); assert!(dict.set_item(8i32, 123i32).is_ok()); assert_eq!(32i32, v[&7i32]); assert_eq!(None, v.get(&8i32));
1167 });
1168 }
1169
1170 #[test]
1171 fn test_del_item() {
1172 Python::attach(|py| {
1173 let mut v = HashMap::new();
1174 v.insert(7, 32);
1175 let dict = v.into_pyobject(py).unwrap();
1176 assert!(dict.del_item(7i32).is_ok());
1177 assert_eq!(0, dict.len());
1178 assert!(dict.get_item(7i32).unwrap().is_none());
1179 });
1180 }
1181
1182 #[test]
1183 fn test_del_item_does_not_update_original_object() {
1184 Python::attach(|py| {
1185 let mut v = HashMap::new();
1186 v.insert(7, 32);
1187 let dict = (&v).into_pyobject(py).unwrap();
1188 assert!(dict.del_item(7i32).is_ok()); assert_eq!(32i32, *v.get(&7i32).unwrap()); });
1191 }
1192
1193 #[test]
1194 fn test_items() {
1195 Python::attach(|py| {
1196 let mut v = HashMap::new();
1197 v.insert(7, 32);
1198 v.insert(8, 42);
1199 v.insert(9, 123);
1200 let dict = v.into_pyobject(py).unwrap();
1201 let mut key_sum = 0;
1203 let mut value_sum = 0;
1204 for el in dict.items() {
1205 let tuple = el.cast::<PyTuple>().unwrap();
1206 key_sum += tuple.get_item(0).unwrap().extract::<i32>().unwrap();
1207 value_sum += tuple.get_item(1).unwrap().extract::<i32>().unwrap();
1208 }
1209 assert_eq!(7 + 8 + 9, key_sum);
1210 assert_eq!(32 + 42 + 123, value_sum);
1211 });
1212 }
1213
1214 #[test]
1215 fn test_keys() {
1216 Python::attach(|py| {
1217 let mut v = HashMap::new();
1218 v.insert(7, 32);
1219 v.insert(8, 42);
1220 v.insert(9, 123);
1221 let dict = v.into_pyobject(py).unwrap();
1222 let mut key_sum = 0;
1224 for el in dict.keys() {
1225 key_sum += el.extract::<i32>().unwrap();
1226 }
1227 assert_eq!(7 + 8 + 9, key_sum);
1228 });
1229 }
1230
1231 #[test]
1232 fn test_values() {
1233 Python::attach(|py| {
1234 let mut v = HashMap::new();
1235 v.insert(7, 32);
1236 v.insert(8, 42);
1237 v.insert(9, 123);
1238 let dict = v.into_pyobject(py).unwrap();
1239 let mut values_sum = 0;
1241 for el in dict.values() {
1242 values_sum += el.extract::<i32>().unwrap();
1243 }
1244 assert_eq!(32 + 42 + 123, values_sum);
1245 });
1246 }
1247
1248 #[test]
1249 fn test_iter() {
1250 Python::attach(|py| {
1251 let mut v = HashMap::new();
1252 v.insert(7, 32);
1253 v.insert(8, 42);
1254 v.insert(9, 123);
1255 let dict = v.into_pyobject(py).unwrap();
1256 let mut key_sum = 0;
1257 let mut value_sum = 0;
1258 for (key, value) in dict {
1259 key_sum += key.extract::<i32>().unwrap();
1260 value_sum += value.extract::<i32>().unwrap();
1261 }
1262 assert_eq!(7 + 8 + 9, key_sum);
1263 assert_eq!(32 + 42 + 123, value_sum);
1264 });
1265 }
1266
1267 #[test]
1268 fn test_iter_bound() {
1269 Python::attach(|py| {
1270 let mut v = HashMap::new();
1271 v.insert(7, 32);
1272 v.insert(8, 42);
1273 v.insert(9, 123);
1274 let dict = v.into_pyobject(py).unwrap();
1275 let mut key_sum = 0;
1276 let mut value_sum = 0;
1277 for (key, value) in dict {
1278 key_sum += key.extract::<i32>().unwrap();
1279 value_sum += value.extract::<i32>().unwrap();
1280 }
1281 assert_eq!(7 + 8 + 9, key_sum);
1282 assert_eq!(32 + 42 + 123, value_sum);
1283 });
1284 }
1285
1286 #[test]
1287 fn test_iter_value_mutated() {
1288 Python::attach(|py| {
1289 let mut v = HashMap::new();
1290 v.insert(7, 32);
1291 v.insert(8, 42);
1292 v.insert(9, 123);
1293
1294 let dict = (&v).into_pyobject(py).unwrap();
1295
1296 for (key, value) in &dict {
1297 dict.set_item(key, value.extract::<i32>().unwrap() + 7)
1298 .unwrap();
1299 }
1300 });
1301 }
1302
1303 #[test]
1304 #[should_panic]
1305 fn test_iter_key_mutated() {
1306 Python::attach(|py| {
1307 let mut v = HashMap::new();
1308 for i in 0..10 {
1309 v.insert(i * 2, i * 2);
1310 }
1311 let dict = v.into_pyobject(py).unwrap();
1312
1313 for (i, (key, value)) in dict.iter().enumerate() {
1314 let key = key.extract::<i32>().unwrap();
1315 let value = value.extract::<i32>().unwrap();
1316
1317 dict.set_item(key + 1, value + 1).unwrap();
1318
1319 if i > 1000 {
1320 break;
1322 };
1323 }
1324 });
1325 }
1326
1327 #[test]
1328 #[should_panic]
1329 fn test_iter_key_mutated_constant_len() {
1330 Python::attach(|py| {
1331 let mut v = HashMap::new();
1332 for i in 0..10 {
1333 v.insert(i * 2, i * 2);
1334 }
1335 let dict = v.into_pyobject(py).unwrap();
1336
1337 for (i, (key, value)) in dict.iter().enumerate() {
1338 let key = key.extract::<i32>().unwrap();
1339 let value = value.extract::<i32>().unwrap();
1340 dict.del_item(key).unwrap();
1341 dict.set_item(key + 1, value + 1).unwrap();
1342
1343 if i > 1000 {
1344 break;
1346 };
1347 }
1348 });
1349 }
1350
1351 #[test]
1352 fn test_iter_size_hint() {
1353 Python::attach(|py| {
1354 let mut v = HashMap::new();
1355 v.insert(7, 32);
1356 v.insert(8, 42);
1357 v.insert(9, 123);
1358 let dict = (&v).into_pyobject(py).unwrap();
1359
1360 let mut iter = dict.iter();
1361 assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
1362 iter.next();
1363 assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
1364
1365 for _ in &mut iter {}
1367
1368 assert_eq!(iter.size_hint(), (0, Some(0)));
1369
1370 assert!(iter.next().is_none());
1371
1372 assert_eq!(iter.size_hint(), (0, Some(0)));
1373 });
1374 }
1375
1376 #[test]
1377 fn test_into_iter() {
1378 Python::attach(|py| {
1379 let mut v = HashMap::new();
1380 v.insert(7, 32);
1381 v.insert(8, 42);
1382 v.insert(9, 123);
1383 let dict = v.into_pyobject(py).unwrap();
1384 let mut key_sum = 0;
1385 let mut value_sum = 0;
1386 for (key, value) in dict {
1387 key_sum += key.extract::<i32>().unwrap();
1388 value_sum += value.extract::<i32>().unwrap();
1389 }
1390 assert_eq!(7 + 8 + 9, key_sum);
1391 assert_eq!(32 + 42 + 123, value_sum);
1392 });
1393 }
1394
1395 #[test]
1396 fn test_hashmap_into_dict() {
1397 Python::attach(|py| {
1398 let mut map = HashMap::<i32, i32>::new();
1399 map.insert(1, 1);
1400
1401 let py_map = map.into_py_dict(py).unwrap();
1402
1403 assert_eq!(py_map.len(), 1);
1404 assert_eq!(
1405 py_map
1406 .get_item(1)
1407 .unwrap()
1408 .unwrap()
1409 .extract::<i32>()
1410 .unwrap(),
1411 1
1412 );
1413 });
1414 }
1415
1416 #[test]
1417 fn test_btreemap_into_dict() {
1418 Python::attach(|py| {
1419 let mut map = BTreeMap::<i32, i32>::new();
1420 map.insert(1, 1);
1421
1422 let py_map = map.into_py_dict(py).unwrap();
1423
1424 assert_eq!(py_map.len(), 1);
1425 assert_eq!(
1426 py_map
1427 .get_item(1)
1428 .unwrap()
1429 .unwrap()
1430 .extract::<i32>()
1431 .unwrap(),
1432 1
1433 );
1434 });
1435 }
1436
1437 #[test]
1438 fn test_vec_into_dict() {
1439 Python::attach(|py| {
1440 let vec = vec![("a", 1), ("b", 2), ("c", 3)];
1441 let py_map = vec.into_py_dict(py).unwrap();
1442
1443 assert_eq!(py_map.len(), 3);
1444 assert_eq!(
1445 py_map
1446 .get_item("b")
1447 .unwrap()
1448 .unwrap()
1449 .extract::<i32>()
1450 .unwrap(),
1451 2
1452 );
1453 });
1454 }
1455
1456 #[test]
1457 fn test_slice_into_dict() {
1458 Python::attach(|py| {
1459 let arr = [("a", 1), ("b", 2), ("c", 3)];
1460 let py_map = arr.into_py_dict(py).unwrap();
1461
1462 assert_eq!(py_map.len(), 3);
1463 assert_eq!(
1464 py_map
1465 .get_item("b")
1466 .unwrap()
1467 .unwrap()
1468 .extract::<i32>()
1469 .unwrap(),
1470 2
1471 );
1472 });
1473 }
1474
1475 #[test]
1476 fn dict_as_mapping() {
1477 Python::attach(|py| {
1478 let mut map = HashMap::<i32, i32>::new();
1479 map.insert(1, 1);
1480
1481 let py_map = map.into_py_dict(py).unwrap();
1482
1483 assert_eq!(py_map.as_mapping().len().unwrap(), 1);
1484 assert_eq!(
1485 py_map
1486 .as_mapping()
1487 .get_item(1)
1488 .unwrap()
1489 .extract::<i32>()
1490 .unwrap(),
1491 1
1492 );
1493 });
1494 }
1495
1496 #[test]
1497 fn dict_into_mapping() {
1498 Python::attach(|py| {
1499 let mut map = HashMap::<i32, i32>::new();
1500 map.insert(1, 1);
1501
1502 let py_map = map.into_py_dict(py).unwrap();
1503
1504 let py_mapping = py_map.into_mapping();
1505 assert_eq!(py_mapping.len().unwrap(), 1);
1506 assert_eq!(py_mapping.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
1507 });
1508 }
1509
1510 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1511 fn abc_dict(py: Python<'_>) -> Bound<'_, PyDict> {
1512 let mut map = HashMap::<&'static str, i32>::new();
1513 map.insert("a", 1);
1514 map.insert("b", 2);
1515 map.insert("c", 3);
1516 map.into_py_dict(py).unwrap()
1517 }
1518
1519 #[test]
1520 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1521 fn dict_keys_view() {
1522 Python::attach(|py| {
1523 let dict = abc_dict(py);
1524 let keys = dict.call_method0("keys").unwrap();
1525 assert!(keys.is_instance(&py.get_type::<PyDictKeys>()).unwrap());
1526 })
1527 }
1528
1529 #[test]
1530 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1531 fn dict_values_view() {
1532 Python::attach(|py| {
1533 let dict = abc_dict(py);
1534 let values = dict.call_method0("values").unwrap();
1535 assert!(values.is_instance(&py.get_type::<PyDictValues>()).unwrap());
1536 })
1537 }
1538
1539 #[test]
1540 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1541 fn dict_items_view() {
1542 Python::attach(|py| {
1543 let dict = abc_dict(py);
1544 let items = dict.call_method0("items").unwrap();
1545 assert!(items.is_instance(&py.get_type::<PyDictItems>()).unwrap());
1546 })
1547 }
1548
1549 #[test]
1550 fn dict_update() {
1551 Python::attach(|py| {
1552 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1553 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1554 dict.update(other.as_mapping()).unwrap();
1555 assert_eq!(dict.len(), 4);
1556 assert_eq!(
1557 dict.get_item("a")
1558 .unwrap()
1559 .unwrap()
1560 .extract::<i32>()
1561 .unwrap(),
1562 1
1563 );
1564 assert_eq!(
1565 dict.get_item("b")
1566 .unwrap()
1567 .unwrap()
1568 .extract::<i32>()
1569 .unwrap(),
1570 4
1571 );
1572 assert_eq!(
1573 dict.get_item("c")
1574 .unwrap()
1575 .unwrap()
1576 .extract::<i32>()
1577 .unwrap(),
1578 5
1579 );
1580 assert_eq!(
1581 dict.get_item("d")
1582 .unwrap()
1583 .unwrap()
1584 .extract::<i32>()
1585 .unwrap(),
1586 6
1587 );
1588
1589 assert_eq!(other.len(), 3);
1590 assert_eq!(
1591 other
1592 .get_item("b")
1593 .unwrap()
1594 .unwrap()
1595 .extract::<i32>()
1596 .unwrap(),
1597 4
1598 );
1599 assert_eq!(
1600 other
1601 .get_item("c")
1602 .unwrap()
1603 .unwrap()
1604 .extract::<i32>()
1605 .unwrap(),
1606 5
1607 );
1608 assert_eq!(
1609 other
1610 .get_item("d")
1611 .unwrap()
1612 .unwrap()
1613 .extract::<i32>()
1614 .unwrap(),
1615 6
1616 );
1617 })
1618 }
1619
1620 #[test]
1621 fn dict_update_if_missing() {
1622 Python::attach(|py| {
1623 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1624 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1625 dict.update_if_missing(other.as_mapping()).unwrap();
1626 assert_eq!(dict.len(), 4);
1627 assert_eq!(
1628 dict.get_item("a")
1629 .unwrap()
1630 .unwrap()
1631 .extract::<i32>()
1632 .unwrap(),
1633 1
1634 );
1635 assert_eq!(
1636 dict.get_item("b")
1637 .unwrap()
1638 .unwrap()
1639 .extract::<i32>()
1640 .unwrap(),
1641 2
1642 );
1643 assert_eq!(
1644 dict.get_item("c")
1645 .unwrap()
1646 .unwrap()
1647 .extract::<i32>()
1648 .unwrap(),
1649 3
1650 );
1651 assert_eq!(
1652 dict.get_item("d")
1653 .unwrap()
1654 .unwrap()
1655 .extract::<i32>()
1656 .unwrap(),
1657 6
1658 );
1659
1660 assert_eq!(other.len(), 3);
1661 assert_eq!(
1662 other
1663 .get_item("b")
1664 .unwrap()
1665 .unwrap()
1666 .extract::<i32>()
1667 .unwrap(),
1668 4
1669 );
1670 assert_eq!(
1671 other
1672 .get_item("c")
1673 .unwrap()
1674 .unwrap()
1675 .extract::<i32>()
1676 .unwrap(),
1677 5
1678 );
1679 assert_eq!(
1680 other
1681 .get_item("d")
1682 .unwrap()
1683 .unwrap()
1684 .extract::<i32>()
1685 .unwrap(),
1686 6
1687 );
1688 })
1689 }
1690
1691 #[test]
1692 fn test_iter_all() {
1693 Python::attach(|py| {
1694 let dict = [(1, true), (2, true), (3, true)].into_py_dict(py).unwrap();
1695 assert!(dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1696
1697 let dict = [(1, true), (2, false), (3, true)].into_py_dict(py).unwrap();
1698 assert!(!dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1699 });
1700 }
1701
1702 #[test]
1703 fn test_iter_any() {
1704 Python::attach(|py| {
1705 let dict = [(1, true), (2, false), (3, false)]
1706 .into_py_dict(py)
1707 .unwrap();
1708 assert!(dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1709
1710 let dict = [(1, false), (2, false), (3, false)]
1711 .into_py_dict(py)
1712 .unwrap();
1713 assert!(!dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1714 });
1715 }
1716
1717 #[test]
1718 #[allow(clippy::search_is_some)]
1719 fn test_iter_find() {
1720 Python::attach(|py| {
1721 let dict = [(1, false), (2, true), (3, false)]
1722 .into_py_dict(py)
1723 .unwrap();
1724
1725 assert_eq!(
1726 Some((2, true)),
1727 dict.iter()
1728 .find(|(_, v)| v.extract::<bool>().unwrap())
1729 .map(|(k, v)| (k.extract().unwrap(), v.extract().unwrap()))
1730 );
1731
1732 let dict = [(1, false), (2, false), (3, false)]
1733 .into_py_dict(py)
1734 .unwrap();
1735
1736 assert!(dict
1737 .iter()
1738 .find(|(_, v)| v.extract::<bool>().unwrap())
1739 .is_none());
1740 });
1741 }
1742
1743 #[test]
1744 #[allow(clippy::search_is_some)]
1745 fn test_iter_position() {
1746 Python::attach(|py| {
1747 let dict = [(1, false), (2, false), (3, true)]
1748 .into_py_dict(py)
1749 .unwrap();
1750 assert_eq!(
1751 Some(2),
1752 dict.iter().position(|(_, v)| v.extract::<bool>().unwrap())
1753 );
1754
1755 let dict = [(1, false), (2, false), (3, false)]
1756 .into_py_dict(py)
1757 .unwrap();
1758 assert!(dict
1759 .iter()
1760 .position(|(_, v)| v.extract::<bool>().unwrap())
1761 .is_none());
1762 });
1763 }
1764
1765 #[test]
1766 fn test_iter_fold() {
1767 Python::attach(|py| {
1768 let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1769 let sum = dict
1770 .iter()
1771 .fold(0, |acc, (_, v)| acc + v.extract::<i32>().unwrap());
1772 assert_eq!(sum, 6);
1773 });
1774 }
1775
1776 #[test]
1777 fn test_iter_try_fold() {
1778 Python::attach(|py| {
1779 let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1780 let sum = dict
1781 .iter()
1782 .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1783 .unwrap();
1784 assert_eq!(sum, 6);
1785
1786 let dict = [(1, "foo"), (2, "bar")].into_py_dict(py).unwrap();
1787 assert!(dict
1788 .iter()
1789 .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1790 .is_err());
1791 });
1792 }
1793
1794 #[test]
1795 fn test_iter_count() {
1796 Python::attach(|py| {
1797 let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1798 assert_eq!(dict.iter().count(), 3);
1799 })
1800 }
1801
1802 #[test]
1803 fn test_set_default() {
1804 Python::attach(|py| {
1805 let dict = PyDict::new(py);
1806 assert!(matches!(dict.set_default("hello", "world"), Ok(true)));
1807 assert_eq!(
1808 dict.get_item("hello")
1809 .unwrap()
1810 .unwrap()
1811 .extract::<String>()
1812 .unwrap(),
1813 "world"
1814 );
1815
1816 assert!(matches!(dict.set_default("hello", "foobar"), Ok(false)));
1817
1818 let invalid_key = PyList::new(py, vec![0]).unwrap();
1820 assert!(dict.set_default(invalid_key, "foobar").is_err());
1821 })
1822 }
1823
1824 #[test]
1825 fn test_set_default_with_result() {
1826 Python::attach(|py| {
1827 let dict = PyDict::new(py);
1828 let res = dict.set_default_with_result("hello", "world");
1829 assert!(res.is_ok());
1830 let (inserted, value) = res.unwrap();
1831 assert!(inserted);
1832 assert!(value.extract::<String>().unwrap() == "world");
1833 assert!(
1834 dict.get_item("hello")
1835 .unwrap()
1836 .unwrap()
1837 .extract::<String>()
1838 .unwrap()
1839 == "world"
1840 );
1841
1842 let (inserted, value) = dict.set_default_with_result("hello", "foobar").unwrap();
1843 assert!(!inserted);
1844 assert_eq!(value.extract::<String>().unwrap(), "world");
1845
1846 let invalid_key = PyList::new(py, vec![0]).unwrap();
1848 assert!(dict.set_default_with_result(invalid_key, "foobar").is_err());
1849 })
1850 }
1851}