changeset 52414:233707101dae

rust-pyo3: simplified API to get core Index references Given the amount of conversion and arcane internal detail, it is easier for the caller and makes a better separation of concerns to just introduce a new unsafe helper. It is actually possible that it would be safe, and we can decide about that later. Actually the reason given in the `cpython` crate for unsafety of the `try_borrow()` method is the following: ``` // try_borrow() and try_borrow_mut() are unsafe because self.data may // have a function returning the inner &'static reference. // If T is &'static U, its lifetime can be easily coerced to &'a U, but // how could we do that for Whatever<'static> in general? ``` Given that we coerce the Index reference to the GIL lifetime and that it is unlikely that the inner data would contain a function returning the a `'static` reference, it is possible that it is actually even safe.
author Georges Racinet <georges.racinet@cloudcrane.io>
date Fri, 29 Nov 2024 23:35:31 +0100
parents 20fe0bf9a9a5
children 4eec920bbb37
files rust/hg-pyo3/src/convert_cpython.rs rust/hg-pyo3/src/dagops.rs
diffstat 2 files changed, 18 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/rust/hg-pyo3/src/convert_cpython.rs	Sat Nov 30 20:58:09 2024 +0100
+++ b/rust/hg-pyo3/src/convert_cpython.rs	Fri Nov 29 23:35:31 2024 +0100
@@ -16,6 +16,7 @@
 use cpython::PythonObject;
 use lazy_static::lazy_static;
 
+use hg::revlog::index::Index as CoreIndex;
 use rusthg::revlog::{InnerRevlog, PySharedIndex};
 
 /// Force cpython's GIL handle with the appropriate lifetime
@@ -165,9 +166,21 @@
     Ok(unsafe { leaked.map(py, |idx| PySharedIndex { inner: &idx.index }) })
 }
 
-pub(crate) fn proxy_index_extract<'py>(
+/// Full extraction of the proxy index object as received in PyO3 to a
+/// [`CoreIndex`] reference.
+///
+/// The safety invariants to maintain are those of the underlying
+/// [`UnsafePyLeaked::try_borrow`]: the caller must not leak the inner
+/// reference.
+pub(crate) unsafe fn proxy_index_extract<'py>(
     index_proxy: &Bound<'py, PyAny>,
-) -> PyResult<(cpython::Python<'py>, cpython::UnsafePyLeaked<PySharedIndex>)> {
+) -> PyResult<&'py CoreIndex> {
     let (py, idx_proxy) = to_cpython_py_object(index_proxy);
-    Ok((py, py_rust_index_to_graph(py, idx_proxy)?))
+    let py_leaked = py_rust_index_to_graph(py, idx_proxy)?;
+    let py_shared = &*unsafe {
+        py_leaked
+            .try_borrow(py)
+            .map_err(|e| from_cpython_pyerr(py, e))?
+    };
+    Ok(py_shared.inner)
 }
--- a/rust/hg-pyo3/src/dagops.rs	Sat Nov 30 20:58:09 2024 +0100
+++ b/rust/hg-pyo3/src/dagops.rs	Fri Nov 29 23:35:31 2024 +0100
@@ -15,7 +15,7 @@
 
 use hg::{dagops, Revision};
 
-use crate::convert_cpython::{from_cpython_pyerr, proxy_index_extract};
+use crate::convert_cpython::proxy_index_extract;
 use crate::exceptions::GraphError;
 use crate::revision::{rev_pyiter_collect, PyRevision};
 use crate::util::new_submodule;
@@ -29,14 +29,8 @@
     index_proxy: &Bound<'_, PyAny>,
     revs: &Bound<'_, PyAny>,
 ) -> PyResult<HashSet<PyRevision>> {
-    let (py, py_leaked) = proxy_index_extract(index_proxy)?;
     // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
-    let index = &*unsafe {
-        py_leaked
-            .try_borrow(py)
-            .map_err(|e| from_cpython_pyerr(py, e))?
-    };
-
+    let index = unsafe { proxy_index_extract(index_proxy)? };
     let mut as_set: HashSet<Revision> = rev_pyiter_collect(revs, index)?;
     dagops::retain_heads(index, &mut as_set).map_err(GraphError::from_hg)?;
     Ok(as_set.into_iter().map(Into::into).collect())