-
Notifications
You must be signed in to change notification settings - Fork 804
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Add PyWeakref_GetRef and use it in weakref wrappers. #4528
Changes from 7 commits
49891c0
806ff8b
e6d7928
d73b0fe
835b3e7
8fab6a6
dd8236d
77c35c6
b4a4e12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
* Added bindings for `pyo3_ffi::PyWeakref_GetRef` on Python 3.13 and newer and | ||
`py03_ffi::compat::PyWeakref_GetRef` for older Python versions. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
use crate::err::{DowncastError, PyResult}; | ||
use crate::ffi_ptr_ext::FfiPtrExt; | ||
use crate::type_object::{PyTypeCheck, PyTypeInfo}; | ||
use crate::types::any::{PyAny, PyAnyMethods}; | ||
use crate::types::{ | ||
any::{PyAny, PyAnyMethods}, | ||
PyNone, | ||
}; | ||
use crate::{ffi, Borrowed, Bound}; | ||
|
||
/// Represents any Python `weakref` reference. | ||
|
@@ -34,7 +37,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// Upgrade the weakref to a direct Bound object reference. | ||
/// | ||
/// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). | ||
/// In Python it would be equivalent to [`PyWeakref_GetObject`]. | ||
/// In Python it would be equivalent to [`PyWeakref_GetRef`]. | ||
/// | ||
/// # Example | ||
#[cfg_attr( | ||
|
@@ -94,7 +97,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function panics is the current object is invalid. | ||
/// If used propperly this is never the case. (NonNull and actually a weakref type) | ||
/// | ||
/// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject | ||
/// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef | ||
/// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType | ||
/// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref | ||
fn upgrade_as<T>(&self) -> PyResult<Option<Bound<'py, T>>> | ||
|
@@ -191,7 +194,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// Upgrade the weakref to a direct Bound object reference unchecked. The type of the recovered object is not checked before downcasting, this could lead to unexpected behavior. Use only when absolutely certain the type can be guaranteed. The `weakref` may still return `None`. | ||
/// | ||
/// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). | ||
/// In Python it would be equivalent to [`PyWeakref_GetObject`]. | ||
/// In Python it would be equivalent to [`PyWeakref_GetRef`]. | ||
/// | ||
/// # Safety | ||
/// Callers must ensure that the type is valid or risk type confusion. | ||
|
@@ -255,7 +258,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function panics is the current object is invalid. | ||
/// If used propperly this is never the case. (NonNull and actually a weakref type) | ||
/// | ||
/// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject | ||
/// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef | ||
/// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType | ||
/// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref | ||
unsafe fn upgrade_as_unchecked<T>(&self) -> Option<Bound<'py, T>> { | ||
|
@@ -342,7 +345,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// Upgrade the weakref to a exact direct Bound object reference. | ||
/// | ||
/// It is named `upgrade` to be inline with [rust's `Weak::upgrade`](std::rc::Weak::upgrade). | ||
/// In Python it would be equivalent to [`PyWeakref_GetObject`]. | ||
/// In Python it would be equivalent to [`PyWeakref_GetRef`]. | ||
/// | ||
/// # Example | ||
#[cfg_attr( | ||
|
@@ -402,7 +405,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function panics is the current object is invalid. | ||
/// If used propperly this is never the case. (NonNull and actually a weakref type) | ||
/// | ||
/// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject | ||
/// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef | ||
/// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType | ||
/// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref | ||
fn upgrade_as_exact<T>(&self) -> PyResult<Option<Bound<'py, T>>> | ||
|
@@ -502,7 +505,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function returns `Some(Bound<'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. | ||
/// | ||
/// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). | ||
/// It produces similair results to using [`PyWeakref_GetObject`] in the C api. | ||
/// It produces similar results to using [`PyWeakref_GetRef`] in the C api. | ||
/// | ||
/// # Example | ||
#[cfg_attr( | ||
|
@@ -553,7 +556,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function panics is the current object is invalid. | ||
/// If used propperly this is never the case. (NonNull and actually a weakref type) | ||
/// | ||
/// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject | ||
/// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef | ||
/// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType | ||
/// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref | ||
fn upgrade(&self) -> Option<Bound<'py, PyAny>> { | ||
|
@@ -572,7 +575,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function returns `Some(Borrowed<'_, 'py, PyAny>)` if the reference still exists, otherwise `None` will be returned. | ||
/// | ||
/// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). | ||
/// It produces similair results to using [`PyWeakref_GetObject`] in the C api. | ||
/// It produces similar results to using [`PyWeakref_GetObject`] in the C api. | ||
/// | ||
/// # Example | ||
#[cfg_attr( | ||
|
@@ -644,7 +647,7 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function returns `Bound<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone). | ||
/// | ||
/// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). | ||
/// It produces similair results to using [`PyWeakref_GetObject`] in the C api. | ||
/// It produces similar results to using [`PyWeakref_GetRef`] in the C api. | ||
/// | ||
/// # Example | ||
#[cfg_attr( | ||
|
@@ -692,20 +695,17 @@ pub trait PyWeakrefMethods<'py> { | |
/// This function panics is the current object is invalid. | ||
/// If used propperly this is never the case. (NonNull and actually a weakref type) | ||
/// | ||
/// [`PyWeakref_GetObject`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetObject | ||
/// [`PyWeakref_GetRef`]: https://docs.python.org/3/c-api/weakref.html#c.PyWeakref_GetRef | ||
/// [`weakref.ReferenceType`]: https://docs.python.org/3/library/weakref.html#weakref.ReferenceType | ||
/// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref | ||
fn get_object(&self) -> Bound<'py, PyAny> { | ||
// PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. | ||
self.get_object_borrowed().to_owned() | ||
} | ||
fn get_object(&self) -> Bound<'py, PyAny>; | ||
|
||
/// Retrieve to a Borrowed object pointed to by the weakref. | ||
/// | ||
/// This function returns `Borrowed<'py, PyAny>`, which is either the object if it still exists, otherwise it will refer to [`PyNone`](crate::types::PyNone). | ||
/// | ||
/// This function gets the optional target of this [`weakref.ReferenceType`] (result of calling [`weakref.ref`]). | ||
/// It produces similair results to using [`PyWeakref_GetObject`] in the C api. | ||
/// It produces similar results to using [`PyWeakref_GetObject`] in the C api. | ||
/// | ||
/// # Example | ||
#[cfg_attr( | ||
|
@@ -758,15 +758,26 @@ pub trait PyWeakrefMethods<'py> { | |
/// [`weakref.ref`]: https://docs.python.org/3/library/weakref.html#weakref.ref | ||
#[track_caller] | ||
// TODO: This function is the reason every function tracks caller, however it only panics when the weakref object is not actually a weakreference type. So is it this neccessary? | ||
// TODO: we should deprecate this because it relies on the semantics of a deprecated C API item | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I think this is fundamentally unsafe even not on the freethreaded build because a GIL thread switch could theoretically remove the last strong reference, and using this borrow further would then be a use-after-free? So I think we have to
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a number of other functions that rely on the semantics of |
||
fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny>; | ||
} | ||
|
||
impl<'py> PyWeakrefMethods<'py> for Bound<'py, PyWeakref> { | ||
fn get_object_borrowed(&self) -> Borrowed<'_, 'py, PyAny> { | ||
// PyWeakref_GetObject does some error checking, however we ensure the passed object is Non-Null and a Weakref type. | ||
#![allow(deprecated)] | ||
unsafe { ffi::PyWeakref_GetObject(self.as_ptr()).assume_borrowed_or_err(self.py()) } | ||
.expect("The 'weakref' weak reference instance should be valid (non-null and actually a weakref reference)") | ||
} | ||
|
||
fn get_object(&self) -> Bound<'py, PyAny> { | ||
let mut obj: *mut ffi::PyObject = std::ptr::null_mut(); | ||
match unsafe { ffi::compat::PyWeakref_GetRef(self.as_ptr(), &mut obj) } { | ||
std::os::raw::c_int::MIN..=-1 => panic!("The 'weakref' weak reference instance should be valid (non-null and actually a weakref reference)"), | ||
0 => PyNone::get(self.py()).to_owned().into_any(), | ||
1..=std::os::raw::c_int::MAX => unsafe { obj.assume_owned(self.py()) }, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you'll need to keep this and tell clippy to ignore the warning on 0.22.4? Or I guess just backport the compat function too.