Skip to main content

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, 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
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/// Represents a Python `dict_keys`.
32#[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/// Represents a Python `dict_values`.
46#[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/// Represents a Python `dict_items`.
60#[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    /// Creates a new empty dictionary.
75    pub fn new(py: Python<'_>) -> Bound<'_, PyDict> {
76        unsafe { ffi::PyDict_New().assume_owned(py).cast_into_unchecked() }
77    }
78
79    /// Creates a new dictionary from the sequence given.
80    ///
81    /// The sequence must consist of `(PyObject, PyObject)`. This is
82    /// equivalent to `dict([("a", 1), ("b", 2)])`.
83    ///
84    /// Returns an error on invalid input. In the case of key collisions,
85    /// this keeps the last entry seen.
86    #[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/// Implementation of functionality for [`PyDict`].
98///
99/// These methods are defined for the `Bound<'py, PyDict>` smart pointer, so to use method call
100/// syntax these methods are separated into a trait, because stable Rust does not yet support
101/// `arbitrary_self_types`.
102#[doc(alias = "PyDict")]
103pub trait PyDictMethods<'py>: crate::sealed::Sealed {
104    /// Returns a new dictionary that contains the same key-value pairs as self.
105    ///
106    /// This is equivalent to the Python expression `self.copy()`.
107    fn copy(&self) -> PyResult<Bound<'py, PyDict>>;
108
109    /// Empties an existing dictionary of all key-value pairs.
110    fn clear(&self);
111
112    /// Return the number of items in the dictionary.
113    ///
114    /// This is equivalent to the Python expression `len(self)`.
115    fn len(&self) -> usize;
116
117    /// Checks if the dict is empty, i.e. `len(self) == 0`.
118    fn is_empty(&self) -> bool;
119
120    /// Determines if the dictionary contains the specified key.
121    ///
122    /// This is equivalent to the Python expression `key in self`.
123    fn contains<K>(&self, key: K) -> PyResult<bool>
124    where
125        K: IntoPyObject<'py>;
126
127    /// Gets an item from the dictionary.
128    ///
129    /// Returns `None` if the item is not present, or if an error occurs.
130    ///
131    /// To get a `KeyError` for non-existing keys, use `PyAny::get_item`.
132    fn get_item<K>(&self, key: K) -> PyResult<Option<Bound<'py, PyAny>>>
133    where
134        K: IntoPyObject<'py>;
135
136    /// Sets an item value.
137    ///
138    /// This is equivalent to the Python statement `self[key] = value`.
139    fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
140    where
141        K: IntoPyObject<'py>,
142        V: IntoPyObject<'py>;
143
144    /// Deletes an item.
145    ///
146    /// This is equivalent to the Python statement `del self[key]`.
147    fn del_item<K>(&self, key: K) -> PyResult<()>
148    where
149        K: IntoPyObject<'py>;
150
151    /// Returns a list of dict keys.
152    ///
153    /// This is equivalent to the Python expression `list(dict.keys())`.
154    fn keys(&self) -> Bound<'py, PyList>;
155
156    /// Returns a list of dict values.
157    ///
158    /// This is equivalent to the Python expression `list(dict.values())`.
159    fn values(&self) -> Bound<'py, PyList>;
160
161    /// Returns a list of dict items.
162    ///
163    /// This is equivalent to the Python expression `list(dict.items())`.
164    fn items(&self) -> Bound<'py, PyList>;
165
166    /// Returns an iterator of `(key, value)` pairs in this dictionary.
167    ///
168    /// # Panics
169    ///
170    /// If PyO3 detects that the dictionary is mutated during iteration, it will panic.
171    /// It is allowed to modify values as you iterate over the dictionary, but only
172    /// so long as the set of keys does not change.
173    fn iter(&self) -> BoundDictIterator<'py>;
174
175    /// Iterates over the contents of this dictionary while holding a critical section on the dict.
176    /// This is useful when the GIL is disabled and the dictionary is shared between threads.
177    /// It is not guaranteed that the dictionary will not be modified during iteration when the
178    /// closure calls arbitrary Python code that releases the critical section held by the
179    /// iterator. Otherwise, the dictionary will not be modified during iteration.
180    ///
181    /// This method is a small performance optimization over `.iter().try_for_each()` when the
182    /// nightly feature is not enabled because we cannot implement an optimised version of
183    /// `iter().try_fold()` on stable yet. If your iteration is infallible then this method has the
184    /// same performance as `.iter().for_each()`.
185    fn locked_for_each<F>(&self, closure: F) -> PyResult<()>
186    where
187        F: Fn(Bound<'py, PyAny>, Bound<'py, PyAny>) -> PyResult<()>;
188
189    /// Returns `self` cast as a `PyMapping`.
190    fn as_mapping(&self) -> &Bound<'py, PyMapping>;
191
192    /// Returns `self` cast as a `PyMapping`.
193    fn into_mapping(self) -> Bound<'py, PyMapping>;
194
195    /// Update this dictionary with the key/value pairs from another.
196    ///
197    /// This is equivalent to the Python expression `self.update(other)`. If `other` is a `PyDict`, you may want
198    /// to use `self.update(other.as_mapping())`, note: `PyDict::as_mapping` is a zero-cost conversion.
199    fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()>;
200
201    /// Add key/value pairs from another dictionary to this one only when they do not exist in this.
202    ///
203    /// This is equivalent to the Python expression `self.update({k: v for k, v in other.items() if k not in self})`.
204    /// If `other` is a `PyDict`, you may want to use `self.update_if_missing(other.as_mapping())`,
205    /// note: `PyDict::as_mapping` is a zero-cost conversion.
206    ///
207    /// This method uses [`PyDict_Merge`](https://docs.python.org/3/c-api/dict.html#c.PyDict_Merge) internally,
208    /// so should have the same performance as `update`.
209    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                    // Safety: PyDict_GetItemRef positive return value means the result is a valid
269                    // owned reference
270                    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            // We don't need a critical section when the nightly feature is enabled because
357            // try_for_each is locked by the implementation of try_fold.
358            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    /// Iterates over the contents of this dictionary without incrementing reference counts.
392    ///
393    /// # Safety
394    /// It must be known that this dictionary will not be modified during iteration,
395    /// for example, when parsing arguments in a keyword arguments dictionary.
396    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
419/// PyO3 implementation of an iterator for a Python `dict` object.
420pub 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    /// Safety: the dict should be locked with a critical section on the free-threaded build
437    /// and otherwise not shared between threads in code that releases the GIL.
438    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                // These checks are similar to what CPython does.
452                //
453                // If the dimension of the dict changes e.g. key-value pairs are removed
454                // or added during iteration, this will panic next time when `next` is called
455                if *di_used != ma_used {
456                    *di_used = -1;
457                    panic!("dictionary changed size during iteration");
458                };
459
460                // If the dict is changed in such a way that the length remains constant
461                // then this will panic at the end of iteration - similar to this:
462                //
463                // d = {"a":1, "b":2, "c": 3}
464                //
465                // for k, v in d.items():
466                //     d[f"{k}_"] = 4
467                //     del d[k]
468                //     print(k)
469                //
470                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                    // Safety:
482                    // - PyDict_Next returns borrowed values
483                    // - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null
484                    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    /// Variant of the above which is used to iterate the items of the dictionary
707    /// without incrementing reference counts. This is only safe if it's known
708    /// that the dictionary will not be modified during iteration.
709    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            // Safety: self.dict lives sufficiently long that the pointer is not dangling
724            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                // Safety:
730                // - PyDict_Next returns borrowed values
731                // - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null
732                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
774/// Conversion trait that allows a sequence of tuples to be converted into `PyDict`
775/// Primary use case for this trait is `call` and `call_method` methods as keywords argument.
776pub trait IntoPyDict<'py>: Sized {
777    /// Converts self into a `PyDict` object pointer. Whether pointer owned or borrowed
778    /// depends on implementation.
779    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
797/// Represents a tuple which can be used as a PyDict item.
798trait 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()); // change
999            assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
1000            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()); // change
1041            assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
1042            assert_eq!(32i32, v[&7i32]); // not updated!
1043            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()); // change
1066            assert_eq!(32i32, *v.get(&7i32).unwrap()); // not updated!
1067        });
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            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1079            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            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1100            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            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1117            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                    // avoid this test just running out of memory if it fails
1198                    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                    // avoid this test just running out of memory if it fails
1222                    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            // Exhaust iterator.
1243            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}