Skip to content

Commit 3795010

Browse files
committed
Avoid overhead of creating a PyErr during downcasting.
1 parent fb8d706 commit 3795010

File tree

2 files changed

+48
-26
lines changed

2 files changed

+48
-26
lines changed

src/array.rs

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::cold;
2626
use crate::convert::{ArrayExt, IntoPyArray, NpyIndex, ToNpyDims, ToPyArray};
2727
use crate::dtype::{Element, PyArrayDescr};
2828
use crate::error::{
29-
BorrowError, DimensionalityError, FromVecError, NotContiguousError, TypeError,
29+
BorrowError, DimensionalityError, FromVecError, IgnoreError, NotContiguousError, TypeError,
3030
DIMENSIONALITY_MISMATCH_ERR, MAX_DIMENSIONALITY_ERR,
3131
};
3232
use crate::npyffi::{self, npy_intp, NPY_ORDER, PY_ARRAY_API};
@@ -131,7 +131,7 @@ unsafe impl<T: Element, D: Dimension> PyTypeInfo for PyArray<T, D> {
131131
}
132132

133133
fn is_type_of(ob: &PyAny) -> bool {
134-
<&Self>::extract(ob).is_ok()
134+
Self::extract::<IgnoreError>(ob).is_ok()
135135
}
136136
}
137137

@@ -145,30 +145,7 @@ impl<T, D> IntoPy<PyObject> for PyArray<T, D> {
145145

146146
impl<'py, T: Element, D: Dimension> FromPyObject<'py> for &'py PyArray<T, D> {
147147
fn extract(ob: &'py PyAny) -> PyResult<Self> {
148-
// Check if the object is an array.
149-
let array = unsafe {
150-
if npyffi::PyArray_Check(ob.py(), ob.as_ptr()) == 0 {
151-
return Err(PyDowncastError::new(ob, PyArray::<T, D>::NAME).into());
152-
}
153-
&*(ob as *const PyAny as *const PyArray<T, D>)
154-
};
155-
156-
// Check if the dimensionality matches `D`.
157-
let src_ndim = array.ndim();
158-
if let Some(dst_ndim) = D::NDIM {
159-
if src_ndim != dst_ndim {
160-
return Err(DimensionalityError::new(src_ndim, dst_ndim).into());
161-
}
162-
}
163-
164-
// Check if the element type matches `T`.
165-
let src_dtype = array.dtype();
166-
let dst_dtype = T::get_dtype(ob.py());
167-
if !src_dtype.is_equiv_to(dst_dtype) {
168-
return Err(TypeError::new(src_dtype, dst_dtype).into());
169-
}
170-
171-
Ok(array)
148+
PyArray::extract(ob)
172149
}
173150
}
174151

@@ -390,6 +367,36 @@ impl<T, D> PyArray<T, D> {
390367
}
391368

392369
impl<T: Element, D: Dimension> PyArray<T, D> {
370+
fn extract<'py, E>(ob: &'py PyAny) -> Result<&'py Self, E>
371+
where
372+
E: From<PyDowncastError<'py>> + From<DimensionalityError> + From<TypeError<'py>>,
373+
{
374+
// Check if the object is an array.
375+
let array = unsafe {
376+
if npyffi::PyArray_Check(ob.py(), ob.as_ptr()) == 0 {
377+
return Err(PyDowncastError::new(ob, Self::NAME).into());
378+
}
379+
&*(ob as *const PyAny as *const Self)
380+
};
381+
382+
// Check if the dimensionality matches `D`.
383+
let src_ndim = array.ndim();
384+
if let Some(dst_ndim) = D::NDIM {
385+
if src_ndim != dst_ndim {
386+
return Err(DimensionalityError::new(src_ndim, dst_ndim).into());
387+
}
388+
}
389+
390+
// Check if the element type matches `T`.
391+
let src_dtype = array.dtype();
392+
let dst_dtype = T::get_dtype(ob.py());
393+
if !src_dtype.is_equiv_to(dst_dtype) {
394+
return Err(TypeError::new(src_dtype, dst_dtype).into());
395+
}
396+
397+
Ok(array)
398+
}
399+
393400
/// Same as [`shape`][Self::shape], but returns `D` insead of `&[usize]`.
394401
#[inline(always)]
395402
pub fn dims(&self) -> D {

src/error.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,18 @@ impl fmt::Display for BorrowError {
162162
}
163163

164164
impl_pyerr!(BorrowError);
165+
166+
/// An internal type used to ignore certain error conditions
167+
///
168+
/// This is beneficial when those errors will never reach a public API anyway
169+
/// but dropping them will improve performance.
170+
pub(crate) struct IgnoreError;
171+
172+
impl<E> From<E> for IgnoreError
173+
where
174+
PyErr: From<E>,
175+
{
176+
fn from(_err: E) -> Self {
177+
Self
178+
}
179+
}

0 commit comments

Comments
 (0)