Skip to content

Commit

Permalink
Add {Py,PyAny}::downcast_unchecked to replace try_from_unchecked calls
Browse files Browse the repository at this point in the history
  • Loading branch information
birkenfeld committed Nov 18, 2022
1 parent d6ac4d5 commit 249c020
Show file tree
Hide file tree
Showing 11 changed files with 52 additions and 26 deletions.
3 changes: 2 additions & 1 deletion newsfragments/2734.added.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Added `Py::downcast()` as a companion to `PyAny::downcast()`.
Added `Py::downcast()` as a companion to `PyAny::downcast()`, as well as
`downcast_unchecked()` for both types.
4 changes: 1 addition & 3 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ use crate::types::{
timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess,
PyTzInfo, PyTzInfoAccess, PyUnicode,
};
use crate::{
AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject,
};
use crate::{AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
use chrono::offset::{FixedOffset, Utc};
use chrono::{
DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike,
Expand Down
16 changes: 8 additions & 8 deletions src/conversions/std/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ mod min_const_generics {
use crate::conversion::{AsPyPointer, IntoPyPointer};
use crate::types::PySequence;
use crate::{
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, PyTryFrom,
Python, ToPyObject,
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python,
ToPyObject,
};

impl<T, const N: usize> IntoPy<PyObject> for [T; N]
Expand Down Expand Up @@ -65,9 +65,9 @@ mod min_const_generics {
{
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
// to support this function and if not, we will only fail extraction safely.
let seq = unsafe {
let seq: &PySequence = unsafe {
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
<PySequence as PyTryFrom>::try_from_unchecked(obj)
obj.downcast_unchecked()
} else {
return Err(PyDowncastError::new(obj, "Sequence").into());
}
Expand Down Expand Up @@ -187,8 +187,8 @@ mod array_impls {
use crate::conversion::{AsPyPointer, IntoPyPointer};
use crate::types::PySequence;
use crate::{
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, PyTryFrom,
Python, ToPyObject,
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python,
ToPyObject,
};
use std::mem::{transmute_copy, ManuallyDrop};

Expand Down Expand Up @@ -288,9 +288,9 @@ mod array_impls {
{
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
// to support this function and if not, we will only fail extraction safely.
let seq = unsafe {
let seq: &PySequence = unsafe {
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
<PySequence as PyTryFrom>::try_from_unchecked(obj)
obj.downcast_unchecked()
} else {
return Err(PyDowncastError::new(obj, "Sequence").into());
}
Expand Down
15 changes: 15 additions & 0 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,21 @@ impl PyObject {
<T as PyTryFrom<'_>>::try_from(self.as_ref(py))
}

/// Casts the PyObject to a concrete Python object type without checking validity.
///
/// This can cast only to native Python types, not types implemented in Rust. For a more
/// flexible alternative, see [`Py::extract`](struct.Py.html#method.extract).
///
/// # Safety
///
/// Callers must ensure that the type is valid or risk type confusion.
pub unsafe fn downcast_unchecked<'p, T>(&'p self, py: Python<'p>) -> &T
where
T: PyTryFrom<'p>,
{
<T as PyTryFrom<'_>>::try_from_unchecked(self.as_ref(py))
}

/// Casts the PyObject to a concrete Python object type.
#[deprecated(since = "0.18.0", note = "use downcast() instead")]
pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError<'_>>
Expand Down
12 changes: 12 additions & 0 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,18 @@ impl PyAny {
<T as PyTryFrom>::try_from(self)
}

/// Converts this `PyAny` to a concrete Python type without checking validity.
///
/// # Safety
///
/// Callers must ensure that the type is valid or risk type confusion.
pub unsafe fn downcast_unchecked<'p, T>(&'p self) -> &'p T
where
T: PyTryFrom<'p>,
{
<T as PyTryFrom>::try_from_unchecked(self)
}

/// Extracts some type from the Python object.
///
/// This is a wrapper function around [`FromPyObject::extract()`].
Expand Down
4 changes: 2 additions & 2 deletions src/types/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::PyMapping;
use crate::err::{self, PyErr, PyResult};
use crate::ffi::Py_ssize_t;
use crate::types::{PyAny, PyList};
use crate::{ffi, AsPyPointer, PyTryFrom, Python, ToPyObject};
use crate::{ffi, AsPyPointer, Python, ToPyObject};
#[cfg(not(PyPy))]
use crate::{IntoPyPointer, PyObject};
use std::ptr::NonNull;
Expand Down Expand Up @@ -255,7 +255,7 @@ impl PyDict {

/// Returns `self` cast as a `PyMapping`.
pub fn as_mapping(&self) -> &PyMapping {
unsafe { PyMapping::try_from_unchecked(self) }
unsafe { self.downcast_unchecked() }
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/types/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl<'v> PyTryFrom<'v> for PyIterator {
let value = value.into();
unsafe {
if ffi::PyIter_Check(value.as_ptr()) != 0 {
Ok(<PyIterator as PyTryFrom>::try_from_unchecked(value))
Ok(value.downcast_unchecked())
} else {
Err(PyDowncastError::new(value, "Iterator"))
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::err::{self, PyResult};
use crate::ffi::{self, Py_ssize_t};
use crate::internal_tricks::get_ssize_index;
use crate::types::PySequence;
use crate::{AsPyPointer, IntoPyPointer, Py, PyAny, PyObject, PyTryFrom, Python, ToPyObject};
use crate::{AsPyPointer, IntoPyPointer, Py, PyAny, PyObject, Python, ToPyObject};

/// Represents a Python `list`.
#[repr(transparent)]
Expand Down Expand Up @@ -115,7 +115,7 @@ impl PyList {

/// Returns `self` cast as a `PySequence`.
pub fn as_sequence(&self) -> &PySequence {
unsafe { PySequence::try_from_unchecked(self) }
unsafe { self.downcast_unchecked() }
}

/// Gets the list item at the specified index.
Expand Down
2 changes: 1 addition & 1 deletion src/types/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ impl<'v> PyTryFrom<'v> for PyMapping {
// TODO: surface specific errors in this chain to the user
if let Ok(abc) = get_mapping_abc(value.py()) {
if value.is_instance(abc).unwrap_or(false) {
unsafe { return Ok(<PyMapping as PyTryFrom>::try_from_unchecked(value)) }
unsafe { return Ok(value.downcast_unchecked()) }
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/types/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,9 @@ where
{
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
// to support this function and if not, we will only fail extraction safely.
let seq = unsafe {
let seq: &PySequence = unsafe {
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
<PySequence as PyTryFrom>::try_from_unchecked(obj)
obj.downcast_unchecked()
} else {
return Err(PyDowncastError::new(obj, "Sequence").into());
}
Expand Down Expand Up @@ -339,7 +339,7 @@ impl<'v> PyTryFrom<'v> for PySequence {
// TODO: surface specific errors in this chain to the user
if let Ok(abc) = get_sequence_abc(value.py()) {
if value.is_instance(abc).unwrap_or(false) {
unsafe { return Ok(<PySequence as PyTryFrom>::try_from_unchecked(value)) }
unsafe { return Ok(value.downcast_unchecked::<PySequence>()) }
}
}

Expand Down Expand Up @@ -386,7 +386,7 @@ impl Py<PySequence> {
#[cfg(test)]
mod tests {
use crate::types::{PyList, PySequence};
use crate::{AsPyPointer, Py, PyObject, PyTryFrom, Python, ToPyObject};
use crate::{AsPyPointer, Py, PyObject, Python, ToPyObject};

fn get_object() -> PyObject {
// Convenience function for getting a single unique object
Expand Down Expand Up @@ -867,13 +867,13 @@ mod tests {
}

#[test]
fn test_seq_try_from_unchecked() {
fn test_seq_downcast_unchecked() {
Python::with_gil(|py| {
let v = vec!["foo", "bar"];
let ob = v.to_object(py);
let seq = ob.downcast::<PySequence>(py).unwrap();
let type_ptr = seq.as_ref();
let seq_from = unsafe { <PySequence as PyTryFrom>::try_from_unchecked(type_ptr) };
let seq_from = unsafe { type_ptr.downcast_unchecked::<PySequence>() };
assert!(seq_from.list().is_ok());
});
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::internal_tricks::get_ssize_index;
use crate::types::PySequence;
use crate::{
exceptions, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, Py, PyAny, PyErr, PyObject,
PyResult, PyTryFrom, Python, ToPyObject,
PyResult, Python, ToPyObject,
};

#[inline]
Expand Down Expand Up @@ -120,7 +120,7 @@ impl PyTuple {

/// Returns `self` cast as a `PySequence`.
pub fn as_sequence(&self) -> &PySequence {
unsafe { PySequence::try_from_unchecked(self) }
unsafe { self.downcast_unchecked() }
}

/// Takes the slice `self[low:high]` and returns it as a new tuple.
Expand Down

0 comments on commit 249c020

Please sign in to comment.