1use super::PyMapping;
2use crate::err::{self, PyErr, PyResult};
3use crate::ffi::Py_ssize_t;
4use crate::ffi_ptr_ext::FfiPtrExt;
5use crate::instance::{Borrowed, Bound};
6use crate::py_result_ext::PyResultExt;
7use crate::types::any::PyAnyMethods;
8use crate::types::{PyAny, PyList};
9#[cfg(feature = "gil-refs")]
10use crate::PyNativeType;
11use crate::{ffi, Python, ToPyObject};
12
13#[repr(transparent)]
21pub struct PyDict(PyAny);
22
23pyobject_native_type!(
24 PyDict,
25 ffi::PyDictObject,
26 pyobject_native_static_type_object!(ffi::PyDict_Type),
27 #checkfunction=ffi::PyDict_Check
28);
29
30#[cfg(not(any(PyPy, GraalPy)))]
32#[repr(transparent)]
33pub struct PyDictKeys(PyAny);
34
35#[cfg(not(any(PyPy, GraalPy)))]
36pyobject_native_type_core!(
37 PyDictKeys,
38 pyobject_native_static_type_object!(ffi::PyDictKeys_Type),
39 #checkfunction=ffi::PyDictKeys_Check
40);
41
42#[cfg(not(any(PyPy, GraalPy)))]
44#[repr(transparent)]
45pub struct PyDictValues(PyAny);
46
47#[cfg(not(any(PyPy, GraalPy)))]
48pyobject_native_type_core!(
49 PyDictValues,
50 pyobject_native_static_type_object!(ffi::PyDictValues_Type),
51 #checkfunction=ffi::PyDictValues_Check
52);
53
54#[cfg(not(any(PyPy, GraalPy)))]
56#[repr(transparent)]
57pub struct PyDictItems(PyAny);
58
59#[cfg(not(any(PyPy, GraalPy)))]
60pyobject_native_type_core!(
61 PyDictItems,
62 pyobject_native_static_type_object!(ffi::PyDictItems_Type),
63 #checkfunction=ffi::PyDictItems_Check
64);
65
66impl PyDict {
67 pub fn new_bound(py: Python<'_>) -> Bound<'_, PyDict> {
69 unsafe { ffi::PyDict_New().assume_owned(py).downcast_into_unchecked() }
70 }
71
72 #[cfg(not(any(PyPy, GraalPy)))]
80 pub fn from_sequence_bound<'py>(seq: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyDict>> {
81 let py = seq.py();
82 let dict = Self::new_bound(py);
83 err::error_on_minusone(py, unsafe {
84 ffi::PyDict_MergeFromSeq2(dict.as_ptr(), seq.as_ptr(), 1)
85 })?;
86 Ok(dict)
87 }
88}
89
90#[cfg(feature = "gil-refs")]
91impl PyDict {
92 #[deprecated(
94 since = "0.21.0",
95 note = "`PyDict::new` will be replaced by `PyDict::new_bound` in a future PyO3 version"
96 )]
97 #[inline]
98 pub fn new(py: Python<'_>) -> &PyDict {
99 Self::new_bound(py).into_gil_ref()
100 }
101
102 #[deprecated(
104 since = "0.21.0",
105 note = "`PyDict::from_sequence` will be replaced by `PyDict::from_sequence_bound` in a future PyO3 version"
106 )]
107 #[inline]
108 #[cfg(not(any(PyPy, GraalPy)))]
109 pub fn from_sequence(seq: &PyAny) -> PyResult<&PyDict> {
110 Self::from_sequence_bound(&seq.as_borrowed()).map(Bound::into_gil_ref)
111 }
112
113 pub fn copy(&self) -> PyResult<&PyDict> {
117 self.as_borrowed().copy().map(Bound::into_gil_ref)
118 }
119
120 pub fn clear(&self) {
122 self.as_borrowed().clear()
123 }
124
125 pub fn len(&self) -> usize {
129 self.as_borrowed().len()
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.as_borrowed().is_empty()
135 }
136
137 pub fn contains<K>(&self, key: K) -> PyResult<bool>
141 where
142 K: ToPyObject,
143 {
144 self.as_borrowed().contains(key)
145 }
146
147 pub fn get_item<K>(&self, key: K) -> PyResult<Option<&PyAny>>
190 where
191 K: ToPyObject,
192 {
193 match self.as_borrowed().get_item(key) {
194 Ok(Some(item)) => Ok(Some(item.into_gil_ref())),
195 Ok(None) => Ok(None),
196 Err(e) => Err(e),
197 }
198 }
199
200 #[deprecated(
202 since = "0.20.0",
203 note = "this is now equivalent to `PyDict::get_item`"
204 )]
205 #[inline]
206 pub fn get_item_with_error<K>(&self, key: K) -> PyResult<Option<&PyAny>>
207 where
208 K: ToPyObject,
209 {
210 self.get_item(key)
211 }
212
213 pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
217 where
218 K: ToPyObject,
219 V: ToPyObject,
220 {
221 self.as_borrowed().set_item(key, value)
222 }
223
224 pub fn del_item<K>(&self, key: K) -> PyResult<()>
228 where
229 K: ToPyObject,
230 {
231 self.as_borrowed().del_item(key)
232 }
233
234 pub fn keys(&self) -> &PyList {
238 self.as_borrowed().keys().into_gil_ref()
239 }
240
241 pub fn values(&self) -> &PyList {
245 self.as_borrowed().values().into_gil_ref()
246 }
247
248 pub fn items(&self) -> &PyList {
252 self.as_borrowed().items().into_gil_ref()
253 }
254
255 pub fn iter(&self) -> PyDictIterator<'_> {
263 PyDictIterator(self.as_borrowed().iter())
264 }
265
266 pub fn as_mapping(&self) -> &PyMapping {
268 unsafe { self.downcast_unchecked() }
269 }
270
271 pub fn update(&self, other: &PyMapping) -> PyResult<()> {
276 self.as_borrowed().update(&other.as_borrowed())
277 }
278
279 pub fn update_if_missing(&self, other: &PyMapping) -> PyResult<()> {
288 self.as_borrowed().update_if_missing(&other.as_borrowed())
289 }
290}
291
292#[doc(alias = "PyDict")]
298pub trait PyDictMethods<'py>: crate::sealed::Sealed {
299 fn copy(&self) -> PyResult<Bound<'py, PyDict>>;
303
304 fn clear(&self);
306
307 fn len(&self) -> usize;
311
312 fn is_empty(&self) -> bool;
314
315 fn contains<K>(&self, key: K) -> PyResult<bool>
319 where
320 K: ToPyObject;
321
322 fn get_item<K>(&self, key: K) -> PyResult<Option<Bound<'py, PyAny>>>
328 where
329 K: ToPyObject;
330
331 fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
335 where
336 K: ToPyObject,
337 V: ToPyObject;
338
339 fn del_item<K>(&self, key: K) -> PyResult<()>
343 where
344 K: ToPyObject;
345
346 fn keys(&self) -> Bound<'py, PyList>;
350
351 fn values(&self) -> Bound<'py, PyList>;
355
356 fn items(&self) -> Bound<'py, PyList>;
360
361 fn iter(&self) -> BoundDictIterator<'py>;
369
370 fn as_mapping(&self) -> &Bound<'py, PyMapping>;
372
373 fn into_mapping(self) -> Bound<'py, PyMapping>;
375
376 fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()>;
381
382 fn update_if_missing(&self, other: &Bound<'_, PyMapping>) -> PyResult<()>;
391}
392
393impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> {
394 fn copy(&self) -> PyResult<Bound<'py, PyDict>> {
395 unsafe {
396 ffi::PyDict_Copy(self.as_ptr())
397 .assume_owned_or_err(self.py())
398 .downcast_into_unchecked()
399 }
400 }
401
402 fn clear(&self) {
403 unsafe { ffi::PyDict_Clear(self.as_ptr()) }
404 }
405
406 fn len(&self) -> usize {
407 dict_len(self) as usize
408 }
409
410 fn is_empty(&self) -> bool {
411 self.len() == 0
412 }
413
414 fn contains<K>(&self, key: K) -> PyResult<bool>
415 where
416 K: ToPyObject,
417 {
418 fn inner(dict: &Bound<'_, PyDict>, key: Bound<'_, PyAny>) -> PyResult<bool> {
419 match unsafe { ffi::PyDict_Contains(dict.as_ptr(), key.as_ptr()) } {
420 1 => Ok(true),
421 0 => Ok(false),
422 _ => Err(PyErr::fetch(dict.py())),
423 }
424 }
425
426 let py = self.py();
427 inner(self, key.to_object(py).into_bound(py))
428 }
429
430 fn get_item<K>(&self, key: K) -> PyResult<Option<Bound<'py, PyAny>>>
431 where
432 K: ToPyObject,
433 {
434 fn inner<'py>(
435 dict: &Bound<'py, PyDict>,
436 key: Bound<'_, PyAny>,
437 ) -> PyResult<Option<Bound<'py, PyAny>>> {
438 let py = dict.py();
439 let mut result: *mut ffi::PyObject = std::ptr::null_mut();
440 match unsafe {
441 ffi::compat::PyDict_GetItemRef(dict.as_ptr(), key.as_ptr(), &mut result)
442 } {
443 std::os::raw::c_int::MIN..=-1 => Err(PyErr::fetch(py)),
444 0 => Ok(None),
445 1..=std::os::raw::c_int::MAX => Ok(Some(unsafe { result.assume_owned(py) })),
446 }
447 }
448
449 let py = self.py();
450 inner(self, key.to_object(py).into_bound(py))
451 }
452
453 fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
454 where
455 K: ToPyObject,
456 V: ToPyObject,
457 {
458 fn inner(
459 dict: &Bound<'_, PyDict>,
460 key: Bound<'_, PyAny>,
461 value: Bound<'_, PyAny>,
462 ) -> PyResult<()> {
463 err::error_on_minusone(dict.py(), unsafe {
464 ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr())
465 })
466 }
467
468 let py = self.py();
469 inner(
470 self,
471 key.to_object(py).into_bound(py),
472 value.to_object(py).into_bound(py),
473 )
474 }
475
476 fn del_item<K>(&self, key: K) -> PyResult<()>
477 where
478 K: ToPyObject,
479 {
480 fn inner(dict: &Bound<'_, PyDict>, key: Bound<'_, PyAny>) -> PyResult<()> {
481 err::error_on_minusone(dict.py(), unsafe {
482 ffi::PyDict_DelItem(dict.as_ptr(), key.as_ptr())
483 })
484 }
485
486 let py = self.py();
487 inner(self, key.to_object(py).into_bound(py))
488 }
489
490 fn keys(&self) -> Bound<'py, PyList> {
491 unsafe {
492 ffi::PyDict_Keys(self.as_ptr())
493 .assume_owned(self.py())
494 .downcast_into_unchecked()
495 }
496 }
497
498 fn values(&self) -> Bound<'py, PyList> {
499 unsafe {
500 ffi::PyDict_Values(self.as_ptr())
501 .assume_owned(self.py())
502 .downcast_into_unchecked()
503 }
504 }
505
506 fn items(&self) -> Bound<'py, PyList> {
507 unsafe {
508 ffi::PyDict_Items(self.as_ptr())
509 .assume_owned(self.py())
510 .downcast_into_unchecked()
511 }
512 }
513
514 fn iter(&self) -> BoundDictIterator<'py> {
515 BoundDictIterator::new(self.clone())
516 }
517
518 fn as_mapping(&self) -> &Bound<'py, PyMapping> {
519 unsafe { self.downcast_unchecked() }
520 }
521
522 fn into_mapping(self) -> Bound<'py, PyMapping> {
523 unsafe { self.into_any().downcast_into_unchecked() }
524 }
525
526 fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()> {
527 err::error_on_minusone(self.py(), unsafe {
528 ffi::PyDict_Update(self.as_ptr(), other.as_ptr())
529 })
530 }
531
532 fn update_if_missing(&self, other: &Bound<'_, PyMapping>) -> PyResult<()> {
533 err::error_on_minusone(self.py(), unsafe {
534 ffi::PyDict_Merge(self.as_ptr(), other.as_ptr(), 0)
535 })
536 }
537}
538
539impl<'a, 'py> Borrowed<'a, 'py, PyDict> {
540 pub(crate) unsafe fn iter_borrowed(self) -> BorrowedDictIter<'a, 'py> {
545 BorrowedDictIter::new(self)
546 }
547}
548
549fn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t {
550 #[cfg(any(not(Py_3_8), PyPy, GraalPy, Py_LIMITED_API))]
551 unsafe {
552 ffi::PyDict_Size(dict.as_ptr())
553 }
554
555 #[cfg(all(Py_3_8, not(PyPy), not(GraalPy), not(Py_LIMITED_API)))]
556 unsafe {
557 (*dict.as_ptr().cast::<ffi::PyDictObject>()).ma_used
558 }
559}
560
561#[cfg(feature = "gil-refs")]
563pub struct PyDictIterator<'py>(BoundDictIterator<'py>);
564
565#[cfg(feature = "gil-refs")]
566impl<'py> Iterator for PyDictIterator<'py> {
567 type Item = (&'py PyAny, &'py PyAny);
568
569 #[inline]
570 fn next(&mut self) -> Option<Self::Item> {
571 let (key, value) = self.0.next()?;
572 Some((key.into_gil_ref(), value.into_gil_ref()))
573 }
574
575 #[inline]
576 fn size_hint(&self) -> (usize, Option<usize>) {
577 self.0.size_hint()
578 }
579}
580
581#[cfg(feature = "gil-refs")]
582impl<'py> ExactSizeIterator for PyDictIterator<'py> {
583 fn len(&self) -> usize {
584 self.0.len()
585 }
586}
587
588#[cfg(feature = "gil-refs")]
589impl<'a> IntoIterator for &'a PyDict {
590 type Item = (&'a PyAny, &'a PyAny);
591 type IntoIter = PyDictIterator<'a>;
592
593 fn into_iter(self) -> Self::IntoIter {
594 self.iter()
595 }
596}
597
598pub struct BoundDictIterator<'py> {
600 dict: Bound<'py, PyDict>,
601 ppos: ffi::Py_ssize_t,
602 di_used: ffi::Py_ssize_t,
603 len: ffi::Py_ssize_t,
604}
605
606impl<'py> Iterator for BoundDictIterator<'py> {
607 type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
608
609 #[inline]
610 fn next(&mut self) -> Option<Self::Item> {
611 let ma_used = dict_len(&self.dict);
612
613 if self.di_used != ma_used {
618 self.di_used = -1;
619 panic!("dictionary changed size during iteration");
620 };
621
622 if self.len == -1 {
633 self.di_used = -1;
634 panic!("dictionary keys changed during iteration");
635 };
636
637 let mut key: *mut ffi::PyObject = std::ptr::null_mut();
638 let mut value: *mut ffi::PyObject = std::ptr::null_mut();
639
640 if unsafe { ffi::PyDict_Next(self.dict.as_ptr(), &mut self.ppos, &mut key, &mut value) }
641 != 0
642 {
643 self.len -= 1;
644 let py = self.dict.py();
645 Some((
649 unsafe { key.assume_borrowed_unchecked(py) }.to_owned(),
650 unsafe { value.assume_borrowed_unchecked(py) }.to_owned(),
651 ))
652 } else {
653 None
654 }
655 }
656
657 #[inline]
658 fn size_hint(&self) -> (usize, Option<usize>) {
659 let len = self.len();
660 (len, Some(len))
661 }
662}
663
664impl<'py> ExactSizeIterator for BoundDictIterator<'py> {
665 fn len(&self) -> usize {
666 self.len as usize
667 }
668}
669
670impl<'py> BoundDictIterator<'py> {
671 fn new(dict: Bound<'py, PyDict>) -> Self {
672 let len = dict_len(&dict);
673 BoundDictIterator {
674 dict,
675 ppos: 0,
676 di_used: len,
677 len,
678 }
679 }
680}
681
682impl<'py> IntoIterator for Bound<'py, PyDict> {
683 type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
684 type IntoIter = BoundDictIterator<'py>;
685
686 fn into_iter(self) -> Self::IntoIter {
687 BoundDictIterator::new(self)
688 }
689}
690
691impl<'py> IntoIterator for &Bound<'py, PyDict> {
692 type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
693 type IntoIter = BoundDictIterator<'py>;
694
695 fn into_iter(self) -> Self::IntoIter {
696 self.iter()
697 }
698}
699
700mod borrowed_iter {
701 use super::*;
702
703 pub struct BorrowedDictIter<'a, 'py> {
707 dict: Borrowed<'a, 'py, PyDict>,
708 ppos: ffi::Py_ssize_t,
709 len: ffi::Py_ssize_t,
710 }
711
712 impl<'a, 'py> Iterator for BorrowedDictIter<'a, 'py> {
713 type Item = (Borrowed<'a, 'py, PyAny>, Borrowed<'a, 'py, PyAny>);
714
715 #[inline]
716 fn next(&mut self) -> Option<Self::Item> {
717 let mut key: *mut ffi::PyObject = std::ptr::null_mut();
718 let mut value: *mut ffi::PyObject = std::ptr::null_mut();
719
720 if unsafe { ffi::PyDict_Next(self.dict.as_ptr(), &mut self.ppos, &mut key, &mut value) }
722 != 0
723 {
724 let py = self.dict.py();
725 self.len -= 1;
726 Some(unsafe { (key.assume_borrowed(py), value.assume_borrowed(py)) })
730 } else {
731 None
732 }
733 }
734
735 #[inline]
736 fn size_hint(&self) -> (usize, Option<usize>) {
737 let len = self.len();
738 (len, Some(len))
739 }
740 }
741
742 impl ExactSizeIterator for BorrowedDictIter<'_, '_> {
743 fn len(&self) -> usize {
744 self.len as usize
745 }
746 }
747
748 impl<'a, 'py> BorrowedDictIter<'a, 'py> {
749 pub(super) fn new(dict: Borrowed<'a, 'py, PyDict>) -> Self {
750 let len = dict_len(&dict);
751 BorrowedDictIter { dict, ppos: 0, len }
752 }
753 }
754}
755
756pub(crate) use borrowed_iter::BorrowedDictIter;
757
758pub trait IntoPyDict: Sized {
761 #[cfg(feature = "gil-refs")]
764 #[deprecated(
765 since = "0.21.0",
766 note = "`IntoPyDict::into_py_dict` will be replaced by `IntoPyDict::into_py_dict_bound` in a future PyO3 version"
767 )]
768 fn into_py_dict(self, py: Python<'_>) -> &PyDict {
769 Self::into_py_dict_bound(self, py).into_gil_ref()
770 }
771
772 fn into_py_dict_bound(self, py: Python<'_>) -> Bound<'_, PyDict>;
775}
776
777impl<T, I> IntoPyDict for I
778where
779 T: PyDictItem,
780 I: IntoIterator<Item = T>,
781{
782 fn into_py_dict_bound(self, py: Python<'_>) -> Bound<'_, PyDict> {
783 let dict = PyDict::new_bound(py);
784 for item in self {
785 dict.set_item(item.key(), item.value())
786 .expect("Failed to set_item on dict");
787 }
788 dict
789 }
790}
791
792pub trait PyDictItem {
794 type K: ToPyObject;
795 type V: ToPyObject;
796 fn key(&self) -> &Self::K;
797 fn value(&self) -> &Self::V;
798}
799
800impl<K, V> PyDictItem for (K, V)
801where
802 K: ToPyObject,
803 V: ToPyObject,
804{
805 type K = K;
806 type V = V;
807 fn key(&self) -> &Self::K {
808 &self.0
809 }
810 fn value(&self) -> &Self::V {
811 &self.1
812 }
813}
814
815impl<K, V> PyDictItem for &(K, V)
816where
817 K: ToPyObject,
818 V: ToPyObject,
819{
820 type K = K;
821 type V = V;
822 fn key(&self) -> &Self::K {
823 &self.0
824 }
825 fn value(&self) -> &Self::V {
826 &self.1
827 }
828}
829
830#[cfg(test)]
831mod tests {
832 use super::*;
833 use crate::types::PyTuple;
834 use std::collections::{BTreeMap, HashMap};
835
836 #[test]
837 fn test_new() {
838 Python::with_gil(|py| {
839 let dict = [(7, 32)].into_py_dict_bound(py);
840 assert_eq!(
841 32,
842 dict.get_item(7i32)
843 .unwrap()
844 .unwrap()
845 .extract::<i32>()
846 .unwrap()
847 );
848 assert!(dict.get_item(8i32).unwrap().is_none());
849 let map: HashMap<i32, i32> = [(7, 32)].iter().cloned().collect();
850 assert_eq!(map, dict.extract().unwrap());
851 let map: BTreeMap<i32, i32> = [(7, 32)].iter().cloned().collect();
852 assert_eq!(map, dict.extract().unwrap());
853 });
854 }
855
856 #[test]
857 #[cfg(not(any(PyPy, GraalPy)))]
858 fn test_from_sequence() {
859 Python::with_gil(|py| {
860 let items = PyList::new_bound(py, vec![("a", 1), ("b", 2)]);
861 let dict = PyDict::from_sequence_bound(&items).unwrap();
862 assert_eq!(
863 1,
864 dict.get_item("a")
865 .unwrap()
866 .unwrap()
867 .extract::<i32>()
868 .unwrap()
869 );
870 assert_eq!(
871 2,
872 dict.get_item("b")
873 .unwrap()
874 .unwrap()
875 .extract::<i32>()
876 .unwrap()
877 );
878 let map: HashMap<String, i32> =
879 [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
880 assert_eq!(map, dict.extract().unwrap());
881 let map: BTreeMap<String, i32> =
882 [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
883 assert_eq!(map, dict.extract().unwrap());
884 });
885 }
886
887 #[test]
888 #[cfg(not(any(PyPy, GraalPy)))]
889 fn test_from_sequence_err() {
890 Python::with_gil(|py| {
891 let items = PyList::new_bound(py, vec!["a", "b"]);
892 assert!(PyDict::from_sequence_bound(&items).is_err());
893 });
894 }
895
896 #[test]
897 fn test_copy() {
898 Python::with_gil(|py| {
899 let dict = [(7, 32)].into_py_dict_bound(py);
900
901 let ndict = dict.copy().unwrap();
902 assert_eq!(
903 32,
904 ndict
905 .get_item(7i32)
906 .unwrap()
907 .unwrap()
908 .extract::<i32>()
909 .unwrap()
910 );
911 assert!(ndict.get_item(8i32).unwrap().is_none());
912 });
913 }
914
915 #[test]
916 fn test_len() {
917 Python::with_gil(|py| {
918 let mut v = HashMap::new();
919 let ob = v.to_object(py);
920 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
921 assert_eq!(0, dict.len());
922 v.insert(7, 32);
923 let ob = v.to_object(py);
924 let dict2 = ob.downcast_bound::<PyDict>(py).unwrap();
925 assert_eq!(1, dict2.len());
926 });
927 }
928
929 #[test]
930 fn test_contains() {
931 Python::with_gil(|py| {
932 let mut v = HashMap::new();
933 v.insert(7, 32);
934 let ob = v.to_object(py);
935 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
936 assert!(dict.contains(7i32).unwrap());
937 assert!(!dict.contains(8i32).unwrap());
938 });
939 }
940
941 #[test]
942 fn test_get_item() {
943 Python::with_gil(|py| {
944 let mut v = HashMap::new();
945 v.insert(7, 32);
946 let ob = v.to_object(py);
947 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
948 assert_eq!(
949 32,
950 dict.get_item(7i32)
951 .unwrap()
952 .unwrap()
953 .extract::<i32>()
954 .unwrap()
955 );
956 assert!(dict.get_item(8i32).unwrap().is_none());
957 });
958 }
959
960 #[cfg(feature = "macros")]
961 #[test]
962 fn test_get_item_error_path() {
963 use crate::exceptions::PyTypeError;
964
965 #[crate::pyclass(crate = "crate")]
966 struct HashErrors;
967
968 #[crate::pymethods(crate = "crate")]
969 impl HashErrors {
970 #[new]
971 fn new() -> Self {
972 HashErrors {}
973 }
974
975 fn __hash__(&self) -> PyResult<isize> {
976 Err(PyTypeError::new_err("Error from __hash__"))
977 }
978 }
979
980 Python::with_gil(|py| {
981 let class = py.get_type_bound::<HashErrors>();
982 let instance = class.call0().unwrap();
983 let d = PyDict::new_bound(py);
984 match d.get_item(instance) {
985 Ok(_) => {
986 panic!("this get_item call should always error")
987 }
988 Err(err) => {
989 assert!(err.is_instance_of::<PyTypeError>(py));
990 assert_eq!(err.value_bound(py).to_string(), "Error from __hash__")
991 }
992 }
993 })
994 }
995
996 #[test]
997 #[allow(deprecated)]
998 #[cfg(all(not(any(PyPy, GraalPy)), feature = "gil-refs"))]
999 fn test_get_item_with_error() {
1000 Python::with_gil(|py| {
1001 let mut v = HashMap::new();
1002 v.insert(7, 32);
1003 let ob = v.to_object(py);
1004 let dict = ob.downcast::<PyDict>(py).unwrap();
1005 assert_eq!(
1006 32,
1007 dict.get_item_with_error(7i32)
1008 .unwrap()
1009 .unwrap()
1010 .extract::<i32>()
1011 .unwrap()
1012 );
1013 assert!(dict.get_item_with_error(8i32).unwrap().is_none());
1014 assert!(dict
1015 .get_item_with_error(dict)
1016 .unwrap_err()
1017 .is_instance_of::<crate::exceptions::PyTypeError>(py));
1018 });
1019 }
1020
1021 #[test]
1022 fn test_set_item() {
1023 Python::with_gil(|py| {
1024 let mut v = HashMap::new();
1025 v.insert(7, 32);
1026 let ob = v.to_object(py);
1027 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1028 assert!(dict.set_item(7i32, 42i32).is_ok()); assert!(dict.set_item(8i32, 123i32).is_ok()); assert_eq!(
1031 42i32,
1032 dict.get_item(7i32)
1033 .unwrap()
1034 .unwrap()
1035 .extract::<i32>()
1036 .unwrap()
1037 );
1038 assert_eq!(
1039 123i32,
1040 dict.get_item(8i32)
1041 .unwrap()
1042 .unwrap()
1043 .extract::<i32>()
1044 .unwrap()
1045 );
1046 });
1047 }
1048
1049 #[test]
1050 fn test_set_item_refcnt() {
1051 Python::with_gil(|py| {
1052 let cnt;
1053 let obj = py.eval_bound("object()", None, None).unwrap();
1054 {
1055 cnt = obj.get_refcnt();
1056 let _dict = [(10, &obj)].into_py_dict_bound(py);
1057 }
1058 {
1059 assert_eq!(cnt, obj.get_refcnt());
1060 }
1061 });
1062 }
1063
1064 #[test]
1065 fn test_set_item_does_not_update_original_object() {
1066 Python::with_gil(|py| {
1067 let mut v = HashMap::new();
1068 v.insert(7, 32);
1069 let ob = v.to_object(py);
1070 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1071 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));
1075 });
1076 }
1077
1078 #[test]
1079 fn test_del_item() {
1080 Python::with_gil(|py| {
1081 let mut v = HashMap::new();
1082 v.insert(7, 32);
1083 let ob = v.to_object(py);
1084 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1085 assert!(dict.del_item(7i32).is_ok());
1086 assert_eq!(0, dict.len());
1087 assert!(dict.get_item(7i32).unwrap().is_none());
1088 });
1089 }
1090
1091 #[test]
1092 fn test_del_item_does_not_update_original_object() {
1093 Python::with_gil(|py| {
1094 let mut v = HashMap::new();
1095 v.insert(7, 32);
1096 let ob = v.to_object(py);
1097 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1098 assert!(dict.del_item(7i32).is_ok()); assert_eq!(32i32, *v.get(&7i32).unwrap()); });
1101 }
1102
1103 #[test]
1104 fn test_items() {
1105 Python::with_gil(|py| {
1106 let mut v = HashMap::new();
1107 v.insert(7, 32);
1108 v.insert(8, 42);
1109 v.insert(9, 123);
1110 let ob = v.to_object(py);
1111 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1112 let mut key_sum = 0;
1114 let mut value_sum = 0;
1115 for el in dict.items() {
1116 let tuple = el.downcast::<PyTuple>().unwrap();
1117 key_sum += tuple.get_item(0).unwrap().extract::<i32>().unwrap();
1118 value_sum += tuple.get_item(1).unwrap().extract::<i32>().unwrap();
1119 }
1120 assert_eq!(7 + 8 + 9, key_sum);
1121 assert_eq!(32 + 42 + 123, value_sum);
1122 });
1123 }
1124
1125 #[test]
1126 fn test_keys() {
1127 Python::with_gil(|py| {
1128 let mut v = HashMap::new();
1129 v.insert(7, 32);
1130 v.insert(8, 42);
1131 v.insert(9, 123);
1132 let ob = v.to_object(py);
1133 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1134 let mut key_sum = 0;
1136 for el in dict.keys() {
1137 key_sum += el.extract::<i32>().unwrap();
1138 }
1139 assert_eq!(7 + 8 + 9, key_sum);
1140 });
1141 }
1142
1143 #[test]
1144 fn test_values() {
1145 Python::with_gil(|py| {
1146 let mut v = HashMap::new();
1147 v.insert(7, 32);
1148 v.insert(8, 42);
1149 v.insert(9, 123);
1150 let ob = v.to_object(py);
1151 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1152 let mut values_sum = 0;
1154 for el in dict.values() {
1155 values_sum += el.extract::<i32>().unwrap();
1156 }
1157 assert_eq!(32 + 42 + 123, values_sum);
1158 });
1159 }
1160
1161 #[test]
1162 fn test_iter() {
1163 Python::with_gil(|py| {
1164 let mut v = HashMap::new();
1165 v.insert(7, 32);
1166 v.insert(8, 42);
1167 v.insert(9, 123);
1168 let ob = v.to_object(py);
1169 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1170 let mut key_sum = 0;
1171 let mut value_sum = 0;
1172 for (key, value) in dict {
1173 key_sum += key.extract::<i32>().unwrap();
1174 value_sum += value.extract::<i32>().unwrap();
1175 }
1176 assert_eq!(7 + 8 + 9, key_sum);
1177 assert_eq!(32 + 42 + 123, value_sum);
1178 });
1179 }
1180
1181 #[test]
1182 fn test_iter_bound() {
1183 Python::with_gil(|py| {
1184 let mut v = HashMap::new();
1185 v.insert(7, 32);
1186 v.insert(8, 42);
1187 v.insert(9, 123);
1188 let ob = v.to_object(py);
1189 let dict: &Bound<'_, PyDict> = ob.downcast_bound(py).unwrap();
1190 let mut key_sum = 0;
1191 let mut value_sum = 0;
1192 for (key, value) in dict {
1193 key_sum += key.extract::<i32>().unwrap();
1194 value_sum += value.extract::<i32>().unwrap();
1195 }
1196 assert_eq!(7 + 8 + 9, key_sum);
1197 assert_eq!(32 + 42 + 123, value_sum);
1198 });
1199 }
1200
1201 #[test]
1202 fn test_iter_value_mutated() {
1203 Python::with_gil(|py| {
1204 let mut v = HashMap::new();
1205 v.insert(7, 32);
1206 v.insert(8, 42);
1207 v.insert(9, 123);
1208
1209 let ob = v.to_object(py);
1210 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1211
1212 for (key, value) in dict {
1213 dict.set_item(key, value.extract::<i32>().unwrap() + 7)
1214 .unwrap();
1215 }
1216 });
1217 }
1218
1219 #[test]
1220 #[should_panic]
1221 fn test_iter_key_mutated() {
1222 Python::with_gil(|py| {
1223 let mut v = HashMap::new();
1224 for i in 0..10 {
1225 v.insert(i * 2, i * 2);
1226 }
1227 let ob = v.to_object(py);
1228 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1229
1230 for (i, (key, value)) in dict.iter().enumerate() {
1231 let key = key.extract::<i32>().unwrap();
1232 let value = value.extract::<i32>().unwrap();
1233
1234 dict.set_item(key + 1, value + 1).unwrap();
1235
1236 if i > 1000 {
1237 break;
1239 };
1240 }
1241 });
1242 }
1243
1244 #[test]
1245 #[should_panic]
1246 fn test_iter_key_mutated_constant_len() {
1247 Python::with_gil(|py| {
1248 let mut v = HashMap::new();
1249 for i in 0..10 {
1250 v.insert(i * 2, i * 2);
1251 }
1252 let ob = v.to_object(py);
1253 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1254
1255 for (i, (key, value)) in dict.iter().enumerate() {
1256 let key = key.extract::<i32>().unwrap();
1257 let value = value.extract::<i32>().unwrap();
1258 dict.del_item(key).unwrap();
1259 dict.set_item(key + 1, value + 1).unwrap();
1260
1261 if i > 1000 {
1262 break;
1264 };
1265 }
1266 });
1267 }
1268
1269 #[test]
1270 fn test_iter_size_hint() {
1271 Python::with_gil(|py| {
1272 let mut v = HashMap::new();
1273 v.insert(7, 32);
1274 v.insert(8, 42);
1275 v.insert(9, 123);
1276 let ob = v.to_object(py);
1277 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1278
1279 let mut iter = dict.iter();
1280 assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
1281 iter.next();
1282 assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
1283
1284 for _ in &mut iter {}
1286
1287 assert_eq!(iter.size_hint(), (0, Some(0)));
1288
1289 assert!(iter.next().is_none());
1290
1291 assert_eq!(iter.size_hint(), (0, Some(0)));
1292 });
1293 }
1294
1295 #[test]
1296 fn test_into_iter() {
1297 Python::with_gil(|py| {
1298 let mut v = HashMap::new();
1299 v.insert(7, 32);
1300 v.insert(8, 42);
1301 v.insert(9, 123);
1302 let ob = v.to_object(py);
1303 let dict = ob.downcast_bound::<PyDict>(py).unwrap();
1304 let mut key_sum = 0;
1305 let mut value_sum = 0;
1306 for (key, value) in dict {
1307 key_sum += key.extract::<i32>().unwrap();
1308 value_sum += value.extract::<i32>().unwrap();
1309 }
1310 assert_eq!(7 + 8 + 9, key_sum);
1311 assert_eq!(32 + 42 + 123, value_sum);
1312 });
1313 }
1314
1315 #[test]
1316 fn test_hashmap_into_dict() {
1317 Python::with_gil(|py| {
1318 let mut map = HashMap::<i32, i32>::new();
1319 map.insert(1, 1);
1320
1321 let py_map = map.into_py_dict_bound(py);
1322
1323 assert_eq!(py_map.len(), 1);
1324 assert_eq!(
1325 py_map
1326 .get_item(1)
1327 .unwrap()
1328 .unwrap()
1329 .extract::<i32>()
1330 .unwrap(),
1331 1
1332 );
1333 });
1334 }
1335
1336 #[test]
1337 fn test_btreemap_into_dict() {
1338 Python::with_gil(|py| {
1339 let mut map = BTreeMap::<i32, i32>::new();
1340 map.insert(1, 1);
1341
1342 let py_map = map.into_py_dict_bound(py);
1343
1344 assert_eq!(py_map.len(), 1);
1345 assert_eq!(
1346 py_map
1347 .get_item(1)
1348 .unwrap()
1349 .unwrap()
1350 .extract::<i32>()
1351 .unwrap(),
1352 1
1353 );
1354 });
1355 }
1356
1357 #[test]
1358 fn test_vec_into_dict() {
1359 Python::with_gil(|py| {
1360 let vec = vec![("a", 1), ("b", 2), ("c", 3)];
1361 let py_map = vec.into_py_dict_bound(py);
1362
1363 assert_eq!(py_map.len(), 3);
1364 assert_eq!(
1365 py_map
1366 .get_item("b")
1367 .unwrap()
1368 .unwrap()
1369 .extract::<i32>()
1370 .unwrap(),
1371 2
1372 );
1373 });
1374 }
1375
1376 #[test]
1377 fn test_slice_into_dict() {
1378 Python::with_gil(|py| {
1379 let arr = [("a", 1), ("b", 2), ("c", 3)];
1380 let py_map = arr.into_py_dict_bound(py);
1381
1382 assert_eq!(py_map.len(), 3);
1383 assert_eq!(
1384 py_map
1385 .get_item("b")
1386 .unwrap()
1387 .unwrap()
1388 .extract::<i32>()
1389 .unwrap(),
1390 2
1391 );
1392 });
1393 }
1394
1395 #[test]
1396 fn dict_as_mapping() {
1397 Python::with_gil(|py| {
1398 let mut map = HashMap::<i32, i32>::new();
1399 map.insert(1, 1);
1400
1401 let py_map = map.into_py_dict_bound(py);
1402
1403 assert_eq!(py_map.as_mapping().len().unwrap(), 1);
1404 assert_eq!(
1405 py_map
1406 .as_mapping()
1407 .get_item(1)
1408 .unwrap()
1409 .extract::<i32>()
1410 .unwrap(),
1411 1
1412 );
1413 });
1414 }
1415
1416 #[test]
1417 fn dict_into_mapping() {
1418 Python::with_gil(|py| {
1419 let mut map = HashMap::<i32, i32>::new();
1420 map.insert(1, 1);
1421
1422 let py_map = map.into_py_dict_bound(py);
1423
1424 let py_mapping = py_map.into_mapping();
1425 assert_eq!(py_mapping.len().unwrap(), 1);
1426 assert_eq!(py_mapping.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
1427 });
1428 }
1429
1430 #[cfg(not(any(PyPy, GraalPy)))]
1431 fn abc_dict(py: Python<'_>) -> Bound<'_, PyDict> {
1432 let mut map = HashMap::<&'static str, i32>::new();
1433 map.insert("a", 1);
1434 map.insert("b", 2);
1435 map.insert("c", 3);
1436 map.into_py_dict_bound(py)
1437 }
1438
1439 #[test]
1440 #[cfg(not(any(PyPy, GraalPy)))]
1441 fn dict_keys_view() {
1442 Python::with_gil(|py| {
1443 let dict = abc_dict(py);
1444 let keys = dict.call_method0("keys").unwrap();
1445 assert!(keys
1446 .is_instance(&py.get_type_bound::<PyDictKeys>().as_borrowed())
1447 .unwrap());
1448 })
1449 }
1450
1451 #[test]
1452 #[cfg(not(any(PyPy, GraalPy)))]
1453 fn dict_values_view() {
1454 Python::with_gil(|py| {
1455 let dict = abc_dict(py);
1456 let values = dict.call_method0("values").unwrap();
1457 assert!(values
1458 .is_instance(&py.get_type_bound::<PyDictValues>().as_borrowed())
1459 .unwrap());
1460 })
1461 }
1462
1463 #[test]
1464 #[cfg(not(any(PyPy, GraalPy)))]
1465 fn dict_items_view() {
1466 Python::with_gil(|py| {
1467 let dict = abc_dict(py);
1468 let items = dict.call_method0("items").unwrap();
1469 assert!(items
1470 .is_instance(&py.get_type_bound::<PyDictItems>().as_borrowed())
1471 .unwrap());
1472 })
1473 }
1474
1475 #[test]
1476 fn dict_update() {
1477 Python::with_gil(|py| {
1478 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict_bound(py);
1479 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict_bound(py);
1480 dict.update(other.as_mapping()).unwrap();
1481 assert_eq!(dict.len(), 4);
1482 assert_eq!(
1483 dict.get_item("a")
1484 .unwrap()
1485 .unwrap()
1486 .extract::<i32>()
1487 .unwrap(),
1488 1
1489 );
1490 assert_eq!(
1491 dict.get_item("b")
1492 .unwrap()
1493 .unwrap()
1494 .extract::<i32>()
1495 .unwrap(),
1496 4
1497 );
1498 assert_eq!(
1499 dict.get_item("c")
1500 .unwrap()
1501 .unwrap()
1502 .extract::<i32>()
1503 .unwrap(),
1504 5
1505 );
1506 assert_eq!(
1507 dict.get_item("d")
1508 .unwrap()
1509 .unwrap()
1510 .extract::<i32>()
1511 .unwrap(),
1512 6
1513 );
1514
1515 assert_eq!(other.len(), 3);
1516 assert_eq!(
1517 other
1518 .get_item("b")
1519 .unwrap()
1520 .unwrap()
1521 .extract::<i32>()
1522 .unwrap(),
1523 4
1524 );
1525 assert_eq!(
1526 other
1527 .get_item("c")
1528 .unwrap()
1529 .unwrap()
1530 .extract::<i32>()
1531 .unwrap(),
1532 5
1533 );
1534 assert_eq!(
1535 other
1536 .get_item("d")
1537 .unwrap()
1538 .unwrap()
1539 .extract::<i32>()
1540 .unwrap(),
1541 6
1542 );
1543 })
1544 }
1545
1546 #[test]
1547 fn dict_update_if_missing() {
1548 Python::with_gil(|py| {
1549 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict_bound(py);
1550 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict_bound(py);
1551 dict.update_if_missing(other.as_mapping()).unwrap();
1552 assert_eq!(dict.len(), 4);
1553 assert_eq!(
1554 dict.get_item("a")
1555 .unwrap()
1556 .unwrap()
1557 .extract::<i32>()
1558 .unwrap(),
1559 1
1560 );
1561 assert_eq!(
1562 dict.get_item("b")
1563 .unwrap()
1564 .unwrap()
1565 .extract::<i32>()
1566 .unwrap(),
1567 2
1568 );
1569 assert_eq!(
1570 dict.get_item("c")
1571 .unwrap()
1572 .unwrap()
1573 .extract::<i32>()
1574 .unwrap(),
1575 3
1576 );
1577 assert_eq!(
1578 dict.get_item("d")
1579 .unwrap()
1580 .unwrap()
1581 .extract::<i32>()
1582 .unwrap(),
1583 6
1584 );
1585
1586 assert_eq!(other.len(), 3);
1587 assert_eq!(
1588 other
1589 .get_item("b")
1590 .unwrap()
1591 .unwrap()
1592 .extract::<i32>()
1593 .unwrap(),
1594 4
1595 );
1596 assert_eq!(
1597 other
1598 .get_item("c")
1599 .unwrap()
1600 .unwrap()
1601 .extract::<i32>()
1602 .unwrap(),
1603 5
1604 );
1605 assert_eq!(
1606 other
1607 .get_item("d")
1608 .unwrap()
1609 .unwrap()
1610 .extract::<i32>()
1611 .unwrap(),
1612 6
1613 );
1614 })
1615 }
1616}