Skip to main content

pyo3/types/
dict.rs

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