Skip to content

Conversation

@mattiapenati
Copy link

@mattiapenati mattiapenati commented Dec 10, 2025

This pull request attempts to resolve issue #444.

Current behavior

When PyArrayLike1<f32, TypeMustMatch> is used, arrays constructed using np.array([...], dtype='float64') are converted implicitly to f32. This results in a new allocation and a truncation error.

Conversely, PyArrayLike2<f32, TypeMustMatch> behaves differently: arrays constructed using np.array([[...], ...], dtype='float64') are not accepted as valid values.

I think the current behavior creates confusion and is not consistent with the current documentation, which states:

Depending on whether TypeMustMatch or AllowTypeChange is used for the C type parameter, the element type must either match the specific type T exactly or will be cast to it by NumPy’s asarray.

New behavior

The condition in the following portion of code has been changed to avoid the implicit conversion introduced by Borrowed::extract call. The conversion should be performed only when the choice of D allows type changes or the input object is not a numpy ndarray.

if matches!(D::NDIM, None | Some(1)) {
if let Ok(vec) = ob.extract::<Vec<T>>() {
let array = Array1::from(vec)
.into_dimensionality()
.expect("D being compatible to Ix1")
.into_pyarray(py)
.readonly();
return Ok(Self(array, PhantomData));
}
}

@mattiapenati mattiapenati changed the title Mismatched behaviour between PyArrayLike1 and PyArrayLike2 when used with floats Mismatched behavior between PyArrayLike1 and PyArrayLike2 when used with floats Dec 10, 2025
Copy link
Contributor

@Icxolu Icxolu left a comment

Choose a reason for hiding this comment

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

Thanks! This mostly looks good to me. I think we can remove the trait here by trying to downcast to an untyped array and checking for success.

Also could you please add a changelog entry for 0.28?

Comment on lines +45 to +61
trait IsNumpyNDArray {
fn is_numpy_ndarray(&self) -> PyResult<bool>;
}

impl<'py> IsNumpyNDArray for Borrowed<'_, 'py, PyAny> {
fn is_numpy_ndarray(&self) -> PyResult<bool> {
let py = self.py();

static NDARRAY: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
let ndarray = NDARRAY
.get_or_try_init(py, || {
get_array_module(py)?.getattr("ndarray").map(Into::into)
})?
.bind(py);

self.is_instance(ndarray)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than this, I think we can just try to cast to a Borrowed<'_, '_, PyUntypedArray>.

Comment on lines +173 to +174
// If the input is already an ndarray and `TypeMustMatch` is used then any conversion
// should be performed.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// If the input is already an ndarray and `TypeMustMatch` is used then any conversion
// should be performed.
// If the input is already an ndarray and `TypeMustMatch` is used then no type conversion
// should be performed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants