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