diff --git a/.circleci/config.yml b/.circleci/config.yml index eb267dffd7fb..3014cb5c5074 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -56,7 +56,7 @@ jobs: . venv/bin/activate pip install --progress-bar=off -r requirements/test_requirements.txt # get newer, pre-release versions of critical packages - pip install --progress-bar=off --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple -r requirements/doc_requirements.txt + pip install --progress-bar=off --pre -r requirements/doc_requirements.txt # then install numpy HEAD, which will override the version installed above pip install . --config-settings=setup-args="-Dallow-noblas=true" diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index ce034d24d2ea..b11c1e1c168f 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -166,7 +166,7 @@ jobs: echo "CIBW_BUILD_FRONTEND=pip; args: --no-build-isolation" >> "$GITHUB_ENV" - name: Build wheels - uses: pypa/cibuildwheel@bd033a44476646b606efccdd5eed92d5ea1d77ad # v2.20.0 + uses: pypa/cibuildwheel@f1859528322d7b29d4493ee241a167807661dfb4 # v2.21.2 env: CIBW_PRERELEASE_PYTHONS: True CIBW_FREE_THREADED_SUPPORT: True diff --git a/.mailmap b/.mailmap index b073f12c416b..23a556dd9fc4 100644 --- a/.mailmap +++ b/.mailmap @@ -304,6 +304,7 @@ Imen Rajhi Inessa Pawson Irina Maria Mocan <28827042+IrinaMaria@users.noreply.github.com> Irvin Probst +Ishan Koradia <39583356+Ishankoradia@users.noreply.github.com> Ivan Meleshko Isabela Presedo-Floyd Ganesh Kathiresan @@ -620,6 +621,7 @@ Simon Gasse Simon Gasse Sista Seetaram Sista Seetaram <65669128+sistaseetaram@users.noreply.github.com> +Slava Gorloff <31761951+gorloffslava@users.noreply.github.com> Søren Rasmussen <47032123+sorenrasmussenai@users.noreply.github.com> Spencer Hill Srimukh Sripada diff --git a/doc/changelog/2.1.2-changelog.rst b/doc/changelog/2.1.2-changelog.rst new file mode 100644 index 000000000000..bd0f7bd2422c --- /dev/null +++ b/doc/changelog/2.1.2-changelog.rst @@ -0,0 +1,38 @@ + +Contributors +============ + +A total of 11 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Sidebottom +* Ishan Koradia + +* João Eiras + +* Katie Rust + +* Marten van Kerkwijk +* Matti Picus +* Nathan Goldbaum +* Peter Hawkins +* Pieter Eendebak +* Slava Gorloff + + +Pull requests merged +==================== + +A total of 14 pull requests were merged for this release. + +* `#27333 `__: MAINT: prepare 2.1.x for further development +* `#27400 `__: BUG: apply critical sections around populating the dispatch cache +* `#27406 `__: BUG: Stub out get_build_msvc_version if distutils.msvccompiler... +* `#27416 `__: BUILD: fix missing include for std::ptrdiff_t for C++23 language... +* `#27433 `__: BLD: pin setuptools to avoid breaking numpy.distutils +* `#27437 `__: BUG: Allow unsigned shift argument for np.roll +* `#27439 `__: BUG: Disable SVE VQSort +* `#27471 `__: BUG: rfftn axis bug +* `#27479 `__: BUG: Fix extra decref of PyArray_UInt8DType. +* `#27480 `__: CI: use PyPI not scientific-python-nightly-wheels for CI doc... +* `#27481 `__: MAINT: Check for SVE support on demand +* `#27484 `__: BUG: initialize the promotion state to be weak +* `#27501 `__: MAINT: Bump pypa/cibuildwheel from 2.20.0 to 2.21.2 +* `#27506 `__: BUG: avoid segfault on bad arguments in ndarray.__array_function__ diff --git a/doc/source/release.rst b/doc/source/release.rst index fa6cb5bbcb8a..04444a198b02 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -5,6 +5,7 @@ Release notes .. toctree:: :maxdepth: 2 + 2.1.2 2.1.1 2.1.0 2.0.2 diff --git a/doc/source/release/2.1.2-notes.rst b/doc/source/release/2.1.2-notes.rst new file mode 100644 index 000000000000..1a187dbd3365 --- /dev/null +++ b/doc/source/release/2.1.2-notes.rst @@ -0,0 +1,48 @@ +.. currentmodule:: numpy + +========================== +NumPy 2.1.2 Release Notes +========================== + +NumPy 2.1.2 is a maintenance release that fixes bugs and regressions +discovered after the 2.1.1 release. + +The Python versions supported by this release are 3.10-3.13. + +Contributors +============ + +A total of 11 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Charles Harris +* Chris Sidebottom +* Ishan Koradia + +* João Eiras + +* Katie Rust + +* Marten van Kerkwijk +* Matti Picus +* Nathan Goldbaum +* Peter Hawkins +* Pieter Eendebak +* Slava Gorloff + + +Pull requests merged +==================== + +A total of 14 pull requests were merged for this release. + +* `#27333 `__: MAINT: prepare 2.1.x for further development +* `#27400 `__: BUG: apply critical sections around populating the dispatch cache +* `#27406 `__: BUG: Stub out get_build_msvc_version if distutils.msvccompiler... +* `#27416 `__: BUILD: fix missing include for std::ptrdiff_t for C++23 language... +* `#27433 `__: BLD: pin setuptools to avoid breaking numpy.distutils +* `#27437 `__: BUG: Allow unsigned shift argument for np.roll +* `#27439 `__: BUG: Disable SVE VQSort +* `#27471 `__: BUG: rfftn axis bug +* `#27479 `__: BUG: Fix extra decref of PyArray_UInt8DType. +* `#27480 `__: CI: use PyPI not scientific-python-nightly-wheels for CI doc... +* `#27481 `__: MAINT: Check for SVE support on demand +* `#27484 `__: BUG: initialize the promotion state to be weak +* `#27501 `__: MAINT: Bump pypa/cibuildwheel from 2.20.0 to 2.21.2 +* `#27506 `__: BUG: avoid segfault on bad arguments in ndarray.__array_function__ diff --git a/environment.yml b/environment.yml index 86ee1058f440..9f0e236ed74e 100644 --- a/environment.yml +++ b/environment.yml @@ -12,7 +12,7 @@ dependencies: - compilers - openblas - nomkl - - setuptools + - setuptools==65.5.1 - ninja - pkg-config - meson-python diff --git a/numpy/_core/meson.build b/numpy/_core/meson.build index dbf1a144ed93..3d4ef36c055c 100644 --- a/numpy/_core/meson.build +++ b/numpy/_core/meson.build @@ -829,7 +829,7 @@ foreach gen_mtargets : [ 'highway_qsort.dispatch.h', 'src/npysort/highway_qsort.dispatch.cpp', use_highway ? [ - SVE, ASIMD, VSX2, # FIXME: disable VXE due to runtime segfault + ASIMD, VSX2, # FIXME: disable VXE due to runtime segfault ] : [] ], [ diff --git a/numpy/_core/numeric.py b/numpy/_core/numeric.py index 1f3f1c20dbd1..61518d5ab56f 100644 --- a/numpy/_core/numeric.py +++ b/numpy/_core/numeric.py @@ -1272,7 +1272,7 @@ def roll(a, shift, axis=None): "'shift' and 'axis' should be scalars or 1D sequences") shifts = {ax: 0 for ax in range(a.ndim)} for sh, ax in broadcasted: - shifts[ax] += sh + shifts[ax] += int(sh) rolls = [((slice(None), slice(None)),)] * a.ndim for ax, offset in shifts.items(): diff --git a/numpy/_core/src/common/npy_hashtable.c b/numpy/_core/src/common/npy_hashtable.c index 5c745ba388cd..596e62cf8354 100644 --- a/numpy/_core/src/common/npy_hashtable.c +++ b/numpy/_core/src/common/npy_hashtable.c @@ -29,18 +29,6 @@ #define _NpyHASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */ #endif -#ifdef Py_GIL_DISABLED -#define LOCK_TABLE(tb) PyMutex_Lock(&tb->mutex) -#define UNLOCK_TABLE(tb) PyMutex_Unlock(&tb->mutex) -#define INITIALIZE_LOCK(tb) memset(&tb->mutex, 0, sizeof(PyMutex)) -#else -// the GIL serializes access to the table so no need -// for locking if it is enabled -#define LOCK_TABLE(tb) -#define UNLOCK_TABLE(tb) -#define INITIALIZE_LOCK(tb) -#endif - /* * This hashing function is basically the Python tuple hash with the type * identity hash inlined. The tuple hash itself is a reduced version of xxHash. @@ -112,8 +100,6 @@ PyArrayIdentityHash_New(int key_len) res->size = 4; /* Start with a size of 4 */ res->nelem = 0; - INITIALIZE_LOCK(res); - res->buckets = PyMem_Calloc(4 * (key_len + 1), sizeof(PyObject *)); if (res->buckets == NULL) { PyErr_NoMemory(); @@ -206,17 +192,14 @@ NPY_NO_EXPORT int PyArrayIdentityHash_SetItem(PyArrayIdentityHash *tb, PyObject *const *key, PyObject *value, int replace) { - LOCK_TABLE(tb); if (value != NULL && _resize_if_necessary(tb) < 0) { /* Shrink, only if a new value is added. */ - UNLOCK_TABLE(tb); return -1; } PyObject **tb_item = find_item(tb, key); if (value != NULL) { if (tb_item[0] != NULL && tb_item[0] != value && !replace) { - UNLOCK_TABLE(tb); PyErr_SetString(PyExc_RuntimeError, "Identity cache already includes an item with this key."); return -1; @@ -230,7 +213,6 @@ PyArrayIdentityHash_SetItem(PyArrayIdentityHash *tb, memset(tb_item, 0, (tb->key_len + 1) * sizeof(PyObject *)); } - UNLOCK_TABLE(tb); return 0; } @@ -238,8 +220,6 @@ PyArrayIdentityHash_SetItem(PyArrayIdentityHash *tb, NPY_NO_EXPORT PyObject * PyArrayIdentityHash_GetItem(PyArrayIdentityHash *tb, PyObject *const *key) { - LOCK_TABLE(tb); PyObject *res = find_item(tb, key)[0]; - UNLOCK_TABLE(tb); return res; } diff --git a/numpy/_core/src/common/npy_hashtable.h b/numpy/_core/src/common/npy_hashtable.h index 583f3d9861a6..a4252da87aff 100644 --- a/numpy/_core/src/common/npy_hashtable.h +++ b/numpy/_core/src/common/npy_hashtable.h @@ -13,13 +13,6 @@ typedef struct { PyObject **buckets; npy_intp size; /* current size */ npy_intp nelem; /* number of elements */ -#ifdef Py_GIL_DISABLED -#if PY_VERSION_HEX < 0x30d00b3 -#error "GIL-disabled builds require Python 3.13.0b3 or newer" -#else - PyMutex mutex; -#endif -#endif } PyArrayIdentityHash; diff --git a/numpy/_core/src/common/pythoncapi-compat b/numpy/_core/src/common/pythoncapi-compat index ea1f7f6eac63..2d18aecd7b2f 160000 --- a/numpy/_core/src/common/pythoncapi-compat +++ b/numpy/_core/src/common/pythoncapi-compat @@ -1 +1 @@ -Subproject commit ea1f7f6eac63ff401937515638252402ff33dccb +Subproject commit 2d18aecd7b2f549d38a13e27b682ea4966f37bd8 diff --git a/numpy/_core/src/multiarray/abstractdtypes.c b/numpy/_core/src/multiarray/abstractdtypes.c index 214833737792..21360a16c1b6 100644 --- a/numpy/_core/src/multiarray/abstractdtypes.c +++ b/numpy/_core/src/multiarray/abstractdtypes.c @@ -177,7 +177,6 @@ int_common_dtype(PyArray_DTypeMeta *NPY_UNUSED(cls), PyArray_DTypeMeta *other) /* This is a back-compat fallback to usually do the right thing... */ PyArray_DTypeMeta *uint8_dt = &PyArray_UInt8DType; PyArray_DTypeMeta *res = NPY_DT_CALL_common_dtype(other, uint8_dt); - Py_DECREF(uint8_dt); if (res == NULL) { PyErr_Clear(); } diff --git a/numpy/_core/src/multiarray/convert_datatype.c b/numpy/_core/src/multiarray/convert_datatype.c index 550d3e253868..1292a2ad2b92 100644 --- a/numpy/_core/src/multiarray/convert_datatype.c +++ b/numpy/_core/src/multiarray/convert_datatype.c @@ -49,7 +49,7 @@ */ NPY_NO_EXPORT npy_intp REQUIRED_STR_LEN[] = {0, 3, 5, 10, 10, 20, 20, 20, 20}; -static NPY_TLS int npy_promotion_state = NPY_USE_LEGACY_PROMOTION; +static NPY_TLS int npy_promotion_state = NPY_USE_WEAK_PROMOTION; NPY_NO_EXPORT int get_npy_promotion_state() { diff --git a/numpy/_core/src/multiarray/methods.c b/numpy/_core/src/multiarray/methods.c index 4a8e1ea4579e..2a950d6ca5d1 100644 --- a/numpy/_core/src/multiarray/methods.c +++ b/numpy/_core/src/multiarray/methods.c @@ -1120,7 +1120,14 @@ array_function(PyArrayObject *NPY_UNUSED(self), PyObject *c_args, PyObject *c_kw &func, &types, &args, &kwargs)) { return NULL; } - + if (!PyTuple_CheckExact(args)) { + PyErr_SetString(PyExc_TypeError, "args must be a tuple."); + return NULL; + } + if (!PyDict_CheckExact(kwargs)) { + PyErr_SetString(PyExc_TypeError, "kwargs must be a dict."); + return NULL; + } types = PySequence_Fast( types, "types argument to ndarray.__array_function__ must be iterable"); diff --git a/numpy/_core/src/multiarray/textreading/rows.c b/numpy/_core/src/multiarray/textreading/rows.c index 4ca1cc00e9f7..214c5c499ad8 100644 --- a/numpy/_core/src/multiarray/textreading/rows.c +++ b/numpy/_core/src/multiarray/textreading/rows.c @@ -6,6 +6,7 @@ #define _MULTIARRAYMODULE #include "numpy/arrayobject.h" #include "numpy/npy_3kcompat.h" +#include "npy_pycompat.h" #include "alloc.h" #include @@ -59,9 +60,7 @@ create_conv_funcs( PyObject *key, *value; Py_ssize_t pos = 0; int error = 0; -#if Py_GIL_DISABLED Py_BEGIN_CRITICAL_SECTION(converters); -#endif while (PyDict_Next(converters, &pos, &key, &value)) { Py_ssize_t column = PyNumber_AsSsize_t(key, PyExc_IndexError); if (column == -1 && PyErr_Occurred()) { @@ -114,9 +113,7 @@ create_conv_funcs( Py_INCREF(value); conv_funcs[column] = value; } -#if Py_GIL_DISABLED Py_END_CRITICAL_SECTION(); -#endif if (error) { goto error; diff --git a/numpy/_core/src/umath/dispatching.c b/numpy/_core/src/umath/dispatching.c index 110e2f40ab32..e76509ad7db2 100644 --- a/numpy/_core/src/umath/dispatching.c +++ b/numpy/_core/src/umath/dispatching.c @@ -976,6 +976,10 @@ promote_and_get_ufuncimpl(PyUFuncObject *ufunc, } } + int error_res = 0; + PyObject *all_dtypes; + PyArrayMethodObject *method; + Py_BEGIN_CRITICAL_SECTION((PyObject *)ufunc); int current_promotion_state = get_npy_promotion_state(); if (force_legacy_promotion && legacy_promotion_is_possible @@ -989,42 +993,51 @@ promote_and_get_ufuncimpl(PyUFuncObject *ufunc, int cacheable = 1; /* unused, as we modify the original `op_dtypes` */ if (legacy_promote_using_legacy_type_resolver(ufunc, ops, signature, op_dtypes, &cacheable, NPY_FALSE) < 0) { - goto handle_error; + error_res = -1; } } - /* Pause warnings and always use "new" path */ - set_npy_promotion_state(NPY_USE_WEAK_PROMOTION); - PyObject *info = promote_and_get_info_and_ufuncimpl(ufunc, - ops, signature, op_dtypes, legacy_promotion_is_possible); - set_npy_promotion_state(current_promotion_state); + PyObject *info = NULL; + if (error_res == 0) { + /* Pause warnings and always use "new" path */ + set_npy_promotion_state(NPY_USE_WEAK_PROMOTION); + info = promote_and_get_info_and_ufuncimpl(ufunc, + ops, signature, op_dtypes, legacy_promotion_is_possible); + set_npy_promotion_state(current_promotion_state); - if (info == NULL) { - goto handle_error; + if (info == NULL) { + error_res = -1; + } } - PyArrayMethodObject *method = (PyArrayMethodObject *)PyTuple_GET_ITEM(info, 1); - PyObject *all_dtypes = PyTuple_GET_ITEM(info, 0); + if (error_res == 0) { + method = (PyArrayMethodObject *)PyTuple_GET_ITEM(info, 1); + all_dtypes = PyTuple_GET_ITEM(info, 0); - /* If necessary, check if the old result would have been different */ - if (NPY_UNLIKELY(current_promotion_state == NPY_USE_WEAK_PROMOTION_AND_WARN) - && (force_legacy_promotion || promoting_pyscalars) - && npy_give_promotion_warnings()) { - PyArray_DTypeMeta *check_dtypes[NPY_MAXARGS]; - for (int i = 0; i < nargs; i++) { - check_dtypes[i] = (PyArray_DTypeMeta *)PyTuple_GET_ITEM( - all_dtypes, i); - } - /* Before calling to the legacy promotion, pretend that is the state: */ - set_npy_promotion_state(NPY_USE_LEGACY_PROMOTION); - int res = legacy_promote_using_legacy_type_resolver(ufunc, - ops, signature, check_dtypes, NULL, NPY_TRUE); - /* Reset the promotion state: */ - set_npy_promotion_state(NPY_USE_WEAK_PROMOTION_AND_WARN); - if (res < 0) { - goto handle_error; + /* If necessary, check if the old result would have been different */ + if (NPY_UNLIKELY(current_promotion_state == NPY_USE_WEAK_PROMOTION_AND_WARN) + && (force_legacy_promotion || promoting_pyscalars) + && npy_give_promotion_warnings()) { + PyArray_DTypeMeta *check_dtypes[NPY_MAXARGS]; + for (int i = 0; i < nargs; i++) { + check_dtypes[i] = (PyArray_DTypeMeta *)PyTuple_GET_ITEM( + all_dtypes, i); + } + /* Before calling to the legacy promotion, pretend that is the state: */ + set_npy_promotion_state(NPY_USE_LEGACY_PROMOTION); + int res = legacy_promote_using_legacy_type_resolver(ufunc, + ops, signature, check_dtypes, NULL, NPY_TRUE); + /* Reset the promotion state: */ + set_npy_promotion_state(NPY_USE_WEAK_PROMOTION_AND_WARN); + if (res < 0) { + error_res = 0; + } } } + Py_END_CRITICAL_SECTION(); + if (error_res < 0) { + goto handle_error; + } /* * In certain cases (only the logical ufuncs really), the loop we found may diff --git a/numpy/_core/src/umath/string_fastsearch.h b/numpy/_core/src/umath/string_fastsearch.h index 61abdcb5ad19..96c1e2d30140 100644 --- a/numpy/_core/src/umath/string_fastsearch.h +++ b/numpy/_core/src/umath/string_fastsearch.h @@ -9,6 +9,7 @@ #include #include +#include #include diff --git a/numpy/_core/tests/test_multiarray.py b/numpy/_core/tests/test_multiarray.py index 0bc9fea9c960..2f7e3c574c79 100644 --- a/numpy/_core/tests/test_multiarray.py +++ b/numpy/_core/tests/test_multiarray.py @@ -30,7 +30,7 @@ assert_array_equal, assert_raises_regex, assert_array_almost_equal, assert_allclose, IS_PYPY, IS_WASM, IS_PYSTON, HAS_REFCOUNT, assert_array_less, runstring, temppath, suppress_warnings, break_cycles, - _SUPPORTS_SVE, assert_array_compare, + check_support_sve, assert_array_compare, ) from numpy.testing._private.utils import requires_memory, _no_tracing from numpy._core.tests._locales import CommaDecimalPointLocale @@ -10107,7 +10107,7 @@ def test_non_c_contiguous(self): assert_array_equal(x.view('= '3.12' -setuptools +setuptools==65.5.1 ; python_version < '3.12' +setuptools ; python_version >= '3.12' hypothesis==6.104.1 pytest==7.4.0 pytz==2023.3.post1 diff --git a/tools/lint_diff.ini b/tools/lint_diff.ini index dbebe483b4ab..810e265d4dec 100644 --- a/tools/lint_diff.ini +++ b/tools/lint_diff.ini @@ -1,5 +1,5 @@ [pycodestyle] -max_line_length = 79 +max_line_length = 88 statistics = True ignore = E121,E122,E123,E125,E126,E127,E128,E226,E241,E251,E265,E266,E302,E402,E704,E712,E721,E731,E741,W291,W293,W391,W503,W504 exclude = numpy/__config__.py,numpy/typing/tests/data,.spin/cmds.py