1use super::PyMapping;
2use crate::err::{self, PyErr, PyResult};
3use crate::ffi::Py_ssize_t;
4use crate::types::{PyAny, PyList};
5use crate::{ffi, PyObject, Python, ToPyObject};
6
7/// Represents a Python `dict`.
8#[repr(transparent)]
9pub struct PyDict(PyAny);
10
11pyobject_native_type!(
12 PyDict,
13 ffi::PyDictObject,
14 pyobject_native_static_type_object!(ffi::PyDict_Type),
15 #checkfunction=ffi::PyDict_Check
16);
17
18/// Represents a Python `dict_keys`.
19#[cfg(not(PyPy))]
20#[repr(transparent)]
21pub struct PyDictKeys(PyAny);
22
23#[cfg(not(PyPy))]
24pyobject_native_type_core!(
25 PyDictKeys,
26 pyobject_native_static_type_object!(ffi::PyDictKeys_Type),
27 #checkfunction=ffi::PyDictKeys_Check
28);
29
30/// Represents a Python `dict_values`.
31#[cfg(not(PyPy))]
32#[repr(transparent)]
33pub struct PyDictValues(PyAny);
34
35#[cfg(not(PyPy))]
36pyobject_native_type_core!(
37 PyDictValues,
38 pyobject_native_static_type_object!(ffi::PyDictValues_Type),
39 #checkfunction=ffi::PyDictValues_Check
40);
41
42/// Represents a Python `dict_items`.
43#[cfg(not(PyPy))]
44#[repr(transparent)]
45pub struct PyDictItems(PyAny);
46
47#[cfg(not(PyPy))]
48pyobject_native_type_core!(
49 PyDictItems,
50 pyobject_native_static_type_object!(ffi::PyDictItems_Type),
51 #checkfunction=ffi::PyDictItems_Check
52);
53
54impl PyDict {
55 /// Creates a new empty dictionary.
56 pub fn new(py: Python<'_>) -> &PyDict {
57 unsafe { py.from_owned_ptr::<PyDict>(ffi::PyDict_New()) }
58 }
59
60 /// Creates a new dictionary from the sequence given.
61 ///
62 /// The sequence must consist of `(PyObject, PyObject)`. This is
63 /// equivalent to `dict([("a", 1), ("b", 2)])`.
64 ///
65 /// Returns an error on invalid input. In the case of key collisions,
66 /// this keeps the last entry seen.
67 #[cfg(not(PyPy))]
68 pub fn from_sequence(py: Python<'_>, seq: PyObject) -> PyResult<&PyDict> {
69 let dict = Self::new(py);
70 err::error_on_minusone(py, unsafe {
71 ffi::PyDict_MergeFromSeq2(dict.into_ptr(), seq.into_ptr(), 1)
72 })?;
73 Ok(dict)
74 }
75
76 /// Returns a new dictionary that contains the same key-value pairs as self.
77 ///
78 /// This is equivalent to the Python expression `self.copy()`.
79 pub fn copy(&self) -> PyResult<&PyDict> {
80 unsafe {
81 self.py()
82 .from_owned_ptr_or_err::<PyDict>(ffi::PyDict_Copy(self.as_ptr()))
83 }
84 }
85
86 /// Empties an existing dictionary of all key-value pairs.
87 pub fn clear(&self) {
88 unsafe { ffi::PyDict_Clear(self.as_ptr()) }
89 }
90
91 /// Return the number of items in the dictionary.
92 ///
93 /// This is equivalent to the Python expression `len(self)`.
94 pub fn len(&self) -> usize {
95 self._len() as usize
96 }
97
98 fn _len(&self) -> Py_ssize_t {
99 #[cfg(any(not(Py_3_8), PyPy, Py_LIMITED_API))]
100 unsafe {
101 ffi::PyDict_Size(self.as_ptr())
102 }
103
104 #[cfg(all(Py_3_8, not(PyPy), not(Py_LIMITED_API)))]
105 unsafe {
106 (*self.as_ptr().cast::<ffi::PyDictObject>()).ma_used
107 }
108 }
109
110 /// Checks if the dict is empty, i.e. `len(self) == 0`.
111 pub fn is_empty(&self) -> bool {
112 self.len() == 0
113 }
114
115 /// Determines if the dictionary contains the specified key.
116 ///
117 /// This is equivalent to the Python expression `key in self`.
118 pub fn contains<K>(&self, key: K) -> PyResult<bool>
119 where
120 K: ToPyObject,
121 {
122 fn inner(dict: &PyDict, key: PyObject) -> PyResult<bool> {
123 match unsafe { ffi::PyDict_Contains(dict.as_ptr(), key.as_ptr()) } {
124 1 => Ok(true),
125 0 => Ok(false),
126 _ => Err(PyErr::fetch(dict.py())),
127 }
128 }
129
130 inner(self, key.to_object(self.py()))
131 }
132
133 /// Gets an item from the dictionary.
134 ///
135 /// Returns `Ok(None)` if the item is not present. To get a `KeyError` for
136 /// non-existing keys, use [`PyAny::get_item`].
137 ///
138 /// Returns `Err(PyErr)` if Python magic methods `__hash__` or `__eq__` used in dictionary
139 /// lookup raise an exception, for example if the key `K` is not hashable. Usually it is
140 /// best to bubble this error up to the caller using the `?` operator.
141 ///
142 /// # Examples
143 ///
144 /// The following example calls `get_item` for the dictionary `{"a": 1}` with various
145 /// keys.
146 /// - `get_item("a")` returns `Ok(Some(...))`, with the `PyAny` being a reference to the Python
147 /// int `1`.
148 /// - `get_item("b")` returns `Ok(None)`, because "b" is not in the dictionary.
149 /// - `get_item(dict)` returns an `Err(PyErr)`. The error will be a `TypeError` because a dict is not
150 /// hashable.
151 ///
152 /// ```rust
153 /// use pyo3::prelude::*;
154 /// use pyo3::types::{PyDict, IntoPyDict};
155 /// use pyo3::exceptions::{PyTypeError, PyKeyError};
156 ///
157 /// # fn main() {
158 /// # let _ =
159 /// Python::with_gil(|py| -> PyResult<()> {
160 /// let dict: &PyDict = [("a", 1)].into_py_dict(py);
161 /// // `a` is in the dictionary, with value 1
162 /// assert!(dict.get_item("a")?.map_or(Ok(false), |x| x.eq(1))?);
163 /// // `b` is not in the dictionary
164 /// assert!(dict.get_item("b")?.is_none());
165 /// // `dict` is not hashable, so this returns an error
166 /// assert!(dict.get_item(dict).unwrap_err().is_instance_of::<PyTypeError>(py));
167 ///
168 /// // `PyAny::get_item("b")` will raise a `KeyError` instead of returning `None`
169 /// let any: &PyAny = dict.as_ref();
170 /// assert!(any.get_item("b").unwrap_err().is_instance_of::<PyKeyError>(py));
171 /// Ok(())
172 /// });
173 /// # }
174 /// ```
175 pub fn get_item<K>(&self, key: K) -> PyResult<Option<&PyAny>>
176 where
177 K: ToPyObject,
178 {
179 fn inner(dict: &PyDict, key: PyObject) -> PyResult<Option<&PyAny>> {
180 let py = dict.py();
181 // PyDict_GetItemWithError returns a borrowed ptr, must make it owned for safety (see #890).
182 // PyObject::from_borrowed_ptr_or_opt will take ownership in this way.
183 unsafe {
184 PyObject::from_borrowed_ptr_or_opt(
185 py,
186 ffi::PyDict_GetItemWithError(dict.as_ptr(), key.as_ptr()),
187 )
188 }
189 .map(|pyobject| Ok(pyobject.into_ref(py)))
190 .or_else(|| PyErr::take(py).map(Err))
191 .transpose()
192 }
193
194 inner(self, key.to_object(self.py()))
195 }
196
197 /// Deprecated version of `get_item`.
198 #[deprecated(
199 since = "0.20.0",
200 note = "this is now equivalent to `PyDict::get_item`"
201 )]
202 #[inline]
203 pub fn get_item_with_error<K>(&self, key: K) -> PyResult<Option<&PyAny>>
204 where
205 K: ToPyObject,
206 {
207 self.get_item(key)
208 }
209
210 /// Sets an item value.
211 ///
212 /// This is equivalent to the Python statement `self[key] = value`.
213 pub fn set_item<K, V>(&self, key: K, value: V) -> PyResult<()>
214 where
215 K: ToPyObject,
216 V: ToPyObject,
217 {
218 fn inner(dict: &PyDict, key: PyObject, value: PyObject) -> PyResult<()> {
219 err::error_on_minusone(dict.py(), unsafe {
220 ffi::PyDict_SetItem(dict.as_ptr(), key.as_ptr(), value.as_ptr())
221 })
222 }
223
224 let py = self.py();
225 inner(self, key.to_object(py), value.to_object(py))
226 }
227
228 /// Deletes an item.
229 ///
230 /// This is equivalent to the Python statement `del self[key]`.
231 pub fn del_item<K>(&self, key: K) -> PyResult<()>
232 where
233 K: ToPyObject,
234 {
235 fn inner(dict: &PyDict, key: PyObject) -> PyResult<()> {
236 err::error_on_minusone(dict.py(), unsafe {
237 ffi::PyDict_DelItem(dict.as_ptr(), key.as_ptr())
238 })
239 }
240
241 inner(self, key.to_object(self.py()))
242 }
243
244 /// Returns a list of dict keys.
245 ///
246 /// This is equivalent to the Python expression `list(dict.keys())`.
247 pub fn keys(&self) -> &PyList {
248 unsafe {
249 self.py()
250 .from_owned_ptr::<PyList>(ffi::PyDict_Keys(self.as_ptr()))
251 }
252 }
253
254 /// Returns a list of dict values.
255 ///
256 /// This is equivalent to the Python expression `list(dict.values())`.
257 pub fn values(&self) -> &PyList {
258 unsafe {
259 self.py()
260 .from_owned_ptr::<PyList>(ffi::PyDict_Values(self.as_ptr()))
261 }
262 }
263
264 /// Returns a list of dict items.
265 ///
266 /// This is equivalent to the Python expression `list(dict.items())`.
267 pub fn items(&self) -> &PyList {
268 unsafe {
269 self.py()
270 .from_owned_ptr::<PyList>(ffi::PyDict_Items(self.as_ptr()))
271 }
272 }
273
274 /// Returns an iterator of `(key, value)` pairs in this dictionary.
275 ///
276 /// # Panics
277 ///
278 /// If PyO3 detects that the dictionary is mutated during iteration, it will panic.
279 /// It is allowed to modify values as you iterate over the dictionary, but only
280 /// so long as the set of keys does not change.
281 pub fn iter(&self) -> PyDictIterator<'_> {
282 IntoIterator::into_iter(self)
283 }
284
285 /// Returns `self` cast as a `PyMapping`.
286 pub fn as_mapping(&self) -> &PyMapping {
287 unsafe { self.downcast_unchecked() }
288 }
289
290 /// Update this dictionary with the key/value pairs from another.
291 ///
292 /// This is equivalent to the Python expression `self.update(other)`. If `other` is a `PyDict`, you may want
293 /// to use `self.update(other.as_mapping())`, note: `PyDict::as_mapping` is a zero-cost conversion.
294 pub fn update(&self, other: &PyMapping) -> PyResult<()> {
295 let py = self.py();
296 err::error_on_minusone(py, unsafe {
297 ffi::PyDict_Update(self.as_ptr(), other.as_ptr())
298 })
299 }
300
301 /// Add key/value pairs from another dictionary to this one only when they do not exist in this.
302 ///
303 /// This is equivalent to the Python expression `self.update({k: v for k, v in other.items() if k not in self})`.
304 /// If `other` is a `PyDict`, you may want to use `self.update_if_missing(other.as_mapping())`,
305 /// note: `PyDict::as_mapping` is a zero-cost conversion.
306 ///
307 /// This method uses [`PyDict_Merge`](https://docs.python.org/3/c-api/dict.html#c.PyDict_Merge) internally,
308 /// so should have the same performance as `update`.
309 pub fn update_if_missing(&self, other: &PyMapping) -> PyResult<()> {
310 let py = self.py();
311 err::error_on_minusone(py, unsafe {
312 ffi::PyDict_Merge(self.as_ptr(), other.as_ptr(), 0)
313 })
314 }
315}
316
317/// PyO3 implementation of an iterator for a Python `dict` object.
318pub struct PyDictIterator<'py> {
319 dict: &'py PyDict,
320 ppos: ffi::Py_ssize_t,
321 di_used: ffi::Py_ssize_t,
322 len: ffi::Py_ssize_t,
323}
324
325impl<'py> Iterator for PyDictIterator<'py> {
326 type Item = (&'py PyAny, &'py PyAny);
327
328 #[inline]
329 fn next(&mut self) -> Option<Self::Item> {
330 let ma_used = self.dict._len();
331
332 // These checks are similar to what CPython does.
333 //
334 // If the dimension of the dict changes e.g. key-value pairs are removed
335 // or added during iteration, this will panic next time when `next` is called
336 if self.di_used != ma_used {
337 self.di_used = -1;
338 panic!("dictionary changed size during iteration");
339 };
340
341 // If the dict is changed in such a way that the length remains constant
342 // then this will panic at the end of iteration - similar to this:
343 //
344 // d = {"a":1, "b":2, "c": 3}
345 //
346 // for k, v in d.items():
347 // d[f"{k}_"] = 4
348 // del d[k]
349 // print(k)
350 //
351 if self.len == -1 {
352 self.di_used = -1;
353 panic!("dictionary keys changed during iteration");
354 };
355
356 let ret = unsafe { self.next_unchecked() };
357 if ret.is_some() {
358 self.len -= 1
359 }
360 ret
361 }
362
363 #[inline]
364 fn size_hint(&self) -> (usize, Option<usize>) {
365 let len = self.len();
366 (len, Some(len))
367 }
368}
369
370impl<'py> ExactSizeIterator for PyDictIterator<'py> {
371 fn len(&self) -> usize {
372 self.len as usize
373 }
374}
375
376impl<'a> std::iter::IntoIterator for &'a PyDict {
377 type Item = (&'a PyAny, &'a PyAny);
378 type IntoIter = PyDictIterator<'a>;
379
380 fn into_iter(self) -> Self::IntoIter {
381 PyDictIterator {
382 dict: self,
383 ppos: 0,
384 di_used: self._len(),
385 len: self._len(),
386 }
387 }
388}
389
390impl<'py> PyDictIterator<'py> {
391 /// Advances the iterator without checking for concurrent modification.
392 ///
393 /// See [`PyDict_Next`](https://docs.python.org/3/c-api/dict.html#c.PyDict_Next)
394 /// for more information.
395 unsafe fn next_unchecked(&mut self) -> Option<(&'py PyAny, &'py PyAny)> {
396 let mut key: *mut ffi::PyObject = std::ptr::null_mut();
397 let mut value: *mut ffi::PyObject = std::ptr::null_mut();
398
399 if ffi::PyDict_Next(self.dict.as_ptr(), &mut self.ppos, &mut key, &mut value) != 0 {
400 let py: Python<'_> = self.dict.py();
401 // PyDict_Next returns borrowed values; for safety must make them owned (see #890)
402 Some((
403 py.from_owned_ptr(ffi::_Py_NewRef(obj:key)),
404 py.from_owned_ptr(ffi::_Py_NewRef(obj:value)),
405 ))
406 } else {
407 None
408 }
409 }
410}
411
412/// Conversion trait that allows a sequence of tuples to be converted into `PyDict`
413/// Primary use case for this trait is `call` and `call_method` methods as keywords argument.
414pub trait IntoPyDict {
415 /// Converts self into a `PyDict` object pointer. Whether pointer owned or borrowed
416 /// depends on implementation.
417 fn into_py_dict(self, py: Python<'_>) -> &PyDict;
418}
419
420impl<T, I> IntoPyDict for I
421where
422 T: PyDictItem,
423 I: IntoIterator<Item = T>,
424{
425 fn into_py_dict(self, py: Python<'_>) -> &PyDict {
426 let dict: &PyDict = PyDict::new(py);
427 for item: T in self {
428 dict.set_item(item.key(), item.value())
429 .expect(msg:"Failed to set_item on dict");
430 }
431 dict
432 }
433}
434
435/// Represents a tuple which can be used as a PyDict item.
436pub trait PyDictItem {
437 type K: ToPyObject;
438 type V: ToPyObject;
439 fn key(&self) -> &Self::K;
440 fn value(&self) -> &Self::V;
441}
442
443impl<K, V> PyDictItem for (K, V)
444where
445 K: ToPyObject,
446 V: ToPyObject,
447{
448 type K = K;
449 type V = V;
450 fn key(&self) -> &Self::K {
451 &self.0
452 }
453 fn value(&self) -> &Self::V {
454 &self.1
455 }
456}
457
458impl<K, V> PyDictItem for &(K, V)
459where
460 K: ToPyObject,
461 V: ToPyObject,
462{
463 type K = K;
464 type V = V;
465 fn key(&self) -> &Self::K {
466 &self.0
467 }
468 fn value(&self) -> &Self::V {
469 &self.1
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 #[cfg(not(PyPy))]
477 use crate::exceptions;
478 #[cfg(not(PyPy))]
479 use crate::types::PyList;
480 use crate::{types::PyTuple, Python, ToPyObject};
481 use std::collections::{BTreeMap, HashMap};
482
483 #[test]
484 fn test_new() {
485 Python::with_gil(|py| {
486 let dict = [(7, 32)].into_py_dict(py);
487 assert_eq!(
488 32,
489 dict.get_item(7i32)
490 .unwrap()
491 .unwrap()
492 .extract::<i32>()
493 .unwrap()
494 );
495 assert!(dict.get_item(8i32).unwrap().is_none());
496 let map: HashMap<i32, i32> = [(7, 32)].iter().cloned().collect();
497 assert_eq!(map, dict.extract().unwrap());
498 let map: BTreeMap<i32, i32> = [(7, 32)].iter().cloned().collect();
499 assert_eq!(map, dict.extract().unwrap());
500 });
501 }
502
503 #[test]
504 #[cfg(not(PyPy))]
505 fn test_from_sequence() {
506 Python::with_gil(|py| {
507 let items = PyList::new(py, &vec![("a", 1), ("b", 2)]);
508 let dict = PyDict::from_sequence(py, items.to_object(py)).unwrap();
509 assert_eq!(
510 1,
511 dict.get_item("a")
512 .unwrap()
513 .unwrap()
514 .extract::<i32>()
515 .unwrap()
516 );
517 assert_eq!(
518 2,
519 dict.get_item("b")
520 .unwrap()
521 .unwrap()
522 .extract::<i32>()
523 .unwrap()
524 );
525 let map: HashMap<&str, i32> = [("a", 1), ("b", 2)].iter().cloned().collect();
526 assert_eq!(map, dict.extract().unwrap());
527 let map: BTreeMap<&str, i32> = [("a", 1), ("b", 2)].iter().cloned().collect();
528 assert_eq!(map, dict.extract().unwrap());
529 });
530 }
531
532 #[test]
533 #[cfg(not(PyPy))]
534 fn test_from_sequence_err() {
535 Python::with_gil(|py| {
536 let items = PyList::new(py, &vec!["a", "b"]);
537 assert!(PyDict::from_sequence(py, items.to_object(py)).is_err());
538 });
539 }
540
541 #[test]
542 fn test_copy() {
543 Python::with_gil(|py| {
544 let dict = [(7, 32)].into_py_dict(py);
545
546 let ndict = dict.copy().unwrap();
547 assert_eq!(
548 32,
549 ndict
550 .get_item(7i32)
551 .unwrap()
552 .unwrap()
553 .extract::<i32>()
554 .unwrap()
555 );
556 assert!(ndict.get_item(8i32).unwrap().is_none());
557 });
558 }
559
560 #[test]
561 fn test_len() {
562 Python::with_gil(|py| {
563 let mut v = HashMap::new();
564 let ob = v.to_object(py);
565 let dict: &PyDict = ob.downcast(py).unwrap();
566 assert_eq!(0, dict.len());
567 v.insert(7, 32);
568 let ob = v.to_object(py);
569 let dict2: &PyDict = ob.downcast(py).unwrap();
570 assert_eq!(1, dict2.len());
571 });
572 }
573
574 #[test]
575 fn test_contains() {
576 Python::with_gil(|py| {
577 let mut v = HashMap::new();
578 v.insert(7, 32);
579 let ob = v.to_object(py);
580 let dict: &PyDict = ob.downcast(py).unwrap();
581 assert!(dict.contains(7i32).unwrap());
582 assert!(!dict.contains(8i32).unwrap());
583 });
584 }
585
586 #[test]
587 fn test_get_item() {
588 Python::with_gil(|py| {
589 let mut v = HashMap::new();
590 v.insert(7, 32);
591 let ob = v.to_object(py);
592 let dict: &PyDict = ob.downcast(py).unwrap();
593 assert_eq!(
594 32,
595 dict.get_item(7i32)
596 .unwrap()
597 .unwrap()
598 .extract::<i32>()
599 .unwrap()
600 );
601 assert!(dict.get_item(8i32).unwrap().is_none());
602 });
603 }
604
605 #[test]
606 #[allow(deprecated)]
607 #[cfg(not(PyPy))]
608 fn test_get_item_with_error() {
609 Python::with_gil(|py| {
610 let mut v = HashMap::new();
611 v.insert(7, 32);
612 let ob = v.to_object(py);
613 let dict: &PyDict = ob.downcast(py).unwrap();
614 assert_eq!(
615 32,
616 dict.get_item_with_error(7i32)
617 .unwrap()
618 .unwrap()
619 .extract::<i32>()
620 .unwrap()
621 );
622 assert!(dict.get_item_with_error(8i32).unwrap().is_none());
623 assert!(dict
624 .get_item_with_error(dict)
625 .unwrap_err()
626 .is_instance_of::<exceptions::PyTypeError>(py));
627 });
628 }
629
630 #[test]
631 fn test_set_item() {
632 Python::with_gil(|py| {
633 let mut v = HashMap::new();
634 v.insert(7, 32);
635 let ob = v.to_object(py);
636 let dict: &PyDict = ob.downcast(py).unwrap();
637 assert!(dict.set_item(7i32, 42i32).is_ok()); // change
638 assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
639 assert_eq!(
640 42i32,
641 dict.get_item(7i32)
642 .unwrap()
643 .unwrap()
644 .extract::<i32>()
645 .unwrap()
646 );
647 assert_eq!(
648 123i32,
649 dict.get_item(8i32)
650 .unwrap()
651 .unwrap()
652 .extract::<i32>()
653 .unwrap()
654 );
655 });
656 }
657
658 #[test]
659 fn test_set_item_refcnt() {
660 Python::with_gil(|py| {
661 let cnt;
662 let obj = py.eval("object()", None, None).unwrap();
663 {
664 let _pool = unsafe { crate::GILPool::new() };
665 cnt = obj.get_refcnt();
666 let _dict = [(10, obj)].into_py_dict(py);
667 }
668 {
669 assert_eq!(cnt, obj.get_refcnt());
670 }
671 });
672 }
673
674 #[test]
675 fn test_set_item_does_not_update_original_object() {
676 Python::with_gil(|py| {
677 let mut v = HashMap::new();
678 v.insert(7, 32);
679 let ob = v.to_object(py);
680 let dict: &PyDict = ob.downcast(py).unwrap();
681 assert!(dict.set_item(7i32, 42i32).is_ok()); // change
682 assert!(dict.set_item(8i32, 123i32).is_ok()); // insert
683 assert_eq!(32i32, v[&7i32]); // not updated!
684 assert_eq!(None, v.get(&8i32));
685 });
686 }
687
688 #[test]
689 fn test_del_item() {
690 Python::with_gil(|py| {
691 let mut v = HashMap::new();
692 v.insert(7, 32);
693 let ob = v.to_object(py);
694 let dict: &PyDict = ob.downcast(py).unwrap();
695 assert!(dict.del_item(7i32).is_ok());
696 assert_eq!(0, dict.len());
697 assert!(dict.get_item(7i32).unwrap().is_none());
698 });
699 }
700
701 #[test]
702 fn test_del_item_does_not_update_original_object() {
703 Python::with_gil(|py| {
704 let mut v = HashMap::new();
705 v.insert(7, 32);
706 let ob = v.to_object(py);
707 let dict: &PyDict = ob.downcast(py).unwrap();
708 assert!(dict.del_item(7i32).is_ok()); // change
709 assert_eq!(32i32, *v.get(&7i32).unwrap()); // not updated!
710 });
711 }
712
713 #[test]
714 fn test_items() {
715 Python::with_gil(|py| {
716 let mut v = HashMap::new();
717 v.insert(7, 32);
718 v.insert(8, 42);
719 v.insert(9, 123);
720 let ob = v.to_object(py);
721 let dict: &PyDict = ob.downcast(py).unwrap();
722 // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
723 let mut key_sum = 0;
724 let mut value_sum = 0;
725 for el in dict.items() {
726 let tuple = el.downcast::<PyTuple>().unwrap();
727 key_sum += tuple.get_item(0).unwrap().extract::<i32>().unwrap();
728 value_sum += tuple.get_item(1).unwrap().extract::<i32>().unwrap();
729 }
730 assert_eq!(7 + 8 + 9, key_sum);
731 assert_eq!(32 + 42 + 123, value_sum);
732 });
733 }
734
735 #[test]
736 fn test_keys() {
737 Python::with_gil(|py| {
738 let mut v = HashMap::new();
739 v.insert(7, 32);
740 v.insert(8, 42);
741 v.insert(9, 123);
742 let ob = v.to_object(py);
743 let dict: &PyDict = ob.downcast(py).unwrap();
744 // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
745 let mut key_sum = 0;
746 for el in dict.keys() {
747 key_sum += el.extract::<i32>().unwrap();
748 }
749 assert_eq!(7 + 8 + 9, key_sum);
750 });
751 }
752
753 #[test]
754 fn test_values() {
755 Python::with_gil(|py| {
756 let mut v = HashMap::new();
757 v.insert(7, 32);
758 v.insert(8, 42);
759 v.insert(9, 123);
760 let ob = v.to_object(py);
761 let dict: &PyDict = ob.downcast(py).unwrap();
762 // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.
763 let mut values_sum = 0;
764 for el in dict.values() {
765 values_sum += el.extract::<i32>().unwrap();
766 }
767 assert_eq!(32 + 42 + 123, values_sum);
768 });
769 }
770
771 #[test]
772 fn test_iter() {
773 Python::with_gil(|py| {
774 let mut v = HashMap::new();
775 v.insert(7, 32);
776 v.insert(8, 42);
777 v.insert(9, 123);
778 let ob = v.to_object(py);
779 let dict: &PyDict = ob.downcast(py).unwrap();
780 let mut key_sum = 0;
781 let mut value_sum = 0;
782 for (key, value) in dict {
783 key_sum += key.extract::<i32>().unwrap();
784 value_sum += value.extract::<i32>().unwrap();
785 }
786 assert_eq!(7 + 8 + 9, key_sum);
787 assert_eq!(32 + 42 + 123, value_sum);
788 });
789 }
790
791 #[test]
792 fn test_iter_value_mutated() {
793 Python::with_gil(|py| {
794 let mut v = HashMap::new();
795 v.insert(7, 32);
796 v.insert(8, 42);
797 v.insert(9, 123);
798
799 let ob = v.to_object(py);
800 let dict: &PyDict = ob.downcast(py).unwrap();
801
802 for (key, value) in dict {
803 dict.set_item(key, value.extract::<i32>().unwrap() + 7)
804 .unwrap();
805 }
806 });
807 }
808
809 #[test]
810 #[should_panic]
811 fn test_iter_key_mutated() {
812 Python::with_gil(|py| {
813 let mut v = HashMap::new();
814 for i in 0..10 {
815 v.insert(i * 2, i * 2);
816 }
817 let ob = v.to_object(py);
818 let dict: &PyDict = ob.downcast(py).unwrap();
819
820 for (i, (key, value)) in dict.iter().enumerate() {
821 let key = key.extract::<i32>().unwrap();
822 let value = value.extract::<i32>().unwrap();
823
824 dict.set_item(key + 1, value + 1).unwrap();
825
826 if i > 1000 {
827 // avoid this test just running out of memory if it fails
828 break;
829 };
830 }
831 });
832 }
833
834 #[test]
835 #[should_panic]
836 fn test_iter_key_mutated_constant_len() {
837 Python::with_gil(|py| {
838 let mut v = HashMap::new();
839 for i in 0..10 {
840 v.insert(i * 2, i * 2);
841 }
842 let ob = v.to_object(py);
843 let dict: &PyDict = ob.downcast(py).unwrap();
844
845 for (i, (key, value)) in dict.iter().enumerate() {
846 let key = key.extract::<i32>().unwrap();
847 let value = value.extract::<i32>().unwrap();
848 dict.del_item(key).unwrap();
849 dict.set_item(key + 1, value + 1).unwrap();
850
851 if i > 1000 {
852 // avoid this test just running out of memory if it fails
853 break;
854 };
855 }
856 });
857 }
858
859 #[test]
860 fn test_iter_size_hint() {
861 Python::with_gil(|py| {
862 let mut v = HashMap::new();
863 v.insert(7, 32);
864 v.insert(8, 42);
865 v.insert(9, 123);
866 let ob = v.to_object(py);
867 let dict: &PyDict = ob.downcast(py).unwrap();
868
869 let mut iter = dict.iter();
870 assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
871 iter.next();
872 assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
873
874 // Exhaust iterator.
875 for _ in &mut iter {}
876
877 assert_eq!(iter.size_hint(), (0, Some(0)));
878
879 assert!(iter.next().is_none());
880
881 assert_eq!(iter.size_hint(), (0, Some(0)));
882 });
883 }
884
885 #[test]
886 fn test_into_iter() {
887 Python::with_gil(|py| {
888 let mut v = HashMap::new();
889 v.insert(7, 32);
890 v.insert(8, 42);
891 v.insert(9, 123);
892 let ob = v.to_object(py);
893 let dict: &PyDict = ob.downcast(py).unwrap();
894 let mut key_sum = 0;
895 let mut value_sum = 0;
896 for (key, value) in dict {
897 key_sum += key.extract::<i32>().unwrap();
898 value_sum += value.extract::<i32>().unwrap();
899 }
900 assert_eq!(7 + 8 + 9, key_sum);
901 assert_eq!(32 + 42 + 123, value_sum);
902 });
903 }
904
905 #[test]
906 fn test_hashmap_into_dict() {
907 Python::with_gil(|py| {
908 let mut map = HashMap::<i32, i32>::new();
909 map.insert(1, 1);
910
911 let py_map = map.into_py_dict(py);
912
913 assert_eq!(py_map.len(), 1);
914 assert_eq!(
915 py_map
916 .get_item(1)
917 .unwrap()
918 .unwrap()
919 .extract::<i32>()
920 .unwrap(),
921 1
922 );
923 });
924 }
925
926 #[test]
927 fn test_btreemap_into_dict() {
928 Python::with_gil(|py| {
929 let mut map = BTreeMap::<i32, i32>::new();
930 map.insert(1, 1);
931
932 let py_map = map.into_py_dict(py);
933
934 assert_eq!(py_map.len(), 1);
935 assert_eq!(
936 py_map
937 .get_item(1)
938 .unwrap()
939 .unwrap()
940 .extract::<i32>()
941 .unwrap(),
942 1
943 );
944 });
945 }
946
947 #[test]
948 fn test_vec_into_dict() {
949 Python::with_gil(|py| {
950 let vec = vec![("a", 1), ("b", 2), ("c", 3)];
951 let py_map = vec.into_py_dict(py);
952
953 assert_eq!(py_map.len(), 3);
954 assert_eq!(
955 py_map
956 .get_item("b")
957 .unwrap()
958 .unwrap()
959 .extract::<i32>()
960 .unwrap(),
961 2
962 );
963 });
964 }
965
966 #[test]
967 fn test_slice_into_dict() {
968 Python::with_gil(|py| {
969 let arr = [("a", 1), ("b", 2), ("c", 3)];
970 let py_map = arr.into_py_dict(py);
971
972 assert_eq!(py_map.len(), 3);
973 assert_eq!(
974 py_map
975 .get_item("b")
976 .unwrap()
977 .unwrap()
978 .extract::<i32>()
979 .unwrap(),
980 2
981 );
982 });
983 }
984
985 #[test]
986 fn dict_as_mapping() {
987 Python::with_gil(|py| {
988 let mut map = HashMap::<i32, i32>::new();
989 map.insert(1, 1);
990
991 let py_map = map.into_py_dict(py);
992
993 assert_eq!(py_map.as_mapping().len().unwrap(), 1);
994 assert_eq!(
995 py_map
996 .as_mapping()
997 .get_item(1)
998 .unwrap()
999 .extract::<i32>()
1000 .unwrap(),
1001 1
1002 );
1003 });
1004 }
1005
1006 #[cfg(not(PyPy))]
1007 fn abc_dict(py: Python<'_>) -> &PyDict {
1008 let mut map = HashMap::<&'static str, i32>::new();
1009 map.insert("a", 1);
1010 map.insert("b", 2);
1011 map.insert("c", 3);
1012 map.into_py_dict(py)
1013 }
1014
1015 #[test]
1016 #[cfg(not(PyPy))]
1017 fn dict_keys_view() {
1018 Python::with_gil(|py| {
1019 let dict = abc_dict(py);
1020 let keys = dict.call_method0("keys").unwrap();
1021 assert!(keys.is_instance(py.get_type::<PyDictKeys>()).unwrap());
1022 })
1023 }
1024
1025 #[test]
1026 #[cfg(not(PyPy))]
1027 fn dict_values_view() {
1028 Python::with_gil(|py| {
1029 let dict = abc_dict(py);
1030 let values = dict.call_method0("values").unwrap();
1031 assert!(values.is_instance(py.get_type::<PyDictValues>()).unwrap());
1032 })
1033 }
1034
1035 #[test]
1036 #[cfg(not(PyPy))]
1037 fn dict_items_view() {
1038 Python::with_gil(|py| {
1039 let dict = abc_dict(py);
1040 let items = dict.call_method0("items").unwrap();
1041 assert!(items.is_instance(py.get_type::<PyDictItems>()).unwrap());
1042 })
1043 }
1044
1045 #[test]
1046 fn dict_update() {
1047 Python::with_gil(|py| {
1048 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py);
1049 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py);
1050 dict.update(other.as_mapping()).unwrap();
1051 assert_eq!(dict.len(), 4);
1052 assert_eq!(
1053 dict.get_item("a")
1054 .unwrap()
1055 .unwrap()
1056 .extract::<i32>()
1057 .unwrap(),
1058 1
1059 );
1060 assert_eq!(
1061 dict.get_item("b")
1062 .unwrap()
1063 .unwrap()
1064 .extract::<i32>()
1065 .unwrap(),
1066 4
1067 );
1068 assert_eq!(
1069 dict.get_item("c")
1070 .unwrap()
1071 .unwrap()
1072 .extract::<i32>()
1073 .unwrap(),
1074 5
1075 );
1076 assert_eq!(
1077 dict.get_item("d")
1078 .unwrap()
1079 .unwrap()
1080 .extract::<i32>()
1081 .unwrap(),
1082 6
1083 );
1084
1085 assert_eq!(other.len(), 3);
1086 assert_eq!(
1087 other
1088 .get_item("b")
1089 .unwrap()
1090 .unwrap()
1091 .extract::<i32>()
1092 .unwrap(),
1093 4
1094 );
1095 assert_eq!(
1096 other
1097 .get_item("c")
1098 .unwrap()
1099 .unwrap()
1100 .extract::<i32>()
1101 .unwrap(),
1102 5
1103 );
1104 assert_eq!(
1105 other
1106 .get_item("d")
1107 .unwrap()
1108 .unwrap()
1109 .extract::<i32>()
1110 .unwrap(),
1111 6
1112 );
1113 })
1114 }
1115
1116 #[test]
1117 fn dict_update_if_missing() {
1118 Python::with_gil(|py| {
1119 let dict = [("a", 1), ("b", 2), ("c", 3)].into_py_dict(py);
1120 let other = [("b", 4), ("c", 5), ("d", 6)].into_py_dict(py);
1121 dict.update_if_missing(other.as_mapping()).unwrap();
1122 assert_eq!(dict.len(), 4);
1123 assert_eq!(
1124 dict.get_item("a")
1125 .unwrap()
1126 .unwrap()
1127 .extract::<i32>()
1128 .unwrap(),
1129 1
1130 );
1131 assert_eq!(
1132 dict.get_item("b")
1133 .unwrap()
1134 .unwrap()
1135 .extract::<i32>()
1136 .unwrap(),
1137 2
1138 );
1139 assert_eq!(
1140 dict.get_item("c")
1141 .unwrap()
1142 .unwrap()
1143 .extract::<i32>()
1144 .unwrap(),
1145 3
1146 );
1147 assert_eq!(
1148 dict.get_item("d")
1149 .unwrap()
1150 .unwrap()
1151 .extract::<i32>()
1152 .unwrap(),
1153 6
1154 );
1155
1156 assert_eq!(other.len(), 3);
1157 assert_eq!(
1158 other
1159 .get_item("b")
1160 .unwrap()
1161 .unwrap()
1162 .extract::<i32>()
1163 .unwrap(),
1164 4
1165 );
1166 assert_eq!(
1167 other
1168 .get_item("c")
1169 .unwrap()
1170 .unwrap()
1171 .extract::<i32>()
1172 .unwrap(),
1173 5
1174 );
1175 assert_eq!(
1176 other
1177 .get_item("d")
1178 .unwrap()
1179 .unwrap()
1180 .extract::<i32>()
1181 .unwrap(),
1182 6
1183 );
1184 })
1185 }
1186}
1187