- Add support for Python 3.14, with a couple of upstream patches:

gh#numpy/numpy#28928 and gh#numpy/numpy#28748, backported in
  python314.patch

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:numeric/python-numpy1?expand=0&rev=24
This commit is contained in:
2026-04-28 08:47:31 +00:00
committed by Git OBS Bridge
commit 259de4cf9f
13 changed files with 4180 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+288
View File
@@ -0,0 +1,288 @@
#
# spec file for package python-numpy1
#
# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%define plainpython python
%if 0%{?suse_version} > 1500
%bcond_without libalternatives
%bcond_without cblas
%else
%bcond_with libalternatives
%bcond_with cblas
%endif
#
%{?sle15_python_module_pythons}
Name: python-numpy1
Version: 1.26.4
Release: 0
Summary: NumPy array processing for numbers, strings, records and objects
License: BSD-3-Clause
URL: http://www.numpy.org/
Source: https://files.pythonhosted.org/packages/source/n/numpy/numpy-%{version}.tar.gz
Source99: python-numpy1-rpmlintrc
# PATCH-FIX-OPENSUSE numpy-buildfix.patch -- openSUSE-specific build fixes
Patch0: numpy-buildfix.patch
# PATCH-FIX-OPENSUSE numpy-1.9.0-remove-__declspec.patch -- fix for spurious compiler warnings that cause build failure
Patch1: numpy-1.9.0-remove-__declspec.patch
# PATCH-FIX-UPSTREAM https://github.com/numpy/numpy/pull/26151
Patch2: 0001-BUG-Fix-test_impossible_feature_enable-failing-witho.patch
# PATCH-FIX-UPSTREAM https://github.com/numpy/meson/pull/12
Patch3: 0001-feature-module-Fix-handling-of-multiple-conflicts-pe.patch
# PATCH-FIX-UPSTREAM Based on gh#numpy/numpy#25839
Patch4: fix-meson-multiple-python-versions.patch
# PATCH-FIX-UPSTREAM gh#numpy/numpy#28928, gh#numpy/numpy#28748
Patch5: python314.patch
BuildRequires: %{python_module Cython >= 3.0}
BuildRequires: %{python_module base >= 3.9}
BuildRequires: %{python_module devel}
BuildRequires: %{python_module meson-python >= 0.15}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module pyproject-metadata >= 0.7.1}
BuildRequires: cmake
%if 0%{?suse_version} < 1600
BuildRequires: gcc12
BuildRequires: gcc12-c++
%else
BuildRequires: gcc
BuildRequires: gcc-c++
%endif
BuildRequires: ninja >= 1.8.2
BuildRequires: patchelf
BuildRequires: python-rpm-macros >= 20210929
BuildConflicts: gcc11 < 11.2
%if 0%{?suse_version}
BuildRequires: fdupes
%endif
# SECTION test requirements
BuildRequires: %{python_module pytest >= 7.4.0}
BuildRequires: %{python_module hypothesis >= 6.75.0}
BuildRequires: %{python_module pytest-xdist}
BuildRequires: %{python_module testsuite}
BuildRequires: %{python_module typing-extensions >= 4.2.0}
# /SECTION
# Last version which packaged %%{_bindir}/f2py without update-alternatives
Conflicts: %{plainpython}-numpy <= 1.12.0
Conflicts: python-numpy >= 2
Provides: python-numpy = %{version}-%{release}
%if 0%{?suse_version}
BuildRequires: gcc-fortran
%else
BuildRequires: gcc-gfortran
%endif
BuildRequires: blas-devel
BuildRequires: lapack-devel
%if %{with cblas}
# openblas has significantly better performance for some operations
BuildRequires: cblas-devel
Recommends: libopenblas_pthreads0
%endif
%if %{with libalternatives}
BuildRequires: alts
Requires: alts
%else
Requires(post): update-alternatives
Requires(postun): update-alternatives
%endif
%python_subpackages
%description
NumPy is a general-purpose array-processing package designed to
efficiently manipulate large multi-dimensional arrays of arbitrary
records without sacrificing too much speed for small multi-dimensional
arrays. NumPy is built on the Numeric code base and adds features
introduced by numarray as well as an extended C-API and the ability to
create arrays of arbitrary type which also makes NumPy suitable for
interfacing with general-purpose data-base applications.
There are also basic facilities for discrete fourier transform,
basic linear algebra and random number generation.
%package devel
Summary: Development files for numpy applications
Requires: %{name} = %{version}
Requires: python-devel
Requires: %plainpython(abi) = %{python_version}
Conflicts: python-numpy-devel >= 2
Provides: python-numpy-devel = %{version}-%{release}
Requires: blas-devel
%if %{with cblas}
Requires: cblas-devel
%endif
Requires: lapack-devel
%description devel
This package contains files for developing applications using numpy.
%prep
%autosetup -p1 -n numpy-%{version}
# Fix non-executable scripts
sed -i '1{/^#!/d}'\
numpy/{distutils,f2py,ma,matrixlib,testing}/setup.py \
numpy/distutils/{conv_template,cpuinfo,from_template,system_info}.py \
numpy/f2py/{__init__,cfuncs,diagnose,crackfortran,f2py2e,rules}.py \
numpy/random/_examples/cython/extending{,_distributions}.pyx \
numpy/testing/print_coercion_tables.py
chmod -x \
numpy/f2py/{crackfortran,f2py2e,rules}.py \
numpy/testing/{print_coercion_tables,setup}.py
# force cythonization
rm -f PKG-INFO
%build
%define _lto_cflags %{nil}
export CFLAGS="%{optflags} -fno-strict-aliasing"
%if 0%{?suse_version} < 1600
export CC=gcc-12
export CXX=g++-12
%endif
%pyproject_wheel
%install
%pyproject_install
%python_clone -a %{buildroot}%{_bindir}/f2py
%if 0%{?suse_version}
%fdupes %{buildroot}%{_prefix}
%endif
%check
# https://numpy.org/doc/stable/dev/development_environment.html#running-tests
export PATH="%{buildroot}%{_bindir}:$PATH"
mkdir -p testing
cp pytest.ini testing/
pushd testing
# flaky tests
test_failok+=" or test_structured_object_indexing"
test_failok+=" or test_structured_object_item_setting"
# flaky due to memory consumption
test_failok+=" or test_big_arrays"
# gh#numpy/numpy#22825
test_failok+=" or TestPrintOptions"
# gh#numpy/numpy#22835
test_failok+=" or test_keepdims_out"
# boo#1148173 gh#numpy/numpy#14438
%ifarch ppc64 ppc64le
test_failok+=" or test_generalized_sq"
# situation with IBM and double numbers is ... complicated
# gh#numpy/numpy#21094
test_failok+=" or test_ppc64_ibm_double_double128"
%endif
# these tests fail on big endian gh#numpy/numpy#11831
%ifarch s390x ppc ppc64
test_failok+=" or TestFReturnCharacter"
%endif
# missing instruction set
%ifarch s390x
test_failok+=" or test_truncate_f32"
%endif
%ifarch %{ix86}
# (arm 32-bit seems okay here)
# gh#numpy/numpy#18387
test_failok+=" or test_pareto"
# gh#numpy/numpy#18388
test_failok+=" or test_float_remainder_overflow"
%endif
%ifarch %{ix86} %{arm32}
# too much memory for 32bit
test_failok+=" or test_identityless_reduction_huge_array"
test_failok+=" or test_huge_vectordot"
# invalid int type for 32bit
test_failok+=" or (test_kind and test_quad_precision)"
test_failok+=" or (test_kind and test_int)"
test_failok+=" or (test_kind and test_real)"
test_failok+=" or (test_multinomial_pvals_float32)"
%endif
%ifarch %{arm}
# https://github.com/numpy/numpy/issues/24001
test_failok+=" or (test_cpu_features and test_features)"
test_failok+=" or (test_umath and test_unary_spurious_fpexception)"
%endif
%ifarch riscv64
# These tests fail due to non-portable assumptions about the signbit of NaN
# gh#numpy/numpy#8213
test_failok+=" or (test_umath and test_fpclass)"
test_failok+=" or (test_numeric and TestBoolCmp and test_float)"
test_failok+=" or (test_umath and test_fp_noncontiguous)"
%endif
# fail on Python 3.13 https://github.com/numpy/numpy/issues/27842
test_failok+=" or (test_nditer and test_iter_refcount) or (test_utils and (test_deprecate_help_indentation or test_deprecate_preserve_whitespace))"
# not supported by setuptools 75
test_failok+=" or test_all_modules_are_expected_2"
test_failok+=" or test_api_importable"
echo "
import sys
import numpy
r = numpy.test(label='full', verbose=2,
extra_argv=['-v', '-n', 'auto', '-k'] + sys.argv[1:])
sys.exit(0 if r else 1)
" > runobstest.py
%{python_expand # for all python3 flavors
export PYTHONPATH=%{buildroot}%{$python_sitearch}
export PYTHONDONTWRITEBYTECODE=1
ignore="--ignore %{buildroot}%{$python_sitearch}/numpy/distutils/tests/test_mingw32ccompiler.py"
ignore+=" --ignore %{buildroot}%{$python_sitearch}/numpy/distutils/tests/test_system_info.py"
[ -n "$test_failok" ] && $python runobstest.py "${test_failok:4}" $ignore ||:
# test_new_policy: duplicates test runs and output and does not follow our deselection
$python runobstest.py "not (test_new_policy ${test_failok} or slow)" $ignore
}
popd
%pre
# If libalternatives is used: Removing old update-alternatives entries.
%python_libalternatives_reset_alternative f2py
%post
%python_install_alternative f2py
%postun
%python_uninstall_alternative f2py
%files %{python_files}
%doc README.md THANKS.txt
%license LICENSE.txt
%python_alternative %{_bindir}/f2py
%{python_sitearch}/numpy/
%{python_sitearch}/numpy-%{version}.dist-info
%exclude %{python_sitearch}/numpy/core/include/
%exclude %{python_sitearch}/numpy/distutils/mingw/*.c
%exclude %{python_sitearch}/numpy/distutils/checks/*.c
%exclude %{python_sitearch}/numpy/f2py/src/
%exclude %{python_sitearch}/numpy/core/lib/libnpymath.a
%exclude %{python_sitearch}/numpy/random/lib/libnpyrandom.a
%files %{python_files devel}
%license LICENSE.txt
%{python_sitearch}/numpy/core/include/
%if 0%{python_version_nodots} < 312
%{python_sitearch}/numpy/distutils/mingw/*.c
%{python_sitearch}/numpy/distutils/checks/*.c
%endif
%{python_sitearch}/numpy/f2py/src/
%{python_sitearch}/numpy/core/lib/libnpymath.a
%{python_sitearch}/numpy/random/lib/libnpyrandom.a
%changelog
+10
View File
@@ -0,0 +1,10 @@
<constraints>
<hardware>
<physicalmemory>
<size unit="G">8</size>
</physicalmemory>
<disk>
<size unit="G">9</size>
</disk>
</hardware>
</constraints>
+24
View File
@@ -0,0 +1,24 @@
*.changes merge=merge-changes
*.7z filter=lfs diff=lfs merge=lfs -text
*.bsp filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.gem filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.jar filter=lfs diff=lfs merge=lfs -text
*.lz filter=lfs diff=lfs merge=lfs -text
*.lzma filter=lfs diff=lfs merge=lfs -text
*.obscpio filter=lfs diff=lfs merge=lfs -text
*.oxt filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.rpm filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.tbz filter=lfs diff=lfs merge=lfs -text
*.tbz2 filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.txz filter=lfs diff=lfs merge=lfs -text
*.whl filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
+4
View File
@@ -0,0 +1,4 @@
*.obscpio
*.osc
_build.*
.pbuild
@@ -0,0 +1,64 @@
From bf50761dee8f0670542bcdc1fd9bff5cdb0448d4 Mon Sep 17 00:00:00 2001
From: Fabian Vogt <fvogt@suse.de>
Date: Thu, 28 Mar 2024 14:39:45 +0100
Subject: [PATCH] BUG: Fix test_impossible_feature_enable failing without
BASELINE_FEAT
If the build has no baseline features set, the test ended up setting
e.g. NPY_ENABLE_CPU_FEATURES="ASIMDHP, None". This actually made the
execution succeed, as the warning for decoding "None" overrode the
error for the real feature. Fix the error handling there by removing
the errorneous "return 0;", add a test for this, and avoid passing
"None" by accident.
---
numpy/core/src/common/npy_cpu_features.c | 1 -
numpy/core/tests/test_cpu_features.py | 14 ++++++++++++--
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/numpy/core/src/common/npy_cpu_features.c b/numpy/core/src/common/npy_cpu_features.c
index bd149f8b4..f4d25cc50 100644
--- a/numpy/core/src/common/npy_cpu_features.c
+++ b/numpy/core/src/common/npy_cpu_features.c
@@ -324,7 +324,6 @@ npy__cpu_check_env(int disable, const char *env) {
) < 0) {
return -1;
}
- return 0;
}
#define NOTSUPP_BODY \
diff --git a/numpy/core/tests/test_cpu_features.py b/numpy/core/tests/test_cpu_features.py
index 48ab30a4a..88e4ad185 100644
--- a/numpy/core/tests/test_cpu_features.py
+++ b/numpy/core/tests/test_cpu_features.py
@@ -308,8 +308,8 @@ def test_impossible_feature_enable(self):
err_type = "RuntimeError"
self._expect_error(msg, err_type)
- # Ensure that only the bad feature gets reported
- feats = f"{bad_feature}, {self.BASELINE_FEAT}"
+ # Ensure that it fails even when providing garbage in addition
+ feats = f"{bad_feature}, Foobar"
self.env['NPY_ENABLE_CPU_FEATURES'] = feats
msg = (
f"You cannot enable CPU features \\({bad_feature}\\), since they "
@@ -317,6 +317,16 @@ def test_impossible_feature_enable(self):
)
self._expect_error(msg, err_type)
+ if self.BASELINE_FEAT is not None:
+ # Ensure that only the bad feature gets reported
+ feats = f"{bad_feature}, {self.BASELINE_FEAT}"
+ self.env['NPY_ENABLE_CPU_FEATURES'] = feats
+ msg = (
+ f"You cannot enable CPU features \\({bad_feature}\\), since "
+ "they are not supported by your machine."
+ )
+ self._expect_error(msg, err_type)
+
is_linux = sys.platform.startswith('linux')
is_cygwin = sys.platform.startswith('cygwin')
machine = platform.machine()
--
2.44.0
@@ -0,0 +1,46 @@
From 79e7c3c3262374de778145946b612135fb7cd581 Mon Sep 17 00:00:00 2001
From: Fabian Vogt <fvogt@suse.de>
Date: Thu, 28 Mar 2024 13:15:54 +0100
Subject: [PATCH] feature module: Fix handling of multiple conflicts per
attribute
- Attributes without match were never actually added to the list
- Only the last conflict actually had an effect, earlier results were
discarded
---
vendored-meson/meson/mesonbuild/modules/features/module.py | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/vendored-meson/meson/mesonbuild/modules/features/module.py b/vendored-meson/meson/mesonbuild/modules/features/module.py
index 0be6af06c..a6f357b3f 100644
--- a/vendored-meson/meson/mesonbuild/modules/features/module.py
+++ b/vendored-meson/meson/mesonbuild/modules/features/module.py
@@ -371,15 +371,12 @@ class Module(NewExtensionModule):
values: List[ConflictAttr] = getattr(fet, attr)
accumulate_values = test_result[attr] # type: ignore
for conflict in values:
- if not conflict.match:
- accumulate_values.append(conflict.val)
- continue
conflict_vals: List[str] = []
# select the acc items based on the match
new_acc: List[str] = []
for acc in accumulate_values:
# not affected by the match so we keep it
- if not conflict.match.match(acc):
+ if not (conflict.match and conflict.match.match(acc)):
new_acc.append(acc)
continue
# no filter so we totaly escape it
@@ -396,7 +393,7 @@ class Module(NewExtensionModule):
continue
conflict_vals.append(conflict.mjoin.join(filter_val))
new_acc.append(conflict.val + conflict.mjoin.join(conflict_vals))
- test_result[attr] = new_acc # type: ignore
+ accumulate_values = test_result[attr] = new_acc # type: ignore
test_args = compiler.has_multi_arguments
args = test_result['args']
--
2.44.0
+53
View File
@@ -0,0 +1,53 @@
From 59694be29a71b17ad1ce58c865a5524c50bfedee Mon Sep 17 00:00:00 2001
From: Ralf Gommers <ralf.gommers@gmail.com>
Date: Fri, 16 Feb 2024 22:18:43 +0100
Subject: [PATCH] TST: fix Cython compile test which invokes `meson`
Closes gh-24956
[skip circle]
---
numpy/core/tests/test_cython.py | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
Index: numpy-1.26.4/numpy/core/tests/test_cython.py
===================================================================
--- numpy-1.26.4.orig/numpy/core/tests/test_cython.py
+++ numpy-1.26.4/numpy/core/tests/test_cython.py
@@ -37,6 +37,13 @@ def install_temp(tmpdir_factory):
srcdir = os.path.join(os.path.dirname(__file__), 'examples', 'cython')
build_dir = tmpdir_factory.mktemp("cython_test") / "build"
os.makedirs(build_dir, exist_ok=True)
+ # Ensure we use the correct Python interpreter even when `meson` is
+ # installed in a different Python environment (see gh-24956)
+ native_file = str(build_dir / 'interpreter-native-file.ini')
+ with open(native_file, 'w') as f:
+ f.write("[binaries]\n")
+ f.write(f"python = '{sys.executable}'")
+
try:
subprocess.check_call(["meson", "--version"])
except FileNotFoundError:
@@ -44,17 +51,20 @@ def install_temp(tmpdir_factory):
if sys.platform == "win32":
subprocess.check_call(["meson", "setup",
"--buildtype=release",
- "--vsenv", str(srcdir)],
+ "--vsenv", "--native-file", native_file,
+ str(srcdir)],
cwd=build_dir,
)
else:
- subprocess.check_call(["meson", "setup", str(srcdir)],
+ subprocess.check_call(["meson", "setup",
+ "--native-file", native_file, str(srcdir)],
cwd=build_dir
)
subprocess.check_call(["meson", "compile", "-vv"], cwd=build_dir)
sys.path.append(str(build_dir))
+
def test_is_timedelta64_object(install_temp):
import checks
BIN
View File
Binary file not shown.
+28
View File
@@ -0,0 +1,28 @@
From: toddrme2178@gmail.com
Date: 2014-09-09 04:45:00 +0000
Subject: Remove windows-specific function
Upstream: openSUSE Fix
References:
http://lists.opensuse.org/opensuse-packaging/2014-09/msg00004.html
http://lists.opensuse.org/opensuse-packaging/2014-09/msg00005.html
__declspec(thread) is a windows-specific function that is causing
spurious compiler warnings. These warnings can be safely ignored,
but are being falsely flagged as serious problems that cause the
build to fail. Since this is windows-specific, it can be
safely removed.
Index: numpy-1.24.0/numpy/core/setup_common.py
===================================================================
--- numpy-1.24.0.orig/numpy/core/setup_common.py
+++ numpy-1.24.0/numpy/core/setup_common.py
@@ -141,7 +141,7 @@ OPTIONAL_FILE_FUNCS = ["ftello", "fseeko
OPTIONAL_MISC_FUNCS = ["backtrace", "madvise"]
# variable attributes tested via "int %s a" % attribute
-OPTIONAL_VARIABLE_ATTRIBUTES = ["__thread", "__declspec(thread)"]
+OPTIONAL_VARIABLE_ATTRIBUTES = ["__thread"]
# Subset of OPTIONAL_*_FUNCS which may already have HAVE_* defined by Python.h
OPTIONAL_FUNCS_MAYBE = [
+26
View File
@@ -0,0 +1,26 @@
Index: numpy-1.19.5/numpy/distutils/command/autodist.py
===================================================================
--- numpy-1.19.5.orig/numpy/distutils/command/autodist.py
+++ numpy-1.19.5/numpy/distutils/command/autodist.py
@@ -51,7 +51,7 @@ def check_compiler_gcc(cmd):
cmd._check_compiler()
body = textwrap.dedent("""
- int
+ void
main()
{
#if (! defined __GNUC__)
Index: numpy-1.19.5/numpy/distutils/command/install.py
===================================================================
--- numpy-1.19.5.orig/numpy/distutils/command/install.py
+++ numpy-1.19.5/numpy/distutils/command/install.py
@@ -67,7 +67,7 @@ class install(old_install):
need_rewrite = False
for l in f:
l = l.rstrip()
- if ' ' in l:
+ if ' ' in l and '%dir ' not in l:
need_rewrite = True
l = '"%s"' % (l)
lines.append(l)
+7
View File
@@ -0,0 +1,7 @@
# These files are required for testing (and testing the package on a live system)
addFilter("devel-file-in-non-devel-package .*numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c")
addFilter("devel-file-in-non-devel-package .*numpy/core/tests/examples")
addFilter("devel-file-in-non-devel-package .*numpy/core/tests/data")
addFilter("hidden-file-or-dir .*numpy/f2py/tests/src/assumed_shape/.f2py_f2cmap")
addFilter("hidden-file-or-dir .*numpy/f2py/tests/src/f2cmap/.f2py_f2cmap")
addFilter("zero-length .*numpy/_core/__init__.pyi")
+296
View File
@@ -0,0 +1,296 @@
From a5f915876d49387b1e8cafd6d58883f3a802822b Mon Sep 17 00:00:00 2001
From: Nathan Goldbaum <nathan.goldbaum@gmail.com>
Date: Thu, 8 May 2025 08:26:13 -0600
Subject: [PATCH] MNT: add support for 3.14.0b1
---
numpy/_core/src/multiarray/temp_elide.c | 39 ++++---------------------
pytest.ini | 3 ++
2 files changed, 8 insertions(+), 34 deletions(-)
Index: numpy-1.26.4/pytest.ini
===================================================================
--- numpy-1.26.4.orig/pytest.ini
+++ numpy-1.26.4/pytest.ini
@@ -27,3 +27,8 @@ filterwarnings =
ignore:\n\n `numpy.distutils`:DeprecationWarning
# Ignore mypy >= 0.971 DeprecationWarnings
ignore:path is deprecated\. Use files\(\) instead:DeprecationWarning:mypy
+# Ignore DeprecationWarning from typing.mypy_plugin
+ ignore:`numpy.typing.mypy_plugin` is deprecated:DeprecationWarning
+# Ignore DeprecationWarning from struct module
+# see https://github.com/numpy/numpy/issues/28926
+ ignore:Due to \'_pack_\', the
Index: numpy-1.26.4/numpy/core/src/multiarray/temp_elide.c
===================================================================
--- numpy-1.26.4.orig/numpy/core/src/multiarray/temp_elide.c
+++ numpy-1.26.4/numpy/core/src/multiarray/temp_elide.c
@@ -62,6 +62,13 @@
#include <feature_detection_misc.h>
+#if PY_VERSION_HEX >= 0x030E00A7 && !defined(PYPY_VERSION)
+#define Py_BUILD_CORE
+#include "internal/pycore_frame.h"
+#include "internal/pycore_interpframe.h"
+#undef Py_BUILD_CORE
+#endif
+
/* 1 prints elided operations, 2 prints stacktraces */
#define NPY_ELIDE_DEBUG 0
#define NPY_MAX_STACKSIZE 10
@@ -110,6 +117,41 @@ find_addr(void * addresses[], npy_intp n
}
static int
+check_unique_temporary(PyObject *lhs)
+{
+#if PY_VERSION_HEX >= 0x030E00A7 && !defined(PYPY_VERSION)
+ // In Python 3.14.0a7 and later, a reference count of one doesn't guarantee
+ // that an object is a unique temporary variable. We scan the top of the
+ // interpreter stack to check if the object exists as an "owned" reference
+ // in the temporary stack.
+ PyFrameObject *frame = PyEval_GetFrame();
+ if (frame == NULL) {
+ return 0;
+ }
+ _PyInterpreterFrame *f = frame->f_frame;
+ _PyStackRef *base = _PyFrame_Stackbase(f);
+ _PyStackRef *stackpointer = f->stackpointer;
+ int found_once = 0;
+ while (stackpointer > base) {
+ stackpointer--;
+ PyObject *obj = PyStackRef_AsPyObjectBorrow(*stackpointer);
+ if (obj == lhs) {
+ if (!found_once && PyStackRef_IsHeapSafe(*stackpointer)) {
+ found_once = 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ return found_once;
+ }
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+static int
check_callers(int * cannot)
{
/*
@@ -295,7 +337,8 @@ can_elide_temp(PyObject *olhs, PyObject
!PyArray_CHKFLAGS(alhs, NPY_ARRAY_OWNDATA) ||
!PyArray_ISWRITEABLE(alhs) ||
PyArray_CHKFLAGS(alhs, NPY_ARRAY_WRITEBACKIFCOPY) ||
- PyArray_NBYTES(alhs) < NPY_MIN_ELIDE_BYTES) {
+ PyArray_NBYTES(alhs) < NPY_MIN_ELIDE_BYTES ||
+ !check_unique_temporary(olhs)) {
return 0;
}
if (PyArray_CheckExact(orhs) ||
@@ -372,7 +415,8 @@ can_elide_temp_unary(PyArrayObject * m1)
!PyArray_ISNUMBER(m1) ||
!PyArray_CHKFLAGS(m1, NPY_ARRAY_OWNDATA) ||
!PyArray_ISWRITEABLE(m1) ||
- PyArray_NBYTES(m1) < NPY_MIN_ELIDE_BYTES) {
+ PyArray_NBYTES(m1) < NPY_MIN_ELIDE_BYTES ||
+ !check_unique_temporary((PyObject *)m1)) {
return 0;
}
if (check_callers(&cannot)) {
Index: numpy-1.26.4/numpy/core/tests/test_dlpack.py
===================================================================
--- numpy-1.26.4.orig/numpy/core/tests/test_dlpack.py
+++ numpy-1.26.4/numpy/core/tests/test_dlpack.py
@@ -5,14 +5,25 @@ import numpy as np
from numpy.testing import assert_array_equal, IS_PYPY
+def new_and_old_dlpack():
+ yield np.arange(5)
+
+ class OldDLPack(np.ndarray):
+ # Support only the "old" version
+ def __dlpack__(self, stream=None):
+ return super().__dlpack__(stream=None)
+
+ yield np.arange(5).view(OldDLPack)
+
+
class TestDLPack:
@pytest.mark.skipif(IS_PYPY, reason="PyPy can't get refcounts.")
def test_dunder_dlpack_refcount(self):
x = np.arange(5)
y = x.__dlpack__()
- assert sys.getrefcount(x) == 3
+ startcount = sys.getrefcount(x)
del y
- assert sys.getrefcount(x) == 2
+ assert startcount - sys.getrefcount(x) == 1
def test_dunder_dlpack_stream(self):
x = np.arange(5)
@@ -30,12 +41,13 @@ class TestDLPack:
np.from_dlpack(z)
@pytest.mark.skipif(IS_PYPY, reason="PyPy can't get refcounts.")
- def test_from_dlpack_refcount(self):
- x = np.arange(5)
- y = np.from_dlpack(x)
- assert sys.getrefcount(x) == 3
+ @pytest.mark.parametrize("arr", new_and_old_dlpack())
+ def test_from_dlpack_refcount(self, arr):
+ arr = arr.copy()
+ y = np.from_dlpack(arr)
+ startcount = sys.getrefcount(arr)
del y
- assert sys.getrefcount(x) == 2
+ assert startcount - sys.getrefcount(arr) == 1
@pytest.mark.parametrize("dtype", [
np.bool_,
Index: numpy-1.26.4/numpy/core/tests/test_indexing.py
===================================================================
--- numpy-1.26.4.orig/numpy/core/tests/test_indexing.py
+++ numpy-1.26.4/numpy/core/tests/test_indexing.py
@@ -1147,6 +1147,8 @@ class TestMultiIndexingAutomated:
"""Compare mimicked result to indexing result.
"""
arr = arr.copy()
+ if HAS_REFCOUNT:
+ startcount = sys.getrefcount(arr)
indexed_arr = arr[index]
assert_array_equal(indexed_arr, mimic_get)
# Check if we got a view, unless its a 0-sized or 0-d array.
@@ -1157,9 +1159,9 @@ class TestMultiIndexingAutomated:
if HAS_REFCOUNT:
if no_copy:
# refcount increases by one:
- assert_equal(sys.getrefcount(arr), 3)
+ assert_equal(sys.getrefcount(arr), startcount + 1)
else:
- assert_equal(sys.getrefcount(arr), 2)
+ assert_equal(sys.getrefcount(arr), startcount)
# Test non-broadcast setitem:
b = arr.copy()
Index: numpy-1.26.4/numpy/core/tests/test_item_selection.py
===================================================================
--- numpy-1.26.4.orig/numpy/core/tests/test_item_selection.py
+++ numpy-1.26.4/numpy/core/tests/test_item_selection.py
@@ -50,19 +50,23 @@ class TestTake:
def test_refcounting(self):
objects = [object() for i in range(10)]
+ if HAS_REFCOUNT:
+ orig_rcs = [sys.getrefcount(o) for o in objects]
for mode in ('raise', 'clip', 'wrap'):
a = np.array(objects)
b = np.array([2, 2, 4, 5, 3, 5])
a.take(b, out=a[:6], mode=mode)
del a
if HAS_REFCOUNT:
- assert_(all(sys.getrefcount(o) == 3 for o in objects))
+ assert_(all(sys.getrefcount(o) == rc + 1
+ for o, rc in zip(objects, orig_rcs)))
# not contiguous, example:
a = np.array(objects * 2)[::2]
a.take(b, out=a[:6], mode=mode)
del a
if HAS_REFCOUNT:
- assert_(all(sys.getrefcount(o) == 3 for o in objects))
+ assert_(all(sys.getrefcount(o) == rc + 1
+ for o, rc in zip(objects, orig_rcs)))
def test_unicode_mode(self):
d = np.arange(10)
Index: numpy-1.26.4/numpy/core/tests/test_multiarray.py
===================================================================
--- numpy-1.26.4.orig/numpy/core/tests/test_multiarray.py
+++ numpy-1.26.4/numpy/core/tests/test_multiarray.py
@@ -6644,10 +6644,12 @@ class TestDot:
v = np.random.random_sample((16, 32))
r = np.empty((1024, 32))
+ if HAS_REFCOUNT:
+ orig_refcount = sys.getrefcount(r)
for i in range(12):
dot(f, v, r)
if HAS_REFCOUNT:
- assert_equal(sys.getrefcount(r), 2)
+ assert_equal(sys.getrefcount(r), orig_refcount)
r2 = dot(f, v, out=None)
assert_array_equal(r2, r)
assert_(r is dot(f, v, out=r))
Index: numpy-1.26.4/numpy/core/tests/test_nditer.py
===================================================================
--- numpy-1.26.4.orig/numpy/core/tests/test_nditer.py
+++ numpy-1.26.4/numpy/core/tests/test_nditer.py
@@ -1122,8 +1122,9 @@ def test_iter_object_arrays_conversions(
rc = sys.getrefcount(ob)
for x in i:
x[...] += 1
- if HAS_REFCOUNT:
- assert_(sys.getrefcount(ob) == rc-1)
+ if HAS_REFCOUNT:
+ newrc = sys.getrefcount(ob)
+ assert_(newrc == rc - 1)
assert_equal(a, np.arange(6)+98172489)
def test_iter_common_dtype():
Index: numpy-1.26.4/numpy/core/tests/test_regression.py
===================================================================
--- numpy-1.26.4.orig/numpy/core/tests/test_regression.py
+++ numpy-1.26.4/numpy/core/tests/test_regression.py
@@ -1600,29 +1600,26 @@ class TestRegression:
def test_fromfile_tofile_seeks(self):
# On Python 3, tofile/fromfile used to get (#1610) the Python
# file handle out of sync
- f0 = tempfile.NamedTemporaryFile()
- f = f0.file
- f.write(np.arange(255, dtype='u1').tobytes())
+ with tempfile.NamedTemporaryFile() as f:
+ f.write(np.arange(255, dtype='u1').tobytes())
- f.seek(20)
- ret = np.fromfile(f, count=4, dtype='u1')
- assert_equal(ret, np.array([20, 21, 22, 23], dtype='u1'))
- assert_equal(f.tell(), 24)
-
- f.seek(40)
- np.array([1, 2, 3], dtype='u1').tofile(f)
- assert_equal(f.tell(), 43)
-
- f.seek(40)
- data = f.read(3)
- assert_equal(data, b"\x01\x02\x03")
-
- f.seek(80)
- f.read(4)
- data = np.fromfile(f, dtype='u1', count=4)
- assert_equal(data, np.array([84, 85, 86, 87], dtype='u1'))
-
- f.close()
+ f.seek(20)
+ ret = np.fromfile(f, count=4, dtype='u1')
+ assert_equal(ret, np.array([20, 21, 22, 23], dtype='u1'))
+ assert_equal(f.tell(), 24)
+
+ f.seek(40)
+ np.array([1, 2, 3], dtype='u1').tofile(f)
+ assert_equal(f.tell(), 43)
+
+ f.seek(40)
+ data = f.read(3)
+ assert_equal(data, b"\x01\x02\x03")
+
+ f.seek(80)
+ f.read(4)
+ data = np.fromfile(f, dtype='u1', count=4)
+ assert_equal(data, np.array([84, 85, 86, 87], dtype='u1'))
def test_complex_scalar_warning(self):
for tp in [np.csingle, np.cdouble, np.clongdouble]: