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    fn count(self) -> usize
539    where
540        Self: Sized,
541    {
542        self.len()
543    }
544
545    #[inline]
546    #[cfg(Py_GIL_DISABLED)]
547    fn fold<B, F>(mut self, init: B, mut f: F) -> B
548    where
549        Self: Sized,
550        F: FnMut(B, Self::Item) -> B,
551    {
552        self.inner.with_critical_section(&self.dict, |inner| {
553            let mut accum = init;
554            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
555                accum = f(accum, x);
556            }
557            accum
558        })
559    }
560
561    #[inline]
562    #[cfg(all(Py_GIL_DISABLED, feature = "nightly"))]
563    fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
564    where
565        Self: Sized,
566        F: FnMut(B, Self::Item) -> R,
567        R: std::ops::Try<Output = B>,
568    {
569        self.inner.with_critical_section(&self.dict, |inner| {
570            let mut accum = init;
571            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
572                accum = f(accum, x)?
573            }
574            R::from_output(accum)
575        })
576    }
577
578    #[inline]
579    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
580    fn all<F>(&mut self, mut f: F) -> bool
581    where
582        Self: Sized,
583        F: FnMut(Self::Item) -> bool,
584    {
585        self.inner.with_critical_section(&self.dict, |inner| {
586            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
587                if !f(x) {
588                    return false;
589                }
590            }
591            true
592        })
593    }
594
595    #[inline]
596    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
597    fn any<F>(&mut self, mut f: F) -> bool
598    where
599        Self: Sized,
600        F: FnMut(Self::Item) -> bool,
601    {
602        self.inner.with_critical_section(&self.dict, |inner| {
603            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
604                if f(x) {
605                    return true;
606                }
607            }
608            false
609        })
610    }
611
612    #[inline]
613    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
614    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
615    where
616        Self: Sized,
617        P: FnMut(&Self::Item) -> bool,
618    {
619        self.inner.with_critical_section(&self.dict, |inner| {
620            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
621                if predicate(&x) {
622                    return Some(x);
623                }
624            }
625            None
626        })
627    }
628
629    #[inline]
630    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
631    fn find_map<B, F>(&mut self, mut f: F) -> Option<B>
632    where
633        Self: Sized,
634        F: FnMut(Self::Item) -> Option<B>,
635    {
636        self.inner.with_critical_section(&self.dict, |inner| {
637            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
638                if let found @ Some(_) = f(x) {
639                    return found;
640                }
641            }
642            None
643        })
644    }
645
646    #[inline]
647    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
648    fn position<P>(&mut self, mut predicate: P) -> Option<usize>
649    where
650        Self: Sized,
651        P: FnMut(Self::Item) -> bool,
652    {
653        self.inner.with_critical_section(&self.dict, |inner| {
654            let mut acc = 0;
655            while let Some(x) = unsafe { inner.next_unchecked(&self.dict) } {
656                if predicate(x) {
657                    return Some(acc);
658                }
659                acc += 1;
660            }
661            None
662        })
663    }
664}
665
666impl ExactSizeIterator for BoundDictIterator<'_> {
667    fn len(&self) -> usize {
668        match self.inner {
669            DictIterImpl::DictIter { remaining, .. } => remaining as usize,
670        }
671    }
672}
673
674impl<'py> BoundDictIterator<'py> {
675    fn new(dict: Bound<'py, PyDict>) -> Self {
676        let remaining = dict_len(&dict);
677
678        Self {
679            dict,
680            inner: DictIterImpl::DictIter {
681                ppos: 0,
682                di_used: remaining,
683                remaining,
684            },
685        }
686    }
687}
688
689impl<'py> IntoIterator for Bound<'py, PyDict> {
690    type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
691    type IntoIter = BoundDictIterator<'py>;
692
693    fn into_iter(self) -> Self::IntoIter {
694        BoundDictIterator::new(self)
695    }
696}
697
698impl<'py> IntoIterator for &Bound<'py, PyDict> {
699    type Item = (Bound<'py, PyAny>, Bound<'py, PyAny>);
700    type IntoIter = BoundDictIterator<'py>;
701
702    fn into_iter(self) -> Self::IntoIter {
703        self.iter()
704    }
705}
706
707mod borrowed_iter {
708    use super::*;
709
710    /// Variant of the above which is used to iterate the items of the dictionary
711    /// without incrementing reference counts. This is only safe if it's known
712    /// that the dictionary will not be modified during iteration.
713    pub struct BorrowedDictIter<'a, 'py> {
714        dict: Borrowed<'a, 'py, PyDict>,
715        ppos: ffi::Py_ssize_t,
716        len: ffi::Py_ssize_t,
717    }
718
719    impl<'a, 'py> Iterator for BorrowedDictIter<'a, 'py> {
720        type Item = (Borrowed<'a, 'py, PyAny>, Borrowed<'a, 'py, PyAny>);
721
722        #[inline]
723        fn next(&mut self) -> Option<Self::Item> {
724            let mut key: *mut ffi::PyObject = std::ptr::null_mut();
725            let mut value: *mut ffi::PyObject = std::ptr::null_mut();
726
727            // Safety: self.dict lives sufficiently long that the pointer is not dangling
728            if unsafe { ffi::PyDict_Next(self.dict.as_ptr(), &mut self.ppos, &mut key, &mut value) }
729                != 0
730            {
731                let py = self.dict.py();
732                self.len -= 1;
733                // Safety:
734                // - PyDict_Next returns borrowed values
735                // - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null
736                Some(unsafe { (key.assume_borrowed(py), value.assume_borrowed(py)) })
737            } else {
738                None
739            }
740        }
741
742        #[inline]
743        fn size_hint(&self) -> (usize, Option<usize>) {
744            let len = self.len();
745            (len, Some(len))
746        }
747
748        #[inline]
749        fn count(self) -> usize
750        where
751            Self: Sized,
752        {
753            self.len()
754        }
755    }
756
757    impl ExactSizeIterator for BorrowedDictIter<'_, '_> {
758        fn len(&self) -> usize {
759            self.len as usize
760        }
761    }
762
763    impl<'a, 'py> BorrowedDictIter<'a, 'py> {
764        pub(super) fn new(dict: Borrowed<'a, 'py, PyDict>) -> Self {
765            let len = dict_len(&dict);
766            BorrowedDictIter { dict, ppos: 0, len }
767        }
768    }
769}
770
771pub(crate) use borrowed_iter::BorrowedDictIter;
772
773/// Conversion trait that allows a sequence of tuples to be converted into `PyDict`
774/// Primary use case for this trait is `call` and `call_method` methods as keywords argument.
775pub trait IntoPyDict<'py>: Sized {
776    /// Converts self into a `PyDict` object pointer. Whether pointer owned or borrowed
777    /// depends on implementation.
778    fn into_py_dict(self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>>;
779
780    /// Deprecated name for [`IntoPyDict::into_py_dict`].
781    #[deprecated(since = "0.23.0", note = "renamed to `IntoPyDict::into_py_dict`")]
782    #[inline]
783    fn into_py_dict_bound(self, py: Python<'py>) -> Bound<'py, PyDict> {
784        self.into_py_dict(py).unwrap()
785    }
786}
787
788impl<'py, T, I> IntoPyDict<'py> for I
789where
790    T: PyDictItem<'py>,
791    I: IntoIterator<Item = T>,
792{
793    fn into_py_dict(self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
794        let dict = PyDict::new(py);
795        self.into_iter().try_for_each(|item| {
796            let (key, value) = item.unpack();
797            dict.set_item(key, value)
798        })?;
799        Ok(dict)
800    }
801}
802
803/// Represents a tuple which can be used as a PyDict item.
804trait PyDictItem<'py> {
805    type K: IntoPyObject<'py>;
806    type V: IntoPyObject<'py>;
807    fn unpack(self) -> (Self::K, Self::V);
808}
809
810impl<'py, K, V> PyDictItem<'py> for (K, V)
811where
812    K: IntoPyObject<'py>,
813    V: IntoPyObject<'py>,
814{
815    type K = K;
816    type V = V;
817
818    fn unpack(self) -> (Self::K, Self::V) {
819        (self.0, self.1)
820    }
821}
822
823impl<'a, 'py, K, V> PyDictItem<'py> for &'a (K, V)
824where
825    &'a K: IntoPyObject<'py>,
826    &'a V: IntoPyObject<'py>,
827{
828    type K = &'a K;
829    type V = &'a V;
830
831    fn unpack(self) -> (Self::K, Self::V) {
832        (&self.0, &self.1)
833    }
834}
835
836#[cfg(test)]
837mod tests {
838    use super::*;
839    use crate::types::PyTuple;
840    use std::collections::{BTreeMap, HashMap};
841
842    #[test]
843    fn test_new() {
844        Python::with_gil(|py| {
845            let dict = [(7, 32)].into_py_dict(py).unwrap();
846            assert_eq!(
847                32,
848                dict.get_item(7i32)
849                    .unwrap()
850                    .unwrap()
851                    .extract::<i32>()
852                    .unwrap()
853            );
854            assert!(dict.get_item(8i32).unwrap().is_none());
855            let map: HashMap<i32, i32> = [(7, 32)].iter().cloned().collect();
856            assert_eq!(map, dict.extract().unwrap());
857            let map: BTreeMap<i32, i32> = [(7, 32)].iter().cloned().collect();
858            assert_eq!(map, dict.extract().unwrap());
859        });
860    }
861
862    #[test]
863    #[cfg(not(any(PyPy, GraalPy)))]
864    fn test_from_sequence() {
865        Python::with_gil(|py| {
866            let items = PyList::new(py, vec![("a", 1), ("b", 2)]).unwrap();
867            let dict = PyDict::from_sequence(&items).unwrap();
868            assert_eq!(
869                1,
870                dict.get_item("a")
871                    .unwrap()
872                    .unwrap()
873                    .extract::<i32>()
874                    .unwrap()
875            );
876            assert_eq!(
877                2,
878                dict.get_item("b")
879                    .unwrap()
880                    .unwrap()
881                    .extract::<i32>()
882                    .unwrap()
883            );
884            let map: HashMap<String, i32> =
885                [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
886            assert_eq!(map, dict.extract().unwrap());
887            let map: BTreeMap<String, i32> =
888                [("a".into(), 1), ("b".into(), 2)].into_iter().collect();
889            assert_eq!(map, dict.extract().unwrap());
890        });
891    }
892
893    #[test]
894    #[cfg(not(any(PyPy, GraalPy)))]
895    fn test_from_sequence_err() {
896        Python::with_gil(|py| {
897            let items = PyList::new(py, vec!["a", "b"]).unwrap();
898            assert!(PyDict::from_sequence(&items).is_err());
899        });
900    }
901
902    #[test]
903    fn test_copy() {
904        Python::with_gil(|py| {
905            let dict = [(7, 32)].into_py_dict(py).unwrap();
906
907            let ndict = dict.copy().unwrap();
908            assert_eq!(
909                32,
910                ndict
911                    .get_item(7i32)
912                    .unwrap()
913                    .unwrap()
914                    .extract::<i32>()
915                    .unwrap()
916            );
917            assert!(ndict.get_item(8i32).unwrap().is_none());
918        });
919    }
920
921    #[test]
922    fn test_len() {
923        Python::with_gil(|py| {
924            let mut v = HashMap::<i32, i32>::new();
925            let dict = (&v).into_pyobject(py).unwrap();
926            assert_eq!(0, dict.len());
927            v.insert(7, 32);
928            let dict2 = v.into_pyobject(py).unwrap();
929            assert_eq!(1, dict2.len());
930        });
931    }
932
933    #[test]
934    fn test_contains() {
935        Python::with_gil(|py| {
936            let mut v = HashMap::new();
937            v.insert(7, 32);
938            let dict = v.into_pyobject(py).unwrap();
939            assert!(dict.contains(7i32).unwrap());
940            assert!(!dict.contains(8i32).unwrap());
941        });
942    }
943
944    #[test]
945    fn test_get_item() {
946        Python::with_gil(|py| {
947            let mut v = HashMap::new();
948            v.insert(7, 32);
949            let dict = v.into_pyobject(py).unwrap();
950            assert_eq!(
951                32,
952                dict.get_item(7i32)
953                    .unwrap()
954                    .unwrap()
955                    .extract::<i32>()
956                    .unwrap()
957            );
958            assert!(dict.get_item(8i32).unwrap().is_none());
959        });
960    }
961
962    #[cfg(feature = "macros")]
963    #[test]
964    fn test_get_item_error_path() {
965        use crate::exceptions::PyTypeError;
966
967        #[crate::pyclass(crate = "crate")]
968        struct HashErrors;
969
970        #[crate::pymethods(crate = "crate")]
971        impl HashErrors {
972            #[new]
973            fn new() -> Self {
974                HashErrors {}
975            }
976
977            fn __hash__(&self) -> PyResult<isize> {
978                Err(PyTypeError::new_err("Error from __hash__"))
979            }
980        }
981
982        Python::with_gil(|py| {
983            let class = py.get_type::<HashErrors>();
984            let instance = class.call0().unwrap();
985            let d = PyDict::new(py);
986            match d.get_item(instance) {
987                Ok(_) => {
988                    panic!("this get_item call should always error")
989                }
990                Err(err) => {
991                    assert!(err.is_instance_of::<PyTypeError>(py));
992                    assert_eq!(err.value(py).to_string(), "Error from __hash__")
993                }
994            }
995        })
996    }
997
998    #[test]
999    fn test_set_item() {
1000        Python::with_gil(|py| {
1001            let mut v = HashMap::new();
1002            v.insert(7, 32);
1003            let dict = v.into_pyobject(py).unwrap();
1004            assert!(dict.set_item(7i32, 42i32).is_ok()); // change
1005            assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
1006            assert_eq!(
1007                42i32,
1008                dict.get_item(7i32)
1009                    .unwrap()
1010                    .unwrap()
1011                    .extract::<i32>()
1012                    .unwrap()
1013            );
1014            assert_eq!(
1015                123i32,
1016                dict.get_item(8i32)
1017                    .unwrap()
1018                    .unwrap()
1019                    .extract::<i32>()
1020                    .unwrap()
1021            );
1022        });
1023    }
1024
1025    #[test]
1026    fn test_set_item_refcnt() {
1027        Python::with_gil(|py| {
1028            let cnt;
1029            let obj = py.eval(ffi::c_str!("object()"), None, None).unwrap();
1030            {
1031                cnt = obj.get_refcnt();
1032                let _dict = [(10, &obj)].into_py_dict(py);
1033            }
1034            {
1035                assert_eq!(cnt, obj.get_refcnt());
1036            }
1037        });
1038    }
1039
1040    #[test]
1041    fn test_set_item_does_not_update_original_object() {
1042        Python::with_gil(|py| {
1043            let mut v = HashMap::new();
1044            v.insert(7, 32);
1045            let dict = (&v).into_pyobject(py).unwrap();
1046            assert!(dict.set_item(7i32, 42i32).is_ok()); // change
1047            assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
1048            assert_eq!(32i32, v[&7i32]); // not updated!
1049            assert_eq!(None, v.get(&8i32));
1050        });
1051    }
1052
1053    #[test]
1054    fn test_del_item() {
1055        Python::with_gil(|py| {
1056            let mut v = HashMap::new();
1057            v.insert(7, 32);
1058            let dict = v.into_pyobject(py).unwrap();
1059            assert!(dict.del_item(7i32).is_ok());
1060            assert_eq!(0, dict.len());
1061            assert!(dict.get_item(7i32).unwrap().is_none());
1062        });
1063    }
1064
1065    #[test]
1066    fn test_del_item_does_not_update_original_object() {
1067        Python::with_gil(|py| {
1068            let mut v = HashMap::new();
1069            v.insert(7, 32);
1070            let dict = (&v).into_pyobject(py).unwrap();
1071            assert!(dict.del_item(7i32).is_ok()); // change
1072            assert_eq!(32i32, *v.get(&7i32).unwrap()); // not updated!
1073        });
1074    }
1075
1076    #[test]
1077    fn test_items() {
1078        Python::with_gil(|py| {
1079            let mut v = HashMap::new();
1080            v.insert(7, 32);
1081            v.insert(8, 42);
1082            v.insert(9, 123);
1083            let dict = v.into_pyobject(py).unwrap();
1084            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1085            let mut key_sum = 0;
1086            let mut value_sum = 0;
1087            for el in dict.items() {
1088                let tuple = el.downcast::<PyTuple>().unwrap();
1089                key_sum += tuple.get_item(0).unwrap().extract::<i32>().unwrap();
1090                value_sum += tuple.get_item(1).unwrap().extract::<i32>().unwrap();
1091            }
1092            assert_eq!(7 + 8 + 9, key_sum);
1093            assert_eq!(32 + 42 + 123, value_sum);
1094        });
1095    }
1096
1097    #[test]
1098    fn test_keys() {
1099        Python::with_gil(|py| {
1100            let mut v = HashMap::new();
1101            v.insert(7, 32);
1102            v.insert(8, 42);
1103            v.insert(9, 123);
1104            let dict = v.into_pyobject(py).unwrap();
1105            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1106            let mut key_sum = 0;
1107            for el in dict.keys() {
1108                key_sum += el.extract::<i32>().unwrap();
1109            }
1110            assert_eq!(7 + 8 + 9, key_sum);
1111        });
1112    }
1113
1114    #[test]
1115    fn test_values() {
1116        Python::with_gil(|py| {
1117            let mut v = HashMap::new();
1118            v.insert(7, 32);
1119            v.insert(8, 42);
1120            v.insert(9, 123);
1121            let dict = v.into_pyobject(py).unwrap();
1122            // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
1123            let mut values_sum = 0;
1124            for el in dict.values() {
1125                values_sum += el.extract::<i32>().unwrap();
1126            }
1127            assert_eq!(32 + 42 + 123, values_sum);
1128        });
1129    }
1130
1131    #[test]
1132    fn test_iter() {
1133        Python::with_gil(|py| {
1134            let mut v = HashMap::new();
1135            v.insert(7, 32);
1136            v.insert(8, 42);
1137            v.insert(9, 123);
1138            let dict = v.into_pyobject(py).unwrap();
1139            let mut key_sum = 0;
1140            let mut value_sum = 0;
1141            for (key, value) in dict {
1142                key_sum += key.extract::<i32>().unwrap();
1143                value_sum += value.extract::<i32>().unwrap();
1144            }
1145            assert_eq!(7 + 8 + 9, key_sum);
1146            assert_eq!(32 + 42 + 123, value_sum);
1147        });
1148    }
1149
1150    #[test]
1151    fn test_iter_bound() {
1152        Python::with_gil(|py| {
1153            let mut v = HashMap::new();
1154            v.insert(7, 32);
1155            v.insert(8, 42);
1156            v.insert(9, 123);
1157            let dict = v.into_pyobject(py).unwrap();
1158            let mut key_sum = 0;
1159            let mut value_sum = 0;
1160            for (key, value) in dict {
1161                key_sum += key.extract::<i32>().unwrap();
1162                value_sum += value.extract::<i32>().unwrap();
1163            }
1164            assert_eq!(7 + 8 + 9, key_sum);
1165            assert_eq!(32 + 42 + 123, value_sum);
1166        });
1167    }
1168
1169    #[test]
1170    fn test_iter_value_mutated() {
1171        Python::with_gil(|py| {
1172            let mut v = HashMap::new();
1173            v.insert(7, 32);
1174            v.insert(8, 42);
1175            v.insert(9, 123);
1176
1177            let dict = (&v).into_pyobject(py).unwrap();
1178
1179            for (key, value) in &dict {
1180                dict.set_item(key, value.extract::<i32>().unwrap() + 7)
1181                    .unwrap();
1182            }
1183        });
1184    }
1185
1186    #[test]
1187    #[should_panic]
1188    fn test_iter_key_mutated() {
1189        Python::with_gil(|py| {
1190            let mut v = HashMap::new();
1191            for i in 0..10 {
1192                v.insert(i * 2, i * 2);
1193            }
1194            let dict = v.into_pyobject(py).unwrap();
1195
1196            for (i, (key, value)) in dict.iter().enumerate() {
1197                let key = key.extract::<i32>().unwrap();
1198                let value = value.extract::<i32>().unwrap();
1199
1200                dict.set_item(key + 1, value + 1).unwrap();
1201
1202                if i > 1000 {
1203                    // avoid this test just running out of memory if it fails
1204                    break;
1205                };
1206            }
1207        });
1208    }
1209
1210    #[test]
1211    #[should_panic]
1212    fn test_iter_key_mutated_constant_len() {
1213        Python::with_gil(|py| {
1214            let mut v = HashMap::new();
1215            for i in 0..10 {
1216                v.insert(i * 2, i * 2);
1217            }
1218            let dict = v.into_pyobject(py).unwrap();
1219
1220            for (i, (key, value)) in dict.iter().enumerate() {
1221                let key = key.extract::<i32>().unwrap();
1222                let value = value.extract::<i32>().unwrap();
1223                dict.del_item(key).unwrap();
1224                dict.set_item(key + 1, value + 1).unwrap();
1225
1226                if i > 1000 {
1227                    // avoid this test just running out of memory if it fails
1228                    break;
1229                };
1230            }
1231        });
1232    }
1233
1234    #[test]
1235    fn test_iter_size_hint() {
1236        Python::with_gil(|py| {
1237            let mut v = HashMap::new();
1238            v.insert(7, 32);
1239            v.insert(8, 42);
1240            v.insert(9, 123);
1241            let dict = (&v).into_pyobject(py).unwrap();
1242
1243            let mut iter = dict.iter();
1244            assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
1245            iter.next();
1246            assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
1247
1248            // Exhaust iterator.
1249            for _ in &mut iter {}
1250
1251            assert_eq!(iter.size_hint(), (0, Some(0)));
1252
1253            assert!(iter.next().is_none());
1254
1255            assert_eq!(iter.size_hint(), (0, Some(0)));
1256        });
1257    }
1258
1259    #[test]
1260    fn test_into_iter() {
1261        Python::with_gil(|py| {
1262            let mut v = HashMap::new();
1263            v.insert(7, 32);
1264            v.insert(8, 42);
1265            v.insert(9, 123);
1266            let dict = v.into_pyobject(py).unwrap();
1267            let mut key_sum = 0;
1268            let mut value_sum = 0;
1269            for (key, value) in dict {
1270                key_sum += key.extract::<i32>().unwrap();
1271                value_sum += value.extract::<i32>().unwrap();
1272            }
1273            assert_eq!(7 + 8 + 9, key_sum);
1274            assert_eq!(32 + 42 + 123, value_sum);
1275        });
1276    }
1277
1278    #[test]
1279    fn test_hashmap_into_dict() {
1280        Python::with_gil(|py| {
1281            let mut map = HashMap::<i32, i32>::new();
1282            map.insert(1, 1);
1283
1284            let py_map = map.into_py_dict(py).unwrap();
1285
1286            assert_eq!(py_map.len(), 1);
1287            assert_eq!(
1288                py_map
1289                    .get_item(1)
1290                    .unwrap()
1291                    .unwrap()
1292                    .extract::<i32>()
1293                    .unwrap(),
1294                1
1295            );
1296        });
1297    }
1298
1299    #[test]
1300    fn test_btreemap_into_dict() {
1301        Python::with_gil(|py| {
1302            let mut map = BTreeMap::<i32, i32>::new();
1303            map.insert(1, 1);
1304
1305            let py_map = map.into_py_dict(py).unwrap();
1306
1307            assert_eq!(py_map.len(), 1);
1308            assert_eq!(
1309                py_map
1310                    .get_item(1)
1311                    .unwrap()
1312                    .unwrap()
1313                    .extract::<i32>()
1314                    .unwrap(),
1315                1
1316            );
1317        });
1318    }
1319
1320    #[test]
1321    fn test_vec_into_dict() {
1322        Python::with_gil(|py| {
1323            let vec = vec![("a", 1), ("b", 2), ("c", 3)];
1324            let py_map = vec.into_py_dict(py).unwrap();
1325
1326            assert_eq!(py_map.len(), 3);
1327            assert_eq!(
1328                py_map
1329                    .get_item("b")
1330                    .unwrap()
1331                    .unwrap()
1332                    .extract::<i32>()
1333                    .unwrap(),
1334                2
1335            );
1336        });
1337    }
1338
1339    #[test]
1340    fn test_slice_into_dict() {
1341        Python::with_gil(|py| {
1342            let arr = [("a", 1), ("b", 2), ("c", 3)];
1343            let py_map = arr.into_py_dict(py).unwrap();
1344
1345            assert_eq!(py_map.len(), 3);
1346            assert_eq!(
1347                py_map
1348                    .get_item("b")
1349                    .unwrap()
1350                    .unwrap()
1351                    .extract::<i32>()
1352                    .unwrap(),
1353                2
1354            );
1355        });
1356    }
1357
1358    #[test]
1359    fn dict_as_mapping() {
1360        Python::with_gil(|py| {
1361            let mut map = HashMap::<i32, i32>::new();
1362            map.insert(1, 1);
1363
1364            let py_map = map.into_py_dict(py).unwrap();
1365
1366            assert_eq!(py_map.as_mapping().len().unwrap(), 1);
1367            assert_eq!(
1368                py_map
1369                    .as_mapping()
1370                    .get_item(1)
1371                    .unwrap()
1372                    .extract::<i32>()
1373                    .unwrap(),
1374                1
1375            );
1376        });
1377    }
1378
1379    #[test]
1380    fn dict_into_mapping() {
1381        Python::with_gil(|py| {
1382            let mut map = HashMap::<i32, i32>::new();
1383            map.insert(1, 1);
1384
1385            let py_map = map.into_py_dict(py).unwrap();
1386
1387            let py_mapping = py_map.into_mapping();
1388            assert_eq!(py_mapping.len().unwrap(), 1);
1389            assert_eq!(py_mapping.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
1390        });
1391    }
1392
1393    #[cfg(not(any(PyPy, GraalPy)))]
1394    fn abc_dict(py: Python<'_>) -> Bound<'_, PyDict> {
1395        let mut map = HashMap::<&'static str, i32>::new();
1396        map.insert("a", 1);
1397        map.insert("b", 2);
1398        map.insert("c", 3);
1399        map.into_py_dict(py).unwrap()
1400    }
1401
1402    #[test]
1403    #[cfg(not(any(PyPy, GraalPy)))]
1404    fn dict_keys_view() {
1405        Python::with_gil(|py| {
1406            let dict = abc_dict(py);
1407            let keys = dict.call_method0("keys").unwrap();
1408            assert!(keys.is_instance(&py.get_type::<PyDictKeys>()).unwrap());
1409        })
1410    }
1411
1412    #[test]
1413    #[cfg(not(any(PyPy, GraalPy)))]
1414    fn dict_values_view() {
1415        Python::with_gil(|py| {
1416            let dict = abc_dict(py);
1417            let values = dict.call_method0("values").unwrap();
1418            assert!(values.is_instance(&py.get_type::<PyDictValues>()).unwrap());
1419        })
1420    }
1421
1422    #[test]
1423    #[cfg(not(any(PyPy, GraalPy)))]
1424    fn dict_items_view() {
1425        Python::with_gil(|py| {
1426            let dict = abc_dict(py);
1427            let items = dict.call_method0("items").unwrap();
1428            assert!(items.is_instance(&py.get_type::<PyDictItems>()).unwrap());
1429        })
1430    }
1431
1432    #[test]
1433    fn dict_update() {
1434        Python::with_gil(|py| {
1435            let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1436            let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1437            dict.update(other.as_mapping()).unwrap();
1438            assert_eq!(dict.len(), 4);
1439            assert_eq!(
1440                dict.get_item("a")
1441                    .unwrap()
1442                    .unwrap()
1443                    .extract::<i32>()
1444                    .unwrap(),
1445                1
1446            );
1447            assert_eq!(
1448                dict.get_item("b")
1449                    .unwrap()
1450                    .unwrap()
1451                    .extract::<i32>()
1452                    .unwrap(),
1453                4
1454            );
1455            assert_eq!(
1456                dict.get_item("c")
1457                    .unwrap()
1458                    .unwrap()
1459                    .extract::<i32>()
1460                    .unwrap(),
1461                5
1462            );
1463            assert_eq!(
1464                dict.get_item("d")
1465                    .unwrap()
1466                    .unwrap()
1467                    .extract::<i32>()
1468                    .unwrap(),
1469                6
1470            );
1471
1472            assert_eq!(other.len(), 3);
1473            assert_eq!(
1474                other
1475                    .get_item("b")
1476                    .unwrap()
1477                    .unwrap()
1478                    .extract::<i32>()
1479                    .unwrap(),
1480                4
1481            );
1482            assert_eq!(
1483                other
1484                    .get_item("c")
1485                    .unwrap()
1486                    .unwrap()
1487                    .extract::<i32>()
1488                    .unwrap(),
1489                5
1490            );
1491            assert_eq!(
1492                other
1493                    .get_item("d")
1494                    .unwrap()
1495                    .unwrap()
1496                    .extract::<i32>()
1497                    .unwrap(),
1498                6
1499            );
1500        })
1501    }
1502
1503    #[test]
1504    fn dict_update_if_missing() {
1505        Python::with_gil(|py| {
1506            let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py).unwrap();
1507            let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py).unwrap();
1508            dict.update_if_missing(other.as_mapping()).unwrap();
1509            assert_eq!(dict.len(), 4);
1510            assert_eq!(
1511                dict.get_item("a")
1512                    .unwrap()
1513                    .unwrap()
1514                    .extract::<i32>()
1515                    .unwrap(),
1516                1
1517            );
1518            assert_eq!(
1519                dict.get_item("b")
1520                    .unwrap()
1521                    .unwrap()
1522                    .extract::<i32>()
1523                    .unwrap(),
1524                2
1525            );
1526            assert_eq!(
1527                dict.get_item("c")
1528                    .unwrap()
1529                    .unwrap()
1530                    .extract::<i32>()
1531                    .unwrap(),
1532                3
1533            );
1534            assert_eq!(
1535                dict.get_item("d")
1536                    .unwrap()
1537                    .unwrap()
1538                    .extract::<i32>()
1539                    .unwrap(),
1540                6
1541            );
1542
1543            assert_eq!(other.len(), 3);
1544            assert_eq!(
1545                other
1546                    .get_item("b")
1547                    .unwrap()
1548                    .unwrap()
1549                    .extract::<i32>()
1550                    .unwrap(),
1551                4
1552            );
1553            assert_eq!(
1554                other
1555                    .get_item("c")
1556                    .unwrap()
1557                    .unwrap()
1558                    .extract::<i32>()
1559                    .unwrap(),
1560                5
1561            );
1562            assert_eq!(
1563                other
1564                    .get_item("d")
1565                    .unwrap()
1566                    .unwrap()
1567                    .extract::<i32>()
1568                    .unwrap(),
1569                6
1570            );
1571        })
1572    }
1573
1574    #[test]
1575    fn test_iter_all() {
1576        Python::with_gil(|py| {
1577            let dict = [(1, true), (2, true), (3, true)].into_py_dict(py).unwrap();
1578            assert!(dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1579
1580            let dict = [(1, true), (2, false), (3, true)].into_py_dict(py).unwrap();
1581            assert!(!dict.iter().all(|(_, v)| v.extract::<bool>().unwrap()));
1582        });
1583    }
1584
1585    #[test]
1586    fn test_iter_any() {
1587        Python::with_gil(|py| {
1588            let dict = [(1, true), (2, false), (3, false)]
1589                .into_py_dict(py)
1590                .unwrap();
1591            assert!(dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1592
1593            let dict = [(1, false), (2, false), (3, false)]
1594                .into_py_dict(py)
1595                .unwrap();
1596            assert!(!dict.iter().any(|(_, v)| v.extract::<bool>().unwrap()));
1597        });
1598    }
1599
1600    #[test]
1601    #[allow(clippy::search_is_some)]
1602    fn test_iter_find() {
1603        Python::with_gil(|py| {
1604            let dict = [(1, false), (2, true), (3, false)]
1605                .into_py_dict(py)
1606                .unwrap();
1607
1608            assert_eq!(
1609                Some((2, true)),
1610                dict.iter()
1611                    .find(|(_, v)| v.extract::<bool>().unwrap())
1612                    .map(|(k, v)| (k.extract().unwrap(), v.extract().unwrap()))
1613            );
1614
1615            let dict = [(1, false), (2, false), (3, false)]
1616                .into_py_dict(py)
1617                .unwrap();
1618
1619            assert!(dict
1620                .iter()
1621                .find(|(_, v)| v.extract::<bool>().unwrap())
1622                .is_none());
1623        });
1624    }
1625
1626    #[test]
1627    #[allow(clippy::search_is_some)]
1628    fn test_iter_position() {
1629        Python::with_gil(|py| {
1630            let dict = [(1, false), (2, false), (3, true)]
1631                .into_py_dict(py)
1632                .unwrap();
1633            assert_eq!(
1634                Some(2),
1635                dict.iter().position(|(_, v)| v.extract::<bool>().unwrap())
1636            );
1637
1638            let dict = [(1, false), (2, false), (3, false)]
1639                .into_py_dict(py)
1640                .unwrap();
1641            assert!(dict
1642                .iter()
1643                .position(|(_, v)| v.extract::<bool>().unwrap())
1644                .is_none());
1645        });
1646    }
1647
1648    #[test]
1649    fn test_iter_fold() {
1650        Python::with_gil(|py| {
1651            let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1652            let sum = dict
1653                .iter()
1654                .fold(0, |acc, (_, v)| acc + v.extract::<i32>().unwrap());
1655            assert_eq!(sum, 6);
1656        });
1657    }
1658
1659    #[test]
1660    fn test_iter_try_fold() {
1661        Python::with_gil(|py| {
1662            let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1663            let sum = dict
1664                .iter()
1665                .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1666                .unwrap();
1667            assert_eq!(sum, 6);
1668
1669            let dict = [(1, "foo"), (2, "bar")].into_py_dict(py).unwrap();
1670            assert!(dict
1671                .iter()
1672                .try_fold(0, |acc, (_, v)| PyResult::Ok(acc + v.extract::<i32>()?))
1673                .is_err());
1674        });
1675    }
1676
1677    #[test]
1678    fn test_iter_count() {
1679        Python::with_gil(|py| {
1680            let dict = [(1, 1), (2, 2), (3, 3)].into_py_dict(py).unwrap();
1681            assert_eq!(dict.iter().count(), 3);
1682        })
1683    }
1684}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here