diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0350039b2..572fd21c78f 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 ### Deprecated * `dpnp.asfarray` is deprecated. Use `dpnp.asarray` with an appropriate dtype instead [#2650](https://github.com/IntelPython/dpnp/pull/2650) +* Passing the output array ``out`` positionally to `dpnp.minimum` and `dpnp.maximum` is deprecated. Pass the output with the keyword form, e.g. ``dpnp.minimum(a, b, out=c)`` [#2659](https://github.com/IntelPython/dpnp/pull/2659) ### Removed diff --git a/doc/conf.py b/doc/conf.py index ddc5ab552f8..32754fc0453 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -12,6 +12,7 @@ from dpnp.dpnp_algo.dpnp_elementwise_common import ( DPNPBinaryFunc, + DPNPBinaryFuncOutKw, DPNPUnaryFunc, DPNPUnaryTwoOutputsFunc, ) @@ -210,7 +211,13 @@ # -- Options for todo extension ---------------------------------------------- def _can_document_member(member, *args, **kwargs): if isinstance( - member, (DPNPBinaryFunc, DPNPUnaryFunc, DPNPUnaryTwoOutputsFunc) + member, + ( + DPNPBinaryFunc, + DPNPBinaryFuncOutKw, + DPNPUnaryFunc, + DPNPUnaryTwoOutputsFunc, + ), ): return True return orig(member, *args, **kwargs) diff --git a/dpnp/dpnp_algo/dpnp_elementwise_common.py b/dpnp/dpnp_algo/dpnp_elementwise_common.py index b0832f4e80c..f8fcb96c732 100644 --- a/dpnp/dpnp_algo/dpnp_elementwise_common.py +++ b/dpnp/dpnp_algo/dpnp_elementwise_common.py @@ -26,6 +26,9 @@ # THE POSSIBILITY OF SUCH DAMAGE. # ***************************************************************************** +import warnings +from functools import wraps + import dpctl.tensor as dpt import dpctl.tensor._copy_utils as dtc import dpctl.tensor._tensor_impl as dti @@ -48,6 +51,7 @@ "DPNPI0", "DPNPAngle", "DPNPBinaryFunc", + "DPNPBinaryFuncOutKw", "DPNPFix", "DPNPImag", "DPNPReal", @@ -775,6 +779,22 @@ def outer( ) +class DPNPBinaryFuncOutKw(DPNPBinaryFunc): + """DPNPBinaryFunc that deprecates positional `out` argument.""" + + @wraps(DPNPBinaryFunc.__call__) + def __call__(self, *args, **kwargs): + if len(args) > self.nin: + warnings.warn( + "Passing more than 2 positional arguments is deprecated. " + "If you meant to use the third argument as an output, " + "use the `out` keyword argument instead.", + DeprecationWarning, + stacklevel=2, + ) + return super().__call__(*args, **kwargs) + + class DPNPAngle(DPNPUnaryFunc): """Class that implements dpnp.angle unary element-wise functions.""" diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index 576b0ac63ae..b4b28695145 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -65,6 +65,7 @@ DPNPI0, DPNPAngle, DPNPBinaryFunc, + DPNPBinaryFuncOutKw, DPNPFix, DPNPImag, DPNPReal, @@ -3244,6 +3245,16 @@ def interp(x, xp, fp, left=None, right=None, period=None): Default: ``"K"``. +Warning +------- +Passing more than 2 positional arguments is deprecated. +If you meant to use the third argument as an output, +use the `out` keyword argument instead. + +For example, ``dpnp.maximum(a, b, c)`` will emit a ``DeprecationWarning``. +Always pass the output array as the keyword argument instead, that is +``dpnp.maximum(a, b, out=c)``. + Returns ------- out : dpnp.ndarray @@ -3297,7 +3308,7 @@ def interp(x, xp, fp, left=None, right=None, period=None): """ -maximum = DPNPBinaryFunc( +maximum = DPNPBinaryFuncOutKw( "maximum", ti._maximum_result_type, ti._maximum, @@ -3341,6 +3352,16 @@ def interp(x, xp, fp, left=None, right=None, period=None): An array containing the element-wise minima. The data type of the returned array is determined by the Type Promotion Rules. +Warning +------- +Passing more than 2 positional arguments is deprecated. +If you meant to use the third argument as an output, +use the `out` keyword argument instead. + +For example, ``dpnp.minimum(a, b, c)`` will emit a ``DeprecationWarning``. +Always pass the output array as the keyword argument instead, that is +``dpnp.minimum(a, b, out=c)``. + Limitations ----------- Parameters `where` and `subok` are supported with their default values. @@ -3387,7 +3408,7 @@ def interp(x, xp, fp, left=None, right=None, period=None): array(-inf) """ -minimum = DPNPBinaryFunc( +minimum = DPNPBinaryFuncOutKw( "minimum", ti._minimum_result_type, ti._minimum, diff --git a/dpnp/tests/test_binary_ufuncs.py b/dpnp/tests/test_binary_ufuncs.py index 8de0c2468af..0de83c5b99c 100644 --- a/dpnp/tests/test_binary_ufuncs.py +++ b/dpnp/tests/test_binary_ufuncs.py @@ -183,7 +183,7 @@ def test_invalid_shape(self, func, shape): ) def test_invalid_out_type(self, func, xp, out): a = xp.arange(10) - assert_raises(TypeError, getattr(xp, func), a, 2, out) + assert_raises(TypeError, getattr(xp, func), a, 2, out=out) class TestDivide: diff --git a/dpnp/tests/test_mathematical.py b/dpnp/tests/test_mathematical.py index c68dfbe9768..bbe71397088 100644 --- a/dpnp/tests/test_mathematical.py +++ b/dpnp/tests/test_mathematical.py @@ -2795,3 +2795,15 @@ def test_elemenwise_outer_scalar(): expected = dpnp.add.outer(x, y) result = dpnp.add.outer(x, s) assert dpnp.allclose(result, expected) + + +@testing.with_requires("numpy>=2.4") +@pytest.mark.parametrize("xp", [dpnp, numpy]) +@pytest.mark.parametrize("func", ["minimum", "maximum"]) +def test_minimum_maximum_out_deprecated(xp, func): + a = xp.array([1, 3, 2]) + b = xp.array([2, 2, 2]) + out = xp.empty_like(a) + + with pytest.warns(DeprecationWarning, match="deprecated"): + _ = getattr(xp, func)(a, b, out) diff --git a/dpnp/tests/test_sycl_queue.py b/dpnp/tests/test_sycl_queue.py index dfd090dbc5d..3ea4105a2cb 100644 --- a/dpnp/tests/test_sycl_queue.py +++ b/dpnp/tests/test_sycl_queue.py @@ -1016,7 +1016,10 @@ def test_invalid_stream(self, stream): "asarray_chkfinite", "asanyarray", "ascontiguousarray", - "asfarray", + pytest.param( + "asfarray", + marks=pytest.mark.filterwarnings("ignore::DeprecationWarning"), + ), "asfortranarray", ], ) diff --git a/dpnp/tests/test_usm_type.py b/dpnp/tests/test_usm_type.py index 90292bd1ce1..c8598263c66 100644 --- a/dpnp/tests/test_usm_type.py +++ b/dpnp/tests/test_usm_type.py @@ -286,7 +286,10 @@ def test_copy_operation(usm_type): "asarray_chkfinite", "asanyarray", "ascontiguousarray", - "asfarray", + pytest.param( + "asfarray", + marks=pytest.mark.filterwarnings("ignore::DeprecationWarning"), + ), "asfortranarray", ], )