issues-2711 Add PyMemoryView#2792
Conversation
| pyobject_native_type_core!(PyMemoryView<T>, ffi::PyMemoryView_Type, #checkfunction=ffi::PyMemoryView_Check; T); | ||
|
|
||
| impl PyMemoryView<u8> { | ||
| pub fn new<'p>(py: Python<'p>, memory: &mut [u8], is_writable: bool) -> &'p PyMemoryView<u8> { |
There was a problem hiding this comment.
Unfortunately this API isn't safe - the memory would have to be borrowed for as long as the returned memoryview lives, but that can't be encoded since Python objects can't have associated lifetimes (except for the 'py pseudo-lifetime).
It might be possible to offer it as unsafe fn with appropriate documentation.
There was a problem hiding this comment.
Agreed this isn't safe as-is. We technically already have the unsafe fn in from_ptr. It would also be very hard to use correctly, as if you pass this object to other Python code it's extremely likely this memoryview may live longer than you expect.
A safe alternative may be to accept &'static [u8], although that impairs usability a bit.
Maybe the PyMemoryView::from already started below is the thing we should encourage users to use (they can then wrap their buffers in #[pyclass] types which expose buffer access).
davidhewitt
left a comment
There was a problem hiding this comment.
Hello! Sorry for the long delay in reviewing this, I was a bit overloaded before the Christmas holidays.
Thank you for working on this. This is a great start, however we need to adjust some of the APIs a bit before this can be ready for merge. See other comments 🙂
|
|
||
| /// Represents a Python `memoryview`. | ||
| #[repr(transparent)] | ||
| pub struct PyMemoryView<T>(PyAny, PhantomData<T>); |
There was a problem hiding this comment.
The choice to make this generic is interesting, and I've been thinking about this further. At the moment none of PyO3's types (other than buffer) are generic. Also PyMemoryView_Check won't check the generic type is correct, which makes it easy for this type to hold memory views with the wrong T.
For these reasons I think it's better if we remove the generic type from this and just have PyMemoryView.
| pyobject_native_type_core!(PyMemoryView<T>, ffi::PyMemoryView_Type, #checkfunction=ffi::PyMemoryView_Check; T); | ||
|
|
||
| impl PyMemoryView<u8> { | ||
| pub fn new<'p>(py: Python<'p>, memory: &mut [u8], is_writable: bool) -> &'p PyMemoryView<u8> { |
There was a problem hiding this comment.
Agreed this isn't safe as-is. We technically already have the unsafe fn in from_ptr. It would also be very hard to use correctly, as if you pass this object to other Python code it's extremely likely this memoryview may live longer than you expect.
A safe alternative may be to accept &'static [u8], although that impairs usability a bit.
Maybe the PyMemoryView::from already started below is the thing we should encourage users to use (they can then wrap their buffers in #[pyclass] types which expose buffer access).
| /// This function dereferences the raw pointer `ptr` as the | ||
| /// leading pointer of a slice of length `len`. [As with | ||
| /// `std::slice::from_raw_parts`, this is | ||
| /// unsafe](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety). |
There was a problem hiding this comment.
Also needs to have a safety note requiring that the created memory view does not outlive the data in ptr. This is extremely hard to use safely!
|
|
||
| /// Creates a new Python `memoryview` object from another Python object that | ||
| /// implements the buffer protocol. | ||
| pub fn from<'p, I>(py: Python<'p>, src: &'p I) -> PyResult<&'p PyMemoryView<T>> |
There was a problem hiding this comment.
I might be tempted to just have pub fn from(&PyAny) -> PyResult<&PyMemoryView> to simplify the signature a bit.
| pub fn len(&self) -> usize { | ||
| unsafe { | ||
| let size = ffi::Py_SIZE(self.as_ptr()); | ||
| // non-negative Py_ssize_t should always fit into Rust usize | ||
| size as usize | ||
| } | ||
| } |
There was a problem hiding this comment.
I think this always returns length in u8, not units of T? So again this looks ok, but only with untyped PyMemoryView.
|
|
||
| /// Gets the Python string as a byte slice. | ||
| #[inline] | ||
| pub fn as_buffer(&self) -> PyResult<PyBuffer<T>> { |
There was a problem hiding this comment.
... and with untyped PyMemoryView, this can still be generic:
| pub fn as_buffer(&self) -> PyResult<PyBuffer<T>> { | |
| pub fn as_buffer<T>(&self) -> PyResult<PyBuffer<T>> { |
| { | ||
| unsafe { py.from_owned_ptr_or_err(ffi::PyMemoryView_FromObject(src.as_ptr())) } | ||
| } | ||
|
|
There was a problem hiding this comment.
There is possibly also scope to have PyMemoryView::from_buffer which takes PyBuffer as input.
There was a problem hiding this comment.
@davidhewitt hmm, I have a question: how I can as_ptr() for PyBuffer?
There was a problem hiding this comment.
See https://docs.python.org/3/c-api/memoryview.html#c.PyMemoryView_FromBuffer - no need to .as_ptr() the buffer structure
c2d8c96 to
2ed62af
Compare
|
@birkenfeld @davidhewitt hello! Sorry for the delay... I extended PyMemoryView and added some new methods. |
| unsafe { self.downcast_unchecked() } | ||
| } | ||
|
|
||
| /// Gets the Python string as a byte slice. |
There was a problem hiding this comment.
Comment here looks out of date.
|
|
||
| /// Gets the Python string as a byte slice. | ||
| #[inline] | ||
| pub fn as_buffer<T: Element>(&self) -> PyResult<PyBuffer<T>> { |
There was a problem hiding this comment.
I'm trying to think, is there a memory safety concern casting from bytes to arbitrary elements T here? Potentially as long as the buffer format is checked, it's fine? We require Element: Copy which implies these types are all POD and don't own heap allocations to trigger use-after-free etc
There was a problem hiding this comment.
If there is, it was already be present in PyBuffer::get, wasn't it? And that one does indeed check the buffer format using Element::is_compatible_format.
What I would like to see is a unit test to check whether this actually works for anything but PyBuffer<u8>.
| { | ||
| unsafe { py.from_owned_ptr_or_err(ffi::PyMemoryView_FromObject(src.as_ptr())) } | ||
| } | ||
|
|
There was a problem hiding this comment.
See https://docs.python.org/3/c-api/memoryview.html#c.PyMemoryView_FromBuffer - no need to .as_ptr() the buffer structure
CodSpeed Performance ReportMerging #2792 will not alter performanceComparing Summary
|
|
This has been stale for so long and we have the basic type merged now, will close this and defer possible improvements for a future time. |
Closed: #2711.