Skip to content

Commit

Permalink
Expose PyDict_GetItemWithError on PyDict object (#2536)
Browse files Browse the repository at this point in the history
* Expose `PyDict_GetItemWithError` on `PyDict` object

* Expose only on non-pypy

* use `unwrap_err` on `GetItemWithError` test

* Add changes info to changelog

* Ignore import for pypy ignored test
  • Loading branch information
dswij committed Aug 6, 2022
1 parent 1b7a850 commit 480fe7e
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `PySuper` object [#2486](https://github.com/PyO3/pyo3/pull/2486)
- Add support for generating PyPy Windows import library. [#2506](https://github.com/PyO3/pyo3/pull/2506)
- Add FFI definitions for `Py_EnterRecursiveCall` and `Py_LeaveRecursiveCall`. [#2511](https://github.com/PyO3/pyo3/pull/2511)
- Add `get_item_with_error` on `PyDict` that exposes `PyDict_GetItemWIthError` for non-PyPy. [#2536](https://github.com/PyO3/pyo3/pull/2536)

### Changed

Expand Down
47 changes: 47 additions & 0 deletions src/types/dict.rs
Expand Up @@ -156,6 +156,27 @@ impl PyDict {
}
}

/// Gets an item from the dictionary,
///
/// returns `Ok(None)` if item is not present, or `Err(PyErr)` if an error occurs.
///
/// To get a `KeyError` for non-existing keys, use `PyAny::get_item_with_error`.
#[cfg(not(PyPy))]
pub fn get_item_with_error<K>(&self, key: K) -> PyResult<Option<&PyAny>>
where
K: ToPyObject,
{
unsafe {
let ptr =
ffi::PyDict_GetItemWithError(self.as_ptr(), key.to_object(self.py()).as_ptr());
if !ffi::PyErr_Occurred().is_null() {
return Err(PyErr::fetch(self.py()));
}

Ok(NonNull::new(ptr).map(|p| self.py().from_owned_ptr(ffi::_Py_NewRef(p.as_ptr()))))
}
}

/// Sets an item value.
///
/// This is equivalent to the Python statement `self[key] = value`.
Expand Down Expand Up @@ -472,6 +493,8 @@ where
mod tests {
use super::*;
#[cfg(not(PyPy))]
use crate::exceptions;
#[cfg(not(PyPy))]
use crate::{types::PyList, PyTypeInfo};
use crate::{types::PyTuple, IntoPy, PyObject, PyTryFrom, Python, ToPyObject};
use std::collections::{BTreeMap, HashMap};
Expand Down Expand Up @@ -562,6 +585,30 @@ mod tests {
});
}

#[test]
#[cfg(not(PyPy))]
fn test_get_item_with_error() {
Python::with_gil(|py| {
let mut v = HashMap::new();
v.insert(7, 32);
let ob = v.to_object(py);
let dict = <PyDict as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
assert_eq!(
32,
dict.get_item_with_error(7i32)
.unwrap()
.unwrap()
.extract::<i32>()
.unwrap()
);
assert!(dict.get_item_with_error(8i32).unwrap().is_none());
assert!(dict
.get_item_with_error(dict)
.unwrap_err()
.is_instance_of::<exceptions::PyTypeError>(py));
});
}

#[test]
fn test_set_item() {
Python::with_gil(|py| {
Expand Down

0 comments on commit 480fe7e

Please sign in to comment.