pyo3/types/
dict.rs

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, PyAnyMethods, PyList, PyMapping};
7use crate::{ffi, BoundObject, IntoPyObject, IntoPyObjectExt, Python};
8
9/// Represents a Python `dict`.
10///
11/// Values of this type are accessed via PyO3's smart pointers, e.g. as
12/// [`Py<PyDict>`][crate::Py] or [`Bound<'py, PyDict>`][Bound].
13///
14/// For APIs available on `dict` objects, see the [`PyDictMethods`] trait which is implemented for
15/// [`Bound<'py, PyDict>`][Bound].
16#[repr(transparent)]
17pub struct PyDict(PyAny);
18
19pyobject_subclassable_native_type!(PyDict, crate::ffi::PyDictObject);
20
21pyobject_native_type!(
22    PyDict,
23    ffi::PyDictObject,
24    pyobject_native_static_type_object!(ffi::PyDict_Type),
25    #checkfunction=ffi::PyDict_Check
26);
27
28/// Represents a Python `dict_keys`.
29#[cfg(not(any(PyPy, GraalPy)))]
30#[repr(transparent)]
31pub struct PyDictKeys(PyAny);
32
33#[cfg(not(any(PyPy, GraalPy)))]
34pyobject_native_type_core!(
35    PyDictKeys,
36    pyobject_native_static_type_object!(ffi::PyDictKeys_Type),
37    #checkfunction=ffi::PyDictKeys_Check
38);
39
40/// Represents a Python `dict_values`.
41#[cfg(not(any(PyPy, GraalPy)))]
42#[repr(transparent)]
43pub struct PyDictValues(PyAny);
44
45#[cfg(not(any(PyPy, GraalPy)))]
46pyobject_native_type_core!(
47    PyDictValues,
48    pyobject_native_static_type_object!(ffi::PyDictValues_Type),
49    #checkfunction=ffi::PyDictValues_Check
50);
51
52/// Represents a Python `dict_items`.
53#[cfg(not(any(PyPy, GraalPy)))]
54#[repr(transparent)]
55pub struct PyDictItems(PyAny);
56
57#[cfg(not(any(PyPy, GraalPy)))]
58pyobject_native_type_core!(
59    PyDictItems,
60    pyobject_native_static_type_object!(ffi::PyDictItems_Type),
61    #checkfunction=ffi::PyDictItems_Check
62);
63
64impl PyDict {
65    /// Creates a new empty dictionary.
66    pub fn new(py: Python<'_>) -> Bound<'_, PyDict> {
67        unsafe { ffi::PyDict_New().assume_owned(py).downcast_into_unchecked() }
68    }
69
70    /// Deprecated name for [`PyDict::new`].
71    #[deprecated(since = "0.23.0", note = "renamed to `PyDict::new`")]
72    #[inline]
73    pub fn new_bound(py: Python<'_>) -> Bound<'_, PyDict> {
74        Self::new(py)
75    }
76
77    /// Creates a new dictionary from the sequence given.
78    ///
79    /// The sequence must consist of `(PyObject, PyObject)`. This is
80    /// equivalent to `dict([("a", 1), ("b", 2)])`.
81    ///
82    /// Returns an error on invalid input. In the case of key collisions,
83    /// this keeps the last entry seen.
84    #[cfg(not(any(PyPy, GraalPy)))]
85    pub fn from_sequence<'py>(seq: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyDict>> {
86        let py = seq.py();
87        let dict = Self::new(py);
88        err::error_on_minusone(py, unsafe {
89            ffi::PyDict_MergeFromSeq2(dict.as_ptr(), seq.as_ptr(), 1)
90        })?;
91        Ok(dict)
92    }
93
94    /// Deprecated name for [`PyDict::from_sequence`].
95    #[cfg(not(any(PyPy, GraalPy)))]
96    #[deprecated(since = "0.23.0", note = "renamed to `PyDict::from_sequence`")]
97    #[inline]
98    pub fn from_sequence_bound<'py>(seq: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyDict>> {
99        Self::from_sequence(seq)
100    }
101}
102
103/// Implementation of functionality for [`PyDict`].
104///
105/// These methods are defined for the `Bound<'py, PyDict>` smart pointer, so to use method call
106/// syntax these methods are separated into a trait, because stable Rust does not yet support
107/// `arbitrary_self_types`.
108#[doc(alias = "PyDict")]
109pub trait PyDictMethods<'py>: crate::sealed::Sealed {
110    /// Returns a new dictionary that contains the same key-value pairs as self.
111    ///
112    /// This is equivalent to the Python expression `self.copy()`.
113    fn copy(&self) -> PyResult<Bound<'py, PyDict>>;
114
115    /// Empties an existing dictionary of all key-value pairs.
116    fn clear(&self);
117
118    /// Return the number of items in the dictionary.
119    ///
120    /// This is equivalent to the Python expression `len(self)`.
121    fn len(&self) -> usize;
122
123    /// Checks if the dict is empty, i.e. `len(self) == 0`.
124    fn is_empty(&self) -> bool;
125
126    /// Determines if the dictionary contains the specified key.
127    ///
128    /// This is equivalent to the Python expression `key in self`.
129    fn contains<K>(&self, key: K) -> PyResult<bool>
130    where
131        K: IntoPyObject<'py>;
132
133    /// Gets an item from the dictionary.
134    ///
135    /// Returns `None` if the item is not present, or if an error occurs.
136    ///
137    /// To get a `KeyError` for non-existing keys, use `PyAny::get_item`.
138    fn get_item<K>(&self, key: K) -> PyResult<Option<Bound<'py, PyAny>>>
139    where
140        K: IntoPyObject<'py>;
141
142    /// Sets an item value.
143    ///
144    /// This is equivalent to the Python statement `self[key] = value`.
145    fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
146    where
147        K: IntoPyObject<'py>,
148        V: IntoPyObject<'py>;
149
150    /// Deletes an item.
151    ///
152    /// This is equivalent to the Python statement `del self[key]`.
153    fn del_item<K>(&self, key: K) -> PyResult<()>
154    where
155        K: IntoPyObject<'py>;
156
157    /// Returns a list of dict keys.
158    ///
159    /// This is equivalent to the Python expression `list(dict.keys())`.
160    fn keys(&self) -> Bound<'py, PyList>;
161
162    /// Returns a list of dict values.
163    ///
164    /// This is equivalent to the Python expression `list(dict.values())`.
165    fn values(&self) -> Bound<'py, PyList>;
166
167    /// Returns a list of dict items.
168    ///
169    /// This is equivalent to the Python expression `list(dict.items())`.
170    fn items(&self) -> Bound<'py, PyList>;
171
172    /// Returns an iterator of `(key, value)` pairs in this dictionary.
173    ///
174    /// # Panics
175    ///
176    /// If PyO3 detects that the dictionary is mutated during iteration, it will panic.
177    /// It is allowed to modify values as you iterate over the dictionary, but only
178    /// so long as the set of keys does not change.
179    fn iter(&self) -> BoundDictIterator<'py>;
180
181    /// Iterates over the contents of this dictionary while holding a critical section on the dict.
182    /// This is useful when the GIL is disabled and the dictionary is shared between threads.
183    /// It is not guaranteed that the dictionary will not be modified during iteration when the
184    /// closure calls arbitrary Python code that releases the critical section held by the
185    /// iterator. Otherwise, the dictionary will not be modified during iteration.
186    ///
187    /// This method is a small performance optimization over `.iter().try_for_each()` when the
188    /// nightly feature is not enabled because we cannot implement an optimised version of
189    /// `iter().try_fold()` on stable yet. If your iteration is infallible then this method has the
190    /// same performance as `.iter().for_each()`.
191    fn locked_for_each<F>(&self, closure: F) -> PyResult<()>
192    where
193        F: Fn(Bound<'py, PyAny>, Bound<'py, PyAny>) -> PyResult<()>;
194
195    /// Returns `self` cast as a `PyMapping`.
196    fn as_mapping(&self) -> &Bound<'py, PyMapping>;
197
198    /// Returns `self` cast as a `PyMapping`.
199    fn into_mapping(self) -> Bound<'py, PyMapping>;
200
201    /// Update this dictionary with the key/value pairs from another.
202    ///
203    /// This is equivalent to the Python expression `self.update(other)`. If `other` is a `PyDict`, you may want
204    /// to use `self.update(other.as_mapping())`, note: `PyDict::as_mapping` is a zero-cost conversion.
205    fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()>;
206
207    /// Add key/value pairs from another dictionary to this one only when they do not exist in this.
208    ///
209    /// This is equivalent to the Python expression `self.update({k: v for k, v in other.items() if k not in self})`.
210    /// If `other` is a `PyDict`, you may want to use `self.update_if_missing(other.as_mapping())`,
211    /// note: `PyDict::as_mapping` is a zero-cost conversion.
212    ///
213    /// This method uses [`PyDict_Merge`](https://docs.python.org/3/c-api/dict.html#c.PyDict_Merge) internally,
214    /// so should have the same performance as `update`.
215    fn update_if_missing(&self, other: &Bound<'_, PyMapping>) -> PyResult<()>;
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                .downcast_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        fn inner(dict: &Bound<'_, PyDict>, key: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
244            match unsafe { ffi::PyDict_Contains(dict.as_ptr(), key.as_ptr()) } {
245                1 => Ok(true),
246                0 => Ok(false),
247                _ => Err(PyErr::fetch(dict.py())),
248            }
249        }
250
251        let py = self.py();
252        inner(
253            self,
254            key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
255        )
256    }
257
258    fn get_item<K>(&self, key: K) -> PyResult<Option<Bound<'py, PyAny>>>
259    where
260        K: IntoPyObject<'py>,
261    {
262        fn inner<'py>(
263            dict: &Bound<'py, PyDict>,
264            key: Borrowed<'_, '_, PyAny>,
265        ) -> PyResult<Option<Bound<'py, PyAny>>> {
266            let py = dict.py();
267            let mut result: *mut ffi::PyObject = std::ptr::null_mut();
268            match unsafe {
269                ffi::compat::PyDict_GetItemRef(dict.as_ptr(), key.as_ptr(), &mut result)
270            } {
271                std::os::raw::c_int::MIN..=-1 => Err(PyErr::fetch(py)),
272                0 => Ok(None),
273                1..=std::os::raw::c_int::MAX => {
274                    // Safety: PyDict_GetItemRef positive return value means the result is a valid
275                    // owned reference
276                    Ok(Some(unsafe { result.assume_owned_unchecked(py) }))
277                }
278            }
279        }
280
281        let py = self.py();
282        inner(
283            self,
284            key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
285        )
286    }
287
288    fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
289    where
290        K: IntoPyObject<'py>,
291        V: IntoPyObject<'py>,
292    {
293        fn inner(
294            dict: &Bound<'_, PyDict>,
295            key: Borrowed<'_, '_, PyAny>,
296            value: Borrowed<'_, '_, PyAny>,
297        ) -> PyResult<()> {
298            err::error_on_minusone(dict.py(), unsafe {
299                ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr())
300            })
301        }
302
303        let py = self.py();
304        inner(
305            self,
306            key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
307            value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
308        )
309    }
310
311    fn del_item<K>(&self, key: K) -> PyResult<()>
312    where
313        K: IntoPyObject<'py>,
314    {
315        fn inner(dict: &Bound<'_, PyDict>, key: Borrowed<'_, '_, PyAny>) -> PyResult<()> {
316            err::error_on_minusone(dict.py(), unsafe {
317                ffi::PyDict_DelItem(dict.as_ptr(), key.as_ptr())
318            })
319        }
320
321        let py = self.py();
322        inner(
323            self,
324            key.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
325        )
326    }
327
328    fn keys(&self) -> Bound<'py, PyList> {
329        unsafe {
330            ffi::PyDict_Keys(self.as_ptr())
331                .assume_owned(self.py())
332                .downcast_into_unchecked()
333        }
334    }
335
336    fn values(&self) -> Bound<'py, PyList> {
337        unsafe {
338            ffi::PyDict_Values(self.as_ptr())
339                .assume_owned(self.py())
340                .downcast_into_unchecked()
341        }
342    }
343
344    fn items(&self) -> Bound<'py, PyList> {
345        unsafe {
346            ffi::PyDict_Items(self.as_ptr())
347                .assume_owned(self.py())
348                .downcast_into_unchecked()
349        }
350    }
351
352    fn iter(&self) -> BoundDictIterator<'py> {
353        BoundDictIterator::new(self.clone())
354    }
355
356    fn locked_for_each<F>(&self, f: F) -> PyResult<()>
357    where
358        F: Fn(Bound<'py, PyAny>, Bound<'py, PyAny>) -> PyResult<()>,
359    {
360        #[cfg(feature = "nightly")]
361        {
362            // We don't need a critical section when the nightly feature is enabled because
363            // try_for_each is locked by the implementation of try_fold.
364            self.iter().try_for_each(|(key, value)| f(key, value))
365        }
366
367        #[cfg(not(feature = "nightly"))]
368        {
369            crate::sync::with_critical_section(self, || {
370                self.iter().try_for_each(|(key, value)| f(key, value))
371            })
372        }
373    }
374
375    fn as_mapping(&self) -> &Bound<'py, PyMapping> {
376        unsafe { self.downcast_unchecked() }
377    }
378
379    fn into_mapping(self) -> Bound<'py, PyMapping> {
380        unsafe { self.into_any().downcast_into_unchecked() }
381    }
382
383    fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()> {
384        err::error_on_minusone(self.py(), unsafe {
385            ffi::PyDict_Update(self.as_ptr(), other.as_ptr())
386        })
387    }
388
389    fn update_if_missing(&self, other: &Bound<'_, PyMapping>) -> PyResult<()> {
390        err::error_on_minusone(self.py(), unsafe {
391            ffi::PyDict_Merge(self.as_ptr(), other.as_ptr(), 0)
392        })
393    }
394}
395
396impl<'a, 'py> Borrowed<'a, 'py, PyDict> {
397    /// Iterates over the contents of this dictionary without incrementing reference counts.
398    ///
399    /// # Safety
400    /// It must be known that this dictionary will not be modified during iteration,
401    /// for example, when parsing arguments in a keyword arguments dictionary.
402    pub(crate) unsafe fn iter_borrowed(self) -> BorrowedDictIter<'a, 'py> {
403        BorrowedDictIter::new(self)
404    }
405}
406
407fn dict_len(dict: &Bound<'_, PyDict>) -> Py_ssize_t {
408    #[cfg(any(not(Py_3_8), PyPy, GraalPy, Py_LIMITED_API, Py_GIL_DISABLED))]
409    unsafe {
410        ffi::PyDict_Size(dict.as_ptr())
411    }
412
413    #[cfg(all(
414        Py_3_8,
415        not(PyPy),
416        not(GraalPy),
417        not(Py_LIMITED_API),
418        not(Py_GIL_DISABLED)
419    ))]
420    unsafe {
421        (*dict.as_ptr().cast::<ffi::PyDictObject>()).ma_used
422    }
423}
424
425/// PyO3 implementation of an iterator for a Python `dict` object.
426pub struct BoundDictIterator<'py> {
427    dict: Bound<'py, PyDict>,
428    inner: DictIterImpl,
429}
430
431enum DictIterImpl {
432    DictIter {
433        ppos: ffi::Py_ssize_t,
434        di_used: ffi::Py_ssize_t,
435        remaining: ffi::Py_ssize_t,
436    },
437}
438
439impl DictIterImpl {
440    #[deny(unsafe_op_in_unsafe_fn)]
441    #[inline]
442    /// Safety: the dict should be locked with a critical section on the free-threaded build
443    /// and otherwise not shared between threads in code that releases the GIL.
444    unsafe fn next_unchecked<'py>(
445        &mut self,
446        dict: &Bound<'py, PyDict>,
447    ) -> Option<(Bound<'py, PyAny>, Bound<'py, PyAny>)> {
448        match self {
449            Self::DictIter {
450                di_used,
451                remaining,
452                ppos,
453                ..
454            } => {
455                let ma_used = dict_len(dict);
456
457                // These checks are similar to what CPython does.
458                //
459                // If the dimension of the dict changes e.g. key-value pairs are removed
460                // or added during iteration, this will panic next time when `next` is called
461                if *di_used != ma_used {
462                    *di_used = -1;
463                    panic!("dictionary changed size during iteration");
464                };
465
466                // If the dict is changed in such a way that the length remains constant
467                // then this will panic at the end of iteration - similar to this:
468                //
469                // d = {"a":1, "b":2, "c": 3}
470                //
471                // for k, v in d.items():
472                //     d[f"{k}_"] = 4
473                //     del d[k]
474                //     print(k)
475                //
476                if *remaining == -1 {
477                    *di_used = -1;
478                    panic!("dictionary keys changed during iteration");
479                };
480
481                let mut key: *mut ffi::PyObject = std::ptr::null_mut();
482                let mut value: *mut ffi::PyObject = std::ptr::null_mut();
483
484                if unsafe { ffi::PyDict_Next(dict.as_ptr(), ppos, &mut key, &mut value) != 0 } {
485                    *remaining -= 1;
486                    let py = dict.py();
487                    // Safety:
488                    // - PyDict_Next returns borrowed values
489                    // - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null
490                    Some((
491                        unsafe { key.assume_borrowed_unchecked(py).to_owned() },
492                        unsafe { value.assume_borrowed_unchecked(py).to_owned() },
493                    ))
494                } else {
495                    None
496                }
497            }
498        }
499    }
500
501    #[cfg(Py_GIL_DISABLED)]
502    #[inline]
503    fn with_critical_section<F, R>(&mut self, dict: &Bound<'_, PyDict>, f: F) -> R
504    where
505        F: FnOnce(&mut Self) -> R,
506    {
507        match self {
508            Self::DictIter { .. } => crate::sync::with_critical_section(dict, || f(self)),
509        }
510    }
511}
512
513impl<'py> Iterator for BoundDictIterator<'py> {
514    type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
515
516    #[inline]
517    fn next(&mut self) -> Option<Self::Item> {
518        #[cfg(Py_GIL_DISABLED)]
519        {
520            self.inner
521                .with_critical_section(&self.dict, |inner| unsafe {
522                    inner.next_unchecked(&self.dict)
523                })
524        }
525        #[cfg(not(Py_GIL_DISABLED))]
526        {
527            unsafe { self.inner.next_unchecked(&self.dict) }
528        }
529    }
530
531    #[inline]
532    fn size_hint(&self) -> (usize, Option<usize>) {
533        let len = self.len();
534        (len, Some(len))
535    }
536
537    #[inline]
538    #[cfg(Py_GIL_DISABLED)]
539    fn fold<B, F>(mut self, init: B, mut f: F) -> B
540    where
541        Self: Sized,
542        F: FnMut(B, Self::Item) -> B,
543    {
544        self.inner.with_critical_section(&self.dict, |inner| {
545            let mut accum = init;
546            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
547                accum = f(accum, x);
548            }
549            accum
550        })
551    }
552
553    #[inline]
554    #[cfg(all(Py_GIL_DISABLED, feature = "nightly"))]
555    fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
556    where
557        Self: Sized,
558        F: FnMut(B, Self::Item) -> R,
559        R: std::ops::Try<Output = B>,
560    {
561        self.inner.with_critical_section(&self.dict, |inner| {
562            let mut accum = init;
563            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
564                accum = f(accum, x)?
565            }
566            R::from_output(accum)
567        })
568    }
569
570    #[inline]
571    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
572    fn all<F>(&mut self, mut f: F) -> bool
573    where
574        Self: Sized,
575        F: FnMut(Self::Item) -> bool,
576    {
577        self.inner.with_critical_section(&self.dict, |inner| {
578            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
579                if !f(x) {
580                    return false;
581                }
582            }
583            true
584        })
585    }
586
587    #[inline]
588    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
589    fn any<F>(&mut self, mut f: F) -> bool
590    where
591        Self: Sized,
592        F: FnMut(Self::Item) -> bool,
593    {
594        self.inner.with_critical_section(&self.dict, |inner| {
595            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
596                if f(x) {
597                    return true;
598                }
599            }
600            false
601        })
602    }
603
604    #[inline]
605    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
606    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
607    where
608        Self: Sized,
609        P: FnMut(&Self::Item) -> bool,
610    {
611        self.inner.with_critical_section(&self.dict, |inner| {
612            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
613                if predicate(&x) {
614                    return Some(x);
615                }
616            }
617            None
618        })
619    }
620
621    #[inline]
622    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
623    fn find_map<B, F>(&mut self, mut f: F) -> Option<B>
624    where
625        Self: Sized,
626        F: FnMut(Self::Item) -> Option<B>,
627    {
628        self.inner.with_critical_section(&self.dict, |inner| {
629            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
630                if let found @ Some(_) = f(x) {
631                    return found;
632                }
633            }
634            None
635        })
636    }
637
638    #[inline]
639    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
640    fn position<P>(&mut self, mut predicate: P) -> Option<usize>
641    where
642        Self: Sized,
643        P: FnMut(Self::Item) -> bool,
644    {
645        self.inner.with_critical_section(&self.dict, |inner| {
646            let mut acc = 0;
647            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
648                if predicate(x) {
649                    return Some(acc);
650                }
651                acc += 1;
652            }
653            None
654        })
655    }
656}
657
658impl ExactSizeIterator for BoundDictIterator<'_> {
659    fn len(&self) -> usize {
660        match self.inner {
661            DictIterImpl::DictIter { remaining, .. } => remaining as usize,
662        }
663    }
664}
665
666impl<'py> BoundDictIterator<'py> {
667    fn new(dict: Bound<'py, PyDict>) -> Self {
668        let remaining = dict_len(&dict);
669
670        Self {
671            dict,
672            inner: DictIterImpl::DictIter {
673                ppos: 0,
674                di_used: remaining,
675                remaining,
676            },
677        }
678    }
679}
680
681impl<'py> IntoIterator for Bound<'py, PyDict> {
682    type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
683    type IntoIter = BoundDictIterator<'py>;
684
685    fn into_iter(self) -> Self::IntoIter {
686        BoundDictIterator::new(self)
687    }
688}
689
690impl<'py> IntoIterator for &Bound<'py, PyDict> {
691    type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
692    type IntoIter = BoundDictIterator<'py>;
693
694    fn into_iter(self) -> Self::IntoIter {
695        self.iter()
696    }
697}
698
699mod borrowed_iter {
700    use super::*;
701
702    /// Variant of the above which is used to iterate the items of the dictionary
703    /// without incrementing reference counts. This is only safe if it's known
704    /// that the dictionary will not be modified during iteration.
705    pub struct BorrowedDictIter<'a, 'py> {
706        dict: Borrowed<'a, 'py, PyDict>,
707        ppos: ffi::Py_ssize_t,
708        len: ffi::Py_ssize_t,
709    }
710
711    impl<'a, 'py> Iterator for BorrowedDictIter<'a, 'py> {
712        type Item = (Borrowed<'a, 'py, PyAny>, Borrowed<'a, 'py, PyAny>);
713
714        #[inline]
715        fn next(&mut self) -> Option<Self::Item> {
716            let mut key: *mut ffi::PyObject = std::ptr::null_mut();
717            let mut value: *mut ffi::PyObject = std::ptr::null_mut();
718
719            // Safety: self.dict lives sufficiently long that the pointer is not dangling
720            if unsafe { ffi::PyDict_Next(self.dict.as_ptr(), &mut self.ppos, &mut key, &mut value) }
721                != 0
722            {
723                let py = self.dict.py();
724                self.len -= 1;
725                // Safety:
726                // - PyDict_Next returns borrowed values
727                // - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null
728                Some(unsafe { (key.assume_borrowed(py), value.assume_borrowed(py)) })
729            } else {
730                None
731            }
732        }
733
734        #[inline]
735        fn size_hint(&self) -> (usize, Option<usize>) {
736            let len = self.len();
737            (len, Some(len))
738        }
739    }
740
741    impl ExactSizeIterator for BorrowedDictIter<'_, '_> {
742        fn len(&self) -> usize {
743            self.len as usize
744        }
745    }
746
747    impl<'a, 'py> BorrowedDictIter<'a, 'py> {
748        pub(super) fn new(dict: Borrowed<'a, 'py, PyDict>) -> Self {
749            let len = dict_len(&dict);
750            BorrowedDictIter { dict, ppos: 0, len }
751        }
752    }
753}
754
755pub(crate) use borrowed_iter::BorrowedDictIter;
756
757/// Conversion trait that allows a sequence of tuples to be converted into `PyDict`
758/// Primary use case for this trait is `call` and `call_method` methods as keywords argument.
759pub trait IntoPyDict<'py>: Sized {
760    /// Converts self into a `PyDict` object pointer. Whether pointer owned or borrowed
761    /// depends on implementation.
762    fn into_py_dict(self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>>;
763
764    /// Deprecated name for [`IntoPyDict::into_py_dict`].
765    #[deprecated(since = "0.23.0", note = "renamed to `IntoPyDict::into_py_dict`")]
766    #[inline]
767    fn into_py_dict_bound(self, py: Python<'py>) -> Bound<'py, PyDict> {
768        self.into_py_dict(py).unwrap()
769    }
770}
771
772impl<'py, T, I> IntoPyDict<'py> for I
773where
774    T: PyDictItem<'py>,
775    I: IntoIterator<Item = T>,
776{
777    fn into_py_dict(self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
778        let dict = PyDict::new(py);
779        self.into_iter().try_for_each(|item| {
780            let (key, value) = item.unpack();
781            dict.set_item(key, value)
782        })?;
783        Ok(dict)
784    }
785}
786
787/// Represents a tuple which can be used as a PyDict item.
788trait PyDictItem<'py> {
789    type K: IntoPyObject<'py>;
790    type V: IntoPyObject<'py>;
791    fn unpack(self) -> (Self::K, Self::V);
792}
793
794impl<'py, K, V> PyDictItem<'py> for (K, V)
795where
796    K: IntoPyObject<'py>,
797    V: IntoPyObject<'py>,
798{
799    type K = K;
800    type V = V;
801
802    fn unpack(self) -> (Self::K, Self::V) {
803        (self.0, self.1)
804    }
805}
806
807impl<'a, 'py, K, V> PyDictItem<'py> for &'a (K, V)
808where
809    &'a K: IntoPyObject<'py>,
810    &'a V: IntoPyObject<'py>,
811{
812    type K = &'a K;
813    type V = &'a V;
814
815    fn unpack(self) -> (Self::K, Self::V) {
816        (&self.0, &self.1)
817    }
818}
819
820#[cfg(test)]
821mod tests {
822    use super::*;
823    use crate::types::PyTuple;
824    use std::collections::{BTreeMap, HashMap};
825
826    #[test]
827    fn test_new() {
828        Python::with_gil(|py| {
829            let dict = [(7, 32)].into_py_dict(py).unwrap();
830            assert_eq!(
831                32,
832                dict.get_item(7i32)
833                    .unwrap()
834                    .unwrap()
835                    .extract::<i32>()
836                    .unwrap()
837            );
838            assert!(dict.get_item(8i32).unwrap().is_none());
839            let map: HashMap<i32, i32> = [(7, 32)].iter().cloned().collect();
840            assert_eq!(map, dict.extract().unwrap());
841            let map: BTreeMap<i32, i32> = [(7, 32)].iter().cloned().collect();
842            assert_eq!(map, dict.extract().unwrap());
843        });
844    }
845
846    #[test]
847    #[cfg(not(any(PyPy, GraalPy)))]
848    fn test_from_sequence() {
849        Python::with_gil(|py| {
850            let items = PyList::new(py, vec![("a", 1), ("b", 2)]).unwrap();
851            let dict = PyDict::from_sequence(&items).unwrap();
852            assert_eq!(
853                1,
854                dict.get_item("a")
855                    .unwrap()
856                    .unwrap()
857                    .extract::<i32>()
858                    .unwrap()
859            );
860            assert_eq!(
861                2,
862                dict.get_item("b")
863                    .unwrap()
864                    .unwrap()
865                    .extract::<i32>()
866                    .unwrap()
867            );
868            let map: HashMap<String, i32> =
869                [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
870            assert_eq!(map, dict.extract().unwrap());
871            let map: BTreeMap<String, i32> =
872                [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
873            assert_eq!(map, dict.extract().unwrap());
874        });
875    }
876
877    #[test]
878    #[cfg(not(any(PyPy, GraalPy)))]
879    fn test_from_sequence_err() {
880        Python::with_gil(|py| {
881            let items = PyList::new(py, vec!["a", "b"]).unwrap();
882            assert!(PyDict::from_sequence(&items).is_err());
883        });
884    }
885
886    #[test]
887    fn test_copy() {
888        Python::with_gil(|py| {
889            let dict = [(7, 32)].into_py_dict(py).unwrap();
890
891            let ndict = dict.copy().unwrap();
892            assert_eq!(
893                32,
894                ndict
895                    .get_item(7i32)
896                    .unwrap()
897                    .unwrap()
898                    .extract::<i32>()
899                    .unwrap()
900            );
901            assert!(ndict.get_item(8i32).unwrap().is_none());
902        });
903    }
904
905    #[test]
906    fn test_len() {
907        Python::with_gil(|py| {
908            let mut v = HashMap::<i32, i32>::new();
909            let dict = (&v).into_pyobject(py).unwrap();
910            assert_eq!(0, dict.len());
911            v.insert(7, 32);
912            let dict2 = v.into_pyobject(py).unwrap();
913            assert_eq!(1, dict2.len());
914        });
915    }
916
917    #[test]
918    fn test_contains() {
919        Python::with_gil(|py| {
920            let mut v = HashMap::new();
921            v.insert(7, 32);
922            let dict = v.into_pyobject(py).unwrap();
923            assert!(dict.contains(7i32).unwrap());
924            assert!(!dict.contains(8i32).unwrap());
925        });
926    }
927
928    #[test]
929    fn test_get_item() {
930        Python::with_gil(|py| {
931            let mut v = HashMap::new();
932            v.insert(7, 32);
933            let dict = v.into_pyobject(py).unwrap();
934            assert_eq!(
935                32,
936                dict.get_item(7i32)
937                    .unwrap()
938                    .unwrap()
939                    .extract::<i32>()
940                    .unwrap()
941            );
942            assert!(dict.get_item(8i32).unwrap().is_none());
943        });
944    }
945
946    #[cfg(feature = "macros")]
947    #[test]
948    fn test_get_item_error_path() {
949        use crate::exceptions::PyTypeError;
950
951        #[crate::pyclass(crate = "crate")]
952        struct HashErrors;
953
954        #[crate::pymethods(crate = "crate")]
955        impl HashErrors {
956            #[new]
957            fn new() -> Self {
958                HashErrors {}
959            }
960
961            fn __hash__(&self) -> PyResult<isize> {
962                Err(PyTypeError::new_err("Error from __hash__"))
963            }
964        }
965
966        Python::with_gil(|py| {
967            let class = py.get_type::<HashErrors>();
968            let instance = class.call0().unwrap();
969            let d = PyDict::new(py);
970            match d.get_item(instance) {
971                Ok(_) => {
972                    panic!("this get_item call should always error")
973                }
974                Err(err) => {
975                    assert!(err.is_instance_of::<PyTypeError>(py));
976                    assert_eq!(err.value(py).to_string(), "Error from __hash__")
977                }
978            }
979        })
980    }
981
982    #[test]
983    fn test_set_item() {
984        Python::with_gil(|py| {
985            let mut v = HashMap::new();
986            v.insert(7, 32);
987            let dict = v.into_pyobject(py).unwrap();
988            assert!(dict.set_item(7i32, 42i32).is_ok()); // change
989            assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
990            assert_eq!(
991                42i32,
992                dict.get_item(7i32)
993                    .unwrap()
994                    .unwrap()
995                    .extract::<i32>()
996                    .unwrap()
997            );
998            assert_eq!(
999                123i32,
1000                dict.get_item(8i32)
1001                    .unwrap()
1002                    .unwrap()
1003                    .extract::<i32>()
1004                    .unwrap()
1005            );
1006        });
1007    }
1008
1009    #[test]
1010    fn test_set_item_refcnt() {
1011        Python::with_gil(|py| {
1012            let cnt;
1013            let obj = py.eval(ffi::c_str!("object()"), None, None).unwrap();
1014            {
1015                cnt = obj.get_refcnt();
1016                let _dict = [(10, &obj)].into_py_dict(py);
1017            }
1018            {
1019                assert_eq!(cnt, obj.get_refcnt());
1020            }
1021        });
1022    }
1023
1024    #[test]
1025    fn test_set_item_does_not_update_original_object() {
1026        Python::with_gil(|py| {
1027            let mut v = HashMap::new();
1028            v.insert(7, 32);
1029            let dict = (&v).into_pyobject(py).unwrap();
1030            assert!(dict.set_item(7i32, 42i32).is_ok()); // change
1031            assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
1032            assert_eq!(32i32, v[&7i32]); // not updated!
1033            assert_eq!(None, v.get(&8i32));
1034        });
1035    }
1036
1037    #[test]
1038    fn test_del_item() {
1039        Python::with_gil(|py| {
1040            let mut v = HashMap::new();
1041            v.insert(7, 32);
1042            let dict = v.into_pyobject(py).unwrap();
1043            assert!(dict.del_item(7i32).is_ok());
1044            assert_eq!(0, dict.len());
1045            assert!(dict.get_item(7i32).unwrap().is_none());
1046        });
1047    }
1048
1049    #[test]
1050    fn test_del_item_does_not_update_original_object() {
1051        Python::with_gil(|py| {
1052            let mut v = HashMap::new();
1053            v.insert(7, 32);
1054            let dict = (&v).into_pyobject(py).unwrap();
1055            assert!(dict.del_item(7i32).is_ok()); // change
1056            assert_eq!(32i32, *v.get(&7i32).unwrap()); // not updated!
1057        });
1058    }
1059
1060    #[test]
1061    fn test_items() {
1062        Python::with_gil(|py| {
1063            let mut v = HashMap::new();
1064            v.insert(7, 32);
1065            v.insert(8, 42);
1066            v.insert(9, 123);
1067            let dict = v.into_pyobject(py).unwrap();
1068            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1069            let mut key_sum = 0;
1070            let mut value_sum = 0;
1071            for el in dict.items() {
1072                let tuple = el.downcast::<PyTuple>().unwrap();
1073                key_sum += tuple.get_item(0).unwrap().extract::<i32>().unwrap();
1074                value_sum += tuple.get_item(1).unwrap().extract::<i32>().unwrap();
1075            }
1076            assert_eq!(7 + 8 + 9, key_sum);
1077            assert_eq!(32 + 42 + 123, value_sum);
1078        });
1079    }
1080
1081    #[test]
1082    fn test_keys() {
1083        Python::with_gil(|py| {
1084            let mut v = HashMap::new();
1085            v.insert(7, 32);
1086            v.insert(8, 42);
1087            v.insert(9, 123);
1088            let dict = v.into_pyobject(py).unwrap();
1089            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1090            let mut key_sum = 0;
1091            for el in dict.keys() {
1092                key_sum += el.extract::<i32>().unwrap();
1093            }
1094            assert_eq!(7 + 8 + 9, key_sum);
1095        });
1096    }
1097
1098    #[test]
1099    fn test_values() {
1100        Python::with_gil(|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            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1107            let mut values_sum = 0;
1108            for el in dict.values() {
1109                values_sum += el.extract::<i32>().unwrap();
1110            }
1111            assert_eq!(32 + 42 + 123, values_sum);
1112        });
1113    }
1114
1115    #[test]
1116    fn test_iter() {
1117        Python::with_gil(|py| {
1118            let mut v = HashMap::new();
1119            v.insert(7, 32);
1120            v.insert(8, 42);
1121            v.insert(9, 123);
1122            let dict = v.into_pyobject(py).unwrap();
1123            let mut key_sum = 0;
1124            let mut value_sum = 0;
1125            for (key, value) in dict {
1126                key_sum += key.extract::<i32>().unwrap();
1127                value_sum += value.extract::<i32>().unwrap();
1128            }
1129            assert_eq!(7 + 8 + 9, key_sum);
1130            assert_eq!(32 + 42 + 123, value_sum);
1131        });
1132    }
1133
1134    #[test]
1135    fn test_iter_bound() {
1136        Python::with_gil(|py| {
1137            let mut v = HashMap::new();
1138            v.insert(7, 32);
1139            v.insert(8, 42);
1140            v.insert(9, 123);
1141            let dict = v.into_pyobject(py).unwrap();
1142            let mut key_sum = 0;
1143            let mut value_sum = 0;
1144            for (key, value) in dict {
1145                key_sum += key.extract::<i32>().unwrap();
1146                value_sum += value.extract::<i32>().unwrap();
1147            }
1148            assert_eq!(7 + 8 + 9, key_sum);
1149            assert_eq!(32 + 42 + 123, value_sum);
1150        });
1151    }
1152
1153    #[test]
1154    fn test_iter_value_mutated() {
1155        Python::with_gil(|py| {
1156            let mut v = HashMap::new();
1157            v.insert(7, 32);
1158            v.insert(8, 42);
1159            v.insert(9, 123);
1160
1161            let dict = (&v).into_pyobject(py).unwrap();
1162
1163            for (key, value) in &dict {
1164                dict.set_item(key, value.extract::<i32>().unwrap() + 7)
1165                    .unwrap();
1166            }
1167        });
1168    }
1169
1170    #[test]
1171    #[should_panic]
1172    fn test_iter_key_mutated() {
1173        Python::with_gil(|py| {
1174            let mut v = HashMap::new();
1175            for i in 0..10 {
1176                v.insert(i * 2, i * 2);
1177            }
1178            let dict = v.into_pyobject(py).unwrap();
1179
1180            for (i, (key, value)) in dict.iter().enumerate() {
1181                let key = key.extract::<i32>().unwrap();
1182                let value = value.extract::<i32>().unwrap();
1183
1184                dict.set_item(key + 1, value + 1).unwrap();
1185
1186                if i > 1000 {
1187                    // avoid this test just running out of memory if it fails
1188                    break;
1189                };
1190            }
1191        });
1192    }
1193
1194    #[test]
1195    #[should_panic]
1196    fn test_iter_key_mutated_constant_len() {
1197        Python::with_gil(|py| {
1198            let mut v = HashMap::new();
1199            for i in 0..10 {
1200                v.insert(i * 2, i * 2);
1201            }
1202            let dict = v.into_pyobject(py).unwrap();
1203
1204            for (i, (key, value)) in dict.iter().enumerate() {
1205                let key = key.extract::<i32>().unwrap();
1206                let value = value.extract::<i32>().unwrap();
1207                dict.del_item(key).unwrap();
1208                dict.set_item(key + 1, value + 1).unwrap();
1209
1210                if i > 1000 {
1211                    // avoid this test just running out of memory if it fails
1212                    break;
1213                };
1214            }
1215        });
1216    }
1217
1218    #[test]
1219    fn test_iter_size_hint() {
1220        Python::with_gil(|py| {
1221            let mut v = HashMap::new();
1222            v.insert(7, 32);
1223            v.insert(8, 42);
1224            v.insert(9, 123);
1225            let dict = (&v).into_pyobject(py).unwrap();
1226
1227            let mut iter = dict.iter();
1228            assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
1229            iter.next();
1230            assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
1231
1232            // Exhaust iterator.
1233            for _ in &mut iter {}
1234
1235            assert_eq!(iter.size_hint(), (0, Some(0)));
1236
1237            assert!(iter.next().is_none());
1238
1239            assert_eq!(iter.size_hint(), (0, Some(0)));
1240        });
1241    }
1242
1243    #[test]
1244    fn test_into_iter() {
1245        Python::with_gil(|py| {
1246            let mut v = HashMap::new();
1247            v.insert(7, 32);
1248            v.insert(8, 42);
1249            v.insert(9, 123);
1250            let dict = v.into_pyobject(py).unwrap();
1251            let mut key_sum = 0;
1252            let mut value_sum = 0;
1253            for (key, value) in dict {
1254                key_sum += key.extract::<i32>().unwrap();
1255                value_sum += value.extract::<i32>().unwrap();
1256            }
1257            assert_eq!(7 + 8 + 9, key_sum);
1258            assert_eq!(32 + 42 + 123, value_sum);
1259        });
1260    }
1261
1262    #[test]
1263    fn test_hashmap_into_dict() {
1264        Python::with_gil(|py| {
1265            let mut map = HashMap::<i32, i32>::new();
1266            map.insert(1, 1);
1267
1268            let py_map = map.into_py_dict(py).unwrap();
1269
1270            assert_eq!(py_map.len(), 1);
1271            assert_eq!(
1272                py_map
1273                    .get_item(1)
1274                    .unwrap()
1275                    .unwrap()
1276                    .extract::<i32>()
1277                    .unwrap(),
1278                1
1279            );
1280        });
1281    }
1282
1283    #[test]
1284    fn test_btreemap_into_dict() {
1285        Python::with_gil(|py| {
1286            let mut map = BTreeMap::<i32, i32>::new();
1287            map.insert(1, 1);
1288
1289            let py_map = map.into_py_dict(py).unwrap();
1290
1291            assert_eq!(py_map.len(), 1);
1292            assert_eq!(
1293                py_map
1294                    .get_item(1)
1295                    .unwrap()
1296                    .unwrap()
1297                    .extract::<i32>()
1298                    .unwrap(),
1299                1
1300            );
1301        });
1302    }
1303
1304    #[test]
1305    fn test_vec_into_dict() {
1306        Python::with_gil(|py| {
1307            let vec = vec![("a", 1), ("b", 2), ("c", 3)];
1308            let py_map = vec.into_py_dict(py).unwrap();
1309
1310            assert_eq!(py_map.len(), 3);
1311            assert_eq!(
1312                py_map
1313                    .get_item("b")
1314                    .unwrap()
1315                    .unwrap()
1316                    .extract::<i32>()
1317                    .unwrap(),
1318                2
1319            );
1320        });
1321    }
1322
1323    #[test]
1324    fn test_slice_into_dict() {
1325        Python::with_gil(|py| {
1326            let arr = [("a", 1), ("b", 2), ("c", 3)];
1327            let py_map = arr.into_py_dict(py).unwrap();
1328
1329            assert_eq!(py_map.len(), 3);
1330            assert_eq!(
1331                py_map
1332                    .get_item("b")
1333                    .unwrap()
1334                    .unwrap()
1335                    .extract::<i32>()
1336                    .unwrap(),
1337                2
1338            );
1339        });
1340    }
1341
1342    #[test]
1343    fn dict_as_mapping() {
1344        Python::with_gil(|py| {
1345            let mut map = HashMap::<i32, i32>::new();
1346            map.insert(1, 1);
1347
1348            let py_map = map.into_py_dict(py).unwrap();
1349
1350            assert_eq!(py_map.as_mapping().len().unwrap(), 1);
1351            assert_eq!(
1352                py_map
1353                    .as_mapping()
1354                    .get_item(1)
1355                    .unwrap()
1356                    .extract::<i32>()
1357                    .unwrap(),
1358                1
1359            );
1360        });
1361    }
1362
1363    #[test]
1364    fn dict_into_mapping() {
1365        Python::with_gil(|py| {
1366            let mut map = HashMap::<i32, i32>::new();
1367            map.insert(1, 1);
1368
1369            let py_map = map.into_py_dict(py).unwrap();
1370
1371            let py_mapping = py_map.into_mapping();
1372            assert_eq!(py_mapping.len().unwrap(), 1);
1373            assert_eq!(py_mapping.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
1374        });
1375    }
1376
1377    #[cfg(not(any(PyPy, GraalPy)))]
1378    fn abc_dict(py: Python<'_>) -> Bound<'_, PyDict> {
1379        let mut map = HashMap::<&'static str, i32>::new();
1380        map.insert("a", 1);
1381        map.insert("b", 2);
1382        map.insert("c", 3);
1383        map.into_py_dict(py).unwrap()
1384    }
1385
1386    #[test]
1387    #[cfg(not(any(PyPy, GraalPy)))]
1388    fn dict_keys_view() {
1389        Python::with_gil(|py| {
1390            let dict = abc_dict(py);
1391            let keys = dict.call_method0("keys").unwrap();
1392            assert!(keys.is_instance(&py.get_type::<PyDictKeys>()).unwrap());
1393        })
1394    }
1395
1396    #[test]
1397    #[cfg(not(any(PyPy, GraalPy)))]
1398    fn dict_values_view() {
1399        Python::with_gil(|py| {
1400            let dict = abc_dict(py);
1401            let values = dict.call_method0("values").unwrap();
1402            assert!(values.is_instance(&py.get_type::<PyDictValues>()).unwrap());
1403        })
1404    }
1405
1406    #[test]
1407    #[cfg(not(any(PyPy, GraalPy)))]
1408    fn dict_items_view() {
1409        Python::with_gil(|py| {
1410            let dict = abc_dict(py);
1411            let items = dict.call_method0("items").unwrap();
1412            assert!(items.is_instance(&py.get_type::<PyDictItems>()).unwrap());
1413        })
1414    }
1415
1416    #[test]
1417    fn dict_update() {
1418        Python::with_gil(|py| {
1419            let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1420            let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1421            dict.update(other.as_mapping()).unwrap();
1422            assert_eq!(dict.len(), 4);
1423            assert_eq!(
1424                dict.get_item("a")
1425                    .unwrap()
1426                    .unwrap()
1427                    .extract::<i32>()
1428                    .unwrap(),
1429                1
1430            );
1431            assert_eq!(
1432                dict.get_item("b")
1433                    .unwrap()
1434                    .unwrap()
1435                    .extract::<i32>()
1436                    .unwrap(),
1437                4
1438            );
1439            assert_eq!(
1440                dict.get_item("c")
1441                    .unwrap()
1442                    .unwrap()
1443                    .extract::<i32>()
1444                    .unwrap(),
1445                5
1446            );
1447            assert_eq!(
1448                dict.get_item("d")
1449                    .unwrap()
1450                    .unwrap()
1451                    .extract::<i32>()
1452                    .unwrap(),
1453                6
1454            );
1455
1456            assert_eq!(other.len(), 3);
1457            assert_eq!(
1458                other
1459                    .get_item("b")
1460                    .unwrap()
1461                    .unwrap()
1462                    .extract::<i32>()
1463                    .unwrap(),
1464                4
1465            );
1466            assert_eq!(
1467                other
1468                    .get_item("c")
1469                    .unwrap()
1470                    .unwrap()
1471                    .extract::<i32>()
1472                    .unwrap(),
1473                5
1474            );
1475            assert_eq!(
1476                other
1477                    .get_item("d")
1478                    .unwrap()
1479                    .unwrap()
1480                    .extract::<i32>()
1481                    .unwrap(),
1482                6
1483            );
1484        })
1485    }
1486
1487    #[test]
1488    fn dict_update_if_missing() {
1489        Python::with_gil(|py| {
1490            let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1491            let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1492            dict.update_if_missing(other.as_mapping()).unwrap();
1493            assert_eq!(dict.len(), 4);
1494            assert_eq!(
1495                dict.get_item("a")
1496                    .unwrap()
1497                    .unwrap()
1498                    .extract::<i32>()
1499                    .unwrap(),
1500                1
1501            );
1502            assert_eq!(
1503                dict.get_item("b")
1504                    .unwrap()
1505                    .unwrap()
1506                    .extract::<i32>()
1507                    .unwrap(),
1508                2
1509            );
1510            assert_eq!(
1511                dict.get_item("c")
1512                    .unwrap()
1513                    .unwrap()
1514                    .extract::<i32>()
1515                    .unwrap(),
1516                3
1517            );
1518            assert_eq!(
1519                dict.get_item("d")
1520                    .unwrap()
1521                    .unwrap()
1522                    .extract::<i32>()
1523                    .unwrap(),
1524                6
1525            );
1526
1527            assert_eq!(other.len(), 3);
1528            assert_eq!(
1529                other
1530                    .get_item("b")
1531                    .unwrap()
1532                    .unwrap()
1533                    .extract::<i32>()
1534                    .unwrap(),
1535                4
1536            );
1537            assert_eq!(
1538                other
1539                    .get_item("c")
1540                    .unwrap()
1541                    .unwrap()
1542                    .extract::<i32>()
1543                    .unwrap(),
1544                5
1545            );
1546            assert_eq!(
1547                other
1548                    .get_item("d")
1549                    .unwrap()
1550                    .unwrap()
1551                    .extract::<i32>()
1552                    .unwrap(),
1553                6
1554            );
1555        })
1556    }
1557
1558    #[test]
1559    fn test_iter_all() {
1560        Python::with_gil(|py| {
1561            let dict = [(1, true), (2, true), (3, true)].into_py_dict(py).unwrap();
1562            assert!(dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1563
1564            let dict = [(1, true), (2, false), (3, true)].into_py_dict(py).unwrap();
1565            assert!(!dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1566        });
1567    }
1568
1569    #[test]
1570    fn test_iter_any() {
1571        Python::with_gil(|py| {
1572            let dict = [(1, true), (2, false), (3, false)]
1573                .into_py_dict(py)
1574                .unwrap();
1575            assert!(dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1576
1577            let dict = [(1, false), (2, false), (3, false)]
1578                .into_py_dict(py)
1579                .unwrap();
1580            assert!(!dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1581        });
1582    }
1583
1584    #[test]
1585    #[allow(clippy::search_is_some)]
1586    fn test_iter_find() {
1587        Python::with_gil(|py| {
1588            let dict = [(1, false), (2, true), (3, false)]
1589                .into_py_dict(py)
1590                .unwrap();
1591
1592            assert_eq!(
1593                Some((2, true)),
1594                dict.iter()
1595                    .find(|(_, v)| v.extract::<bool>().unwrap())
1596                    .map(|(k, v)| (k.extract().unwrap(), v.extract().unwrap()))
1597            );
1598
1599            let dict = [(1, false), (2, false), (3, false)]
1600                .into_py_dict(py)
1601                .unwrap();
1602
1603            assert!(dict
1604                .iter()
1605                .find(|(_, v)| v.extract::<bool>().unwrap())
1606                .is_none());
1607        });
1608    }
1609
1610    #[test]
1611    #[allow(clippy::search_is_some)]
1612    fn test_iter_position() {
1613        Python::with_gil(|py| {
1614            let dict = [(1, false), (2, false), (3, true)]
1615                .into_py_dict(py)
1616                .unwrap();
1617            assert_eq!(
1618                Some(2),
1619                dict.iter().position(|(_, v)| v.extract::<bool>().unwrap())
1620            );
1621
1622            let dict = [(1, false), (2, false), (3, false)]
1623                .into_py_dict(py)
1624                .unwrap();
1625            assert!(dict
1626                .iter()
1627                .position(|(_, v)| v.extract::<bool>().unwrap())
1628                .is_none());
1629        });
1630    }
1631
1632    #[test]
1633    fn test_iter_fold() {
1634        Python::with_gil(|py| {
1635            let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1636            let sum = dict
1637                .iter()
1638                .fold(0, |acc, (_, v)| acc + v.extract::<i32>().unwrap());
1639            assert_eq!(sum, 6);
1640        });
1641    }
1642
1643    #[test]
1644    fn test_iter_try_fold() {
1645        Python::with_gil(|py| {
1646            let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1647            let sum = dict
1648                .iter()
1649                .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1650                .unwrap();
1651            assert_eq!(sum, 6);
1652
1653            let dict = [(1, "foo"), (2, "bar")].into_py_dict(py).unwrap();
1654            assert!(dict
1655                .iter()
1656                .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1657                .is_err());
1658        });
1659    }
1660}