diff --git a/CHANGELOG.md b/CHANGELOG.md index fb73358ca88..265dc2a990f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implement `ToPyObject` for `[T; N]`. [#2313](https://github.com/PyO3/pyo3/pull/2313) +- Add `PyDictKeys`, `PyDictValues` and `PyDictItems` Rust types to represent `dict_keys`, `dict_values` and `dict_items` types. [#2358](https://github.com/PyO3/pyo3/pull/2358) ### Changed diff --git a/src/types/dict.rs b/src/types/dict.rs index f9e4ffb279f..f6e0099084e 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -22,6 +22,36 @@ pyobject_native_type!( #checkfunction=ffi::PyDict_Check ); +/// Represents a Python `dict_keys`. +#[repr(transparent)] +pub struct PyDictKeys(PyAny); + +pyobject_native_type_core!( + PyDictKeys, + ffi::PyDictKeys_Type, + #checkfunction=ffi::PyDictKeys_Check +); + +/// Represents a Python `dict_values`. +#[repr(transparent)] +pub struct PyDictValues(PyAny); + +pyobject_native_type_core!( + PyDictValues, + ffi::PyDictValues_Type, + #checkfunction=ffi::PyDictValues_Check +); + +/// Represents a Python `dict_items`. +#[repr(transparent)] +pub struct PyDictItems(PyAny); + +pyobject_native_type_core!( + PyDictItems, + ffi::PyDictItems_Type, + #checkfunction=ffi::PyDictItems_Check +); + impl PyDict { /// Creates a new empty dictionary. pub fn new(py: Python<'_>) -> &PyDict { @@ -379,12 +409,12 @@ where #[cfg(test)] mod tests { + use super::*; use crate::conversion::IntoPy; - use crate::types::dict::IntoPyDict; #[cfg(not(PyPy))] - use crate::types::PyList; - use crate::types::{PyDict, PyTuple}; + use crate::types::{PyList, PyTuple}; use crate::PyObject; + use crate::PyTypeInfo; use crate::Python; use crate::{PyTryFrom, ToPyObject}; use std::collections::{BTreeMap, HashMap}; @@ -796,4 +826,39 @@ mod tests { ); }); } + + fn abc_dict(py: Python<'_>) -> &PyDict { + let mut map = HashMap::<&'static str, i32>::new(); + map.insert("a", 1); + map.insert("b", 2); + map.insert("c", 3); + map.into_py_dict(py) + } + + #[test] + fn dict_keys_view() { + Python::with_gil(|py| { + let dict = abc_dict(py); + let keys = dict.call_method0("keys").unwrap(); + assert!(keys.is_instance(PyDictKeys::type_object(py)).unwrap()); + }) + } + + #[test] + fn dict_values_view() { + Python::with_gil(|py| { + let dict = abc_dict(py); + let values = dict.call_method0("values").unwrap(); + assert!(values.is_instance(PyDictValues::type_object(py)).unwrap()); + }) + } + + #[test] + fn dict_items_view() { + Python::with_gil(|py| { + let dict = abc_dict(py); + let items = dict.call_method0("items").unwrap(); + assert!(items.is_instance(PyDictItems::type_object(py)).unwrap()); + }) + } }