Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/5870.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `obj()` getter to `PyUntypedBuffer` to retrieve the Python object owning the buffer.
41 changes: 41 additions & 0 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,24 @@ impl PyUntypedBuffer {
self.raw().buf
}

///Returns the Python object that owns the buffer data.
///
///This is the object that was passed to [`PyBuffer::get()`] when the buffer was created.
/// Returns the Python object that owns the buffer data.
///
/// This is the object passed to [`PyUntypedBuffer::get()`].
Comment on lines +573 to +578
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, applying the copilot suggestion messed things up a bit.

Suggested change
///Returns the Python object that owns the buffer data.
///
///This is the object that was passed to [`PyBuffer::get()`] when the buffer was created.
/// Returns the Python object that owns the buffer data.
///
/// This is the object passed to [`PyUntypedBuffer::get()`].
/// Returns the Python object that owns the buffer data.
///
/// This is the object that was passed to [`PyBuffer::get()`]
/// when the buffer was created.

/// Calling this before [`release()`][Self::release] allows you to clone an owned reference
/// and keeps the object alive after the buffer is released.
pub fn obj<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyAny>> {
let ptr = self.raw().obj;
// SAFETY: Py_buffer.obj is a borrowed reference to a Python object, so it is always valid to create a Bound from it
if ptr.is_null() {
None
} else {
Some(unsafe { Bound::from_borrowed_ptr(py, ptr) })
}
Comment on lines +582 to +588
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use Bound::from_borrowed_ptr_or_opt here to simplify the null checking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Thanks!

}

/// Gets a pointer to the specified item.
///
/// If `indices.len() < self.dimensions()`, returns the start address of the sub-array at the specified dimension.
Expand Down Expand Up @@ -1044,4 +1062,27 @@ mod tests {
assert_eq!(typed.shape(), [5]);
});
}

#[test]
fn test_obj_getter() {
Python::attach(|py| {
let bytes = PyBytes::new(py, b"hello");
let buf = PyUntypedBuffer::get(bytes.as_any()).unwrap();

// obj() returns the same object that owns the buffer
let owner = buf.obj(py).unwrap();
assert!(owner.is_instance_of::<PyBytes>());
assert!(owner.is(&bytes));

// can keep the owner alive after releasing the buffer
let owner_ref: crate::Py<PyAny> = owner.unbind();
buf.release(py);
drop(bytes);
// owner_ref still valid after buffer and original are dropped
Python::attach(|py| {
let rebound = owner_ref.bind(py);
assert!(rebound.is_instance_of::<PyBytes>());
});
});
}
}
Loading