summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build_test.yml8
-rw-r--r--doc/release/numpy2_changes/23089.change.rst2
-rw-r--r--doc/release/numpy2_changes/README.md4
-rw-r--r--doc/source/reference/global_state.rst14
-rw-r--r--numpy/__init__.py6
-rw-r--r--numpy/core/multiarray.py5
-rw-r--r--numpy/core/numeric.py6
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c23
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.h2
-rw-r--r--numpy/lib/function_base.py2
-rw-r--r--numpy/lib/tests/test_function_base.py7
11 files changed, 73 insertions, 6 deletions
diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
index 08d531105..8168f5809 100644
--- a/.github/workflows/build_test.yml
+++ b/.github/workflows/build_test.yml
@@ -276,11 +276,17 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- uses: ./.github/actions
- no_array_func:
+ numpy2_flag_and_no_array_func:
needs: [smoke_test]
runs-on: ubuntu-latest
env:
NUMPY_EXPERIMENTAL_ARRAY_FUNCTION: 0
+ # Array func and numpy-2 should be involved, so use this
+ # to have a test for feature flagged behavior.
+ NPY_NUMPY_2_BEHAVIOR: 1
+ # Using the future "weak" state doesn't pass tests
+ # currently unfortunately
+ NPY_PROMOTION_STATE: legacy
steps:
- uses: actions/checkout@v3
with:
diff --git a/doc/release/numpy2_changes/23089.change.rst b/doc/release/numpy2_changes/23089.change.rst
new file mode 100644
index 000000000..fbd87e0aa
--- /dev/null
+++ b/doc/release/numpy2_changes/23089.change.rst
@@ -0,0 +1,2 @@
+* ``np.gradient()`` now returns a tuple rather than list making the
+ return value immutable.
diff --git a/doc/release/numpy2_changes/README.md b/doc/release/numpy2_changes/README.md
new file mode 100644
index 000000000..fb59749a9
--- /dev/null
+++ b/doc/release/numpy2_changes/README.md
@@ -0,0 +1,4 @@
+Same as ``../upcoming_changes`` but for changes that currently
+are opt-in behind ``NPY_NUMPY_2_BEHAVIOR=1``.
+
+This is to get a start on release notes for such changes.
diff --git a/doc/source/reference/global_state.rst b/doc/source/reference/global_state.rst
index f5c96c355..a110ce7c1 100644
--- a/doc/source/reference/global_state.rst
+++ b/doc/source/reference/global_state.rst
@@ -95,3 +95,17 @@ memory allocation policy, the default will be to call ``free``. If
``NUMPY_WARN_IF_NO_MEM_POLICY`` is set to ``"1"``, a ``RuntimeWarning`` will
be emitted. A better alternative is to use a ``PyCapsule`` with a deallocator
and set the ``ndarray.base``.
+
+
+Testing planned future behavior
+===============================
+
+NumPy has some code paths which are planned to be activated in the future
+but are not yet the default behavior.
+You can try testing some of these which may be shipped with a new "major"
+release (NumPy 2.0) by setting an environment before importing NumPy:
+
+ NPY_NUMPY_2_BEHAVIOR=1
+
+By default this will also activate the :ref:`NEP 50 <NEP50>` related setting
+``NPY_PROMOTION_STATE`` (please see the NEP for details on this).
diff --git a/numpy/__init__.py b/numpy/__init__.py
index 9c50e2493..e1666a8d2 100644
--- a/numpy/__init__.py
+++ b/numpy/__init__.py
@@ -415,13 +415,17 @@ 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 _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 db7fda32d..4fa58c4df 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)
@@ -4416,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,
@@ -4641,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 */
@@ -4913,6 +4930,12 @@ initialize_static_globals(void)
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/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;
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index 0ca3c21c1..f5af314b5 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._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 ecba35f2d..cc8003f61 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._using_numpy2_behavior():
+ assert type(res) is tuple
+ else:
+ assert type(res) is list
+
class TestAngle: