-
Notifications
You must be signed in to change notification settings - Fork 779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add bound
method variants for PyTypeInfo
#3782
Changes from all commits
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 |
---|---|---|
@@ -1,7 +1,9 @@ | ||
//! Python type object information | ||
use crate::ffi_ptr_ext::FfiPtrExt; | ||
use crate::types::any::PyAnyMethods; | ||
use crate::types::{PyAny, PyType}; | ||
use crate::{ffi, PyNativeType, Python}; | ||
use crate::{ffi, Bound, PyNativeType, Python}; | ||
|
||
/// `T: PyLayout<U>` represents that `T` is a concrete representation of `U` in the Python heap. | ||
/// E.g., `PyCell` is a concrete representation of all `pyclass`es, and `ffi::PyObject` | ||
|
@@ -64,19 +66,71 @@ pub unsafe trait PyTypeInfo: Sized + HasPyGilRef { | |
|
||
/// Returns the safe abstraction over the type object. | ||
#[inline] | ||
#[cfg_attr( | ||
not(feature = "gil-refs"), | ||
deprecated( | ||
since = "0.21.0", | ||
note = "`PyTypeInfo::type_object` will be replaced by `PyTypeInfo::type_object_bound` in a future PyO3 version" | ||
) | ||
)] | ||
fn type_object(py: Python<'_>) -> &PyType { | ||
// This isn't implemented in terms of `type_object_bound` because this just borrowed the | ||
// object, for legacy reasons. | ||
unsafe { py.from_borrowed_ptr(Self::type_object_raw(py) as _) } | ||
} | ||
|
||
/// Returns the safe abstraction over the type object. | ||
#[inline] | ||
fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType> { | ||
// Making the borrowed object `Bound` is necessary for soundness reasons. It's an extreme | ||
// edge case, but arbitrary Python code _could_ change the __class__ of an object and cause | ||
// the type object to be freed. | ||
// | ||
// By making `Bound` we assume ownership which is then safe against races. | ||
unsafe { | ||
Self::type_object_raw(py) | ||
.cast::<ffi::PyObject>() | ||
.assume_borrowed_unchecked(py) | ||
.to_owned() | ||
.downcast_into_unchecked() | ||
} | ||
} | ||
|
||
/// Checks if `object` is an instance of this type or a subclass of this type. | ||
#[inline] | ||
#[cfg_attr( | ||
not(feature = "gil-refs"), | ||
deprecated( | ||
since = "0.21.0", | ||
note = "`PyTypeInfo::is_type_of` will be replaced by `PyTypeInfo::is_type_of_bound` in a future PyO3 version" | ||
) | ||
)] | ||
fn is_type_of(object: &PyAny) -> bool { | ||
Self::is_type_of_bound(&object.as_borrowed()) | ||
} | ||
|
||
/// Checks if `object` is an instance of this type or a subclass of this type. | ||
#[inline] | ||
fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool { | ||
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 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. Hmm, I'm a bit stuck on that too. I don't really like either option, so my thinking was to stick with 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. I tend to agree, they both read a bit odd, so its probably better to stick with the system we've introduced |
||
unsafe { ffi::PyObject_TypeCheck(object.as_ptr(), Self::type_object_raw(object.py())) != 0 } | ||
} | ||
|
||
/// Checks if `object` is an instance of this type. | ||
#[inline] | ||
#[cfg_attr( | ||
not(feature = "gil-refs"), | ||
deprecated( | ||
since = "0.21.0", | ||
note = "`PyTypeInfo::is_exact_type_of` will be replaced by `PyTypeInfo::is_exact_type_of_bound` in a future PyO3 version" | ||
) | ||
)] | ||
fn is_exact_type_of(object: &PyAny) -> bool { | ||
Self::is_exact_type_of_bound(&object.as_borrowed()) | ||
} | ||
|
||
/// Checks if `object` is an instance of this type. | ||
#[inline] | ||
fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool { | ||
unsafe { ffi::Py_TYPE(object.as_ptr()) == Self::type_object_raw(object.py()) } | ||
} | ||
} | ||
|
@@ -89,7 +143,7 @@ pub trait PyTypeCheck: HasPyGilRef { | |
/// Checks if `object` is an instance of `Self`, which may include a subtype. | ||
/// | ||
/// This should be equivalent to the Python expression `isinstance(object, Self)`. | ||
fn type_check(object: &PyAny) -> bool; | ||
fn type_check(object: &Bound<'_, PyAny>) -> bool; | ||
} | ||
|
||
impl<T> PyTypeCheck for T | ||
|
@@ -99,8 +153,8 @@ where | |
const NAME: &'static str = <T as PyTypeInfo>::NAME; | ||
|
||
#[inline] | ||
fn type_check(object: &PyAny) -> bool { | ||
<T as PyTypeInfo>::is_type_of(object) | ||
fn type_check(object: &Bound<'_, PyAny>) -> bool { | ||
T::is_type_of_bound(object) | ||
} | ||
} | ||
|
||
|
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 considered whether to make
type_object_bound
,is_type_of_bound
andis_exact_type_of_bound
have defaults implemented in terms of their original methods (or vice versa).Given that we don't expect users to be implementing
PyTypeInfo
, I decided it was probably simpler to just give them all defaults which match their final expected forms.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 agree that the new methods should be implemented in their final form. Whats the reason not reimplementing the old API in terms of the new one?
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.
That's a very good point, I will go ahead and do that 👍
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.
Ah, there is one reason:
type_object_bound
now returns an ownedBound
instead of a borrowed gil-ref, which I think is correct for the safety reasoning I express in the note below.But
is_type_of
andis_exact_type_of
can be implemented in terms of their new replacements 👍