Add into_mapping and into_sequence methods to Bound types (#3982)
* Add Bound<'py, PyDict>.into_mapping method * Add Bound<'py, PyTuple>.into_sequence method * Add Bound<'py, PyList>.into_sequence method * Add newsfragment * Add tests for into_mapping and into_sequence
This commit is contained in:
parent
9808f7111c
commit
009cd32a44
1
newsfragments/3982.added.md
Normal file
1
newsfragments/3982.added.md
Normal file
|
@ -0,0 +1 @@
|
|||
Added `Bound<'py, PyDict>::into_mapping`, `Bound<'py, PyList>::into_sequence` and `Bound<'py, PyTuple>::into_sequence`.
|
|
@ -365,6 +365,9 @@ pub trait PyDictMethods<'py>: crate::sealed::Sealed {
|
|||
/// Returns `self` cast as a `PyMapping`.
|
||||
fn as_mapping(&self) -> &Bound<'py, PyMapping>;
|
||||
|
||||
/// Returns `self` cast as a `PyMapping`.
|
||||
fn into_mapping(self) -> Bound<'py, PyMapping>;
|
||||
|
||||
/// Update this dictionary with the key/value pairs from another.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.update(other)`. If `other` is a `PyDict`, you may want
|
||||
|
@ -511,6 +514,10 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> {
|
|||
unsafe { self.downcast_unchecked() }
|
||||
}
|
||||
|
||||
fn into_mapping(self) -> Bound<'py, PyMapping> {
|
||||
unsafe { self.into_any().downcast_into_unchecked() }
|
||||
}
|
||||
|
||||
fn update(&self, other: &Bound<'_, PyMapping>) -> PyResult<()> {
|
||||
err::error_on_minusone(self.py(), unsafe {
|
||||
ffi::PyDict_Update(self.as_ptr(), other.as_ptr())
|
||||
|
@ -1367,6 +1374,20 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dict_into_mapping() {
|
||||
Python::with_gil(|py| {
|
||||
let mut map = HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let py_map = map.into_py_dict_bound(py);
|
||||
|
||||
let py_mapping = py_map.into_mapping();
|
||||
assert_eq!(py_mapping.len().unwrap(), 1);
|
||||
assert_eq!(py_mapping.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(PyPy))]
|
||||
fn abc_dict(py: Python<'_>) -> Bound<'_, PyDict> {
|
||||
let mut map = HashMap::<&'static str, i32>::new();
|
||||
|
|
|
@ -295,6 +295,9 @@ pub trait PyListMethods<'py>: crate::sealed::Sealed {
|
|||
/// Returns `self` cast as a `PySequence`.
|
||||
fn as_sequence(&self) -> &Bound<'py, PySequence>;
|
||||
|
||||
/// Returns `self` cast as a `PySequence`.
|
||||
fn into_sequence(self) -> Bound<'py, PySequence>;
|
||||
|
||||
/// Gets the list item at the specified index.
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -408,6 +411,11 @@ impl<'py> PyListMethods<'py> for Bound<'py, PyList> {
|
|||
unsafe { self.downcast_unchecked() }
|
||||
}
|
||||
|
||||
/// Returns `self` cast as a `PySequence`.
|
||||
fn into_sequence(self) -> Bound<'py, PySequence> {
|
||||
unsafe { self.into_any().downcast_into_unchecked() }
|
||||
}
|
||||
|
||||
/// Gets the list item at the specified index.
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -715,6 +723,9 @@ impl<'py> IntoIterator for &Bound<'py, PyList> {
|
|||
#[cfg(test)]
|
||||
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
|
||||
mod tests {
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::types::list::PyListMethods;
|
||||
use crate::types::sequence::PySequenceMethods;
|
||||
use crate::types::{PyList, PyTuple};
|
||||
use crate::Python;
|
||||
use crate::{IntoPy, PyObject, ToPyObject};
|
||||
|
@ -934,6 +945,35 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_sequence() {
|
||||
Python::with_gil(|py| {
|
||||
let list = PyList::new_bound(py, [1, 2, 3, 4]);
|
||||
|
||||
assert_eq!(list.as_sequence().len().unwrap(), 4);
|
||||
assert_eq!(
|
||||
list.as_sequence()
|
||||
.get_item(1)
|
||||
.unwrap()
|
||||
.extract::<i32>()
|
||||
.unwrap(),
|
||||
2
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_sequence() {
|
||||
Python::with_gil(|py| {
|
||||
let list = PyList::new_bound(py, [1, 2, 3, 4]);
|
||||
|
||||
let sequence = list.into_sequence();
|
||||
|
||||
assert_eq!(sequence.len().unwrap(), 4);
|
||||
assert_eq!(sequence.get_item(1).unwrap().extract::<i32>().unwrap(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract() {
|
||||
Python::with_gil(|py| {
|
||||
|
|
|
@ -258,6 +258,9 @@ pub trait PyTupleMethods<'py>: crate::sealed::Sealed {
|
|||
/// Returns `self` cast as a `PySequence`.
|
||||
fn as_sequence(&self) -> &Bound<'py, PySequence>;
|
||||
|
||||
/// Returns `self` cast as a `PySequence`.
|
||||
fn into_sequence(self) -> Bound<'py, PySequence>;
|
||||
|
||||
/// Takes the slice `self[low:high]` and returns it as a new tuple.
|
||||
///
|
||||
/// Indices must be nonnegative, and out-of-range indices are clipped to
|
||||
|
@ -353,6 +356,10 @@ impl<'py> PyTupleMethods<'py> for Bound<'py, PyTuple> {
|
|||
unsafe { self.downcast_unchecked() }
|
||||
}
|
||||
|
||||
fn into_sequence(self) -> Bound<'py, PySequence> {
|
||||
unsafe { self.into_any().downcast_into_unchecked() }
|
||||
}
|
||||
|
||||
fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyTuple> {
|
||||
unsafe {
|
||||
ffi::PyTuple_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
|
||||
|
@ -1415,7 +1422,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_tuple_as_sequence() {
|
||||
Python::with_gil(|py| {
|
||||
let tuple = PyTuple::new(py, vec![1, 2, 3]);
|
||||
let tuple = PyTuple::new_bound(py, vec![1, 2, 3]);
|
||||
let sequence = tuple.as_sequence();
|
||||
assert!(tuple.get_item(0).unwrap().eq(1).unwrap());
|
||||
assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
|
||||
|
@ -1425,6 +1432,16 @@ mod tests {
|
|||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_into_sequence() {
|
||||
Python::with_gil(|py| {
|
||||
let tuple = PyTuple::new_bound(py, vec![1, 2, 3]);
|
||||
let sequence = tuple.into_sequence();
|
||||
assert!(sequence.get_item(0).unwrap().eq(1).unwrap());
|
||||
assert_eq!(sequence.len().unwrap(), 3);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bound_tuple_get_item() {
|
||||
Python::with_gil(|py| {
|
||||
|
|
Loading…
Reference in a new issue