Skip to content
2 changes: 2 additions & 0 deletions changes/3151.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed an issue preventing correct parsing of NumPy ``int32`` dtypes when constructed via
``np.dtype('i')``.
20 changes: 20 additions & 0 deletions src/zarr/core/dtype/npy/int.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,26 @@ class Int32(BaseInt[np.dtypes.Int32DType, np.int32], HasEndianness):
_zarr_v3_name: ClassVar[Literal["int32"]] = "int32"
_zarr_v2_names: ClassVar[tuple[Literal[">i4"], Literal["<i4"]]] = (">i4", "<i4")

@classmethod
def _check_native_dtype(cls: type[Self], dtype: TBaseDType) -> TypeGuard[np.dtypes.Int32DType]:
"""
A type guard that checks if the input is assignable to the type of ``cls.dtype_class``

This method is overridden for this particular data type because of a windows-specific issue where
np.dtype('i') is an instance of ``np.dtypes.IntDType``, not an instance of ``np.dtypes.Int32DType``.

Parameters
----------
dtype : TDType
The dtype to check.

Returns
-------
Bool
True if the dtype matches, False otherwise.
"""
return super()._check_native_dtype(dtype) or dtype == np.dtypes.Int32DType()

@classmethod
def from_native_dtype(cls: type[Self], dtype: TBaseDType) -> Self:
if cls._check_native_dtype(dtype):
Expand Down
5 changes: 4 additions & 1 deletion tests/test_dtype/test_npy/test_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ class TestInt16(BaseTestZDType):
class TestInt32(BaseTestZDType):
test_cls = Int32
scalar_type = np.int32
valid_dtype = (np.dtype(">i4"), np.dtype("<i4"))
# The behavior of some tests associated with this class variable are
# order-dependent -- np.dtype('i') correctly fails certain tests only if it's not
# in the last position of the tuple. I have no idea how this is possible!
valid_dtype = (np.dtype("i"), np.dtype(">i4"), np.dtype("<i4"))
invalid_dtype = (
np.dtype(np.int8),
np.dtype(np.uint16),
Expand Down