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::HashMap;
955 use crate::types::{PyAnyMethods as _, PyTuple};
956 use alloc::collections::BTreeMap;
957
958 #[test]
959 fn test_new() {
960 Python::attach(|py| {
961 let dict = [(7, 32)].into_py_dict(py).unwrap();
962 assert_eq!(
963 32,
964 dict.get_item(7i32)
965 .unwrap()
966 .unwrap()
967 .extract::<i32>()
968 .unwrap()
969 );
970 assert!(dict.get_item(8i32).unwrap().is_none());
971 let map: HashMap<i32, i32> = [(7, 32)].iter().cloned().collect();
972 assert_eq!(map, dict.extract().unwrap());
973 let map: BTreeMap<i32, i32> = [(7, 32)].iter().cloned().collect();
974 assert_eq!(map, dict.extract().unwrap());
975 });
976 }
977
978 #[test]
979 #[cfg(not(any(PyPy, GraalPy)))]
980 fn test_from_sequence() {
981 Python::attach(|py| {
982 let items = PyList::new(py, vec![("a", 1), ("b", 2)]).unwrap();
983 let dict = PyDict::from_sequence(&items).unwrap();
984 assert_eq!(
985 1,
986 dict.get_item("a")
987 .unwrap()
988 .unwrap()
989 .extract::<i32>()
990 .unwrap()
991 );
992 assert_eq!(
993 2,
994 dict.get_item("b")
995 .unwrap()
996 .unwrap()
997 .extract::<i32>()
998 .unwrap()
999 );
1000 let map: HashMap<String, i32> =
1001 [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
1002 assert_eq!(map, dict.extract().unwrap());
1003 let map: BTreeMap<String, i32> =
1004 [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
1005 assert_eq!(map, dict.extract().unwrap());
1006 });
1007 }
1008
1009 #[test]
1010 #[cfg(not(any(PyPy, GraalPy)))]
1011 fn test_from_sequence_err() {
1012 Python::attach(|py| {
1013 let items = PyList::new(py, vec!["a", "b"]).unwrap();
1014 assert!(PyDict::from_sequence(&items).is_err());
1015 });
1016 }
1017
1018 #[test]
1019 fn test_copy() {
1020 Python::attach(|py| {
1021 let dict = [(7, 32)].into_py_dict(py).unwrap();
1022
1023 let ndict = dict.copy().unwrap();
1024 assert_eq!(
1025 32,
1026 ndict
1027 .get_item(7i32)
1028 .unwrap()
1029 .unwrap()
1030 .extract::<i32>()
1031 .unwrap()
1032 );
1033 assert!(ndict.get_item(8i32).unwrap().is_none());
1034 });
1035 }
1036
1037 #[test]
1038 fn test_len() {
1039 Python::attach(|py| {
1040 let mut v = HashMap::<i32, i32>::new();
1041 let dict = (&v).into_pyobject(py).unwrap();
1042 assert_eq!(0, dict.len());
1043 v.insert(7, 32);
1044 let dict2 = v.into_pyobject(py).unwrap();
1045 assert_eq!(1, dict2.len());
1046 });
1047 }
1048
1049 #[test]
1050 fn test_contains() {
1051 Python::attach(|py| {
1052 let mut v = HashMap::new();
1053 v.insert(7, 32);
1054 let dict = v.into_pyobject(py).unwrap();
1055 assert!(dict.contains(7i32).unwrap());
1056 assert!(!dict.contains(8i32).unwrap());
1057 });
1058 }
1059
1060 #[test]
1061 fn test_get_item() {
1062 Python::attach(|py| {
1063 let mut v = HashMap::new();
1064 v.insert(7, 32);
1065 let dict = v.into_pyobject(py).unwrap();
1066 assert_eq!(
1067 32,
1068 dict.get_item(7i32)
1069 .unwrap()
1070 .unwrap()
1071 .extract::<i32>()
1072 .unwrap()
1073 );
1074 assert!(dict.get_item(8i32).unwrap().is_none());
1075 });
1076 }
1077
1078 #[cfg(feature = "macros")]
1079 #[test]
1080 fn test_get_item_error_path() {
1081 use crate::exceptions::PyTypeError;
1082
1083 #[crate::pyclass(crate = "crate")]
1084 struct HashErrors;
1085
1086 #[crate::pymethods(crate = "crate")]
1087 impl HashErrors {
1088 #[new]
1089 fn new() -> Self {
1090 HashErrors {}
1091 }
1092
1093 fn __hash__(&self) -> PyResult<isize> {
1094 Err(PyTypeError::new_err("Error from __hash__"))
1095 }
1096 }
1097
1098 Python::attach(|py| {
1099 let class = py.get_type::<HashErrors>();
1100 let instance = class.call0().unwrap();
1101 let d = PyDict::new(py);
1102 match d.get_item(instance) {
1103 Ok(_) => {
1104 panic!("this get_item call should always error")
1105 }
1106 Err(err) => {
1107 assert!(err.is_instance_of::<PyTypeError>(py));
1108 assert!(err.value(py).to_string().contains("Error from __hash__"));
1109 }
1110 }
1111 })
1112 }
1113
1114 #[test]
1115 fn test_set_item() {
1116 Python::attach(|py| {
1117 let mut v = HashMap::new();
1118 v.insert(7, 32);
1119 let dict = v.into_pyobject(py).unwrap();
1120 assert!(dict.set_item(7i32, 42i32).is_ok()); assert!(dict.set_item(8i32, 123i32).is_ok()); assert_eq!(
1123 42i32,
1124 dict.get_item(7i32)
1125 .unwrap()
1126 .unwrap()
1127 .extract::<i32>()
1128 .unwrap()
1129 );
1130 assert_eq!(
1131 123i32,
1132 dict.get_item(8i32)
1133 .unwrap()
1134 .unwrap()
1135 .extract::<i32>()
1136 .unwrap()
1137 );
1138 });
1139 }
1140
1141 #[test]
1142 fn test_set_item_refcnt() {
1143 Python::attach(|py| {
1144 let cnt;
1145 let obj = py.eval(c"object()", None, None).unwrap();
1146 {
1147 cnt = obj._get_refcnt();
1148 let _dict = [(10, &obj)].into_py_dict(py);
1149 }
1150 {
1151 assert_eq!(cnt, obj._get_refcnt());
1152 }
1153 });
1154 }
1155
1156 #[test]
1157 fn test_set_item_does_not_update_original_object() {
1158 Python::attach(|py| {
1159 let mut v = HashMap::new();
1160 v.insert(7, 32);
1161 let dict = (&v).into_pyobject(py).unwrap();
1162 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));
1166 });
1167 }
1168
1169 #[test]
1170 fn test_del_item() {
1171 Python::attach(|py| {
1172 let mut v = HashMap::new();
1173 v.insert(7, 32);
1174 let dict = v.into_pyobject(py).unwrap();
1175 assert!(dict.del_item(7i32).is_ok());
1176 assert_eq!(0, dict.len());
1177 assert!(dict.get_item(7i32).unwrap().is_none());
1178 });
1179 }
1180
1181 #[test]
1182 fn test_del_item_does_not_update_original_object() {
1183 Python::attach(|py| {
1184 let mut v = HashMap::new();
1185 v.insert(7, 32);
1186 let dict = (&v).into_pyobject(py).unwrap();
1187 assert!(dict.del_item(7i32).is_ok()); assert_eq!(32i32, *v.get(&7i32).unwrap()); });
1190 }
1191
1192 #[test]
1193 fn test_items() {
1194 Python::attach(|py| {
1195 let mut v = HashMap::new();
1196 v.insert(7, 32);
1197 v.insert(8, 42);
1198 v.insert(9, 123);
1199 let dict = v.into_pyobject(py).unwrap();
1200 let mut key_sum = 0;
1202 let mut value_sum = 0;
1203 for el in dict.items() {
1204 let tuple = el.cast::<PyTuple>().unwrap();
1205 key_sum += tuple.get_item(0).unwrap().extract::<i32>().unwrap();
1206 value_sum += tuple.get_item(1).unwrap().extract::<i32>().unwrap();
1207 }
1208 assert_eq!(7 + 8 + 9, key_sum);
1209 assert_eq!(32 + 42 + 123, value_sum);
1210 });
1211 }
1212
1213 #[test]
1214 fn test_keys() {
1215 Python::attach(|py| {
1216 let mut v = HashMap::new();
1217 v.insert(7, 32);
1218 v.insert(8, 42);
1219 v.insert(9, 123);
1220 let dict = v.into_pyobject(py).unwrap();
1221 let mut key_sum = 0;
1223 for el in dict.keys() {
1224 key_sum += el.extract::<i32>().unwrap();
1225 }
1226 assert_eq!(7 + 8 + 9, key_sum);
1227 });
1228 }
1229
1230 #[test]
1231 fn test_values() {
1232 Python::attach(|py| {
1233 let mut v = HashMap::new();
1234 v.insert(7, 32);
1235 v.insert(8, 42);
1236 v.insert(9, 123);
1237 let dict = v.into_pyobject(py).unwrap();
1238 let mut values_sum = 0;
1240 for el in dict.values() {
1241 values_sum += el.extract::<i32>().unwrap();
1242 }
1243 assert_eq!(32 + 42 + 123, values_sum);
1244 });
1245 }
1246
1247 #[test]
1248 fn test_iter() {
1249 Python::attach(|py| {
1250 let mut v = HashMap::new();
1251 v.insert(7, 32);
1252 v.insert(8, 42);
1253 v.insert(9, 123);
1254 let dict = v.into_pyobject(py).unwrap();
1255 let mut key_sum = 0;
1256 let mut value_sum = 0;
1257 for (key, value) in dict {
1258 key_sum += key.extract::<i32>().unwrap();
1259 value_sum += value.extract::<i32>().unwrap();
1260 }
1261 assert_eq!(7 + 8 + 9, key_sum);
1262 assert_eq!(32 + 42 + 123, value_sum);
1263 });
1264 }
1265
1266 #[test]
1267 fn test_iter_bound() {
1268 Python::attach(|py| {
1269 let mut v = HashMap::new();
1270 v.insert(7, 32);
1271 v.insert(8, 42);
1272 v.insert(9, 123);
1273 let dict = v.into_pyobject(py).unwrap();
1274 let mut key_sum = 0;
1275 let mut value_sum = 0;
1276 for (key, value) in dict {
1277 key_sum += key.extract::<i32>().unwrap();
1278 value_sum += value.extract::<i32>().unwrap();
1279 }
1280 assert_eq!(7 + 8 + 9, key_sum);
1281 assert_eq!(32 + 42 + 123, value_sum);
1282 });
1283 }
1284
1285 #[test]
1286 fn test_iter_value_mutated() {
1287 Python::attach(|py| {
1288 let mut v = HashMap::new();
1289 v.insert(7, 32);
1290 v.insert(8, 42);
1291 v.insert(9, 123);
1292
1293 let dict = (&v).into_pyobject(py).unwrap();
1294
1295 for (key, value) in &dict {
1296 dict.set_item(key, value.extract::<i32>().unwrap() + 7)
1297 .unwrap();
1298 }
1299 });
1300 }
1301
1302 #[test]
1303 #[should_panic]
1304 fn test_iter_key_mutated() {
1305 Python::attach(|py| {
1306 let mut v = HashMap::new();
1307 for i in 0..10 {
1308 v.insert(i * 2, i * 2);
1309 }
1310 let dict = v.into_pyobject(py).unwrap();
1311
1312 for (i, (key, value)) in dict.iter().enumerate() {
1313 let key = key.extract::<i32>().unwrap();
1314 let value = value.extract::<i32>().unwrap();
1315
1316 dict.set_item(key + 1, value + 1).unwrap();
1317
1318 if i > 1000 {
1319 break;
1321 };
1322 }
1323 });
1324 }
1325
1326 #[test]
1327 #[should_panic]
1328 fn test_iter_key_mutated_constant_len() {
1329 Python::attach(|py| {
1330 let mut v = HashMap::new();
1331 for i in 0..10 {
1332 v.insert(i * 2, i * 2);
1333 }
1334 let dict = v.into_pyobject(py).unwrap();
1335
1336 for (i, (key, value)) in dict.iter().enumerate() {
1337 let key = key.extract::<i32>().unwrap();
1338 let value = value.extract::<i32>().unwrap();
1339 dict.del_item(key).unwrap();
1340 dict.set_item(key + 1, value + 1).unwrap();
1341
1342 if i > 1000 {
1343 break;
1345 };
1346 }
1347 });
1348 }
1349
1350 #[test]
1351 fn test_iter_size_hint() {
1352 Python::attach(|py| {
1353 let mut v = HashMap::new();
1354 v.insert(7, 32);
1355 v.insert(8, 42);
1356 v.insert(9, 123);
1357 let dict = (&v).into_pyobject(py).unwrap();
1358
1359 let mut iter = dict.iter();
1360 assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
1361 iter.next();
1362 assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
1363
1364 for _ in &mut iter {}
1366
1367 assert_eq!(iter.size_hint(), (0, Some(0)));
1368
1369 assert!(iter.next().is_none());
1370
1371 assert_eq!(iter.size_hint(), (0, Some(0)));
1372 });
1373 }
1374
1375 #[test]
1376 fn test_into_iter() {
1377 Python::attach(|py| {
1378 let mut v = HashMap::new();
1379 v.insert(7, 32);
1380 v.insert(8, 42);
1381 v.insert(9, 123);
1382 let dict = v.into_pyobject(py).unwrap();
1383 let mut key_sum = 0;
1384 let mut value_sum = 0;
1385 for (key, value) in dict {
1386 key_sum += key.extract::<i32>().unwrap();
1387 value_sum += value.extract::<i32>().unwrap();
1388 }
1389 assert_eq!(7 + 8 + 9, key_sum);
1390 assert_eq!(32 + 42 + 123, value_sum);
1391 });
1392 }
1393
1394 #[test]
1395 fn test_hashmap_into_dict() {
1396 Python::attach(|py| {
1397 let mut map = HashMap::<i32, i32>::new();
1398 map.insert(1, 1);
1399
1400 let py_map = map.into_py_dict(py).unwrap();
1401
1402 assert_eq!(py_map.len(), 1);
1403 assert_eq!(
1404 py_map
1405 .get_item(1)
1406 .unwrap()
1407 .unwrap()
1408 .extract::<i32>()
1409 .unwrap(),
1410 1
1411 );
1412 });
1413 }
1414
1415 #[test]
1416 fn test_btreemap_into_dict() {
1417 Python::attach(|py| {
1418 let mut map = BTreeMap::<i32, i32>::new();
1419 map.insert(1, 1);
1420
1421 let py_map = map.into_py_dict(py).unwrap();
1422
1423 assert_eq!(py_map.len(), 1);
1424 assert_eq!(
1425 py_map
1426 .get_item(1)
1427 .unwrap()
1428 .unwrap()
1429 .extract::<i32>()
1430 .unwrap(),
1431 1
1432 );
1433 });
1434 }
1435
1436 #[test]
1437 fn test_vec_into_dict() {
1438 Python::attach(|py| {
1439 let vec = vec![("a", 1), ("b", 2), ("c", 3)];
1440 let py_map = vec.into_py_dict(py).unwrap();
1441
1442 assert_eq!(py_map.len(), 3);
1443 assert_eq!(
1444 py_map
1445 .get_item("b")
1446 .unwrap()
1447 .unwrap()
1448 .extract::<i32>()
1449 .unwrap(),
1450 2
1451 );
1452 });
1453 }
1454
1455 #[test]
1456 fn test_slice_into_dict() {
1457 Python::attach(|py| {
1458 let arr = [("a", 1), ("b", 2), ("c", 3)];
1459 let py_map = arr.into_py_dict(py).unwrap();
1460
1461 assert_eq!(py_map.len(), 3);
1462 assert_eq!(
1463 py_map
1464 .get_item("b")
1465 .unwrap()
1466 .unwrap()
1467 .extract::<i32>()
1468 .unwrap(),
1469 2
1470 );
1471 });
1472 }
1473
1474 #[test]
1475 fn dict_as_mapping() {
1476 Python::attach(|py| {
1477 let mut map = HashMap::<i32, i32>::new();
1478 map.insert(1, 1);
1479
1480 let py_map = map.into_py_dict(py).unwrap();
1481
1482 assert_eq!(py_map.as_mapping().len().unwrap(), 1);
1483 assert_eq!(
1484 py_map
1485 .as_mapping()
1486 .get_item(1)
1487 .unwrap()
1488 .extract::<i32>()
1489 .unwrap(),
1490 1
1491 );
1492 });
1493 }
1494
1495 #[test]
1496 fn dict_into_mapping() {
1497 Python::attach(|py| {
1498 let mut map = HashMap::<i32, i32>::new();
1499 map.insert(1, 1);
1500
1501 let py_map = map.into_py_dict(py).unwrap();
1502
1503 let py_mapping = py_map.into_mapping();
1504 assert_eq!(py_mapping.len().unwrap(), 1);
1505 assert_eq!(py_mapping.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
1506 });
1507 }
1508
1509 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1510 fn abc_dict(py: Python<'_>) -> Bound<'_, PyDict> {
1511 let mut map = HashMap::<&'static str, i32>::new();
1512 map.insert("a", 1);
1513 map.insert("b", 2);
1514 map.insert("c", 3);
1515 map.into_py_dict(py).unwrap()
1516 }
1517
1518 #[test]
1519 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1520 fn dict_keys_view() {
1521 Python::attach(|py| {
1522 let dict = abc_dict(py);
1523 let keys = dict.call_method0("keys").unwrap();
1524 assert!(keys.is_instance(&py.get_type::<PyDictKeys>()).unwrap());
1525 })
1526 }
1527
1528 #[test]
1529 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1530 fn dict_values_view() {
1531 Python::attach(|py| {
1532 let dict = abc_dict(py);
1533 let values = dict.call_method0("values").unwrap();
1534 assert!(values.is_instance(&py.get_type::<PyDictValues>()).unwrap());
1535 })
1536 }
1537
1538 #[test]
1539 #[cfg(not(any(PyPy, GraalPy, RustPython)))]
1540 fn dict_items_view() {
1541 Python::attach(|py| {
1542 let dict = abc_dict(py);
1543 let items = dict.call_method0("items").unwrap();
1544 assert!(items.is_instance(&py.get_type::<PyDictItems>()).unwrap());
1545 })
1546 }
1547
1548 #[test]
1549 fn dict_update() {
1550 Python::attach(|py| {
1551 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1552 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1553 dict.update(other.as_mapping()).unwrap();
1554 assert_eq!(dict.len(), 4);
1555 assert_eq!(
1556 dict.get_item("a")
1557 .unwrap()
1558 .unwrap()
1559 .extract::<i32>()
1560 .unwrap(),
1561 1
1562 );
1563 assert_eq!(
1564 dict.get_item("b")
1565 .unwrap()
1566 .unwrap()
1567 .extract::<i32>()
1568 .unwrap(),
1569 4
1570 );
1571 assert_eq!(
1572 dict.get_item("c")
1573 .unwrap()
1574 .unwrap()
1575 .extract::<i32>()
1576 .unwrap(),
1577 5
1578 );
1579 assert_eq!(
1580 dict.get_item("d")
1581 .unwrap()
1582 .unwrap()
1583 .extract::<i32>()
1584 .unwrap(),
1585 6
1586 );
1587
1588 assert_eq!(other.len(), 3);
1589 assert_eq!(
1590 other
1591 .get_item("b")
1592 .unwrap()
1593 .unwrap()
1594 .extract::<i32>()
1595 .unwrap(),
1596 4
1597 );
1598 assert_eq!(
1599 other
1600 .get_item("c")
1601 .unwrap()
1602 .unwrap()
1603 .extract::<i32>()
1604 .unwrap(),
1605 5
1606 );
1607 assert_eq!(
1608 other
1609 .get_item("d")
1610 .unwrap()
1611 .unwrap()
1612 .extract::<i32>()
1613 .unwrap(),
1614 6
1615 );
1616 })
1617 }
1618
1619 #[test]
1620 fn dict_update_if_missing() {
1621 Python::attach(|py| {
1622 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1623 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1624 dict.update_if_missing(other.as_mapping()).unwrap();
1625 assert_eq!(dict.len(), 4);
1626 assert_eq!(
1627 dict.get_item("a")
1628 .unwrap()
1629 .unwrap()
1630 .extract::<i32>()
1631 .unwrap(),
1632 1
1633 );
1634 assert_eq!(
1635 dict.get_item("b")
1636 .unwrap()
1637 .unwrap()
1638 .extract::<i32>()
1639 .unwrap(),
1640 2
1641 );
1642 assert_eq!(
1643 dict.get_item("c")
1644 .unwrap()
1645 .unwrap()
1646 .extract::<i32>()
1647 .unwrap(),
1648 3
1649 );
1650 assert_eq!(
1651 dict.get_item("d")
1652 .unwrap()
1653 .unwrap()
1654 .extract::<i32>()
1655 .unwrap(),
1656 6
1657 );
1658
1659 assert_eq!(other.len(), 3);
1660 assert_eq!(
1661 other
1662 .get_item("b")
1663 .unwrap()
1664 .unwrap()
1665 .extract::<i32>()
1666 .unwrap(),
1667 4
1668 );
1669 assert_eq!(
1670 other
1671 .get_item("c")
1672 .unwrap()
1673 .unwrap()
1674 .extract::<i32>()
1675 .unwrap(),
1676 5
1677 );
1678 assert_eq!(
1679 other
1680 .get_item("d")
1681 .unwrap()
1682 .unwrap()
1683 .extract::<i32>()
1684 .unwrap(),
1685 6
1686 );
1687 })
1688 }
1689
1690 #[test]
1691 fn test_iter_all() {
1692 Python::attach(|py| {
1693 let dict = [(1, true), (2, true), (3, true)].into_py_dict(py).unwrap();
1694 assert!(dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1695
1696 let dict = [(1, true), (2, false), (3, true)].into_py_dict(py).unwrap();
1697 assert!(!dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1698 });
1699 }
1700
1701 #[test]
1702 fn test_iter_any() {
1703 Python::attach(|py| {
1704 let dict = [(1, true), (2, false), (3, false)]
1705 .into_py_dict(py)
1706 .unwrap();
1707 assert!(dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1708
1709 let dict = [(1, false), (2, false), (3, false)]
1710 .into_py_dict(py)
1711 .unwrap();
1712 assert!(!dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1713 });
1714 }
1715
1716 #[test]
1717 #[allow(clippy::search_is_some)]
1718 fn test_iter_find() {
1719 Python::attach(|py| {
1720 let dict = [(1, false), (2, true), (3, false)]
1721 .into_py_dict(py)
1722 .unwrap();
1723
1724 assert_eq!(
1725 Some((2, true)),
1726 dict.iter()
1727 .find(|(_, v)| v.extract::<bool>().unwrap())
1728 .map(|(k, v)| (k.extract().unwrap(), v.extract().unwrap()))
1729 );
1730
1731 let dict = [(1, false), (2, false), (3, false)]
1732 .into_py_dict(py)
1733 .unwrap();
1734
1735 assert!(dict
1736 .iter()
1737 .find(|(_, v)| v.extract::<bool>().unwrap())
1738 .is_none());
1739 });
1740 }
1741
1742 #[test]
1743 #[allow(clippy::search_is_some)]
1744 fn test_iter_position() {
1745 Python::attach(|py| {
1746 let dict = [(1, false), (2, false), (3, true)]
1747 .into_py_dict(py)
1748 .unwrap();
1749 assert_eq!(
1750 Some(2),
1751 dict.iter().position(|(_, v)| v.extract::<bool>().unwrap())
1752 );
1753
1754 let dict = [(1, false), (2, false), (3, false)]
1755 .into_py_dict(py)
1756 .unwrap();
1757 assert!(dict
1758 .iter()
1759 .position(|(_, v)| v.extract::<bool>().unwrap())
1760 .is_none());
1761 });
1762 }
1763
1764 #[test]
1765 fn test_iter_fold() {
1766 Python::attach(|py| {
1767 let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1768 let sum = dict
1769 .iter()
1770 .fold(0, |acc, (_, v)| acc + v.extract::<i32>().unwrap());
1771 assert_eq!(sum, 6);
1772 });
1773 }
1774
1775 #[test]
1776 fn test_iter_try_fold() {
1777 Python::attach(|py| {
1778 let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1779 let sum = dict
1780 .iter()
1781 .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1782 .unwrap();
1783 assert_eq!(sum, 6);
1784
1785 let dict = [(1, "foo"), (2, "bar")].into_py_dict(py).unwrap();
1786 assert!(dict
1787 .iter()
1788 .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1789 .is_err());
1790 });
1791 }
1792
1793 #[test]
1794 fn test_iter_count() {
1795 Python::attach(|py| {
1796 let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1797 assert_eq!(dict.iter().count(), 3);
1798 })
1799 }
1800
1801 #[test]
1802 fn test_set_default() {
1803 Python::attach(|py| {
1804 let dict = PyDict::new(py);
1805 assert!(matches!(dict.set_default("hello", "world"), Ok(true)));
1806 assert_eq!(
1807 dict.get_item("hello")
1808 .unwrap()
1809 .unwrap()
1810 .extract::<String>()
1811 .unwrap(),
1812 "world"
1813 );
1814
1815 assert!(matches!(dict.set_default("hello", "foobar"), Ok(false)));
1816
1817 let invalid_key = PyList::new(py, vec![0]).unwrap();
1819 assert!(dict.set_default(invalid_key, "foobar").is_err());
1820 })
1821 }
1822
1823 #[test]
1824 fn test_set_default_with_result() {
1825 Python::attach(|py| {
1826 let dict = PyDict::new(py);
1827 let res = dict.set_default_with_result("hello", "world");
1828 assert!(res.is_ok());
1829 let (inserted, value) = res.unwrap();
1830 assert!(inserted);
1831 assert!(value.extract::<String>().unwrap() == "world");
1832 assert!(
1833 dict.get_item("hello")
1834 .unwrap()
1835 .unwrap()
1836 .extract::<String>()
1837 .unwrap()
1838 == "world"
1839 );
1840
1841 let (inserted, value) = dict.set_default_with_result("hello", "foobar").unwrap();
1842 assert!(!inserted);
1843 assert_eq!(value.extract::<String>().unwrap(), "world");
1844
1845 let invalid_key = PyList::new(py, vec![0]).unwrap();
1847 assert!(dict.set_default_with_result(invalid_key, "foobar").is_err());
1848 })
1849 }
1850}