Add {Py,PyAny}::downcast_unchecked to replace try_from_unchecked calls

This commit is contained in:
Georg Brandl 2022-11-17 10:42:52 +01:00
parent d6ac4d51b7
commit 249c0209fd
11 changed files with 52 additions and 26 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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]
@ -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());
}
@ -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};
@ -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());
}

View File

@ -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<'_>>

View File

@ -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()`].

View File

@ -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;
@ -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() }
}
}

View File

@ -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"))
}

View File

@ -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)]
@ -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.

View File

@ -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()) }
}
}

View File

@ -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());
}
@ -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>()) }
}
}
@ -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
@ -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());
});
}

View File

@ -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]
@ -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.