From 61505e4a9ccca483a9a68f55137b5dd50b7ed51a Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Fri, 21 Nov 2025 06:53:38 -0800 Subject: [PATCH 1/3] Disallow scalar conversion for non-0D arrays --- dpnp/dpnp_array.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 656f099a0c4..273503c22db 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -194,6 +194,7 @@ def __bytes__(self): def __complex__(self, /): """Convert a zero-dimensional array to a Python complex object.""" + self._check_scalar_convertible() return self._array_obj.__complex__() def __contains__(self, value, /): @@ -300,6 +301,7 @@ def __eq__(self, other, /): def __float__(self, /): """Convert a zero-dimensional array to a Python float object.""" + self._check_scalar_convertible() return self._array_obj.__float__() def __floordiv__(self, other, /): @@ -391,6 +393,7 @@ def __index__(self, /): def __int__(self, /): """Convert a zero-dimensional array to a Python int object.""" + self._check_scalar_convertible() return self._array_obj.__int__() def __invert__(self, /): @@ -608,6 +611,14 @@ def __xor__(self, other, /): r"""Return :math:`\text{self ^ value}`.""" return dpnp.bitwise_xor(self, other) + def _check_scalar_convertible(self): + """Raise if array cannot be converted to a Python scalar.""" + if self.ndim != 0: + raise TypeError( + "Only 0-dimensional dpnp.ndarray can be converted " + "to a Python scalar" + ) + @staticmethod def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray): """ From 320202d999d146958bf4a7cd19e4e703bc760227 Mon Sep 17 00:00:00 2001 From: "vladislav.perevezentsev" Date: Tue, 25 Nov 2025 05:30:05 -0800 Subject: [PATCH 2/3] Add TestPythonScalarConversion --- dpnp/tests/test_ndarray.py | 70 ++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/dpnp/tests/test_ndarray.py b/dpnp/tests/test_ndarray.py index c7e9dc65b99..3a03a8079f9 100644 --- a/dpnp/tests/test_ndarray.py +++ b/dpnp/tests/test_ndarray.py @@ -5,6 +5,7 @@ assert_allclose, assert_array_equal, assert_equal, + assert_raises, assert_raises_regex, ) @@ -17,6 +18,7 @@ get_complex_dtypes, get_float_dtypes, has_support_aspect64, + numpy_version, ) from .third_party.cupy import testing @@ -530,34 +532,50 @@ def test_print_dpnp_zero_shape(): assert result == expected -# Numpy will raise an error when converting a.ndim > 0 to a scalar -# TODO: Discuss dpnp behavior according to these future changes -@pytest.mark.filterwarnings("ignore::DeprecationWarning") -@pytest.mark.parametrize("func", [bool, float, int, complex]) -@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)]) -@pytest.mark.parametrize( - "dtype", get_all_dtypes(no_float16=False, no_complex=True) -) -def test_scalar_type_casting(func, shape, dtype): - a = numpy.full(shape, 5, dtype=dtype) - ia = dpnp.full(shape, 5, dtype=dtype) - assert func(a) == func(ia) +class TestPythonScalarConversion: + @pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)]) + @pytest.mark.parametrize( + "dtype", get_all_dtypes(no_float16=False, no_complex=True) + ) + def test_bool_conversion(shape, dtype): + a = numpy.full(shape, 5, dtype=dtype) + ia = dpnp.full(shape, 5, dtype=dtype) + assert bool(a) == bool(ia) + @pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)]) + @pytest.mark.parametrize( + "dtype", get_all_dtypes(no_float16=False, no_complex=True) + ) + def test_bool_method_conversion(shape, dtype): + a = numpy.full(shape, 5, dtype=dtype) + ia = dpnp.full(shape, 5, dtype=dtype) + assert a.__bool__() == ia.__bool__() -# Numpy will raise an error when converting a.ndim > 0 to a scalar -# TODO: Discuss dpnp behavior according to these future changes -@pytest.mark.filterwarnings("ignore::DeprecationWarning") -@pytest.mark.parametrize( - "method", ["__bool__", "__float__", "__int__", "__complex__"] -) -@pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)]) -@pytest.mark.parametrize( - "dtype", get_all_dtypes(no_float16=False, no_complex=True) -) -def test_scalar_type_casting_by_method(method, shape, dtype): - a = numpy.full(shape, 4.7, dtype=dtype) - ia = dpnp.full(shape, 4.7, dtype=dtype) - assert_allclose(getattr(a, method)(), getattr(ia, method)(), rtol=1e-06) + @pytest.mark.parametrize("func", [float, int, complex]) + @pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)]) + @pytest.mark.parametrize( + "dtype", get_all_dtypes(no_float16=False, no_complex=True) + ) + def test_non_bool_conversion(func, shape, dtype): + a = numpy.full(shape, 5, dtype=dtype) + ia = dpnp.full(shape, 5, dtype=dtype) + assert_raises(TypeError, func(ia)) + + if numpy_version() >= "2.4.0": + assert_raises(TypeError, func(a)) + + @pytest.mark.parametrize("method", ["__float__", "__int__", "__complex__"]) + @pytest.mark.parametrize("shape", [tuple(), (1,), (1, 1), (1, 1, 1)]) + @pytest.mark.parametrize( + "dtype", get_all_dtypes(no_float16=False, no_complex=True) + ) + def test_non_bool_method_conversion(method, shape, dtype): + a = numpy.full(shape, 5, dtype=dtype) + ia = dpnp.full(shape, 5, dtype=dtype) + assert_raises(TypeError, getattr(ia, method)()) + + if numpy_version() >= "2.4.0": + assert_raises(TypeError, getattr(a, method)()) @pytest.mark.parametrize("shape", [(1,), (1, 1), (1, 1, 1)]) From db4bd1fcc07eae1c16ecc203f18690ee230aff38 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Tue, 25 Nov 2025 08:38:56 -0600 Subject: [PATCH 3/3] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a0610bd18..13c5b4e27dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Also, that release drops support for Python 3.9, making Python 3.10 the minimum * Added support for the `out` keyword to accept a tuple, bringing ufunc signatures into alignment with those in NumPy [#2664](https://github.com/IntelPython/dpnp/pull/2664) * Unified public API definitions in `dpnp.linalg` and `dpnp.scipy` submodules [#2663](https://github.com/IntelPython/dpnp/pull/2663) * Aligned the signature of `dpnp.reshape` function with Python array API by making `shape` a required argument [#2673](https://github.com/IntelPython/dpnp/pull/2673) +* Disallowed conversion of `dpnp.ndarray` with more than one dimension to Python scalars (`int`, `float`, `complex`) to align with NumPy 2.4.0 [#2694](https://github.com/IntelPython/dpnp/pull/2694) ### Deprecated