diff --git a/CHANGELOG.md b/CHANGELOG.md index 870157acaf9..835cf095fd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implement `ToPyObject` for `[T; N]`. [#2313](https://github.com/PyO3/pyo3/pull/2313) - Added the internal `IntoPyResult` trait to give better error messages when function return types do not implement `IntoPy`. [#2326](https://github.com/PyO3/pyo3/pull/2326) +- 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..ed030c87c29 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -22,6 +22,42 @@ pyobject_native_type!( #checkfunction=ffi::PyDict_Check ); +/// Represents a Python `dict_keys`. +#[cfg(not(PyPy))] +#[repr(transparent)] +pub struct PyDictKeys(PyAny); + +#[cfg(not(PyPy))] +pyobject_native_type_core!( + PyDictKeys, + ffi::PyDictKeys_Type, + #checkfunction=ffi::PyDictKeys_Check +); + +/// Represents a Python `dict_values`. +#[cfg(not(PyPy))] +#[repr(transparent)] +pub struct PyDictValues(PyAny); + +#[cfg(not(PyPy))] +pyobject_native_type_core!( + PyDictValues, + ffi::PyDictValues_Type, + #checkfunction=ffi::PyDictValues_Check +); + +/// Represents a Python `dict_items`. +#[cfg(not(PyPy))] +#[repr(transparent)] +pub struct PyDictItems(PyAny); + +#[cfg(not(PyPy))] +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,14 +415,10 @@ where #[cfg(test)] mod tests { - use crate::conversion::IntoPy; - use crate::types::dict::IntoPyDict; + use super::*; #[cfg(not(PyPy))] use crate::types::PyList; - use crate::types::{PyDict, PyTuple}; - use crate::PyObject; - use crate::Python; - use crate::{PyTryFrom, ToPyObject}; + use crate::{types::PyTuple, IntoPy, PyObject, PyTryFrom, PyTypeInfo, Python, ToPyObject}; use std::collections::{BTreeMap, HashMap}; #[test] @@ -796,4 +828,43 @@ mod tests { ); }); } + + #[cfg(not(PyPy))] + 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] + #[cfg(not(PyPy))] + 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] + #[cfg(not(PyPy))] + 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] + #[cfg(not(PyPy))] + 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()); + }) + } }