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