Skip to content

Commit

Permalink
add PyDict::get_item_with_error for PyPy
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Jun 25, 2023
1 parent edf47b5 commit 6d1f025
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 13 deletions.
1 change: 1 addition & 0 deletions newsfragments/3270.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `PyDict::get_item_with_error` on PyPy.
1 change: 1 addition & 0 deletions pyo3-ffi/src/dictobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern "C" {
pub fn PyDict_New() -> *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyDict_GetItem")]
pub fn PyDict_GetItem(mp: *mut PyObject, key: *mut PyObject) -> *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyDict_GetItemWithError")]
pub fn PyDict_GetItemWithError(mp: *mut PyObject, key: *mut PyObject) -> *mut PyObject;
#[cfg_attr(PyPy, link_name = "PyPyDict_SetItem")]
pub fn PyDict_SetItem(mp: *mut PyObject, key: *mut PyObject, item: *mut PyObject) -> c_int;
Expand Down
35 changes: 22 additions & 13 deletions src/types/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::types::{PyAny, PyList};
use crate::{ffi, AsPyPointer, Python, ToPyObject};
#[cfg(not(PyPy))]
use crate::{IntoPyPointer, PyObject};
use std::ptr::NonNull;

/// Represents a Python `dict`.
#[repr(transparent)]
Expand Down Expand Up @@ -143,12 +142,17 @@ impl PyDict {
where
K: ToPyObject,
{
let key = key.to_object(self.py());
self.get_item_impl(key)
}

fn get_item_impl(&self, key: PyObject) -> Option<&PyAny> {
let py = self.py();
unsafe {
let ptr = ffi::PyDict_GetItem(self.as_ptr(), key.to_object(self.py()).as_ptr());
NonNull::new(ptr).map(|p| {
// PyDict_GetItem return s borrowed ptr, must make it owned for safety (see #890).
self.py().from_owned_ptr(ffi::_Py_NewRef(p.as_ptr()))
})
let ptr = ffi::PyDict_GetItem(self.as_ptr(), key.as_ptr());
// PyDict_GetItem returns a borrowed ptr, must make it owned for safety (see #890).
// PyObject::from_borrowed_ptr_or_opt will take ownership in this way.
PyObject::from_borrowed_ptr_or_opt(py, ptr).map(|pyobject| pyobject.into_ref(py))
}
}

Expand All @@ -157,19 +161,24 @@ impl PyDict {
/// 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,
{
let key = key.to_object(self.py());
self.get_item_with_error_impl(key)
}

fn get_item_with_error_impl(&self, key: PyObject) -> PyResult<Option<&PyAny>> {
let py = self.py();
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()));
let ptr = ffi::PyDict_GetItemWithError(self.as_ptr(), key.as_ptr());
// PyDict_GetItemWithError returns a borrowed ptr, must make it owned for safety (see #890).
// PyObject::from_borrowed_ptr_or_opt will take ownership in this way.
match PyObject::from_borrowed_ptr_or_opt(py, ptr) {
Some(pyobject) => Ok(Some(pyobject.into_ref(py))),
None => PyErr::take(py).map(Err).transpose(),
}

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

Expand Down

0 comments on commit 6d1f025

Please sign in to comment.