|
@@
-1,286
+1,285
|
|
1
|
//! This module takes care of all conversions involving `rusthg` (hg-cpython)
|
|
1
|
//! This module takes care of all conversions involving `rusthg` (hg-cpython)
|
|
2
|
//! objects in the PyO3 call context.
|
|
2
|
//! objects in the PyO3 call context.
|
|
3
|
//!
|
|
3
|
//!
|
|
4
|
//! For source code clarity, we only import (`use`) [`cpython`] traits and not
|
|
4
|
//! For source code clarity, we only import (`use`) [`cpython`] traits and not
|
|
5
|
//! any of its data objects. We are instead using full qualifiers, such as
|
|
5
|
//! any of its data objects. We are instead using full qualifiers, such as
|
|
6
|
//! `cpython::PyObject`, and believe that the added heaviness is an acceptatble
|
|
6
|
//! `cpython::PyObject`, and believe that the added heaviness is an acceptatble
|
|
7
|
//! price to pay to avoid confusion.
|
|
7
|
//! price to pay to avoid confusion.
|
|
8
|
//!
|
|
8
|
//!
|
|
9
|
//! Also it, is customary in [`cpython`] to label the GIL lifetime as `'p`,
|
|
9
|
//! Also it, is customary in [`cpython`] to label the GIL lifetime as `'p`,
|
|
10
|
//! whereas it is `'py` in PyO3 context. We keep both these conventions in
|
|
10
|
//! whereas it is `'py` in PyO3 context. We keep both these conventions in
|
|
11
|
//! the arguments side of function signatures when they are not simply elided.
|
|
11
|
//! the arguments side of function signatures when they are not simply elided.
|
|
12
|
use pyo3::exceptions::PyTypeError;
|
|
12
|
use pyo3::exceptions::PyTypeError;
|
|
13
|
use pyo3::prelude::*;
|
|
13
|
use pyo3::prelude::*;
|
|
14
|
use pyo3::{pyclass::boolean_struct::False, PyClass};
|
|
14
|
use pyo3::{pyclass::boolean_struct::False, PyClass};
|
|
15
|
|
|
15
|
|
|
16
|
use cpython::ObjectProtocol;
|
|
16
|
use cpython::ObjectProtocol;
|
|
17
|
use cpython::PythonObject;
|
|
17
|
use cpython::PythonObject;
|
|
18
|
use lazy_static::lazy_static;
|
|
18
|
use lazy_static::lazy_static;
|
|
19
|
|
|
19
|
|
|
20
|
use hg::revlog::index::Index as CoreIndex;
|
|
20
|
use hg::revlog::index::Index as CoreIndex;
|
|
21
|
use rusthg::revlog::{InnerRevlog, PySharedIndex};
|
|
21
|
use rusthg::revlog::{InnerRevlog, PySharedIndex};
|
|
22
|
|
|
22
|
|
|
23
|
/// Marker trait for PyO3 objects with a lifetime representing the acquired GIL
|
|
23
|
/// Marker trait for PyO3 objects with a lifetime representing the acquired GIL
|
|
24
|
///
|
|
24
|
///
|
|
25
|
/// # Safety
|
|
25
|
/// # Safety
|
|
26
|
///
|
|
26
|
///
|
|
27
|
/// This trait must not be implemented for objects with lifetimes that
|
|
27
|
/// This trait must not be implemented for objects with lifetimes that
|
|
28
|
/// do not imply in PyO3 that the GIL is acquired during the whole lifetime.
|
|
28
|
/// do not imply in PyO3 that the GIL is acquired during the whole lifetime.
|
|
29
|
pub unsafe trait WithGIL<'py> {}
|
|
29
|
pub unsafe trait WithGIL<'py> {}
|
|
30
|
|
|
30
|
|
|
31
|
// Safety: the lifetime on these PyO3 objects all represent the acquired GIL
|
|
31
|
// Safety: the lifetime on these PyO3 objects all represent the acquired GIL
|
|
32
|
unsafe impl<'py> WithGIL<'py> for Python<'py> {}
|
|
32
|
unsafe impl<'py> WithGIL<'py> for Python<'py> {}
|
|
33
|
unsafe impl<'py, T> WithGIL<'py> for Bound<'py, T> {}
|
|
33
|
unsafe impl<'py, T> WithGIL<'py> for Bound<'py, T> {}
|
|
34
|
unsafe impl<'py, T: PyClass> WithGIL<'py> for PyRef<'py, T> {}
|
|
34
|
unsafe impl<'py, T: PyClass> WithGIL<'py> for PyRef<'py, T> {}
|
|
35
|
unsafe impl<'py, T: PyClass<Frozen = False>> WithGIL<'py>
|
|
35
|
unsafe impl<'py, T: PyClass<Frozen = False>> WithGIL<'py>
|
|
36
|
for PyRefMut<'py, T>
|
|
36
|
for PyRefMut<'py, T>
|
|
37
|
{
|
|
37
|
{
|
|
38
|
}
|
|
38
|
}
|
|
39
|
|
|
39
|
|
|
40
|
/// Force cpython's GIL handle with the appropriate lifetime
|
|
40
|
/// Force cpython's GIL handle with the appropriate lifetime
|
|
41
|
///
|
|
41
|
///
|
|
42
|
/// In `pyo3`, the fact that we have the GIL is expressed by the lifetime of
|
|
42
|
/// In `pyo3`, the fact that we have the GIL is expressed by the lifetime of
|
|
43
|
/// the incoming [`Bound`] smart pointer. We therefore simply instantiate
|
|
43
|
/// the incoming [`Bound`] smart pointer. We therefore simply instantiate
|
|
44
|
/// the `cpython` handle and coerce its lifetime by the function signature.
|
|
44
|
/// the `cpython` handle and coerce its lifetime by the function signature.
|
|
45
|
///
|
|
45
|
///
|
|
46
|
/// Reacquiring the GIL is also a possible alternative, as the CPython
|
|
46
|
/// Reacquiring the GIL is also a possible alternative, as the CPython
|
|
47
|
/// documentation explicitely states that "recursive calls are allowed"
|
|
47
|
/// documentation explicitely states that "recursive calls are allowed"
|
|
48
|
/// (we interpret that as saying that acquiring the GIL within a thread that
|
|
48
|
/// (we interpret that as saying that acquiring the GIL within a thread that
|
|
49
|
/// already has it works) *as long as it is properly released*
|
|
49
|
/// already has it works) *as long as it is properly released*
|
|
50
|
/// reference:
|
|
50
|
/// reference:
|
|
51
|
/// <https://docs.python.org/3.8/c-api/init.html#c.PyGILState_Ensure>
|
|
51
|
/// <https://docs.python.org/3.8/c-api/init.html#c.PyGILState_Ensure>
|
|
52
|
pub(crate) fn cpython_handle<'py, T: WithGIL<'py>>(
|
|
52
|
pub(crate) fn cpython_handle<'py, T: WithGIL<'py>>(
|
|
53
|
_with_gil: &T,
|
|
53
|
_with_gil: &T,
|
|
54
|
) -> cpython::Python<'py> {
|
|
54
|
) -> cpython::Python<'py> {
|
|
55
|
// safety: this is safe because the returned object has the same lifetime
|
|
55
|
// safety: this is safe because the returned object has the same lifetime
|
|
56
|
// as the incoming object.
|
|
56
|
// as the incoming object.
|
|
57
|
unsafe { cpython::Python::assume_gil_acquired() }
|
|
57
|
unsafe { cpython::Python::assume_gil_acquired() }
|
|
58
|
}
|
|
58
|
}
|
|
59
|
|
|
59
|
|
|
60
|
/// Force PyO3 GIL handle from cpython's.
|
|
60
|
/// Force PyO3 GIL handle from cpython's.
|
|
61
|
///
|
|
61
|
///
|
|
62
|
/// Very similar to [`cpython_handle`]
|
|
62
|
/// Very similar to [`cpython_handle`]
|
|
63
|
pub fn pyo3_handle(_py: cpython::Python<'_>) -> Python<'_> {
|
|
63
|
pub fn pyo3_handle(_py: cpython::Python<'_>) -> Python<'_> {
|
|
64
|
// safety: this is safe because the returned object has the same lifetime
|
|
64
|
// safety: this is safe because the returned object has the same lifetime
|
|
65
|
// as the incoming object.
|
|
65
|
// as the incoming object.
|
|
66
|
unsafe { Python::assume_gil_acquired() }
|
|
66
|
unsafe { Python::assume_gil_acquired() }
|
|
67
|
}
|
|
67
|
}
|
|
68
|
|
|
68
|
|
|
69
|
/// Convert a PyO3 [`PyObject`] into a [`cpython::PyObject`]
|
|
69
|
/// Convert a PyO3 [`PyObject`] into a [`cpython::PyObject`]
|
|
70
|
///
|
|
70
|
///
|
|
71
|
/// During this process, the reference count is increased, then decreased.
|
|
71
|
/// During this process, the reference count is increased, then decreased.
|
|
72
|
/// This means that the GIL (symbolized by the lifetime on the `obj`
|
|
72
|
/// This means that the GIL (symbolized by the lifetime on the `obj`
|
|
73
|
/// argument) is needed.
|
|
73
|
/// argument) is needed.
|
|
74
|
///
|
|
74
|
///
|
|
75
|
/// We could make something perhaps more handy by simply stealing the
|
|
75
|
/// We could make something perhaps more handy by simply stealing the
|
|
76
|
/// pointer, forgetting the incoming and then implement `From` with "newtype".
|
|
76
|
/// pointer, forgetting the incoming and then implement `From` with "newtype".
|
|
77
|
/// It would be worth the effort for a generic cpython-to-pyo3 crate, perhaps
|
|
77
|
/// It would be worth the effort for a generic cpython-to-pyo3 crate, perhaps
|
|
78
|
/// not for the current endeavour.
|
|
78
|
/// not for the current endeavour.
|
|
79
|
pub(crate) fn to_cpython_py_object<'py>(
|
|
79
|
pub(crate) fn to_cpython_py_object<'py>(
|
|
80
|
obj: &Bound<'py, PyAny>,
|
|
80
|
obj: &Bound<'py, PyAny>,
|
|
81
|
) -> (cpython::Python<'py>, cpython::PyObject) {
|
|
81
|
) -> (cpython::Python<'py>, cpython::PyObject) {
|
|
82
|
let py = cpython_handle(obj);
|
|
82
|
let py = cpython_handle(obj);
|
|
83
|
// public alias of the private cpython::fii::PyObject (!)
|
|
83
|
// public alias of the private cpython::fii::PyObject (!)
|
|
84
|
let raw = obj.as_ptr() as *mut python3_sys::PyObject;
|
|
84
|
let raw = obj.as_ptr() as *mut python3_sys::PyObject;
|
|
85
|
// both pyo3 and rust-cpython will decrement the refcount on drop.
|
|
85
|
// both pyo3 and rust-cpython will decrement the refcount on drop.
|
|
86
|
// If we use from_owned_ptr, that's a segfault.
|
|
86
|
// If we use from_owned_ptr, that's a segfault.
|
|
87
|
(py, unsafe { cpython::PyObject::from_borrowed_ptr(py, raw) })
|
|
87
|
(py, unsafe { cpython::PyObject::from_borrowed_ptr(py, raw) })
|
|
88
|
}
|
|
88
|
}
|
|
89
|
|
|
89
|
|
|
90
|
/// Convert a [`cpython::PyObject`] into a PyO3 [`PyObject`]
|
|
90
|
/// Convert a [`cpython::PyObject`] into a PyO3 [`PyObject`]
|
|
91
|
///
|
|
91
|
///
|
|
92
|
/// During this process, the reference count is increased, then decreased.
|
|
92
|
/// During this process, the reference count is increased, then decreased.
|
|
93
|
/// This means that the GIL (symbolized by the PyO3 [`Python`] handle is
|
|
93
|
/// This means that the GIL (symbolized by the PyO3 [`Python`] handle is
|
|
94
|
/// needed.
|
|
94
|
/// needed.
|
|
95
|
///
|
|
95
|
///
|
|
96
|
/// We could make something perhaps more handy by simply stealing the
|
|
96
|
/// We could make something perhaps more handy by simply stealing the
|
|
97
|
/// pointer, forgetting the incoming and then implement `From` with "newtype".
|
|
97
|
/// pointer, forgetting the incoming and then implement `From` with "newtype".
|
|
98
|
/// It would be worth the effort for a generic cpython-to-pyo3 crate, perhaps
|
|
98
|
/// It would be worth the effort for a generic cpython-to-pyo3 crate, perhaps
|
|
99
|
/// not for the current endeavour.
|
|
99
|
/// not for the current endeavour.
|
|
100
|
pub(crate) fn from_cpython_py_object(
|
|
100
|
pub(crate) fn from_cpython_py_object(
|
|
101
|
py: Python<'_>,
|
|
101
|
py: Python<'_>,
|
|
102
|
obj: cpython::PyObject,
|
|
102
|
obj: cpython::PyObject,
|
|
103
|
) -> PyObject {
|
|
103
|
) -> PyObject {
|
|
104
|
let raw = obj.as_ptr() as *mut pyo3::ffi::PyObject;
|
|
104
|
let raw = obj.as_ptr() as *mut pyo3::ffi::PyObject;
|
|
105
|
unsafe { Py::from_borrowed_ptr(py, raw) }
|
|
105
|
unsafe { Py::from_borrowed_ptr(py, raw) }
|
|
106
|
}
|
|
106
|
}
|
|
107
|
|
|
107
|
|
|
108
|
/// Convert [`cpython::PyErr`] into [`pyo3::PyErr`]
|
|
108
|
/// Convert [`cpython::PyErr`] into [`pyo3::PyErr`]
|
|
109
|
///
|
|
109
|
///
|
|
110
|
/// The exception class remains the same as the original exception,
|
|
110
|
/// The exception class remains the same as the original exception,
|
|
111
|
/// hence if it is also defined in another dylib based on `cpython` crate,
|
|
111
|
/// hence if it is also defined in another dylib based on `cpython` crate,
|
|
112
|
/// it will need to be converted to be downcasted in this crate.
|
|
112
|
/// it will need to be converted to be downcasted in this crate.
|
|
113
|
pub(crate) fn from_cpython_pyerr(
|
|
113
|
pub(crate) fn from_cpython_pyerr(
|
|
114
|
py: cpython::Python<'_>,
|
|
114
|
py: cpython::Python<'_>,
|
|
115
|
mut e: cpython::PyErr,
|
|
115
|
mut e: cpython::PyErr,
|
|
116
|
) -> PyErr {
|
|
116
|
) -> PyErr {
|
|
117
|
let pyo3_py = pyo3_handle(py);
|
|
117
|
let pyo3_py = pyo3_handle(py);
|
|
118
|
let cpython_exc_obj = e.instance(py);
|
|
118
|
let cpython_exc_obj = e.instance(py);
|
|
119
|
let pyo3_exc_obj = from_cpython_py_object(pyo3_py, cpython_exc_obj);
|
|
119
|
let pyo3_exc_obj = from_cpython_py_object(pyo3_py, cpython_exc_obj);
|
|
120
|
PyErr::from_value(pyo3_exc_obj.into_bound(pyo3_py))
|
|
120
|
PyErr::from_value(pyo3_exc_obj.into_bound(pyo3_py))
|
|
121
|
}
|
|
121
|
}
|
|
122
|
|
|
122
|
|
|
123
|
/// Retrieve the PyType for objects from the `mercurial.rustext` crate.
|
|
123
|
/// Retrieve the PyType for objects from the `mercurial.rustext` crate.
|
|
124
|
fn retrieve_cpython_py_type(
|
|
124
|
fn retrieve_cpython_py_type(
|
|
125
|
submodule_name: &str,
|
|
125
|
submodule_name: &str,
|
|
126
|
type_name: &str,
|
|
126
|
type_name: &str,
|
|
127
|
) -> cpython::PyResult<cpython::PyType> {
|
|
127
|
) -> cpython::PyResult<cpython::PyType> {
|
|
128
|
let guard = cpython::Python::acquire_gil();
|
|
128
|
let guard = cpython::Python::acquire_gil();
|
|
129
|
let py = guard.python();
|
|
129
|
let py = guard.python();
|
|
130
|
let module = py.import(&format!("mercurial.rustext.{submodule_name}"))?;
|
|
130
|
let module = py.import(&format!("mercurial.rustext.{submodule_name}"))?;
|
|
131
|
module.get(py, type_name)?.extract::<cpython::PyType>(py)
|
|
131
|
module.get(py, type_name)?.extract::<cpython::PyType>(py)
|
|
132
|
}
|
|
132
|
}
|
|
133
|
|
|
133
|
|
|
134
|
lazy_static! {
|
|
134
|
lazy_static! {
|
|
135
|
static ref INNER_REVLOG_PY_TYPE: cpython::PyType = {
|
|
135
|
static ref INNER_REVLOG_PY_TYPE: cpython::PyType = {
|
|
136
|
retrieve_cpython_py_type("revlog", "InnerRevlog")
|
|
136
|
retrieve_cpython_py_type("revlog", "InnerRevlog")
|
|
137
|
.expect("Could not import InnerRevlog in Python")
|
|
137
|
.expect("Could not import InnerRevlog in Python")
|
|
138
|
};
|
|
138
|
};
|
|
139
|
}
|
|
139
|
}
|
|
140
|
|
|
140
|
|
|
141
|
/// Downcast [`InnerRevlog`], with the appropriate Python type checking.
|
|
141
|
/// Downcast [`InnerRevlog`], with the appropriate Python type checking.
|
|
142
|
///
|
|
142
|
///
|
|
143
|
/// The PyType object representing the `InnerRevlog` Python class is not the
|
|
143
|
/// The PyType object representing the `InnerRevlog` Python class is not the
|
|
144
|
/// the same in this dylib as it is in the `mercurial.rustext` module.
|
|
144
|
/// the same in this dylib as it is in the `mercurial.rustext` module.
|
|
145
|
/// This is because the code created with the [`cpython::py_class!`]
|
|
145
|
/// This is because the code created with the [`cpython::py_class!`]
|
|
146
|
/// macro is itself duplicated in both dylibs. In the case of this crate, this
|
|
146
|
/// macro is itself duplicated in both dylibs. In the case of this crate, this
|
|
147
|
/// happens by linking to the [`rusthg`] crate and provides the `InnerRevlog`
|
|
147
|
/// happens by linking to the [`rusthg`] crate and provides the `InnerRevlog`
|
|
148
|
/// that is visible from this crate. The `InnerRevlog::get_type` associated
|
|
148
|
/// that is visible from this crate. The `InnerRevlog::get_type` associated
|
|
149
|
/// function turns out to return a `static mut` (look for `TYPE_OBJECT` in
|
|
149
|
/// function turns out to return a `static mut` (look for `TYPE_OBJECT` in
|
|
150
|
/// `py_class_impl3.rs`), which obviously is different in both dylibs.
|
|
150
|
/// `py_class_impl3.rs`), which obviously is different in both dylibs.
|
|
151
|
///
|
|
151
|
///
|
|
152
|
/// The consequence of that is that downcasting an `InnerRevlog` originally
|
|
152
|
/// The consequence of that is that downcasting an `InnerRevlog` originally
|
|
153
|
/// from the `mecurial.rustext` module to our `InnerRevlog` cannot be done with
|
|
153
|
/// from the `mecurial.rustext` module to our `InnerRevlog` cannot be done with
|
|
154
|
/// the usual `extract::<InnerRevlog>(py)`, as it would perform the type
|
|
154
|
/// the usual `extract::<InnerRevlog>(py)`, as it would perform the type
|
|
155
|
/// checking with the `PyType` that is embedded in `mercurial.pyo3_rustext`.
|
|
155
|
/// checking with the `PyType` that is embedded in `mercurial.pyo3_rustext`.
|
|
156
|
/// We must check the `PyType` that is within `mercurial.rustext` instead.
|
|
156
|
/// We must check the `PyType` that is within `mercurial.rustext` instead.
|
|
157
|
/// This is what this function does.
|
|
157
|
/// This is what this function does.
|
|
158
|
fn extract_inner_revlog(
|
|
158
|
fn extract_inner_revlog(
|
|
159
|
py: cpython::Python,
|
|
159
|
py: cpython::Python,
|
|
160
|
inner_revlog: cpython::PyObject,
|
|
160
|
inner_revlog: cpython::PyObject,
|
|
161
|
) -> PyResult<InnerRevlog> {
|
|
161
|
) -> PyResult<InnerRevlog> {
|
|
162
|
if !(*INNER_REVLOG_PY_TYPE).is_instance(py, &inner_revlog) {
|
|
162
|
if !(*INNER_REVLOG_PY_TYPE).is_instance(py, &inner_revlog) {
|
|
163
|
return Err(PyTypeError::new_err("Not an InnerRevlog instance"));
|
|
163
|
return Err(PyTypeError::new_err("Not an InnerRevlog instance"));
|
|
164
|
}
|
|
164
|
}
|
|
165
|
// Safety: this is safe because we checked the PyType already, with the
|
|
165
|
// Safety: this is safe because we checked the PyType already, with the
|
|
166
|
// value embedded in `mercurial.rustext`.
|
|
166
|
// value embedded in `mercurial.rustext`.
|
|
167
|
Ok(unsafe { InnerRevlog::unchecked_downcast_from(inner_revlog) })
|
|
167
|
Ok(unsafe { InnerRevlog::unchecked_downcast_from(inner_revlog) })
|
|
168
|
}
|
|
168
|
}
|
|
169
|
|
|
169
|
|
|
170
|
/// This is similar to [`rusthg.py_rust_index_to_graph`], with difference in
|
|
170
|
/// This is similar to [`rusthg.py_rust_index_to_graph`], with difference in
|
|
171
|
/// how we retrieve the [`InnerRevlog`].
|
|
171
|
/// how we retrieve the [`InnerRevlog`].
|
|
172
|
pub fn py_rust_index_to_graph(
|
|
172
|
pub fn py_rust_index_to_graph(
|
|
173
|
py: cpython::Python,
|
|
173
|
py: cpython::Python,
|
|
174
|
index_proxy: cpython::PyObject,
|
|
174
|
index_proxy: cpython::PyObject,
|
|
175
|
) -> PyResult<cpython::UnsafePyLeaked<PySharedIndex>> {
|
|
175
|
) -> PyResult<cpython::UnsafePyLeaked<PySharedIndex>> {
|
|
176
|
let inner_revlog = extract_inner_revlog(
|
|
176
|
let inner_revlog = extract_inner_revlog(
|
|
177
|
py,
|
|
177
|
py,
|
|
178
|
index_proxy
|
|
178
|
index_proxy
|
|
179
|
.getattr(py, "inner")
|
|
179
|
.getattr(py, "inner")
|
|
180
|
.map_err(|e| from_cpython_pyerr(py, e))?,
|
|
180
|
.map_err(|e| from_cpython_pyerr(py, e))?,
|
|
181
|
)?;
|
|
181
|
)?;
|
|
182
|
|
|
182
|
|
|
183
|
let leaked = inner_revlog.pub_inner(py).leak_immutable();
|
|
183
|
let leaked = inner_revlog.pub_inner(py).leak_immutable();
|
|
184
|
// Safety: we don't leak the "faked" reference out of the `UnsafePyLeaked`
|
|
184
|
// Safety: we don't leak the "faked" reference out of the `UnsafePyLeaked`
|
|
185
|
Ok(unsafe { leaked.map(py, |idx| PySharedIndex { inner: &idx.index }) })
|
|
185
|
Ok(unsafe { leaked.map(py, |idx| PySharedIndex { inner: &idx.index }) })
|
|
186
|
}
|
|
186
|
}
|
|
187
|
|
|
187
|
|
|
188
|
pub(crate) fn proxy_index_py_leak<'py>(
|
|
188
|
pub(crate) fn proxy_index_py_leak<'py>(
|
|
189
|
index_proxy: &Bound<'py, PyAny>,
|
|
189
|
index_proxy: &Bound<'py, PyAny>,
|
|
190
|
) -> PyResult<(cpython::Python<'py>, cpython::UnsafePyLeaked<PySharedIndex>)> {
|
|
190
|
) -> PyResult<(cpython::Python<'py>, cpython::UnsafePyLeaked<PySharedIndex>)> {
|
|
191
|
let (py, idx_proxy) = to_cpython_py_object(index_proxy);
|
|
191
|
let (py, idx_proxy) = to_cpython_py_object(index_proxy);
|
|
192
|
let py_leaked = py_rust_index_to_graph(py, idx_proxy)?;
|
|
192
|
let py_leaked = py_rust_index_to_graph(py, idx_proxy)?;
|
|
193
|
Ok((py, py_leaked))
|
|
193
|
Ok((py, py_leaked))
|
|
194
|
}
|
|
194
|
}
|
|
195
|
|
|
195
|
|
|
196
|
/// Full extraction of the proxy index object as received in PyO3 to a
|
|
196
|
/// Full extraction of the proxy index object as received in PyO3 to a
|
|
197
|
/// [`CoreIndex`] reference.
|
|
197
|
/// [`CoreIndex`] reference.
|
|
198
|
///
|
|
198
|
///
|
|
199
|
/// # Safety
|
|
199
|
/// # Safety
|
|
200
|
///
|
|
200
|
///
|
|
201
|
/// The invariants to maintain are those of the underlying
|
|
201
|
/// The invariants to maintain are those of the underlying
|
|
202
|
/// [`UnsafePyLeaked::try_borrow`]: the caller must not leak the inner
|
|
202
|
/// [`UnsafePyLeaked::try_borrow`]: the caller must not leak the inner
|
|
203
|
/// reference.
|
|
203
|
/// reference.
|
|
204
|
pub(crate) unsafe fn proxy_index_extract<'py>(
|
|
204
|
pub(crate) unsafe fn proxy_index_extract<'py>(
|
|
205
|
index_proxy: &Bound<'py, PyAny>,
|
|
205
|
index_proxy: &Bound<'py, PyAny>,
|
|
206
|
) -> PyResult<&'py CoreIndex> {
|
|
206
|
) -> PyResult<&'py CoreIndex> {
|
|
207
|
let (py, py_leaked) = proxy_index_py_leak(index_proxy)?;
|
|
207
|
let (py, py_leaked) = proxy_index_py_leak(index_proxy)?;
|
|
208
|
let py_shared = &*unsafe {
|
|
208
|
let py_shared = &*unsafe {
|
|
209
|
py_leaked
|
|
209
|
py_leaked
|
|
210
|
.try_borrow(py)
|
|
210
|
.try_borrow(py)
|
|
211
|
.map_err(|e| from_cpython_pyerr(py, e))?
|
|
211
|
.map_err(|e| from_cpython_pyerr(py, e))?
|
|
212
|
};
|
|
212
|
};
|
|
213
|
Ok(py_shared.inner)
|
|
213
|
Ok(py_shared.inner)
|
|
214
|
}
|
|
214
|
}
|
|
215
|
|
|
215
|
|
|
216
|
/// Generic borrow of [`cpython::UnsafePyLeaked`], with proper mapping.
|
|
216
|
/// Generic borrow of [`cpython::UnsafePyLeaked`], with proper mapping.
|
|
217
|
///
|
|
217
|
///
|
|
218
|
/// # Safety
|
|
218
|
/// # Safety
|
|
219
|
///
|
|
219
|
///
|
|
220
|
/// The invariants to maintain are those of the underlying
|
|
220
|
/// The invariants to maintain are those of the underlying
|
|
221
|
/// [`UnsafePyLeaked::try_borrow`]: the caller must not leak the inner
|
|
221
|
/// [`UnsafePyLeaked::try_borrow`]: the caller must not leak the inner
|
|
222
|
/// static reference. It is possible, depending on `T` that such a leak cannot
|
|
222
|
/// static reference. It is possible, depending on `T` that such a leak cannot
|
|
223
|
/// occur in practice. We may later on define a marker trait for this,
|
|
223
|
/// occur in practice. We may later on define a marker trait for this,
|
|
224
|
/// which will allow us to make declare this function to be safe.
|
|
224
|
/// which will allow us to make declare this function to be safe.
|
|
225
|
#[allow(dead_code)]
|
|
225
|
#[allow(dead_code)]
|
|
226
|
pub(crate) unsafe fn py_leaked_borrow<'a, 'py: 'a, T>(
|
|
226
|
pub(crate) unsafe fn py_leaked_borrow<'a, 'py: 'a, T>(
|
|
227
|
py: &impl WithGIL<'py>,
|
|
227
|
py: &impl WithGIL<'py>,
|
|
228
|
leaked: &'a cpython::UnsafePyLeaked<T>,
|
|
228
|
leaked: &'a cpython::UnsafePyLeaked<T>,
|
|
229
|
) -> PyResult<cpython::PyLeakedRef<'a, T>> {
|
|
229
|
) -> PyResult<cpython::PyLeakedRef<'a, T>> {
|
|
230
|
let py = cpython_handle(py);
|
|
230
|
let py = cpython_handle(py);
|
|
231
|
leaked.try_borrow(py).map_err(|e| from_cpython_pyerr(py, e))
|
|
231
|
leaked.try_borrow(py).map_err(|e| from_cpython_pyerr(py, e))
|
|
232
|
}
|
|
232
|
}
|
|
233
|
|
|
233
|
|
|
234
|
/// Mutable variant of [`py_leaked_borrow`]
|
|
234
|
/// Mutable variant of [`py_leaked_borrow`]
|
|
235
|
///
|
|
235
|
///
|
|
236
|
/// # Safety
|
|
236
|
/// # Safety
|
|
237
|
///
|
|
237
|
///
|
|
238
|
/// See [`py_leaked_borrow`]
|
|
238
|
/// See [`py_leaked_borrow`]
|
|
239
|
#[allow(dead_code)]
|
|
239
|
#[allow(dead_code)]
|
|
240
|
pub(crate) unsafe fn py_leaked_borrow_mut<'a, 'py: 'a, T>(
|
|
240
|
pub(crate) unsafe fn py_leaked_borrow_mut<'a, 'py: 'a, T>(
|
|
241
|
py: &impl WithGIL<'py>,
|
|
241
|
py: &impl WithGIL<'py>,
|
|
242
|
leaked: &'a mut cpython::UnsafePyLeaked<T>,
|
|
242
|
leaked: &'a mut cpython::UnsafePyLeaked<T>,
|
|
243
|
) -> PyResult<cpython::PyLeakedRefMut<'a, T>> {
|
|
243
|
) -> PyResult<cpython::PyLeakedRefMut<'a, T>> {
|
|
244
|
let py = cpython_handle(py);
|
|
244
|
let py = cpython_handle(py);
|
|
245
|
leaked
|
|
245
|
leaked
|
|
246
|
.try_borrow_mut(py)
|
|
246
|
.try_borrow_mut(py)
|
|
247
|
.map_err(|e| from_cpython_pyerr(py, e))
|
|
247
|
.map_err(|e| from_cpython_pyerr(py, e))
|
|
248
|
}
|
|
248
|
}
|
|
249
|
|
|
249
|
|
|
250
|
/// Error propagation for an [`UnsafePyLeaked`] wrapping a [`Result`]
|
|
250
|
/// Error propagation for an [`UnsafePyLeaked`] wrapping a [`Result`]
|
|
251
|
///
|
|
251
|
///
|
|
252
|
/// TODO (will consider when implementing UnsafePyLeaked in PyO3):
|
|
252
|
/// TODO (will consider when implementing UnsafePyLeaked in PyO3):
|
|
253
|
/// It would be nice for UnsafePyLeaked to provide this directly as a variant
|
|
253
|
/// It would be nice for UnsafePyLeaked to provide this directly as a variant
|
|
254
|
/// of the `map` method with a signature such as:
|
|
254
|
/// of the `map` method with a signature such as:
|
|
255
|
///
|
|
255
|
///
|
|
256
|
/// ```
|
|
256
|
/// ```
|
|
257
|
/// unsafe fn map_or_err(&self,
|
|
257
|
/// unsafe fn map_or_err(&self,
|
|
258
|
/// py: Python,
|
|
258
|
/// py: Python,
|
|
259
|
/// f: impl FnOnce(T) -> Result(U, E),
|
|
259
|
/// f: impl FnOnce(T) -> Result(U, E),
|
|
260
|
/// convert_err: impl FnOnce(E) -> PyErr)
|
|
260
|
/// convert_err: impl FnOnce(E) -> PyErr)
|
|
261
|
/// ```
|
|
261
|
/// ```
|
|
262
|
///
|
|
262
|
///
|
|
263
|
/// This would spare users of the `cpython` crate the additional `unsafe` deref
|
|
263
|
/// This would spare users of the `cpython` crate the additional `unsafe` deref
|
|
264
|
/// to inspect the error and return it outside `UnsafePyLeaked`, and the
|
|
264
|
/// to inspect the error and return it outside `UnsafePyLeaked`, and the
|
|
265
|
/// subsequent unwrapping that this function performs.
|
|
265
|
/// subsequent unwrapping that this function performs.
|
|
266
|
#[allow(dead_code)]
|
|
|
|
|
267
|
pub(crate) fn py_leaked_or_map_err<T, E: std::fmt::Debug + Copy>(
|
|
266
|
pub(crate) fn py_leaked_or_map_err<T, E: std::fmt::Debug + Copy>(
|
|
268
|
py: cpython::Python,
|
|
267
|
py: cpython::Python,
|
|
269
|
leaked: cpython::UnsafePyLeaked<Result<T, E>>,
|
|
268
|
leaked: cpython::UnsafePyLeaked<Result<T, E>>,
|
|
270
|
convert_err: impl FnOnce(E) -> PyErr,
|
|
269
|
convert_err: impl FnOnce(E) -> PyErr,
|
|
271
|
) -> PyResult<cpython::UnsafePyLeaked<T>> {
|
|
270
|
) -> PyResult<cpython::UnsafePyLeaked<T>> {
|
|
272
|
// Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
|
|
271
|
// Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
|
|
273
|
if let Err(e) = *unsafe {
|
|
272
|
if let Err(e) = *unsafe {
|
|
274
|
leaked
|
|
273
|
leaked
|
|
275
|
.try_borrow(py)
|
|
274
|
.try_borrow(py)
|
|
276
|
.map_err(|e| from_cpython_pyerr(py, e))?
|
|
275
|
.map_err(|e| from_cpython_pyerr(py, e))?
|
|
277
|
} {
|
|
276
|
} {
|
|
278
|
return Err(convert_err(e));
|
|
277
|
return Err(convert_err(e));
|
|
279
|
}
|
|
278
|
}
|
|
280
|
// Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
|
|
279
|
// Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
|
|
281
|
Ok(unsafe {
|
|
280
|
Ok(unsafe {
|
|
282
|
leaked.map(py, |res| {
|
|
281
|
leaked.map(py, |res| {
|
|
283
|
res.expect("Error case should have already be treated")
|
|
282
|
res.expect("Error case should have already be treated")
|
|
284
|
})
|
|
283
|
})
|
|
285
|
})
|
|
284
|
})
|
|
286
|
}
|
|
285
|
}
|