diff --git a/.gitignore b/.gitignore index a49d28c544d..48e96b283bc 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ pip-wheel-metadata valgrind-python.supp *.pyd lcov.info +.idea/ +.vscode/ +.python-version \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 42fb3c37640..5612e5880f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add `as_sequence()` method on dict views. [#2527](https://github.com/PyO3/pyo3/pull/2527) - Add `timezone_utc()`. [#1588](https://github.com/PyO3/pyo3/pull/1588) - 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) diff --git a/src/types/dict.rs b/src/types/dict.rs index bd75d89722b..ed35df47e34 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -4,9 +4,9 @@ use super::PyMapping; use crate::err::{self, PyErr, PyResult}; use crate::ffi::Py_ssize_t; use crate::types::{PyAny, PyList}; -#[cfg(not(PyPy))] -use crate::IntoPyPointer; use crate::{ffi, AsPyPointer, FromPyObject, IntoPy, PyObject, PyTryFrom, Python, ToPyObject}; +#[cfg(not(PyPy))] +use crate::{types::PySequence, IntoPyPointer}; use std::collections::{BTreeMap, HashMap}; use std::ptr::NonNull; use std::{cmp, collections, hash}; @@ -34,6 +34,14 @@ pyobject_native_type_core!( #checkfunction=ffi::PyDictKeys_Check ); +#[cfg(not(PyPy))] +impl PyDictKeys { + /// Returns `self` cast as a `PySequence`. + pub fn as_sequence(&self) -> &PySequence { + unsafe { PySequence::try_from_unchecked(self) } + } +} + /// Represents a Python `dict_values`. #[cfg(not(PyPy))] #[repr(transparent)] @@ -46,6 +54,14 @@ pyobject_native_type_core!( #checkfunction=ffi::PyDictValues_Check ); +#[cfg(not(PyPy))] +impl PyDictValues { + /// Returns `self` cast as a `PySequence`. + pub fn as_sequence(&self) -> &PySequence { + unsafe { PySequence::try_from_unchecked(self) } + } +} + /// Represents a Python `dict_items`. #[cfg(not(PyPy))] #[repr(transparent)] @@ -58,6 +74,14 @@ pyobject_native_type_core!( #checkfunction=ffi::PyDictItems_Check ); +#[cfg(not(PyPy))] +impl PyDictItems { + /// Returns `self` cast as a `PySequence`. + pub fn as_sequence(&self) -> &PySequence { + unsafe { PySequence::try_from_unchecked(self) } + } +} + impl PyDict { /// Creates a new empty dictionary. pub fn new(py: Python<'_>) -> &PyDict { @@ -972,6 +996,14 @@ mod tests { let dict = abc_dict(py); let keys = dict.call_method0("keys").unwrap(); assert!(keys.is_instance(PyDictKeys::type_object(py)).unwrap()); + assert_eq!( + keys.cast_as::() + .unwrap() + .as_sequence() + .len() + .unwrap(), + 3 + ); }) } @@ -982,6 +1014,15 @@ mod tests { let dict = abc_dict(py); let values = dict.call_method0("values").unwrap(); assert!(values.is_instance(PyDictValues::type_object(py)).unwrap()); + assert_eq!( + values + .cast_as::() + .unwrap() + .as_sequence() + .len() + .unwrap(), + 3 + ); }) } @@ -992,6 +1033,15 @@ mod tests { let dict = abc_dict(py); let items = dict.call_method0("items").unwrap(); assert!(items.is_instance(PyDictItems::type_object(py)).unwrap()); + assert_eq!( + items + .cast_as::() + .unwrap() + .as_sequence() + .len() + .unwrap(), + 3 + ); }) } }