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/5472.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate `PyAnyMethods::downcast` functions in favour of `Bound::cast` functions
12 changes: 8 additions & 4 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2186,7 +2186,7 @@ impl Py<PyAny> {
/// })
/// # }
/// ```
// FIXME(icxolu) deprecate in favor of `Py::cast_bound`
#[deprecated(since = "0.27.0", note = "use `Py::cast_bound` instead")]
#[inline]
pub fn downcast_bound<'py, T>(
&self,
Expand All @@ -2195,18 +2195,22 @@ impl Py<PyAny> {
where
T: PyTypeCheck,
{
self.cast_bound(py)
#[allow(deprecated)]
self.bind(py).downcast()
}

/// Casts the `Py<PyAny>` to a concrete Python object type without checking validity.
///
/// # Safety
///
/// Callers must ensure that the type is valid or risk type confusion.
// FIXME(icxolu) deprecate in favor of `Py::cast_bound_unchecked`
#[deprecated(since = "0.27.0", note = "use `Py::cast_bound_unchecked` instead")]
#[inline]
pub unsafe fn downcast_bound_unchecked<'py, T>(&self, py: Python<'py>) -> &Bound<'py, T> {
unsafe { self.cast_bound_unchecked(py) }
#[allow(deprecated)]
unsafe {
self.bind(py).downcast_unchecked()
}
}
}

Expand Down
44 changes: 34 additions & 10 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// })
/// # }
/// ```
// FIXME(icxolu) deprecate in favor of `Bound::cast`
#[deprecated(since = "0.27.0", note = "use `Bound::cast` instead")]
fn downcast<T>(&self) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
where
T: PyTypeCheck;
Expand Down Expand Up @@ -800,7 +800,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// assert!(obj.downcast_into::<PyDict>().is_ok());
/// })
/// ```
// FIXME(icxolu) deprecate in favor of `Bound::cast_into`
#[deprecated(since = "0.27.0", note = "use `Bound::cast_into` instead")]
fn downcast_into<T>(self) -> Result<Bound<'py, T>, DowncastIntoError<'py>>
where
T: PyTypeCheck;
Expand Down Expand Up @@ -836,13 +836,13 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// assert!(any.downcast_exact::<PyBool>().is_ok());
/// });
/// ```
// FIXME(icxolu) deprecate in favor of `Bound::cast_exact`
#[deprecated(since = "0.27.0", note = "use `Bound::cast_exact` instead")]
fn downcast_exact<T>(&self) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
where
T: PyTypeInfo;

/// Like `downcast_exact` but takes ownership of `self`.
// FIXME(icxolu) deprecate in favor of `Bound::cast_into_exact`
#[deprecated(since = "0.27.0", note = "use `Bound::cast_into_exact` instead")]
fn downcast_into_exact<T>(self) -> Result<Bound<'py, T>, DowncastIntoError<'py>>
where
T: PyTypeInfo;
Expand All @@ -852,15 +852,15 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// # Safety
///
/// Callers must ensure that the type is valid or risk type confusion.
// FIXME(icxolu) deprecate in favor of `Bound::cast_unchecked`
#[deprecated(since = "0.27.0", note = "use `Bound::cast_unchecked` instead")]
unsafe fn downcast_unchecked<T>(&self) -> &Bound<'py, T>;

/// Like `downcast_unchecked` but takes ownership of `self`.
///
/// # Safety
///
/// Callers must ensure that the type is valid or risk type confusion.
// FIXME(icxolu) deprecate in favor of `Bound::cast_into_unchecked`
#[deprecated(since = "0.27.0", note = "use `Bound::cast_into_unchecked` instead")]
unsafe fn downcast_into_unchecked<T>(self) -> Bound<'py, T>;

/// Extracts some type from the Python object.
Expand Down Expand Up @@ -1449,31 +1449,55 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
where
T: PyTypeCheck,
{
self.cast()
if T::type_check(self) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I think this implies T has to be unsafe, because otherwise it should be legal to have a PyTypeCheck::type_check that always returned true?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ungh, you're right. PyTypeCheck should be an unsafe trait. 🤦

Copy link
Member Author

Choose a reason for hiding this comment

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

Given that:

  • A user would have to write a PyTypeCheck implementation and then cast to that type, which feels low risk to be an exploit.
  • Changing the trait definition in a patch release is breaking.

I think we just fix this for 0.27? 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

That seems fine, I don't think there's a lot of people implementing themselves so it'd be low risk for 0.27

// Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.cast_unchecked() })
} else {
#[allow(deprecated)]
Err(DowncastError::new(self, T::NAME))
}
}

#[inline]
fn downcast_into<T>(self) -> Result<Bound<'py, T>, DowncastIntoError<'py>>
where
T: PyTypeCheck,
{
self.cast_into()
if T::type_check(&self) {
// Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.cast_into_unchecked() })
} else {
#[allow(deprecated)]
Err(DowncastIntoError::new(self, T::NAME))
}
}

#[inline]
fn downcast_exact<T>(&self) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
where
T: PyTypeInfo,
{
self.cast_exact()
if T::is_exact_type_of(self) {
// Safety: is_exact_type_of is responsible for ensuring that the type is correct
Ok(unsafe { self.cast_unchecked() })
} else {
#[allow(deprecated)]
Err(DowncastError::new(self, T::NAME))
}
}

#[inline]
fn downcast_into_exact<T>(self) -> Result<Bound<'py, T>, DowncastIntoError<'py>>
where
T: PyTypeInfo,
{
self.cast_into_exact()
if T::is_exact_type_of(&self) {
// Safety: is_exact_type_of is responsible for ensuring that the type is correct
Ok(unsafe { self.cast_into_unchecked() })
} else {
#[allow(deprecated)]
Err(DowncastIntoError::new(self, T::NAME))
}
}

#[inline]
Expand Down
Loading