From 65fdb0a623c959f6502bcfb7b1ba38081eb75839 Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:55:16 +0000 Subject: [PATCH 01/10] ENH: updates based on feedback --- numpy/_core/src/umath/ufunc_object.c | 13 +++++++++++++ numpy/ma/tests/test_deprecations.py | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/numpy/_core/src/umath/ufunc_object.c b/numpy/_core/src/umath/ufunc_object.c index 1d2c3edbd3b9..839bbf2176cc 100644 --- a/numpy/_core/src/umath/ufunc_object.c +++ b/numpy/_core/src/umath/ufunc_object.c @@ -4368,6 +4368,19 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, Py_INCREF(tmp); PyTuple_SET_ITEM(full_args.out, i-nin, tmp); } + + /* Extra positional args but *no* keywords */ + /* DEPRECATED NumPy 2.3.0, 2025-08-03 */ + if (strcmp(ufunc->name, "maximum") == 0) { + DEPRECATE("Passing more than 2 positional arguments to np.maximum " + "is deprecated; use out=keyword or np.maximum.reduce."); + } + /* DEPRECATED NumPy 2.3.0, 2025-08-03 */ + else if (strcmp(ufunc->name, "minimum") == 0) { + DEPRECATE("Passing more than 2 positional arguments to np.minimum " + "is deprecated; use out=keyword or np.minimum.reduce."); + } + if (all_none) { Py_SETREF(full_args.out, NULL); } diff --git a/numpy/ma/tests/test_deprecations.py b/numpy/ma/tests/test_deprecations.py index a2c98d0c229d..221ceeb4f541 100644 --- a/numpy/ma/tests/test_deprecations.py +++ b/numpy/ma/tests/test_deprecations.py @@ -84,3 +84,21 @@ def test_fromtextfile_delimitor(self): with pytest.warns(DeprecationWarning): result = np.ma.mrecords.fromtextfile(textfile, delimitor=';') +# class TestMaxMin: +# def test_maximum_too_many_args(self): +# a = np.array([1]) +# b = np.array([2]) +# out = np.empty_like(a) +# with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.maximum is deprecated"): +# result = np.maximum(a, b, out) +# np.testing.assert_array_equal(result, [2]) + +# def test_minimum_too_many_args(self): +# a = np.array([1]) +# b = np.array([2]) +# out = np.empty_like(a) + +# with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.minimum is deprecated"): +# result = np.minimum(a, b, out) + +# np.testing.assert_array_equal(result, [1]) \ No newline at end of file From d787e39d55e1a307cc34ecaee482038f9444d745 Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:31:44 +0000 Subject: [PATCH 02/10] ENH: more changes based on feedback --- numpy/ma/tests/test_deprecations.py | 37 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/numpy/ma/tests/test_deprecations.py b/numpy/ma/tests/test_deprecations.py index 221ceeb4f541..29abff5d98a8 100644 --- a/numpy/ma/tests/test_deprecations.py +++ b/numpy/ma/tests/test_deprecations.py @@ -84,21 +84,22 @@ def test_fromtextfile_delimitor(self): with pytest.warns(DeprecationWarning): result = np.ma.mrecords.fromtextfile(textfile, delimitor=';') -# class TestMaxMin: -# def test_maximum_too_many_args(self): -# a = np.array([1]) -# b = np.array([2]) -# out = np.empty_like(a) -# with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.maximum is deprecated"): -# result = np.maximum(a, b, out) -# np.testing.assert_array_equal(result, [2]) - -# def test_minimum_too_many_args(self): -# a = np.array([1]) -# b = np.array([2]) -# out = np.empty_like(a) - -# with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.minimum is deprecated"): -# result = np.minimum(a, b, out) - -# np.testing.assert_array_equal(result, [1]) \ No newline at end of file + +class TestMaxMin: + def test_maximum_too_many_args(self): + a = np.array([1]) + b = np.array([2]) + out = np.empty_like(a) + with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.maximum is deprecated"): + result = np.maximum(a, b, out) + np.testing.assert_array_equal(result, [2]) + + def test_minimum_too_many_args(self): + a = np.array([1]) + b = np.array([2]) + out = np.empty_like(a) + + with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.minimum is deprecated"): + result = np.minimum(a, b, out) + + np.testing.assert_array_equal(result, [1]) \ No newline at end of file From 5d596dc2cba5b87475cce21cc2ccc31446808f15 Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Wed, 6 Aug 2025 00:20:59 +0000 Subject: [PATCH 03/10] ENH: updating the error checks --- numpy/_core/src/umath/ufunc_object.c | 21 ++++++++++++--------- numpy/ma/tests/test_deprecations.py | 19 ------------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/numpy/_core/src/umath/ufunc_object.c b/numpy/_core/src/umath/ufunc_object.c index 839bbf2176cc..6fa031ac788e 100644 --- a/numpy/_core/src/umath/ufunc_object.c +++ b/numpy/_core/src/umath/ufunc_object.c @@ -4370,15 +4370,18 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, } /* Extra positional args but *no* keywords */ - /* DEPRECATED NumPy 2.3.0, 2025-08-03 */ - if (strcmp(ufunc->name, "maximum") == 0) { - DEPRECATE("Passing more than 2 positional arguments to np.maximum " - "is deprecated; use out=keyword or np.maximum.reduce."); - } - /* DEPRECATED NumPy 2.3.0, 2025-08-03 */ - else if (strcmp(ufunc->name, "minimum") == 0) { - DEPRECATE("Passing more than 2 positional arguments to np.minimum " - "is deprecated; use out=keyword or np.minimum.reduce."); + /* DEPRECATED NumPy 2.4, 2025-08 */ + if (strcmp(ufunc->name, "maximum") == 0 || strcmp(ufunc->name, "minimum") == 0) { + const char *which = ufunc->name; // "maximum" or "minimum" + char msg[256]; + snprintf(msg, sizeof(msg), + "Passing more than 2 positional arguments to np.%s " + "is deprecated; use out=keyword or np.%s.reduce.", + which, which); + + if (DEPRECATE(msg) < 0 && PyErr_Occurred()) { + return NULL; + } } if (all_none) { diff --git a/numpy/ma/tests/test_deprecations.py b/numpy/ma/tests/test_deprecations.py index 29abff5d98a8..a2c98d0c229d 100644 --- a/numpy/ma/tests/test_deprecations.py +++ b/numpy/ma/tests/test_deprecations.py @@ -84,22 +84,3 @@ def test_fromtextfile_delimitor(self): with pytest.warns(DeprecationWarning): result = np.ma.mrecords.fromtextfile(textfile, delimitor=';') - -class TestMaxMin: - def test_maximum_too_many_args(self): - a = np.array([1]) - b = np.array([2]) - out = np.empty_like(a) - with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.maximum is deprecated"): - result = np.maximum(a, b, out) - np.testing.assert_array_equal(result, [2]) - - def test_minimum_too_many_args(self): - a = np.array([1]) - b = np.array([2]) - out = np.empty_like(a) - - with pytest.warns(DeprecationWarning, match=r"Passing more than 2 positional arguments to np\.minimum is deprecated"): - result = np.minimum(a, b, out) - - np.testing.assert_array_equal(result, [1]) \ No newline at end of file From 6fce1fb1401736de483264cbbe5dcb1b26423f2e Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Thu, 7 Aug 2025 17:54:47 +0000 Subject: [PATCH 04/10] ENH: simplify tests, remove snprintf/PyWarn_Format --- numpy/_core/src/umath/ufunc_object.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/numpy/_core/src/umath/ufunc_object.c b/numpy/_core/src/umath/ufunc_object.c index 6fa031ac788e..a4f9334e4959 100644 --- a/numpy/_core/src/umath/ufunc_object.c +++ b/numpy/_core/src/umath/ufunc_object.c @@ -4369,17 +4369,16 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, PyTuple_SET_ITEM(full_args.out, i-nin, tmp); } - /* Extra positional args but *no* keywords */ + /* Extra positional args but no keywords */ /* DEPRECATED NumPy 2.4, 2025-08 */ if (strcmp(ufunc->name, "maximum") == 0 || strcmp(ufunc->name, "minimum") == 0) { - const char *which = ufunc->name; // "maximum" or "minimum" - char msg[256]; - snprintf(msg, sizeof(msg), - "Passing more than 2 positional arguments to np.%s " - "is deprecated; use out=keyword or np.%s.reduce.", - which, which); - if (DEPRECATE(msg) < 0 && PyErr_Occurred()) { + if (DEPRECATE( + "Passing more than 2 positional arguments to np.maximum and np.minimum " + "is deprecated. If you meant to use the third argument as an output, " + "use the 'out' keyword argument instead. If you hoped to work with " + "more than 2 inputs, combine them into a single array and get the extrema " + "for the relevant axis.") < 0) { return NULL; } } From 353a39db9626d64c4ee5d072efe7bc9449ecf820 Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Thu, 7 Aug 2025 21:34:05 +0000 Subject: [PATCH 05/10] ENH: re-insert faster comparison --- numpy/_core/src/umath/ufunc_object.c | 4 +++- numpy/_core/src/umath/umathmodule.c | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/numpy/_core/src/umath/ufunc_object.c b/numpy/_core/src/umath/ufunc_object.c index a4f9334e4959..d7491adf74aa 100644 --- a/numpy/_core/src/umath/ufunc_object.c +++ b/numpy/_core/src/umath/ufunc_object.c @@ -90,6 +90,8 @@ typedef struct { /* ---------------------------------------------------------------- */ +extern PyUFuncObject *UFUNC_MAXIMUM; +extern PyUFuncObject *UFUNC_MINIMUM; static PyObject * prepare_input_arguments_for_outer(PyObject *args, PyUFuncObject *ufunc); @@ -4371,7 +4373,7 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, /* Extra positional args but no keywords */ /* DEPRECATED NumPy 2.4, 2025-08 */ - if (strcmp(ufunc->name, "maximum") == 0 || strcmp(ufunc->name, "minimum") == 0) { + if (ufunc == UFUNC_MAXIMUM || ufunc == UFUNC_MINIMUM) { if (DEPRECATE( "Passing more than 2 positional arguments to np.maximum and np.minimum " diff --git a/numpy/_core/src/umath/umathmodule.c b/numpy/_core/src/umath/umathmodule.c index 3efb02bd4a49..f4fa2daf2565 100644 --- a/numpy/_core/src/umath/umathmodule.c +++ b/numpy/_core/src/umath/umathmodule.c @@ -40,6 +40,8 @@ static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om}; +PyUFuncObject *UFUNC_MAXIMUM = NULL; +PyUFuncObject *UFUNC_MINIMUM = NULL; static int object_ufunc_type_resolver(PyUFuncObject *ufunc, @@ -282,6 +284,17 @@ int initumath(PyObject *m) PyDict_SetItemString(d, "conj", s); PyDict_SetItemString(d, "mod", s2); + PyObject *tmp; + if (PyDict_GetItemStringRef(d, "maximum", &tmp) < 0) { + return -1; + } + UFUNC_MAXIMUM = (PyUFuncObject *)tmp; + + if (PyDict_GetItemStringRef(d, "minimum", &tmp) < 0) { + return -1; + } + UFUNC_MINIMUM = (PyUFuncObject *)tmp; + /* * Set up promoters for logical functions * TODO: This should probably be done at a better place, or even in the From 1d9811237a90e1603b89fa503001f96fb15e1ece Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:16:46 +0000 Subject: [PATCH 06/10] ENH: use n_ops struct for comparison --- numpy/_core/src/umath/ufunc_object.c | 5 ++--- numpy/_core/src/umath/umathmodule.c | 13 ------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/numpy/_core/src/umath/ufunc_object.c b/numpy/_core/src/umath/ufunc_object.c index d7491adf74aa..61e303d598ce 100644 --- a/numpy/_core/src/umath/ufunc_object.c +++ b/numpy/_core/src/umath/ufunc_object.c @@ -65,6 +65,7 @@ #include "mapping.h" #include "npy_static_data.h" #include "multiarraymodule.h" +#include "../multiarray/number.h" /********** PRINTF DEBUG TRACING **************/ #define NPY_UF_DBG_TRACING 0 @@ -90,8 +91,6 @@ typedef struct { /* ---------------------------------------------------------------- */ -extern PyUFuncObject *UFUNC_MAXIMUM; -extern PyUFuncObject *UFUNC_MINIMUM; static PyObject * prepare_input_arguments_for_outer(PyObject *args, PyUFuncObject *ufunc); @@ -4373,7 +4372,7 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, /* Extra positional args but no keywords */ /* DEPRECATED NumPy 2.4, 2025-08 */ - if (ufunc == UFUNC_MAXIMUM || ufunc == UFUNC_MINIMUM) { + if ((PyObject *)ufunc == n_ops.maximum || (PyObject *)ufunc == n_ops.minimum) { if (DEPRECATE( "Passing more than 2 positional arguments to np.maximum and np.minimum " diff --git a/numpy/_core/src/umath/umathmodule.c b/numpy/_core/src/umath/umathmodule.c index f4fa2daf2565..3efb02bd4a49 100644 --- a/numpy/_core/src/umath/umathmodule.c +++ b/numpy/_core/src/umath/umathmodule.c @@ -40,8 +40,6 @@ static PyUFuncGenericFunction pyfunc_functions[] = {PyUFunc_On_Om}; -PyUFuncObject *UFUNC_MAXIMUM = NULL; -PyUFuncObject *UFUNC_MINIMUM = NULL; static int object_ufunc_type_resolver(PyUFuncObject *ufunc, @@ -284,17 +282,6 @@ int initumath(PyObject *m) PyDict_SetItemString(d, "conj", s); PyDict_SetItemString(d, "mod", s2); - PyObject *tmp; - if (PyDict_GetItemStringRef(d, "maximum", &tmp) < 0) { - return -1; - } - UFUNC_MAXIMUM = (PyUFuncObject *)tmp; - - if (PyDict_GetItemStringRef(d, "minimum", &tmp) < 0) { - return -1; - } - UFUNC_MINIMUM = (PyUFuncObject *)tmp; - /* * Set up promoters for logical functions * TODO: This should probably be done at a better place, or even in the From abd842f5754723cda0b7302f80ff2203d239af99 Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:49:52 +0000 Subject: [PATCH 07/10] DEP: resolve conflicts --- doc/release/upcoming_changes/29052.deprecation.rst | 10 ++++++++++ numpy/_core/tests/test_deprecations.py | 11 +++++++++++ 2 files changed, 21 insertions(+) create mode 100644 doc/release/upcoming_changes/29052.deprecation.rst diff --git a/doc/release/upcoming_changes/29052.deprecation.rst b/doc/release/upcoming_changes/29052.deprecation.rst new file mode 100644 index 000000000000..ee8a6a195659 --- /dev/null +++ b/doc/release/upcoming_changes/29052.deprecation.rst @@ -0,0 +1,10 @@ +Positional ``out`` argument to `numpy.maximum`/`numpy.minimum` is deprecated +--------------------------------------------------------------------------- +Passing the output array ``out`` positionally to `numpy.maximum` and +`numpy.minimum` is deprecated. For example, ``np.maximum(a, b, c)`` will +emit a deprecation warning, since ``c`` is treated as the output buffer + rather than a third input. + +Always pass the output with the keyword form, e.g. +``np.maximum(a, b, out=c)``. This makes intent clear and simplifies +type annotations. \ No newline at end of file diff --git a/numpy/_core/tests/test_deprecations.py b/numpy/_core/tests/test_deprecations.py index 9be253f7c95c..0753ff0467f9 100644 --- a/numpy/_core/tests/test_deprecations.py +++ b/numpy/_core/tests/test_deprecations.py @@ -535,3 +535,14 @@ def use_suppress_warnings(): sup.filter(RuntimeWarning, 'invalid value encountered in divide') self.assert_deprecated(use_suppress_warnings) + + +class TestTooManyArgsExtremum(_DeprecationTestCase): + + # Deprecated in Numpy 2.4, 2025-08, gh-27639 + message = "Passing more than 2 positional arguments to np.maximum and np.minimum " + + @pytest.mark.parametrize("ufunc", [np.minimum, np.maximum]) + def test_extremem_3_args(self, ufunc): + + self.assert_deprecated(ufunc, args=(np.ones(1), np.zeros(1), np.empty(1))) From 964a9b33536093252ad4e300fa023677e34c4aa1 Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Tue, 19 Aug 2025 01:29:41 +0000 Subject: [PATCH 08/10] DEP: adding release note / updates based on feedback --- doc/release/upcoming_changes/29052.deprecation.rst | 4 ++-- numpy/_core/src/umath/ufunc_object.c | 4 ++-- numpy/_core/tests/test_deprecations.py | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/release/upcoming_changes/29052.deprecation.rst b/doc/release/upcoming_changes/29052.deprecation.rst index ee8a6a195659..1fb325b37bf2 100644 --- a/doc/release/upcoming_changes/29052.deprecation.rst +++ b/doc/release/upcoming_changes/29052.deprecation.rst @@ -3,8 +3,8 @@ Positional ``out`` argument to `numpy.maximum`/`numpy.minimum` is deprecated Passing the output array ``out`` positionally to `numpy.maximum` and `numpy.minimum` is deprecated. For example, ``np.maximum(a, b, c)`` will emit a deprecation warning, since ``c`` is treated as the output buffer - rather than a third input. +rather than a third input. Always pass the output with the keyword form, e.g. ``np.maximum(a, b, out=c)``. This makes intent clear and simplifies -type annotations. \ No newline at end of file +type annotations. diff --git a/numpy/_core/src/umath/ufunc_object.c b/numpy/_core/src/umath/ufunc_object.c index 61e303d598ce..6b8cc1789f94 100644 --- a/numpy/_core/src/umath/ufunc_object.c +++ b/numpy/_core/src/umath/ufunc_object.c @@ -65,7 +65,7 @@ #include "mapping.h" #include "npy_static_data.h" #include "multiarraymodule.h" -#include "../multiarray/number.h" +#include "number.h" /********** PRINTF DEBUG TRACING **************/ #define NPY_UF_DBG_TRACING 0 @@ -4377,7 +4377,7 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc, if (DEPRECATE( "Passing more than 2 positional arguments to np.maximum and np.minimum " "is deprecated. If you meant to use the third argument as an output, " - "use the 'out' keyword argument instead. If you hoped to work with " + "use the `out` keyword argument instead. If you hoped to work with " "more than 2 inputs, combine them into a single array and get the extrema " "for the relevant axis.") < 0) { return NULL; diff --git a/numpy/_core/tests/test_deprecations.py b/numpy/_core/tests/test_deprecations.py index 0753ff0467f9..252189ef321a 100644 --- a/numpy/_core/tests/test_deprecations.py +++ b/numpy/_core/tests/test_deprecations.py @@ -538,11 +538,9 @@ def use_suppress_warnings(): class TestTooManyArgsExtremum(_DeprecationTestCase): - # Deprecated in Numpy 2.4, 2025-08, gh-27639 message = "Passing more than 2 positional arguments to np.maximum and np.minimum " @pytest.mark.parametrize("ufunc", [np.minimum, np.maximum]) def test_extremem_3_args(self, ufunc): - self.assert_deprecated(ufunc, args=(np.ones(1), np.zeros(1), np.empty(1))) From 09f06daf215ca276a99f4ef43520ed25f927995f Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Tue, 19 Aug 2025 23:07:53 +0000 Subject: [PATCH 09/10] DEP: formatting changes on release note --- doc/release/upcoming_changes/29052.deprecation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release/upcoming_changes/29052.deprecation.rst b/doc/release/upcoming_changes/29052.deprecation.rst index 1fb325b37bf2..6371ecf0d840 100644 --- a/doc/release/upcoming_changes/29052.deprecation.rst +++ b/doc/release/upcoming_changes/29052.deprecation.rst @@ -1,5 +1,5 @@ Positional ``out`` argument to `numpy.maximum`/`numpy.minimum` is deprecated ---------------------------------------------------------------------------- +------------------------------------------------------ Passing the output array ``out`` positionally to `numpy.maximum` and `numpy.minimum` is deprecated. For example, ``np.maximum(a, b, c)`` will emit a deprecation warning, since ``c`` is treated as the output buffer From 2f86527b38bc8dd5b12c8dcf9200300f90fee3ae Mon Sep 17 00:00:00 2001 From: lvllvl <24905907+lvllvl@users.noreply.github.com> Date: Tue, 19 Aug 2025 23:41:33 +0000 Subject: [PATCH 10/10] DEP: formatting changes on release note2 --- doc/release/upcoming_changes/29052.deprecation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/release/upcoming_changes/29052.deprecation.rst b/doc/release/upcoming_changes/29052.deprecation.rst index 6371ecf0d840..e302907abfba 100644 --- a/doc/release/upcoming_changes/29052.deprecation.rst +++ b/doc/release/upcoming_changes/29052.deprecation.rst @@ -1,5 +1,5 @@ -Positional ``out`` argument to `numpy.maximum`/`numpy.minimum` is deprecated ------------------------------------------------------- +Positional ``out`` argument to `np.maximum`, `np.minimum` is deprecated +----------------------------------------------------------------------- Passing the output array ``out`` positionally to `numpy.maximum` and `numpy.minimum` is deprecated. For example, ``np.maximum(a, b, c)`` will emit a deprecation warning, since ``c`` is treated as the output buffer