Skip to content

Commit 726d657

Browse files
committed
use Py2Borrowed to make PyBytesMethods slightly nicer
1 parent c5dce01 commit 726d657

File tree

3 files changed

+159
-9
lines changed

3 files changed

+159
-9
lines changed

src/ffi_ptr_ext.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use crate::{ffi, instance::Py2, PyAny, PyResult, Python};
1+
use crate::{
2+
ffi,
3+
instance::{Py2, Py2Borrowed},
4+
PyAny, PyResult, Python,
5+
};
26

37
mod sealed {
48
use super::*;
@@ -13,6 +17,24 @@ use sealed::Sealed;
1317
pub(crate) trait FfiPtrExt: Sealed {
1418
unsafe fn assume_owned_or_err(self, py: Python<'_>) -> PyResult<Py2<'_, PyAny>>;
1519
unsafe fn assume_owned(self, py: Python<'_>) -> Py2<'_, PyAny>;
20+
21+
/// Assumes this pointer is borrowed from a parent object.
22+
///
23+
/// Warning: the lifetime `'a` is not bounded by the function arguments; the caller is
24+
/// responsible to ensure this is tied to some appropriate lifetime.
25+
unsafe fn assume_borrowed_or_err<'a, 'py>(
26+
self,
27+
py: Python<'py>,
28+
) -> PyResult<Py2Borrowed<'a, 'py, PyAny>>;
29+
30+
/// Same as `assume_borrowed_or_err`, but doesn't fetch an error on NULL.
31+
unsafe fn assume_borrowed_or_opt<'a, 'py>(
32+
self,
33+
py: Python<'py>,
34+
) -> Option<Py2Borrowed<'a, 'py, PyAny>>;
35+
36+
/// Same as `assume_borrowed_or_err`, but panics on NULL.
37+
unsafe fn assume_borrowed<'a, 'py>(self, py: Python<'py>) -> Py2Borrowed<'a, 'py, PyAny>;
1638
}
1739

1840
impl FfiPtrExt for *mut ffi::PyObject {
@@ -25,4 +47,25 @@ impl FfiPtrExt for *mut ffi::PyObject {
2547
unsafe fn assume_owned(self, py: Python<'_>) -> Py2<'_, PyAny> {
2648
Py2::from_owned_ptr(py, self)
2749
}
50+
51+
#[inline]
52+
unsafe fn assume_borrowed_or_err<'a, 'py>(
53+
self,
54+
py: Python<'py>,
55+
) -> PyResult<Py2Borrowed<'a, 'py, PyAny>> {
56+
Py2Borrowed::from_ptr_or_err(py, self)
57+
}
58+
59+
#[inline]
60+
unsafe fn assume_borrowed_or_opt<'a, 'py>(
61+
self,
62+
py: Python<'py>,
63+
) -> Option<Py2Borrowed<'a, 'py, PyAny>> {
64+
Py2Borrowed::from_ptr_or_opt(py, self)
65+
}
66+
67+
#[inline]
68+
unsafe fn assume_borrowed<'a, 'py>(self, py: Python<'py>) -> Py2Borrowed<'a, 'py, PyAny> {
69+
Py2Borrowed::from_ptr(py, self)
70+
}
2871
}

src/instance.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ impl<'py> Py2<'py, PyAny> {
6767
) -> PyResult<Self> {
6868
Py::from_owned_ptr_or_err(py, ptr).map(|obj| Self(py, ManuallyDrop::new(obj)))
6969
}
70+
71+
/// Constructs a new Py2 from a pointer. Does not check for null.
72+
pub(crate) unsafe fn from_owned_ptr_unchecked(
73+
py: Python<'py>,
74+
ptr: *mut ffi::PyObject,
75+
) -> Self {
76+
Self(
77+
py,
78+
ManuallyDrop::new(Py(NonNull::new_unchecked(ptr), PhantomData)),
79+
)
80+
}
7081
}
7182

7283
impl<'py, T> Py2<'py, T> {
@@ -217,6 +228,96 @@ unsafe impl<T> AsPyPointer for Py2<'_, T> {
217228
}
218229
}
219230

231+
/// A borrowed equivalent to `Py2`.
232+
///
233+
/// The advantage of this over `&Py2` is that it avoids the need to have a pointer-to-pointer, as Py2
234+
/// is already a pointer to an `ffi::PyObject``.
235+
#[repr(transparent)]
236+
pub(crate) struct Py2Borrowed<'a, 'py, T>(
237+
NonNull<ffi::PyObject>,
238+
PhantomData<&'a Py<T>>,
239+
Python<'py>,
240+
);
241+
242+
impl<'a, 'py> Py2Borrowed<'a, 'py, PyAny> {
243+
/// # Safety
244+
/// This is similar to `std::slice::from_raw_parts`, the lifetime `'a` is completely defined by
245+
/// the caller and it's the caller's responsibility to ensure that the reference this is
246+
/// derived from is valid for the lifetime `'a`.
247+
pub(crate) unsafe fn from_ptr_or_err(
248+
py: Python<'py>,
249+
ptr: *mut ffi::PyObject,
250+
) -> PyResult<Self> {
251+
NonNull::new(ptr).map_or_else(
252+
|| Err(PyErr::fetch(py)),
253+
|ptr| Ok(Self(ptr, PhantomData, py)),
254+
)
255+
}
256+
257+
/// # Safety
258+
/// This is similar to `std::slice::from_raw_parts`, the lifetime `'a` is completely defined by
259+
/// the caller and it's the caller's responsibility to ensure that the reference this is
260+
/// derived from is valid for the lifetime `'a`.
261+
pub(crate) unsafe fn from_ptr_or_opt(py: Python<'py>, ptr: *mut ffi::PyObject) -> Option<Self> {
262+
NonNull::new(ptr).map(|ptr| Self(ptr, PhantomData, py))
263+
}
264+
265+
/// # Safety
266+
/// This is similar to `std::slice::from_raw_parts`, the lifetime `'a` is completely defined by
267+
/// the caller and it's the caller's responsibility to ensure that the reference this is
268+
/// derived from is valid for the lifetime `'a`.
269+
pub(crate) unsafe fn from_ptr(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
270+
Self(
271+
NonNull::new(ptr).unwrap_or_else(|| crate::err::panic_after_error(py)),
272+
PhantomData,
273+
py,
274+
)
275+
}
276+
}
277+
278+
impl<'a, 'py, T> From<&'a Py2<'py, T>> for Py2Borrowed<'a, 'py, T> {
279+
/// Create borrow on a Py2
280+
fn from(instance: &'a Py2<'py, T>) -> Self {
281+
Self(
282+
unsafe { NonNull::new_unchecked(instance.as_ptr()) },
283+
PhantomData,
284+
instance.py(),
285+
)
286+
}
287+
}
288+
289+
impl<'py, T> Py2Borrowed<'py, 'py, T>
290+
where
291+
T: HasPyGilRef,
292+
{
293+
pub(crate) fn from_gil_ref(gil_ref: &'py T::AsRefTarget) -> Self {
294+
// Safety: &'py T::AsRefTarget is expected to be a Python pointer,
295+
// so &'py T::AsRefTarget has the same layout as Self.
296+
unsafe { std::mem::transmute(gil_ref) }
297+
}
298+
299+
// pub(crate) fn into_gil_ref(self) -> &'py T::AsRefTarget {
300+
// // Safety: self is a borrow over `'py`.
301+
// unsafe { self.py().from_borrowed_ptr(self.0.as_ptr()) }
302+
// }
303+
}
304+
305+
impl<T> std::fmt::Debug for Py2Borrowed<'_, '_, T> {
306+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307+
Py2::fmt(self, f)
308+
}
309+
}
310+
311+
impl<'py, T> Deref for Py2Borrowed<'_, 'py, T> {
312+
type Target = Py2<'py, T>;
313+
314+
#[inline]
315+
fn deref(&self) -> &Py2<'py, T> {
316+
// safety: Py2 has the same layout as NonNull<ffi::PyObject>
317+
unsafe { &*(&self.0 as *const _ as *const Py2<'py, T>) }
318+
}
319+
}
320+
220321
/// A GIL-independent reference to an object allocated on the Python heap.
221322
///
222323
/// This type does not auto-dereference to the inner object because you must prove you hold the GIL to access it.
@@ -727,6 +828,10 @@ impl<T> Py<T> {
727828
Py2(py, ManuallyDrop::new(self))
728829
}
729830

831+
pub(crate) fn attach_borrow<'a, 'py>(&'a self, py: Python<'py>) -> Py2Borrowed<'a, 'py, T> {
832+
Py2Borrowed(self.0, PhantomData, py)
833+
}
834+
730835
/// Returns whether `self` and `other` point to the same object. To compare
731836
/// the equality of two objects (the `==` operator), use [`eq`](PyAny::eq).
732837
///

src/types/bytes.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::instance::Py2;
1+
use crate::instance::{Py2, Py2Borrowed};
22
use crate::{ffi, FromPyObject, IntoPy, Py, PyAny, PyResult, Python, ToPyObject};
33
use std::borrow::Cow;
44
use std::ops::Index;
@@ -89,9 +89,7 @@ impl PyBytes {
8989
/// Gets the Python string as a byte slice.
9090
#[inline]
9191
pub fn as_bytes(&self) -> &[u8] {
92-
let slice = Py2::borrowed_from_gil_ref(&self).as_bytes();
93-
// SAFETY: &self guarantees the reference is alive long enough
94-
unsafe { std::slice::from_raw_parts(slice.as_ptr(), slice.len()) }
92+
Py2Borrowed::from_gil_ref(self).as_bytes()
9593
}
9694
}
9795

@@ -109,6 +107,13 @@ pub(crate) trait PyBytesMethods<'py> {
109107
impl<'py> PyBytesMethods<'py> for Py2<'py, PyBytes> {
110108
#[inline]
111109
fn as_bytes(&self) -> &[u8] {
110+
Py2Borrowed::from(self).as_bytes()
111+
}
112+
}
113+
114+
impl<'a> Py2Borrowed<'a, '_, PyBytes> {
115+
/// Gets the Python string as a byte slice.
116+
fn as_bytes(self) -> &'a [u8] {
112117
unsafe {
113118
let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8;
114119
let length = ffi::PyBytes_Size(self.as_ptr()) as usize;
@@ -123,10 +128,7 @@ impl Py<PyBytes> {
123128
/// immutable, the result may be used for as long as the reference to
124129
/// `self` is held, including when the GIL is released.
125130
pub fn as_bytes<'a>(&'a self, py: Python<'_>) -> &'a [u8] {
126-
let slice = self.attach(py).as_bytes();
127-
// SAFETY: it is safe to access the immutable slice as long as the
128-
// reference to `self` is held.
129-
unsafe { std::slice::from_raw_parts(slice.as_ptr(), slice.len()) }
131+
self.attach_borrow(py).as_bytes()
130132
}
131133
}
132134

0 commit comments

Comments
 (0)