From 624b18090ae567f3cfd528a8ae156b2ae7db6d82 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 24 Jan 2023 22:28:35 +0100 Subject: API: Add environment variable for behavior planned in a 2.0 The idea of the flag is not to allow to change it right now, since there may be some things where that is hard to do in general, and it doesn't seem relevant: nobody is supposed to use it besides for testing. --- numpy/__init__.py | 10 ++++++++-- numpy/core/src/multiarray/multiarraymodule.c | 17 +++++++++++++++++ numpy/core/src/multiarray/multiarraymodule.h | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.py b/numpy/__init__.py index 052ba7327..976062d13 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -122,6 +122,10 @@ except NameError: if __NUMPY_SETUP__: sys.stderr.write('Running from numpy source directory.\n') else: + # Make variable available during multiarray/C initialization + import os + _numpy2_behavior = os.environ.get("NPY_NUMPY_2_BEHAVIOR", "0") != "0" + try: from numpy.__config__ import show as show_config except ImportError as e: @@ -392,7 +396,6 @@ else: # is slow and thus better avoided. # Specifically kernel version 4.6 had a bug fix which probably fixed this: # https://github.com/torvalds/linux/commit/7cf91a98e607c2f935dbcc177d70011e95b8faff - import os use_hugepage = os.environ.get("NUMPY_MADVISE_HUGEPAGE", None) if sys.platform == "linux" and use_hugepage is None: # If there is an issue with parsing the kernel version, @@ -415,13 +418,16 @@ else: # Note that this will currently only make a difference on Linux core.multiarray._set_madvise_hugepage(use_hugepage) + del use_hugepage # Give a warning if NumPy is reloaded or imported on a sub-interpreter # We do this from python, since the C-module may not be reloaded and # it is tidier organized. core.multiarray._multiarray_umath._reload_guard() - core._set_promotion_state(os.environ.get("NPY_PROMOTION_STATE", "legacy")) + # default to "weak" promotion for "NumPy 2". + core._set_promotion_state(os.environ.get( + "NPY_PROMOTION_STATE", "weak" if _numpy2_behavior else "legacy")) # Tell PyInstaller where to find hook-numpy.py def _pyinstaller_hooks_dir(): diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index db7fda32d..86869c8e4 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -98,6 +98,12 @@ _umath_strings_richcompare( * future. */ int npy_legacy_print_mode = INT_MAX; +/* + * Global variable to check whether NumPy 2.0 behavior is opted in. + * This flag is considered a runtime constant. + */ +int npy_numpy2_behavior = NPY_FALSE; + static PyObject * set_legacy_print_mode(PyObject *NPY_UNUSED(self), PyObject *args) @@ -4913,6 +4919,17 @@ initialize_static_globals(void) return -1; } + PyObject *_is_numpy2 = NULL; + npy_cache_import("numpy", "_numpy2_behavior", &_is_numpy2); + if (_is_numpy2 == NULL) { + return -1; + } + npy_numpy2_behavior = PyObject_IsTrue(_is_numpy2); + Py_DECREF(_is_numpy2); + if (npy_numpy2_behavior < 0) { + return -1; + } + return 0; } diff --git a/numpy/core/src/multiarray/multiarraymodule.h b/numpy/core/src/multiarray/multiarraymodule.h index 809736cd2..992acd09f 100644 --- a/numpy/core/src/multiarray/multiarraymodule.h +++ b/numpy/core/src/multiarray/multiarraymodule.h @@ -1,6 +1,8 @@ #ifndef NUMPY_CORE_SRC_MULTIARRAY_MULTIARRAYMODULE_H_ #define NUMPY_CORE_SRC_MULTIARRAY_MULTIARRAYMODULE_H_ +NPY_VISIBILITY_HIDDEN extern int npy_numpy2_behavior; + NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_current_allocator; NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array; NPY_VISIBILITY_HIDDEN extern PyObject * npy_ma_str_array_function; -- cgit v1.2.1 From c03e84044a05f0b2358a1cbfc4158e83cba4b835 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Thu, 26 Jan 2023 12:16:23 +0100 Subject: API: Modify `gradient` to return a tuple rather than a list This change is staged for NumPy 2.0, I assume we could get away with it otherwise, but it is a nice example and probably not pressing. --- numpy/lib/function_base.py | 2 ++ numpy/lib/tests/test_function_base.py | 7 +++++++ 2 files changed, 9 insertions(+) (limited to 'numpy') diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 0ca3c21c1..1f39ebc71 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1311,6 +1311,8 @@ def gradient(f, *varargs, axis=None, edge_order=1): if len_axes == 1: return outvals[0] + elif np._numpy2_behavior: + return tuple(outvals) else: return outvals diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index ecba35f2d..fb98a94cd 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1217,6 +1217,13 @@ class TestGradient: dfdx = gradient(f, x) assert_array_equal(dfdx, [0.5, 0.5]) + def test_return_type(self): + res = np.gradient(([1, 2], [2, 3])) + if np._numpy2_behavior: + assert type(res) is tuple + else: + assert type(res) is list + class TestAngle: -- cgit v1.2.1 From 9d5eafe596e75e30a85c01ed62bb5bea9389adc8 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Fri, 10 Feb 2023 15:51:57 +0100 Subject: MAINT: Use `np._using_numpy2_behavior()` and initialize it in C --- numpy/__init__.py | 10 ++++------ numpy/core/multiarray.py | 5 +++-- numpy/core/numeric.py | 6 ++++-- numpy/core/src/multiarray/multiarraymodule.c | 24 +++++++++++++++--------- numpy/lib/function_base.py | 2 +- numpy/lib/tests/test_function_base.py | 2 +- 6 files changed, 28 insertions(+), 21 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.py b/numpy/__init__.py index 976062d13..9fd7717a9 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -122,10 +122,6 @@ except NameError: if __NUMPY_SETUP__: sys.stderr.write('Running from numpy source directory.\n') else: - # Make variable available during multiarray/C initialization - import os - _numpy2_behavior = os.environ.get("NPY_NUMPY_2_BEHAVIOR", "0") != "0" - try: from numpy.__config__ import show as show_config except ImportError as e: @@ -396,6 +392,7 @@ else: # is slow and thus better avoided. # Specifically kernel version 4.6 had a bug fix which probably fixed this: # https://github.com/torvalds/linux/commit/7cf91a98e607c2f935dbcc177d70011e95b8faff + import os use_hugepage = os.environ.get("NUMPY_MADVISE_HUGEPAGE", None) if sys.platform == "linux" and use_hugepage is None: # If there is an issue with parsing the kernel version, @@ -426,8 +423,9 @@ else: core.multiarray._multiarray_umath._reload_guard() # default to "weak" promotion for "NumPy 2". - core._set_promotion_state(os.environ.get( - "NPY_PROMOTION_STATE", "weak" if _numpy2_behavior else "legacy")) + core._set_promotion_state( + os.environ.get("NPY_PROMOTION_STATE", + "weak" if _using_numpy2_behavior() else "legacy")) # Tell PyInstaller where to find hook-numpy.py def _pyinstaller_hooks_dir(): diff --git a/numpy/core/multiarray.py b/numpy/core/multiarray.py index ec1294b85..d11283345 100644 --- a/numpy/core/multiarray.py +++ b/numpy/core/multiarray.py @@ -17,7 +17,7 @@ from ._multiarray_umath import ( fastCopyAndTranspose, _flagdict, from_dlpack, _place, _reconstruct, _vec_string, _ARRAY_API, _monotonicity, _get_ndarray_c_version, _get_madvise_hugepage, _set_madvise_hugepage, - _get_promotion_state, _set_promotion_state, + _get_promotion_state, _set_promotion_state, _using_numpy2_behavior ) __all__ = [ @@ -42,7 +42,7 @@ __all__ = [ 'set_legacy_print_mode', 'set_numeric_ops', 'set_string_function', 'set_typeDict', 'shares_memory', 'tracemalloc_domain', 'typeinfo', 'unpackbits', 'unravel_index', 'vdot', 'where', 'zeros', - '_get_promotion_state', '_set_promotion_state'] + '_get_promotion_state', '_set_promotion_state', '_using_numpy2_behavior'] # For backward compatibility, make sure pickle imports these functions from here _reconstruct.__module__ = 'numpy.core.multiarray' @@ -72,6 +72,7 @@ seterrobj.__module__ = 'numpy' zeros.__module__ = 'numpy' _get_promotion_state.__module__ = 'numpy' _set_promotion_state.__module__ = 'numpy' +_using_numpy2_behavior.__module__ = 'numpy' # We can't verify dispatcher signatures because NumPy's C functions don't diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 864f47947..fbb284696 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -17,7 +17,8 @@ from .multiarray import ( fromstring, inner, lexsort, matmul, may_share_memory, min_scalar_type, ndarray, nditer, nested_iters, promote_types, putmask, result_type, set_numeric_ops, shares_memory, vdot, where, - zeros, normalize_axis_index, _get_promotion_state, _set_promotion_state) + zeros, normalize_axis_index, _get_promotion_state, _set_promotion_state, + _using_numpy2_behavior) from . import overrides from . import umath @@ -54,7 +55,8 @@ __all__ = [ 'False_', 'True_', 'bitwise_not', 'CLIP', 'RAISE', 'WRAP', 'MAXDIMS', 'BUFSIZE', 'ALLOW_THREADS', 'full', 'full_like', 'matmul', 'shares_memory', 'may_share_memory', 'MAY_SHARE_BOUNDS', - 'MAY_SHARE_EXACT', '_get_promotion_state', '_set_promotion_state'] + 'MAY_SHARE_EXACT', '_get_promotion_state', '_set_promotion_state', + '_using_numpy2_behavior'] def _zeros_like_dispatcher(a, dtype=None, order=None, subok=None, shape=None): diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index 86869c8e4..4fa58c4df 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -4422,6 +4422,15 @@ _reload_guard(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args)) { Py_RETURN_NONE; } + +static PyObject * +_using_numpy2_behavior( + PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args)) +{ + return PyBool_FromLong(npy_numpy2_behavior); +} + + static struct PyMethodDef array_module_methods[] = { {"_get_implementing_args", (PyCFunction)array__get_implementing_args, @@ -4647,6 +4656,8 @@ static struct PyMethodDef array_module_methods[] = { {"_reload_guard", (PyCFunction)_reload_guard, METH_NOARGS, "Give a warning on reload and big warning in sub-interpreters."}, + {"_using_numpy2_behavior", (PyCFunction)_using_numpy2_behavior, + METH_NOARGS, NULL}, {"from_dlpack", (PyCFunction)from_dlpack, METH_O, NULL}, {NULL, NULL, 0, NULL} /* sentinel */ @@ -4919,15 +4930,10 @@ initialize_static_globals(void) return -1; } - PyObject *_is_numpy2 = NULL; - npy_cache_import("numpy", "_numpy2_behavior", &_is_numpy2); - if (_is_numpy2 == NULL) { - return -1; - } - npy_numpy2_behavior = PyObject_IsTrue(_is_numpy2); - Py_DECREF(_is_numpy2); - if (npy_numpy2_behavior < 0) { - return -1; + /* Initialize from certain environment variabels: */ + char *env = getenv("NPY_NUMPY_2_BEHAVIOR"); + if (env != NULL && strcmp(env, "0") != 0) { + npy_numpy2_behavior = NPY_TRUE; } return 0; diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 1f39ebc71..f5af314b5 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1311,7 +1311,7 @@ def gradient(f, *varargs, axis=None, edge_order=1): if len_axes == 1: return outvals[0] - elif np._numpy2_behavior: + elif np._using_numpy2_behavior(): return tuple(outvals) else: return outvals diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index fb98a94cd..cc8003f61 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1219,7 +1219,7 @@ class TestGradient: def test_return_type(self): res = np.gradient(([1, 2], [2, 3])) - if np._numpy2_behavior: + if np._using_numpy2_behavior(): assert type(res) is tuple else: assert type(res) is list -- cgit v1.2.1