Skip to content

Commit 97437c7

Browse files
authored
deprecate downcast functions in favour of cast functions (#5472)
* deprecate `downcast` functions in favour of `cast` functions * newsfragment * fix some `.downcast()` uses -> `cast()`
1 parent 17ecd26 commit 97437c7

File tree

7 files changed

+47
-24
lines changed

7 files changed

+47
-24
lines changed

newsfragments/5472.changed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deprecate `PyAnyMethods::downcast` functions in favour of `Bound::cast` functions

src/conversions/std/vec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ where
8989
// to support this function and if not, we will only fail extraction safely.
9090
let seq = unsafe {
9191
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
92-
obj.downcast_unchecked::<PySequence>()
92+
obj.cast_unchecked::<PySequence>()
9393
} else {
9494
return Err(DowncastError::new_from_type(
9595
obj,

src/err/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -857,12 +857,12 @@ impl std::fmt::Display for TypeNameOrValue<'_> {
857857
match self {
858858
Self::Name(name) => name.fmt(f),
859859
Self::Value(t) => {
860-
if let Ok(t) = t.downcast::<PyType>() {
860+
if let Ok(t) = t.cast::<PyType>() {
861861
t.qualname()
862862
.map_err(|_| std::fmt::Error)?
863863
.to_string_lossy()
864864
.fmt(f)
865-
} else if let Ok(t) = t.downcast::<PyTuple>() {
865+
} else if let Ok(t) = t.cast::<PyTuple>() {
866866
for (i, t) in t.iter().enumerate() {
867867
if i > 0 {
868868
f.write_str(" | ")?;

src/impl_/extract_argument.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,17 +188,15 @@ pub fn extract_pyclass_ref<'a, 'holder, T: PyClass>(
188188
obj: &'a Bound<'_, PyAny>,
189189
holder: &'holder mut Option<PyClassGuard<'a, T>>,
190190
) -> PyResult<&'holder T> {
191-
Ok(&*holder.insert(PyClassGuard::try_borrow(obj.downcast()?.as_unbound())?))
191+
Ok(&*holder.insert(PyClassGuard::try_borrow(obj.cast()?.as_unbound())?))
192192
}
193193

194194
#[inline]
195195
pub fn extract_pyclass_ref_mut<'a, 'holder, T: PyClass<Frozen = False>>(
196196
obj: &'a Bound<'_, PyAny>,
197197
holder: &'holder mut Option<PyClassGuardMut<'a, T>>,
198198
) -> PyResult<&'holder mut T> {
199-
Ok(&mut *holder.insert(PyClassGuardMut::try_borrow_mut(
200-
obj.downcast()?.as_unbound(),
201-
)?))
199+
Ok(&mut *holder.insert(PyClassGuardMut::try_borrow_mut(obj.cast()?.as_unbound())?))
202200
}
203201

204202
/// The standard implementation of how PyO3 extracts a `#[pyfunction]` or `#[pymethod]` function argument.

src/instance.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2254,6 +2254,7 @@ impl Py<PyAny> {
22542254
/// # Example: Downcasting to a specific Python object
22552255
///
22562256
/// ```rust
2257+
/// # #![allow(deprecated)]
22572258
/// use pyo3::prelude::*;
22582259
/// use pyo3::types::{PyDict, PyList};
22592260
///
@@ -2270,6 +2271,7 @@ impl Py<PyAny> {
22702271
/// This is useful if you want to mutate a `Py<PyAny>` that might actually be a pyclass.
22712272
///
22722273
/// ```rust
2274+
/// # #![allow(deprecated)]
22732275
/// # fn main() -> Result<(), pyo3::PyErr> {
22742276
/// use pyo3::prelude::*;
22752277
///
@@ -2292,7 +2294,7 @@ impl Py<PyAny> {
22922294
/// })
22932295
/// # }
22942296
/// ```
2295-
// FIXME(icxolu) deprecate in favor of `Py::cast_bound`
2297+
#[deprecated(since = "0.27.0", note = "use `Py::cast_bound` instead")]
22962298
#[inline]
22972299
pub fn downcast_bound<'py, T>(
22982300
&self,
@@ -2301,15 +2303,16 @@ impl Py<PyAny> {
23012303
where
23022304
T: PyTypeCheck,
23032305
{
2304-
self.cast_bound(py)
2306+
#[allow(deprecated)]
2307+
self.bind(py).downcast()
23052308
}
23062309

23072310
/// Casts the `Py<PyAny>` to a concrete Python object type without checking validity.
23082311
///
23092312
/// # Safety
23102313
///
23112314
/// Callers must ensure that the type is valid or risk type confusion.
2312-
// FIXME(icxolu) deprecate in favor of `Py::cast_bound_unchecked`
2315+
#[deprecated(since = "0.27.0", note = "use `Py::cast_bound_unchecked` instead")]
23132316
#[inline]
23142317
pub unsafe fn downcast_bound_unchecked<'py, T>(&self, py: Python<'py>) -> &Bound<'py, T> {
23152318
// SAFETY: caller has upheld the safety contract

src/sync/once_lock.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,7 @@ where
162162
attr_name: &str,
163163
) -> PyResult<&Bound<'py, T>> {
164164
self.get_or_try_init(py, || {
165-
let type_object = py
166-
.import(module_name)?
167-
.getattr(attr_name)?
168-
.downcast_into()?;
165+
let type_object = py.import(module_name)?.getattr(attr_name)?.cast_into()?;
169166
Ok(type_object.unbind())
170167
})
171168
.map(|ty| ty.bind(py))

src/types/any.rs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
772772
/// })
773773
/// # }
774774
/// ```
775-
// FIXME(icxolu) deprecate in favor of `Bound::cast`
775+
#[deprecated(since = "0.27.0", note = "use `Bound::cast` instead")]
776776
fn downcast<T>(&self) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
777777
where
778778
T: PyTypeCheck;
@@ -800,7 +800,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
800800
/// assert!(obj.downcast_into::<PyDict>().is_ok());
801801
/// })
802802
/// ```
803-
// FIXME(icxolu) deprecate in favor of `Bound::cast_into`
803+
#[deprecated(since = "0.27.0", note = "use `Bound::cast_into` instead")]
804804
fn downcast_into<T>(self) -> Result<Bound<'py, T>, DowncastIntoError<'py>>
805805
where
806806
T: PyTypeCheck;
@@ -836,13 +836,13 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
836836
/// assert!(any.downcast_exact::<PyBool>().is_ok());
837837
/// });
838838
/// ```
839-
// FIXME(icxolu) deprecate in favor of `Bound::cast_exact`
839+
#[deprecated(since = "0.27.0", note = "use `Bound::cast_exact` instead")]
840840
fn downcast_exact<T>(&self) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
841841
where
842842
T: PyTypeInfo;
843843

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

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

866866
/// Extracts some type from the Python object.
@@ -1449,31 +1449,55 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
14491449
where
14501450
T: PyTypeCheck,
14511451
{
1452-
self.cast()
1452+
if T::type_check(self) {
1453+
// Safety: type_check is responsible for ensuring that the type is correct
1454+
Ok(unsafe { self.cast_unchecked() })
1455+
} else {
1456+
#[allow(deprecated)]
1457+
Err(DowncastError::new(self, T::NAME))
1458+
}
14531459
}
14541460

14551461
#[inline]
14561462
fn downcast_into<T>(self) -> Result<Bound<'py, T>, DowncastIntoError<'py>>
14571463
where
14581464
T: PyTypeCheck,
14591465
{
1460-
self.cast_into()
1466+
if T::type_check(&self) {
1467+
// Safety: type_check is responsible for ensuring that the type is correct
1468+
Ok(unsafe { self.cast_into_unchecked() })
1469+
} else {
1470+
#[allow(deprecated)]
1471+
Err(DowncastIntoError::new(self, T::NAME))
1472+
}
14611473
}
14621474

14631475
#[inline]
14641476
fn downcast_exact<T>(&self) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>>
14651477
where
14661478
T: PyTypeInfo,
14671479
{
1468-
self.cast_exact()
1480+
if T::is_exact_type_of(self) {
1481+
// Safety: is_exact_type_of is responsible for ensuring that the type is correct
1482+
Ok(unsafe { self.cast_unchecked() })
1483+
} else {
1484+
#[allow(deprecated)]
1485+
Err(DowncastError::new(self, T::NAME))
1486+
}
14691487
}
14701488

14711489
#[inline]
14721490
fn downcast_into_exact<T>(self) -> Result<Bound<'py, T>, DowncastIntoError<'py>>
14731491
where
14741492
T: PyTypeInfo,
14751493
{
1476-
self.cast_into_exact()
1494+
if T::is_exact_type_of(&self) {
1495+
// Safety: is_exact_type_of is responsible for ensuring that the type is correct
1496+
Ok(unsafe { self.cast_into_unchecked() })
1497+
} else {
1498+
#[allow(deprecated)]
1499+
Err(DowncastIntoError::new(self, T::NAME))
1500+
}
14771501
}
14781502

14791503
#[inline]

0 commit comments

Comments
 (0)