diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4d1e5f9161df..0efbf18aa0ae 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,7 @@ updates: - package-ecosystem: github-actions directory: / schedule: - interval: daily + interval: weekly cooldown: default-days: 7 # optional commit-message: @@ -16,6 +16,7 @@ updates: directory: /requirements schedule: interval: daily + versioning-strategy: increase-if-necessary cooldown: default-days: 7 # optional groups: @@ -32,3 +33,4 @@ updates: - dependency-name: "scipy-openblas64" - dependency-name: "jupyterlite-pyodide-kernel" - dependency-name: "sphinx" + - dependency-name: "pkgconf" diff --git a/.github/workflows/compiler_sanitizers.yml b/.github/workflows/compiler_sanitizers.yml index ccc9a1ad0d81..56d88194d013 100644 --- a/.github/workflows/compiler_sanitizers.yml +++ b/.github/workflows/compiler_sanitizers.yml @@ -33,7 +33,7 @@ jobs: clang_ASAN_UBSAN: # To enable this workflow on a fork, comment out: if: github.repository == 'numpy/numpy' - runs-on: macos-latest + runs-on: macos-26 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -42,7 +42,7 @@ jobs: persist-credentials: false - name: Set up pyenv run: | - git clone --branch v2.6.25 --depth 1 https://github.com/pyenv/pyenv.git "$HOME/.pyenv" + git clone --branch v2.7.3 --depth 1 https://github.com/pyenv/pyenv.git "$HOME/.pyenv" PYENV_ROOT="$HOME/.pyenv" PYENV_BIN="$PYENV_ROOT/bin" PYENV_SHIMS="$PYENV_ROOT/shims" @@ -54,15 +54,15 @@ jobs: pyenv --version - name: Set up LLVM run: | - brew install llvm@20 - LLVM_PREFIX=$(brew --prefix llvm@20) + brew install llvm + LLVM_PREFIX=$(brew --prefix llvm) echo CC="$LLVM_PREFIX/bin/clang" >> $GITHUB_ENV echo CXX="$LLVM_PREFIX/bin/clang++" >> $GITHUB_ENV echo LDFLAGS="-L$LLVM_PREFIX/lib" >> $GITHUB_ENV echo CPPFLAGS="-I$LLVM_PREFIX/include" >> $GITHUB_ENV - name: Build Python with address sanitizer run: | - CONFIGURE_OPTS="--with-address-sanitizer" pyenv install 3.14 + CONFIGURE_OPTS="--with-address-sanitizer" pyenv install -v 3.14 pyenv global 3.14 - name: Install dependencies run: | diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f266e23f3650..3192a2fcb3c8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -43,7 +43,7 @@ jobs: submodules: recursive fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - name: Install linter requirements @@ -74,7 +74,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.version }} - uses: ./.github/meson_actions @@ -124,7 +124,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.version }} - uses: ./.github/meson_actions @@ -139,7 +139,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - name: Install build and test dependencies from PyPI @@ -226,7 +226,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - name: Install build and benchmarking dependencies @@ -265,7 +265,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - name: Install gfortran and setup OpenBLAS (sdist build) @@ -315,7 +315,7 @@ jobs: path: 'array-api-tests' persist-credentials: false - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - name: Install build and test dependencies from PyPI @@ -357,7 +357,7 @@ jobs: path: 'ml_dtypes' persist-credentials: false - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - name: Install build and test dependencies from PyPI @@ -397,7 +397,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/.github/workflows/linux_blas.yml b/.github/workflows/linux_blas.yml index 9b84082fe181..d86937f9c058 100644 --- a/.github/workflows/linux_blas.yml +++ b/.github/workflows/linux_blas.yml @@ -75,7 +75,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' @@ -198,7 +198,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' @@ -226,7 +226,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' @@ -289,7 +289,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' @@ -354,7 +354,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' @@ -391,7 +391,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' diff --git a/.github/workflows/linux_qemu.yml b/.github/workflows/linux_qemu.yml index 3031d04fa110..e22e70c09b9b 100644 --- a/.github/workflows/linux_qemu.yml +++ b/.github/workflows/linux_qemu.yml @@ -82,7 +82,7 @@ jobs: sudo apt install -y ninja-build gcc-${TOOLCHAIN_NAME} g++-${TOOLCHAIN_NAME} gfortran-${TOOLCHAIN_NAME} - name: Cache docker container - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: container-cache with: path: ~/docker_${{ matrix.BUILD_PROP[1] }} @@ -199,7 +199,7 @@ jobs: sudo apt install -y ninja-build gcc-14-${TOOLCHAIN_NAME} g++-14-${TOOLCHAIN_NAME} gfortran-14-${TOOLCHAIN_NAME} - name: Cache docker container - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: container-cache with: path: ~/docker_${{ matrix.BUILD_PROP[1] }} diff --git a/.github/workflows/linux_riscv64.yml b/.github/workflows/linux_riscv64.yml index d73466254b40..79e741b15b7e 100644 --- a/.github/workflows/linux_riscv64.yml +++ b/.github/workflows/linux_riscv64.yml @@ -49,7 +49,7 @@ jobs: persist-credentials: false - name: Restore compilation cache - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: ccache-restore with: path: ${{ env.CCACHE_DIR }} @@ -75,7 +75,7 @@ jobs: - name: Save compilation cache if: always() && github.ref == 'refs/heads/main' - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ env.CCACHE_DIR }} key: ccache-wheels-manylinux_riscv64-${{ matrix.python }}-${{ github.run_id }} diff --git a/.github/workflows/linux_simd.yml b/.github/workflows/linux_simd.yml index d79adca0cc29..3d221174d7c2 100644 --- a/.github/workflows/linux_simd.yml +++ b/.github/workflows/linux_simd.yml @@ -67,7 +67,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - uses: ./.github/meson_actions @@ -85,23 +85,15 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - - name: Install GCC9/10 + - name: Install GCC10 run: | echo "deb http://archive.ubuntu.com/ubuntu focal main universe" | sudo tee /etc/apt/sources.list.d/focal.list sudo apt update - sudo apt install -y g++-9 g++-10 - - - name: Enable gcc-9 - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 1 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 1 - - - uses: ./.github/meson_actions - name: Build/Test against gcc-9 + sudo apt install -y g++-10 - name: Enable gcc-10 run: | @@ -131,7 +123,7 @@ jobs: with: submodules: recursive fetch-tags: true - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' - name: Install dependencies @@ -179,7 +171,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "${{ matrix.BUILD_PROP[2] }}" - uses: ./.github/meson_actions @@ -194,7 +186,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' @@ -248,7 +240,7 @@ jobs: submodules: recursive fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.12' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 426c04dae726..f1f0d1531035 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -50,7 +50,7 @@ jobs: echo "today=$(/bin/date -u '+%Y%m%d')" >> $GITHUB_OUTPUT - name: Setup compiler cache - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: cache-ccache with: path: ${{ steps.prep-ccache.outputs.dir }} @@ -74,7 +74,7 @@ jobs: # ensure we re-solve once a day (since we don't lock versions). Could be # replaced by a conda-lock based approach in the future. - name: Cache conda environment - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 env: # Increase this value to reset cache if environment.yml has not changed CACHE_NUMBER: 1 @@ -128,7 +128,7 @@ jobs: fetch-tags: true persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.version }} diff --git a/.github/workflows/mypy_primer.yml b/.github/workflows/mypy_primer.yml index 2fb5aa54994f..688e4a0f9c7a 100644 --- a/.github/workflows/mypy_primer.yml +++ b/.github/workflows/mypy_primer.yml @@ -32,7 +32,7 @@ jobs: with: path: numpy_to_test fetch-depth: 0 - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" - name: Install dependencies diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 8d38d70110d0..a33d2f2b1d24 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -25,7 +25,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v3.1.0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v2.1.27 + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: sarif_file: results.sarif diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 81fc307ffd48..2bc4d6326988 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,7 +33,7 @@ jobs: persist-credentials: false - name: Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.15t-dev" @@ -93,7 +93,7 @@ jobs: persist-credentials: false - name: Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.python_version }} architecture: ${{ matrix.architecture }} @@ -149,7 +149,7 @@ jobs: persist-credentials: false - name: Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ matrix.pyver }} diff --git a/.mailmap b/.mailmap index f609723222c5..b8a23ae370e8 100644 --- a/.mailmap +++ b/.mailmap @@ -83,6 +83,7 @@ Abhishek Kumar Abhishek Kumar <142383124+abhishek-iitmadras@users.noreply.github.com> Abhishek Tiwari <27881020+Abhi210@users.noreply.github.com> Abraham Medina +Adhyan Gupta <123641130+Adhyansinghgupta@users.noreply.github.com> Adrin Jalali Akhil Kannan Akhil Kannan <143798318+Alverok@users.noreply.github.com> diff --git a/benchmarks/benchmarks/bench_app.py b/benchmarks/benchmarks/bench_app.py index 06a9401b02f5..cbad8263a599 100644 --- a/benchmarks/benchmarks/bench_app.py +++ b/benchmarks/benchmarks/bench_app.py @@ -70,7 +70,7 @@ def maxes_of_dots(self, arrays): Arrays must agree only on the first dimension. - Numpy uses this as a simultaneous benchmark of 1) dot products + NumPy uses this as a simultaneous benchmark of 1) dot products and 2) max(, axis=). """ feature_scores = ([0] * len(arrays)) diff --git a/doc/changelog/2.5.1-changelog.rst b/doc/changelog/2.5.1-changelog.rst new file mode 100644 index 000000000000..ec56838f13e9 --- /dev/null +++ b/doc/changelog/2.5.1-changelog.rst @@ -0,0 +1,44 @@ + +Contributors +============ + +A total of 10 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Adhyan Gupta + +* Ankit Ahlawat +* Charles Harris +* Iason Krommydas +* Joren Hammudoglu +* Kumar Aditya +* Nathan Goldbaum +* Sebastian Berg +* Ties Jan Hefting + +* Vineet Kumar + +Pull requests merged +==================== + +A total of 20 pull requests were merged for this release. + +* `#31707 `__: MAINT: Prepare 2.5.x for further development +* `#31721 `__: CI: fix new ``cython-lint`` errors (#31711) +* `#31723 `__: MAINT: Update meson to match main +* `#31729 `__: TST: use setup-sde instead of curl to get SDE binaries (#31727) +* `#31829 `__: BUG: Relax finfo to be easier accessible for all user dtypes... +* `#31831 `__: TYP: Fix ``flatiter.__next__`` return type for ``object_`` and... +* `#31832 `__: BUG: avoid deadlocks using NpyString API (#31682) +* `#31833 `__: BUG: fix out array leak in reduceat and accumulate when dtype... +* `#31835 `__: BUG: fix numpy datetime cython APIs to be compatible with older... +* `#31836 `__: TYP: Fix incorrect dtype inference of ``asarray([])`` (#31732) +* `#31837 `__: TYP: Fix ``np.ma.masked_array`` 2.5.0 regression +* `#31838 `__: FIX: Refactor error handling in array_setstate to prevent typecode... +* `#31839 `__: TST: xfail multithreaded BLAS test more generously +* `#31840 `__: MAINT: Rename subroutine for crackfortran tests +* `#31842 `__: BUG: fix leak in reductions when a ufunc override errors or is... +* `#31849 `__: BLD: set minimum required gcc version to 10.3 (#31843) +* `#31855 `__: CI: fix hangs on MacOS ASan CI (#31853) +* `#31856 `__: BUG: fix several bugs in StringDType operations (#31846) +* `#31857 `__: BUG: Fix segfault in MT19937 by preventing recursive seed lists... +* `#31858 `__: BUG: Fix signed integer overflow in datetime.c (#31688) + diff --git a/doc/release/upcoming_changes/31620.improvement.rst b/doc/release/upcoming_changes/31620.improvement.rst new file mode 100644 index 000000000000..3fa3465b1f13 --- /dev/null +++ b/doc/release/upcoming_changes/31620.improvement.rst @@ -0,0 +1,4 @@ +``np.linspace`` no longer returns NaN for equal infinite endpoints +------------------------------------------------------------------ +``np.linspace(np.inf, np.inf, N)`` (and ``-np.inf``) now correctly returns +an array filled with ``inf`` instead of ``nan``. diff --git a/doc/release/upcoming_changes/31782.compatibility.rst b/doc/release/upcoming_changes/31782.compatibility.rst new file mode 100644 index 000000000000..eaed3cabfecf --- /dev/null +++ b/doc/release/upcoming_changes/31782.compatibility.rst @@ -0,0 +1,6 @@ +`numpy.insert` out-of-bounds index detection +--------------------------------------------- +``numpy.insert`` now consistently raises ``IndexError`` for any +out-of-bounds index, including when out-of-bounds and in-bounds +indices are mixed in the same call. Previously such cases could +silently succeed or produce incorrect results. diff --git a/doc/release/upcoming_changes/31843.change.rst b/doc/release/upcoming_changes/31843.change.rst new file mode 100644 index 000000000000..78071a9c960c --- /dev/null +++ b/doc/release/upcoming_changes/31843.change.rst @@ -0,0 +1 @@ +* The minimum supported GCC version has been updated from 9.3.0 to 10.3.0 diff --git a/doc/release/upcoming_changes/31845.performance.rst b/doc/release/upcoming_changes/31845.performance.rst new file mode 100644 index 000000000000..0d950fc27798 --- /dev/null +++ b/doc/release/upcoming_changes/31845.performance.rst @@ -0,0 +1,6 @@ +Faster reductions on small arrays +--------------------------------- +``numpy.sum``, ``numpy.prod``, ``numpy.min``, ``numpy.max``, ``numpy.any``, +and ``numpy.all`` are now faster for small arrays. This reduces the +Python-level overhead of calling these reductions, which is most noticeable +when the reduction itself is cheap. diff --git a/doc/source/dev/development_advanced_debugging.rst b/doc/source/dev/development_advanced_debugging.rst index eb81b335f56a..da8bf2cbf832 100644 --- a/doc/source/dev/development_advanced_debugging.rst +++ b/doc/source/dev/development_advanced_debugging.rst @@ -72,7 +72,7 @@ e.g. ``pythond -m sysconfig`` to get the build configuration for the Python executable. A debug build will be built with debug compiler options in ``CFLAGS`` (e.g. ``-g -Og``). -Running the Numpy tests or an interactive terminal is usually as easy as:: +Running the NumPy tests or an interactive terminal is usually as easy as:: python3.8d runtests.py # or @@ -231,7 +231,7 @@ crash. The call stack often provides valuable context to understand the nature of a crash. C debuggers are also very useful during development, allowing interactive debugging in the C implementation of NumPy. -The NumPy developers often use both ``gdb`` and ``lldb`` to debug Numpy. As a +The NumPy developers often use both ``gdb`` and ``lldb`` to debug NumPy. As a rule of thumb, ``gdb`` is often easier to use on Linux while ``lldb`` is easier to use on a Mac environment. They have disjoint user interfaces, so you will need to learn how to use whichever one you land on. The ``gdb`` to ``lldb`` `command map diff --git a/doc/source/dev/development_workflow.rst b/doc/source/dev/development_workflow.rst index 16e383e125dc..7215d55af371 100644 --- a/doc/source/dev/development_workflow.rst +++ b/doc/source/dev/development_workflow.rst @@ -223,7 +223,7 @@ these fragments in each commit message of a PR: Test building wheels ~~~~~~~~~~~~~~~~~~~~ -Numpy currently uses `cibuildwheel `_ +NumPy currently uses `cibuildwheel `_ in order to build wheels through continuous integration services. To save resources, the cibuildwheel wheel builders are not run by default on every single PR or commit to main. diff --git a/doc/source/reference/arrays.dtypes.rst b/doc/source/reference/arrays.dtypes.rst index b1887fc09d23..a4296d1f58dc 100644 --- a/doc/source/reference/arrays.dtypes.rst +++ b/doc/source/reference/arrays.dtypes.rst @@ -270,16 +270,23 @@ Array-protocol type strings (see :ref:`arrays.interface`) ``'L'`` unsigned long integer ``'q'`` (signed) long long integer ``'Q'`` unsigned long long integer + ``'n'`` signed :class:`intp` + ``'N'`` unsigned :class:`uintp` + ``'p'`` signed integer large enough for pointers + ``'P'`` unsigned integer large enough for pointers + ``'e'`` half precision ``'f'`` single precision ``'F'`` complex single precision ``'d'`` double precision ``'D'`` complex double precision ``'g'`` long precision ``'G'`` complex long double precision - ``'O'`` (Python) objects ``'S'`` zero-terminated bytes (not recommended) ``'U'`` Unicode string ``'V'`` raw data (:class:`void`) + ``'O'`` (Python) objects + ``'M'`` :class:`datetime64` + ``'m'`` :class:`timedelta64` ================== ======================== .. admonition:: Example diff --git a/doc/source/reference/c-api/array.rst b/doc/source/reference/c-api/array.rst index 07dd7068f3a9..daeef17d1ad7 100644 --- a/doc/source/reference/c-api/array.rst +++ b/doc/source/reference/c-api/array.rst @@ -4088,7 +4088,7 @@ Other conversions Convert any Python sequence (or single Python number) passed in as *seq* to (up to) *maxvals* pointer-sized integers and place them - in the *vals* array. The sequence can be smaller then *maxvals* as + in the *vals* array. The sequence can be smaller than *maxvals* as the number of converted objects is returned. .. _including-the-c-api: diff --git a/doc/source/reference/c-api/datetimes.rst b/doc/source/reference/c-api/datetimes.rst index 34fc81ed1351..63d2365f6358 100644 --- a/doc/source/reference/c-api/datetimes.rst +++ b/doc/source/reference/c-api/datetimes.rst @@ -3,7 +3,7 @@ Datetime API NumPy represents dates internally using an int64 counter and a unit metadata struct. Time differences are represented similarly using an int64 and a unit -metadata struct. The functions described below are available to to facilitate +metadata struct. The functions described below are available to facilitate converting between ISO 8601 date strings, NumPy datetimes, and Python datetime objects in C. diff --git a/doc/source/reference/c-api/strings.rst b/doc/source/reference/c-api/strings.rst index 2e7dc34a337f..e338685742d6 100644 --- a/doc/source/reference/c-api/strings.rst +++ b/doc/source/reference/c-api/strings.rst @@ -188,8 +188,8 @@ Functions Acquire the mutex locking the allocator attached to ``descr``. ``NpyString_release_allocator`` must be called on the allocator returned by this function exactly once. Note that functions requiring the - GIL should not be called while the allocator mutex is held, as doing so may - cause deadlocks. + GIL should not be called while the allocator mutex is held unless + re-entrancy is impossible, as doing so may cause deadlocks. .. c:function:: void NpyString_acquire_allocators( \ size_t n_descriptors, PyArray_Descr *const descrs[], \ @@ -212,7 +212,8 @@ Functions ``NpyString_release_allocators``. Note that functions requiring the GIL should not be called while the - allocator mutex is held, as doing so may cause deadlocks. + allocator mutex is held and reentrancy is possible, as doing so may cause + deadlocks. .. c:function:: void NpyString_release_allocator( \ npy_string_allocator *allocator) diff --git a/doc/source/release.rst b/doc/source/release.rst index 6019f9ad9249..d33a53e4d606 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -6,6 +6,7 @@ Release notes :maxdepth: 2 2.6.0 + 2.5.1 2.5.0 2.4.6 2.4.5 diff --git a/doc/source/release/2.5.1-notes.rst b/doc/source/release/2.5.1-notes.rst new file mode 100644 index 000000000000..cc3e91e59d9e --- /dev/null +++ b/doc/source/release/2.5.1-notes.rst @@ -0,0 +1,66 @@ +.. currentmodule:: numpy + +========================= +NumPy 2.5.1 Release Notes +========================= + +The NumPy 2.5.1 is a patch release that fixes bugs discovered after the 2.5.0 +release. The most noticeable is the fix is to the numpy datetime cython API +which should allow downstream to support NumPy versions older than 2.5. +Preparation for Python 3.15 continues along with typing improvements. + +This release supports Python versions 3.12-3.14 + + +Changes +======= + +* The minimum supported GCC version has been updated from 9.3.0 to 10.3.0 + + (`gh-31843 `__) + + +Contributors +============ + +A total of 10 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Adhyan Gupta + +* Ankit Ahlawat +* Charles Harris +* Iason Krommydas +* Joren Hammudoglu +* Kumar Aditya +* Nathan Goldbaum +* Sebastian Berg +* Ties Jan Hefting + +* Vineet Kumar + + +Pull requests merged +==================== + +A total of 20 pull requests were merged for this release. + +* `#31707 `__: MAINT: Prepare 2.5.x for further development +* `#31721 `__: CI: fix new ``cython-lint`` errors (#31711) +* `#31723 `__: MAINT: Update meson to match main +* `#31729 `__: TST: use setup-sde instead of curl to get SDE binaries (#31727) +* `#31829 `__: BUG: Relax finfo to be easier accessible for all user dtypes... +* `#31831 `__: TYP: Fix ``flatiter.__next__`` return type for ``object_`` and... +* `#31832 `__: BUG: avoid deadlocks using NpyString API (#31682) +* `#31833 `__: BUG: fix out array leak in reduceat and accumulate when dtype... +* `#31835 `__: BUG: fix numpy datetime cython APIs to be compatible with older... +* `#31836 `__: TYP: Fix incorrect dtype inference of ``asarray([])`` (#31732) +* `#31837 `__: TYP: Fix ``np.ma.masked_array`` 2.5.0 regression +* `#31838 `__: FIX: Refactor error handling in array_setstate to prevent typecode... +* `#31839 `__: TST: xfail multithreaded BLAS test more generously +* `#31840 `__: MAINT: Rename subroutine for crackfortran tests +* `#31842 `__: BUG: fix leak in reductions when a ufunc override errors or is... +* `#31849 `__: BLD: set minimum required gcc version to 10.3 (#31843) +* `#31855 `__: CI: fix hangs on MacOS ASan CI (#31853) +* `#31856 `__: BUG: fix several bugs in StringDType operations (#31846) +* `#31857 `__: BUG: Fix segfault in MT19937 by preventing recursive seed lists... +* `#31858 `__: BUG: Fix signed integer overflow in datetime.c (#31688) + diff --git a/doc/source/user/theory.broadcasting.rst b/doc/source/user/theory.broadcasting.rst index f277d4afde15..82400f2e172f 100644 --- a/doc/source/user/theory.broadcasting.rst +++ b/doc/source/user/theory.broadcasting.rst @@ -1,7 +1,7 @@ :orphan: =========================== -Array broadcasting in Numpy +Array broadcasting in NumPy =========================== .. diff --git a/meson.build b/meson.build index b5c4347fddb0..cb6af31b1ed1 100644 --- a/meson.build +++ b/meson.build @@ -21,8 +21,8 @@ cy = meson.get_compiler('cython') # Check compiler is recent enough (see the SciPy Toolchain Roadmap for details) if cc.get_id() == 'gcc' - if not cc.version().version_compare('>=9.3') - error('NumPy requires GCC >= 9.3') + if not cc.version().version_compare('>=10.3') + error('NumPy requires GCC >= 10.3') endif elif cc.get_id() == 'msvc' if not cc.version().version_compare('>=19.35') @@ -31,8 +31,8 @@ elif cc.get_id() == 'msvc' endif add_project_arguments('/experimental:c11atomics', language: 'c') endif -if not cy.version().version_compare('>=3.0.6') - error('NumPy requires Cython >= 3.0.6') +if not cy.version().version_compare('>=3.1.0') + error('NumPy requires Cython >= 3.1.0') endif py = import('python').find_installation(pure: false) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index d3dbac6f5656..22db9ae40e37 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -111,9 +111,7 @@ from numpy._typing import ( # type: ignore[deprecated] _InexactCodes, _CharacterCodes, # Ufuncs - _UFunc_Nin1_Nout1, _UFunc_Nin2_Nout1, - _UFunc_Nin1_Nout2, _UFunc_Nin2_Nout2, _GUFunc_Nin2_Nout1, ) @@ -405,6 +403,7 @@ from numpy._core.umath import ( arctan as atan, arctanh, arctanh as atanh, + bitwise_count, cbrt, ceil, conjugate, @@ -413,21 +412,35 @@ from numpy._core.umath import ( cosh, deg2rad, degrees, + equal, exp, exp2, expm1, fabs, floor, + frexp, + greater, + greater_equal, invert, + invert as bitwise_invert, + invert as bitwise_not, isfinite, isinf, isnan, isnat, + ldexp, + less, + less_equal, log, log10, log1p, log2, + logical_and, logical_not, + logical_or, + logical_xor, + modf, + not_equal, positive, negative, rad2deg, @@ -1003,7 +1016,7 @@ type _TimeUnit = L[_NativeTimeUnit, _IntTimeUnit] type _NativeTD64Unit = L[_DayUnit, _NativeTimeUnit] type _IntTD64Unit = L[_MonthUnit, _IntTimeUnit] type _TD64Unit = L[_DateUnit, _TimeUnit] -type _TimeUnitSpec[UnitT: _TD64Unit] = _TD64Unit | tuple[_TD64Unit, SupportsIndex] +type _TimeUnitSpec[UnitT: _TD64Unit] = UnitT | tuple[UnitT, SupportsIndex] ### TypedDict's (for internal use only) @@ -2507,8 +2520,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): # keep in sync with `sum` below (but without `timedelta64`) @override # type: ignore[override] - @overload # ~number - def prod[ScalarT: number]( + @overload # ~inexact + def prod[ScalarT: inexact]( self: NDArray[ScalarT], axis: None = None, dtype: None = None, @@ -2518,8 +2531,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ScalarT: ... - @overload # ~number, axis: - def prod[ScalarT: number | object_]( + @overload # ~inexact | object_, axis: + def prod[ScalarT: inexact | object_]( self: NDArray[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -2529,8 +2542,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[ScalarT]: ... - @overload # ~number | object_, keepdims=True - def prod[ArrayT: NDArray[number | object_]]( + @overload # ~inexact | object_, keepdims=True + def prod[ArrayT: NDArray[inexact | object_]]( self: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, @@ -2540,9 +2553,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ArrayT: ... - @overload # bool_ + @overload # +integer def prod( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: None = None, dtype: None = None, out: None = None, @@ -2551,9 +2564,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> int_: ... - @overload # bool_, axis: + @overload # +integer, axis: def prod( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -2562,9 +2575,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[int_]: ... - @overload # bool_, keepdims=True + @overload # +integer, keepdims=True def prod( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -2686,8 +2699,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): # keep in sync with `prod` above (but also accept `timedelta64`) @override # type: ignore[override] - @overload # ~number | timedelta64 - def sum[ScalarT: number | timedelta64]( + @overload # ~inexact | timedelta64 + def sum[ScalarT: inexact | timedelta64]( self: NDArray[ScalarT], axis: None = None, dtype: None = None, @@ -2697,8 +2710,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ScalarT: ... - @overload # ~number | timedelta64, axis: - def sum[ScalarT: number | timedelta64 | object_]( + @overload # ~inexact | timedelta64, axis: + def sum[ScalarT: inexact | timedelta64 | object_]( self: NDArray[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -2708,8 +2721,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[ScalarT]: ... - @overload # ~number | timedelta64 | object_, keepdims=True - def sum[ArrayT: NDArray[number | timedelta64 | object_]]( + @overload # ~inexact | timedelta64 | object_, keepdims=True + def sum[ArrayT: NDArray[inexact | timedelta64 | object_]]( self: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, @@ -2719,9 +2732,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ArrayT: ... - @overload # bool_ + @overload # +integer def sum( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: None = None, dtype: None = None, out: None = None, @@ -2730,9 +2743,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> int_: ... - @overload # bool_, axis: + @overload # +integer, axis: def sum( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -2741,9 +2754,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[int_]: ... - @overload # bool_, keepdims=True + @overload # +integer, keepdims=True def sum( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -2865,16 +2878,16 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): # keep in sync with `MaskedArray.cumprod` @override # type: ignore[override] - @overload # number | object_ - def cumprod[DTypeT: dtype[number | object_]]( + @overload # inexact | object_ + def cumprod[DTypeT: dtype[inexact | object_]]( self: ndarray[Any, DTypeT], axis: None = None, dtype: None = None, out: None = None, ) -> ndarray[_1D, DTypeT]: ... - @overload # bool_ + @overload # +integer def cumprod( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: None = None, dtype: None = None, out: None = None, @@ -2909,16 +2922,16 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): dtype: DTypeLike, out: None = None, ) -> ndarray[_1D]: ... - @overload # axis: - def cumprod[ArrayT: NDArray[number | object_]]( + @overload # inexact | object_, axis: + def cumprod[ArrayT: NDArray[inexact | object_]]( self: ArrayT, axis: SupportsIndex, dtype: None = None, out: None = None, ) -> ArrayT: ... - @overload # bool_, axis: + @overload # +integer, axis: def cumprod[ShapeT: _Shape]( - self: ndarray[ShapeT, dtype[bool_]], + self: ndarray[ShapeT, dtype[integer | bool_]], axis: SupportsIndex, dtype: None = None, out: None = None, @@ -2955,16 +2968,16 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): # keep in sync with `MaskedArray.cumsum` @override # type: ignore[override] - @overload # number | timedelta64 | object_ - def cumsum[DTypeT: dtype[number | timedelta64 | object_]]( + @overload # inexact | timedelta64 | object_ + def cumsum[DTypeT: dtype[inexact | timedelta64 | object_]]( self: ndarray[Any, DTypeT], axis: None = None, dtype: None = None, out: None = None, ) -> ndarray[_1D, DTypeT]: ... - @overload # bool_ + @overload # +integer def cumsum( - self: NDArray[bool_], + self: NDArray[integer | bool_], axis: None = None, dtype: None = None, out: None = None, @@ -2999,16 +3012,16 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): dtype: DTypeLike, out: None = None, ) -> ndarray[_1D]: ... - @overload # axis: - def cumsum[ArrayT: NDArray[number | timedelta64 | object_]]( + @overload # inexact | timedelta64 | object_, axis: + def cumsum[ArrayT: NDArray[inexact | timedelta64 | object_]]( self: ArrayT, axis: SupportsIndex, dtype: None = None, out: None = None, ) -> ArrayT: ... - @overload # bool_, axis: + @overload # +integer, axis: def cumsum[ShapeT: _Shape]( - self: ndarray[ShapeT, dtype[bool_]], + self: ndarray[ShapeT, dtype[integer | bool_]], axis: SupportsIndex, dtype: None = None, out: None = None, @@ -3188,9 +3201,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): # keep in sync with `ndarray.mean` above @override # type: ignore[override] - @overload # +integer | ~object_ + @overload # ~complex128 | +integer | ~object_ def std( - self: NDArray[integer | bool_ | object_], + self: NDArray[complex128 | integer | bool_ | object_], axis: None = None, dtype: None = None, out: None = None, @@ -3201,9 +3214,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> float64: ... - @overload # +integer, axis: + @overload # ~complex128 | +integer, axis: def std( - self: NDArray[integer | bool_], + self: NDArray[complex128 | integer | bool_], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -3214,9 +3227,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> NDArray[float64]: ... - @overload # +integer, keepdims=True + @overload # ~complex128 | +integer, keepdims=True def std( - self: NDArray[integer | bool_], + self: NDArray[complex128 | integer | bool_], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -3227,8 +3240,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> ndarray[_ShapeT_co, dtype[float64]]: ... - @overload # ~inexact | timedelta64 - def std[ScalarT: inexact | timedelta64]( + @overload # ~floating | timedelta64 + def std[ScalarT: floating | timedelta64]( self: NDArray[ScalarT], axis: None = None, dtype: None = None, @@ -3240,8 +3253,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> ScalarT: ... - @overload # ~inexact | timedelta64, axis: - def std[ScalarT: inexact | timedelta64 | object_]( + @overload # ~floating | timedelta64, axis: + def std[ScalarT: floating | timedelta64 | object_]( self: NDArray[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -3253,8 +3266,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> NDArray[ScalarT]: ... - @overload # ~inexact | timedelta64 | object_, keepdims=True - def std[ArrayT: NDArray[inexact | timedelta64 | object_]]( + @overload # ~floating | timedelta64 | object_, keepdims=True + def std[ArrayT: NDArray[floating | timedelta64 | object_]]( self: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, @@ -3373,9 +3386,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): # keep in sync with `ndarray.std` above @override # type: ignore[override] - @overload # +integer | ~object_ + @overload # ~complex128 | +integer | ~object_ def var( - self: NDArray[integer | bool_ | object_], + self: NDArray[complex128 | integer | bool_ | object_], axis: None = None, dtype: None = None, out: None = None, @@ -3386,9 +3399,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> float64: ... - @overload # +integer, axis: + @overload # ~complex128 | +integer, axis: def var( - self: NDArray[integer | bool_], + self: NDArray[complex128 | integer | bool_], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -3399,9 +3412,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> NDArray[float64]: ... - @overload # +integer, keepdims=True + @overload # ~complex128 | +integer, keepdims=True def var( - self: NDArray[integer | bool_], + self: NDArray[complex128 | integer | bool_], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -3412,8 +3425,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> ndarray[_ShapeT_co, dtype[float64]]: ... - @overload # ~inexact | timedelta64 - def var[ScalarT: inexact | timedelta64]( + @overload # ~floating | timedelta64 + def var[ScalarT: floating | timedelta64]( self: NDArray[ScalarT], axis: None = None, dtype: None = None, @@ -3425,8 +3438,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> ScalarT: ... - @overload # ~inexact | timedelta64, axis: - def var[ScalarT: inexact | timedelta64 | object_]( + @overload # ~floating | timedelta64, axis: + def var[ScalarT: floating | timedelta64 | object_]( self: NDArray[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -3438,8 +3451,8 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): mean: _ArrayLikeNumber_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> NDArray[ScalarT]: ... - @overload # ~inexact | timedelta64 | object_, keepdims=True - def var[ArrayT: NDArray[inexact | timedelta64 | object_]]( + @overload # ~floating | timedelta64 | object_, keepdims=True + def var[ArrayT: NDArray[floating | timedelta64 | object_]]( self: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, @@ -7319,13 +7332,13 @@ class datetime64(_RealMixin, generic[_DT64ItemT_co], Generic[_DT64ItemT_co]): @overload def __new__(cls, value: _DT64Date, format: _TimeUnitSpec[_DateUnit] = ..., /) -> datetime64[dt.date]: ... @overload - def __new__(cls, value: int | bytes | str | dt.date, format: _TimeUnitSpec[_IntTimeUnit], /) -> datetime64[int]: ... + def __new__(cls, value: int | bytes | str | dt.date | datetime64, format: _TimeUnitSpec[_IntTimeUnit], /) -> datetime64[int]: ... @overload - def __new__( # type: ignore[overload-cannot-match] - cls, value: int | bytes | str | dt.date, format: _TimeUnitSpec[_NativeTimeUnit], / + def __new__( + cls, value: int | bytes | str | dt.date | datetime64, format: _TimeUnitSpec[_NativeTimeUnit], / ) -> datetime64[dt.datetime]: ... @overload - def __new__(cls, value: int | bytes | str | dt.date, format: _TimeUnitSpec[_DateUnit], /) -> datetime64[dt.date]: ... # type: ignore[overload-cannot-match] + def __new__(cls, value: int | bytes | str | dt.date | datetime64, format: _TimeUnitSpec[_DateUnit], /) -> datetime64[dt.date]: ... @overload def __new__(cls, value: bytes | str | dt.date | None, format: _TimeUnitSpec[_TD64Unit] = ..., /) -> Self: ... @@ -7498,6 +7511,13 @@ class bytes_(character[bytes], bytes): # type: ignore[misc] @override def __hash__(self, /) -> int: ... + # re-declare inherited `bytes.__getitem__` shadowed by `generic.__getitem__` + @override # type: ignore[override] + @overload + def __getitem__(self, key: SupportsIndex, /) -> int: ... + @overload + def __getitem__(self, key: slice[SupportsIndex | None], /) -> bytes: ... # pyright: ignore[reportIncompatibleMethodOverride] + # def __bytes__(self, /) -> bytes: ... @@ -7511,6 +7531,10 @@ class str_(character[str], str): # type: ignore[misc] @override def __hash__(self, /) -> int: ... + # re-declare inherited `str.__getitem__` shadowed by `generic.__getitem__` + @override + def __getitem__(self, key: SupportsIndex | slice[SupportsIndex | None], /) -> str: ... # type: ignore[override] + # See `numpy._typing._ufunc` for more concrete nin-/nout-specific stubs @final class ufunc: @@ -7601,42 +7625,29 @@ class ufunc: add: _UFunc_Nin2_Nout1[L["add"], L[22], L[0]] arctan2: _UFunc_Nin2_Nout1[L["arctan2"], L[5], None] bitwise_and: _UFunc_Nin2_Nout1[L["bitwise_and"], L[12], L[-1]] -bitwise_count: _UFunc_Nin1_Nout1[L["bitwise_count"], L[11], None] bitwise_or: _UFunc_Nin2_Nout1[L["bitwise_or"], L[12], L[0]] bitwise_xor: _UFunc_Nin2_Nout1[L["bitwise_xor"], L[12], L[0]] copysign: _UFunc_Nin2_Nout1[L["copysign"], L[4], None] divide: _UFunc_Nin2_Nout1[L["divide"], L[11], None] divmod: _UFunc_Nin2_Nout2[L["divmod"], L[15], None] -equal: _UFunc_Nin2_Nout1[L["equal"], L[23], None] float_power: _UFunc_Nin2_Nout1[L["float_power"], L[4], None] floor_divide: _UFunc_Nin2_Nout1[L["floor_divide"], L[21], None] fmax: _UFunc_Nin2_Nout1[L["fmax"], L[21], None] fmin: _UFunc_Nin2_Nout1[L["fmin"], L[21], None] fmod: _UFunc_Nin2_Nout1[L["fmod"], L[15], None] -frexp: _UFunc_Nin1_Nout2[L["frexp"], L[4], None] gcd: _UFunc_Nin2_Nout1[L["gcd"], L[11], L[0]] -greater: _UFunc_Nin2_Nout1[L["greater"], L[23], None] -greater_equal: _UFunc_Nin2_Nout1[L["greater_equal"], L[23], None] heaviside: _UFunc_Nin2_Nout1[L["heaviside"], L[4], None] hypot: _UFunc_Nin2_Nout1[L["hypot"], L[5], L[0]] lcm: _UFunc_Nin2_Nout1[L["lcm"], L[11], None] -ldexp: _UFunc_Nin2_Nout1[L["ldexp"], L[8], None] left_shift: _UFunc_Nin2_Nout1[L["left_shift"], L[11], None] -less: _UFunc_Nin2_Nout1[L["less"], L[23], None] -less_equal: _UFunc_Nin2_Nout1[L["less_equal"], L[23], None] logaddexp2: _UFunc_Nin2_Nout1[L["logaddexp2"], L[4], float] logaddexp: _UFunc_Nin2_Nout1[L["logaddexp"], L[4], float] -logical_and: _UFunc_Nin2_Nout1[L["logical_and"], L[20], L[True]] -logical_or: _UFunc_Nin2_Nout1[L["logical_or"], L[20], L[False]] -logical_xor: _UFunc_Nin2_Nout1[L["logical_xor"], L[19], L[False]] matmul: _GUFunc_Nin2_Nout1[L["matmul"], L[19], None, L["(n?,k),(k,m?)->(n?,m?)"]] matvec: _GUFunc_Nin2_Nout1[L["matvec"], L[19], None, L["(m,n),(n)->(m)"]] maximum: _UFunc_Nin2_Nout1[L["maximum"], L[21], None] minimum: _UFunc_Nin2_Nout1[L["minimum"], L[21], None] -modf: _UFunc_Nin1_Nout2[L["modf"], L[4], None] multiply: _UFunc_Nin2_Nout1[L["multiply"], L[23], L[1]] nextafter: _UFunc_Nin2_Nout1[L["nextafter"], L[4], None] -not_equal: _UFunc_Nin2_Nout1[L["not_equal"], L[23], None] power: _UFunc_Nin2_Nout1[L["power"], L[18], None] remainder: _UFunc_Nin2_Nout1[L["remainder"], L[16], None] right_shift: _UFunc_Nin2_Nout1[L["right_shift"], L[11], None] @@ -7647,8 +7658,6 @@ vecmat: _GUFunc_Nin2_Nout1[L["vecmat"], L[19], None, L["(n),(n,m)->(m)"]] atan2 = arctan2 concat = concatenate bitwise_left_shift = left_shift -bitwise_not = invert -bitwise_invert = invert bitwise_right_shift = right_shift mod = remainder permute_dims = transpose diff --git a/numpy/_core/__init__.py b/numpy/_core/__init__.py index ede50aaeefc3..e32751d220ca 100644 --- a/numpy/_core/__init__.py +++ b/numpy/_core/__init__.py @@ -49,7 +49,7 @@ tag = sys.implementation.cache_tag or sys.implementation.name bad_c_module_info = ( f"The following compiled module files exist, but seem incompatible\n" - f"with with either python '{tag}' or the " + f"with either python '{tag}' or the " f"platform '{sys.platform}':\n\n * {candidate_str}\n" ) else: diff --git a/numpy/_core/_umath_tests.pyi b/numpy/_core/_umath_tests.pyi index cce84afdd081..bedb51f59e83 100644 --- a/numpy/_core/_umath_tests.pyi +++ b/numpy/_core/_umath_tests.pyi @@ -4,7 +4,7 @@ from typing import Final, Literal as L, TypedDict, type_check_only import numpy as np -from numpy._typing import _GUFunc_Nin2_Nout1, _UFunc_Nin1_Nout1, _UFunc_Nin2_Nout1 +from numpy._typing import _GUFunc_Nin2_Nout1, _UFunc_Nin2_Nout1 @type_check_only class _TestDispatchResult(TypedDict): @@ -36,7 +36,7 @@ def restore_loop(ufunc: np.ufunc, capsule: object, /) -> None: ... # undocumented ufuncs and gufuncs always_error: Final[_UFunc_Nin2_Nout1[L["always_error"], L[1], None]] = ... -always_error_unary: Final[_UFunc_Nin1_Nout1[L["always_error_unary"], L[1], None]] = ... +always_error_unary: Final[np.ufunc] = ... always_error_gufunc: Final[_GUFunc_Nin2_Nout1[L["always_error_gufunc"], L[1], None, L["(i),()->()"]]] = ... inner1d: Final[_GUFunc_Nin2_Nout1[L["inner1d"], L[2], None, L["(i),(i)->()"]]] = ... innerwt: Final[np.ufunc] = ... # we have no specialized type for 3->1 gufuncs @@ -47,5 +47,5 @@ cumsum: Final[np.ufunc] = ... # we have no specialized type for 1->1 gufuncs inner1d_no_doc: Final[_GUFunc_Nin2_Nout1[L["inner1d_no_doc"], L[2], None, L["(i),(i)->()"]]] = ... cross1d: Final[_GUFunc_Nin2_Nout1[L["cross1d"], L[2], None, L["(3),(3)->(3)"]]] = ... _pickleable_module_global_ufunc: Final[np.ufunc] = ... # 0->0 ufunc; segfaults if called -indexed_negative: Final[_UFunc_Nin1_Nout1[L["indexed_negative"], L[0], L[0]]] = ... # ntypes=0; can't be called +indexed_negative: Final[np.ufunc] = ... # ntypes=0; can't be called conv1d_full: Final[_GUFunc_Nin2_Nout1[L["conv1d_full"], L[1], None, L["(m),(n)->(p)"]]] = ... diff --git a/numpy/_core/code_generators/cversions.txt b/numpy/_core/code_generators/cversions.txt index 57dde2ef8131..959d9a45b913 100644 --- a/numpy/_core/code_generators/cversions.txt +++ b/numpy/_core/code_generators/cversions.txt @@ -87,4 +87,4 @@ # Version 22 (NumPy 2.5.0) Opaque PyObject ABI support and # NPY_DT_legacy_descriptor_proto support. # Version 22 (NumPy 2.6.0) No change -0x00000016 = cdfba10e4e01454262d2293c394cbed5 +0x00000016 = 83149cba850edb61bf48e1dd180f17a6 diff --git a/numpy/_core/code_generators/numpy_api.py b/numpy/_core/code_generators/numpy_api.py index 7f78d467d10b..e06d6d15dccf 100644 --- a/numpy/_core/code_generators/numpy_api.py +++ b/numpy/_core/code_generators/numpy_api.py @@ -415,10 +415,10 @@ def get_annotations(): '_PyDataType_GET_ITEM_DATA': (372, MinVersion("2.5")), '_PyArrayMultiIter_GET_ITEM_DATA': (373, MinVersion("2.5")), '_PyArrayNeighborhoodIter_GET_ITEM_DATA': (374, MinVersion("2.5")), - '_PyDatetimeScalarObject_GetMetadata': (375,), - '_PyTimedeltaScalarObject_GetMetadata': (376,), - '_PyDatetimeScalarObject_GetValue': (377,), - '_PyTimedeltaScalarObject_GetValue': (378,), + '_PyDatetimeScalarObject_GetMetadata': (375, MinVersion("2.5")), + '_PyTimedeltaScalarObject_GetMetadata': (376, MinVersion("2.5")), + '_PyDatetimeScalarObject_GetValue': (377, MinVersion("2.5")), + '_PyTimedeltaScalarObject_GetValue': (378, MinVersion("2.5")), } ufunc_types_api = { diff --git a/numpy/_core/fromnumeric.py b/numpy/_core/fromnumeric.py index ee5e65456178..e6ce77e259e9 100644 --- a/numpy/_core/fromnumeric.py +++ b/numpy/_core/fromnumeric.py @@ -29,6 +29,7 @@ _gentype = types.GeneratorType # save away Python sum _sum_ = sum +_NoValue = np._NoValue array_function_dispatch = functools.partial( overrides.array_function_dispatch, module='numpy') @@ -63,9 +64,20 @@ def _wrapfunc(obj, method, *args, **kwds): return _wrapit(obj, method, *args, **kwds) -def _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs): - passkwargs = {k: v for k, v in kwargs.items() - if v is not np._NoValue} +# The positional-only signature and unrolled _NoValue checks (rather than +# **kwargs with a dict comprehension) are deliberate: these helpers are on +# the hot path of every reduction (sum, prod, min, max, any, all), and +# avoiding the creation and iteration of a temporary kwargs dict measurably +# reduces call overhead for small arrays. See gh-31845. +def _wrapreduction(obj, ufunc, method, axis, dtype, out, + keepdims=_NoValue, initial=_NoValue, where=_NoValue, /): + passkwargs = {} + if keepdims is not _NoValue: + passkwargs["keepdims"] = keepdims + if initial is not _NoValue: + passkwargs["initial"] = initial + if where is not _NoValue: + passkwargs["where"] = where if type(obj) is not mu.ndarray: try: @@ -83,10 +95,14 @@ def _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs): return ufunc.reduce(obj, axis, dtype, out, **passkwargs) -def _wrapreduction_any_all(obj, ufunc, method, axis, out, **kwargs): +def _wrapreduction_any_all(obj, ufunc, method, axis, out, + keepdims=_NoValue, where=_NoValue, /): # Same as above function, but dtype is always bool (but never passed on) - passkwargs = {k: v for k, v in kwargs.items() - if v is not np._NoValue} + passkwargs = {} + if keepdims is not _NoValue: + passkwargs["keepdims"] = keepdims + if where is not _NoValue: + passkwargs["where"] = where if type(obj) is not mu.ndarray: try: @@ -2486,8 +2502,7 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, ) return _wrapreduction( - a, np.add, 'sum', axis, dtype, out, - keepdims=keepdims, initial=initial, where=where + a, np.add, 'sum', axis, dtype, out, keepdims, initial, where ) @@ -2600,7 +2615,7 @@ def any(a, axis=None, out=None, keepdims=np._NoValue, *, where=np._NoValue): """ return _wrapreduction_any_all(a, np.logical_or, 'any', axis, out, - keepdims=keepdims, where=where) + keepdims, where) def _all_dispatcher(a, axis=None, out=None, keepdims=None, *, @@ -2695,7 +2710,7 @@ def all(a, axis=None, out=None, keepdims=np._NoValue, *, where=np._NoValue): """ return _wrapreduction_any_all(a, np.logical_and, 'all', axis, out, - keepdims=keepdims, where=where) + keepdims, where) def _cumulative_func(x, func, axis, dtype, out, include_initial): @@ -3184,7 +3199,7 @@ def max(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, 5 """ return _wrapreduction(a, np.maximum, 'max', axis, None, out, - keepdims=keepdims, initial=initial, where=where) + keepdims, initial, where) @array_function_dispatch(_max_dispatcher) @@ -3201,7 +3216,7 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, ndarray.max : equivalent method """ return _wrapreduction(a, np.maximum, 'max', axis, None, out, - keepdims=keepdims, initial=initial, where=where) + keepdims, initial, where) def _min_dispatcher(a, axis=None, out=None, keepdims=None, initial=None, @@ -3322,7 +3337,7 @@ def min(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, 6 """ return _wrapreduction(a, np.minimum, 'min', axis, None, out, - keepdims=keepdims, initial=initial, where=where) + keepdims, initial, where) @array_function_dispatch(_min_dispatcher) @@ -3339,7 +3354,7 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, ndarray.min : equivalent method """ return _wrapreduction(a, np.minimum, 'min', axis, None, out, - keepdims=keepdims, initial=initial, where=where) + keepdims, initial, where) def _prod_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, @@ -3465,7 +3480,7 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, 10 """ return _wrapreduction(a, np.multiply, 'prod', axis, dtype, out, - keepdims=keepdims, initial=initial, where=where) + keepdims, initial, where) def _cumprod_dispatcher(a, axis=None, dtype=None, out=None): diff --git a/numpy/_core/fromnumeric.pyi b/numpy/_core/fromnumeric.pyi index 4da4a6b7ddd1..f9705ca0f9f5 100644 --- a/numpy/_core/fromnumeric.pyi +++ b/numpy/_core/fromnumeric.pyi @@ -1029,27 +1029,48 @@ def any[ArrayT: np.ndarray]( # keep in sync with `cumprod` below @overload -def cumsum[ScalarT: np.generic]( +def cumsum[ScalarT: np.inexact | np.timedelta64 | np.object_]( a: _ArrayLike[ScalarT], axis: None = None, dtype: None = None, out: None = None, ) -> _Array1D[ScalarT]: ... @overload -def cumsum[ArrayT: np.ndarray]( +def cumsum[ArrayT: np.ndarray[Any, np.dtype[np.inexact | np.timedelta64 | np.object_] | np.dtypes.StringDType]]( a: ArrayT, axis: SupportsIndex, dtype: None = None, out: None = None, ) -> ArrayT: ... @overload -def cumsum[ScalarT: np.generic]( +def cumsum[ScalarT: np.inexact | np.timedelta64 | np.object_]( a: _ArrayLike[ScalarT], axis: SupportsIndex, dtype: None = None, out: None = None, ) -> NDArray[ScalarT]: ... @overload +def cumsum( + a: _ArrayLikeInt_co, + axis: None = None, + dtype: None = None, + out: None = None, +) -> _Array1D[np.int_]: ... +@overload +def cumsum[ShapeT: _Shape]( + a: np.ndarray[ShapeT, np.dtype[np.bool | np.integer]], + axis: SupportsIndex, + dtype: None = None, + out: None = None, +) -> np.ndarray[ShapeT, np.dtype[np.int_]]: ... +@overload +def cumsum( + a: _ArrayLikeInt_co, + axis: SupportsIndex, + dtype: None = None, + out: None = None, +) -> NDArray[np.int_]: ... +@overload def cumsum( a: ArrayLike, axis: None = None, @@ -1117,7 +1138,7 @@ def cumsum[ArrayT: np.ndarray]( # keep in sync with `cumulative_prod` below @overload -def cumulative_sum[ScalarT: np.generic]( +def cumulative_sum[ScalarT: np.inexact | np.timedelta64 | np.object_]( x: _ArrayLike[ScalarT], /, *, @@ -1127,7 +1148,7 @@ def cumulative_sum[ScalarT: np.generic]( include_initial: bool = False, ) -> _Array1D[ScalarT]: ... @overload -def cumulative_sum[ArrayT: np.ndarray]( +def cumulative_sum[ArrayT: np.ndarray[Any, np.dtype[np.inexact | np.timedelta64 | np.object_] | np.dtypes.StringDType]]( x: ArrayT, /, *, @@ -1137,7 +1158,7 @@ def cumulative_sum[ArrayT: np.ndarray]( include_initial: bool = False, ) -> ArrayT: ... @overload -def cumulative_sum[ScalarT: np.generic]( +def cumulative_sum[ScalarT: np.inexact | np.timedelta64 | np.object_]( x: _ArrayLike[ScalarT], /, *, @@ -1147,6 +1168,36 @@ def cumulative_sum[ScalarT: np.generic]( include_initial: bool = False, ) -> NDArray[ScalarT]: ... @overload +def cumulative_sum( + x: _ArrayLikeInt_co, + /, + *, + axis: None = None, + dtype: None = None, + out: None = None, + include_initial: bool = False, +) -> _Array1D[np.int_]: ... +@overload +def cumulative_sum[ShapeT: _Shape]( + x: np.ndarray[ShapeT, np.dtype[np.bool | np.integer]], + /, + *, + axis: SupportsIndex, + dtype: None = None, + out: None = None, + include_initial: bool = False, +) -> np.ndarray[ShapeT, np.dtype[np.int_]]: ... +@overload +def cumulative_sum( + x: _ArrayLikeInt_co, + /, + *, + axis: SupportsIndex, + dtype: None = None, + out: None = None, + include_initial: bool = False, +) -> NDArray[np.int_]: ... +@overload def cumulative_sum( x: ArrayLike, /, @@ -1766,29 +1817,50 @@ def amin( min = amin -# keep in sync with `cumsum` above +# keep in sync with `cumsum` above (minus `timedelta64` and `StringDType`) @overload -def cumprod[ScalarT: np.number | np.bool | np.object_]( +def cumprod[ScalarT: np.inexact | np.object_]( a: _ArrayLike[ScalarT], axis: None = None, dtype: None = None, out: None = None, ) -> _Array1D[ScalarT]: ... @overload -def cumprod[ArrayT: NDArray[np.number | np.bool | np.object_]]( +def cumprod[ArrayT: NDArray[np.inexact | np.object_]]( a: ArrayT, axis: SupportsIndex, dtype: None = None, out: None = None, ) -> ArrayT: ... @overload -def cumprod[ScalarT: np.number | np.bool | np.object_]( +def cumprod[ScalarT: np.inexact | np.object_]( a: _ArrayLike[ScalarT], axis: SupportsIndex, dtype: None = None, out: None = None, ) -> NDArray[ScalarT]: ... @overload +def cumprod( + a: _ArrayLikeInt_co, + axis: None = None, + dtype: None = None, + out: None = None, +) -> _Array1D[np.int_]: ... +@overload +def cumprod[ShapeT: _Shape]( + a: np.ndarray[ShapeT, np.dtype[np.bool | np.integer]], + axis: SupportsIndex, + dtype: None = None, + out: None = None, +) -> np.ndarray[ShapeT, np.dtype[np.int_]]: ... +@overload +def cumprod( + a: _ArrayLikeInt_co, + axis: SupportsIndex, + dtype: None = None, + out: None = None, +) -> NDArray[np.int_]: ... +@overload def cumprod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, axis: None = None, @@ -1854,9 +1926,9 @@ def cumprod[ArrayT: np.ndarray]( out: ArrayT, ) -> ArrayT: ... -# keep in sync with `cumulative_sum` above +# keep in sync with `cumulative_sum` above (minus `timedelta64` and `StringDType`) @overload -def cumulative_prod[ScalarT: np.number | np.bool | np.object_]( +def cumulative_prod[ScalarT: np.inexact | np.object_]( x: _ArrayLike[ScalarT], /, *, @@ -1866,7 +1938,7 @@ def cumulative_prod[ScalarT: np.number | np.bool | np.object_]( include_initial: bool = False, ) -> _Array1D[ScalarT]: ... @overload -def cumulative_prod[ArrayT: NDArray[np.number | np.bool | np.object_]]( +def cumulative_prod[ArrayT: NDArray[np.inexact | np.object_]]( x: ArrayT, /, *, @@ -1876,7 +1948,7 @@ def cumulative_prod[ArrayT: NDArray[np.number | np.bool | np.object_]]( include_initial: bool = False, ) -> ArrayT: ... @overload -def cumulative_prod[ScalarT: np.generic]( +def cumulative_prod[ScalarT: np.inexact | np.object_]( x: _ArrayLike[ScalarT], /, *, @@ -1887,7 +1959,37 @@ def cumulative_prod[ScalarT: np.generic]( ) -> NDArray[ScalarT]: ... @overload def cumulative_prod( - x: _ArrayLikeComplex_co | _ArrayLikeObject_co, + x: _ArrayLikeInt_co, + /, + *, + axis: None = None, + dtype: None = None, + out: None = None, + include_initial: bool = False, +) -> _Array1D[np.int_]: ... +@overload +def cumulative_prod[ShapeT: _Shape]( + x: np.ndarray[ShapeT, np.dtype[np.bool | np.integer]], + /, + *, + axis: SupportsIndex, + dtype: None = None, + out: None = None, + include_initial: bool = False, +) -> np.ndarray[ShapeT, np.dtype[np.int_]]: ... +@overload +def cumulative_prod( + x: _ArrayLikeInt_co, + /, + *, + axis: SupportsIndex, + dtype: None = None, + out: None = None, + include_initial: bool = False, +) -> NDArray[np.int_]: ... +@overload +def cumulative_prod( + x: ArrayLike, /, *, axis: None = None, @@ -2074,8 +2176,8 @@ def prod( initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[np.complex128]: ... -@overload # ~number -def prod[ScalarT: np.number]( +@overload # ~inexact +def prod[ScalarT: np.inexact]( a: _ArrayLike[ScalarT], axis: None = None, dtype: None = None, @@ -2084,8 +2186,8 @@ def prod[ScalarT: np.number]( initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ScalarT: ... -@overload # ~number | timedelta64 | object_, axis: -def prod[ScalarT: np.number | np.object_]( +@overload # ~inexact | timedelta64 | object_, axis: +def prod[ScalarT: np.inexact | np.object_]( a: _ArrayLike[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -2094,8 +2196,8 @@ def prod[ScalarT: np.number | np.object_]( initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[ScalarT]: ... -@overload # ~number | object_, keepdims=True -def prod[ArrayT: NDArray[np.number | np.object_]]( +@overload # ~inexact | object_, keepdims=True +def prod[ArrayT: NDArray[np.inexact | np.object_]]( a: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, @@ -2105,9 +2207,9 @@ def prod[ArrayT: NDArray[np.number | np.object_]]( initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ArrayT: ... -@overload # bool_ | +builtins.int +@overload # bool | integer | +builtins.int def prod( - a: _DualArrayLike[np.dtype[np.bool], int], + a: _DualArrayLike[np.dtype[np.bool | np.integer], int], axis: None = None, dtype: None = None, out: None = None, @@ -2115,9 +2217,9 @@ def prod( initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> np.int_: ... -@overload # bool_ | +builtins.int, axis: +@overload # bool | np.integer | +builtins.int, axis: def prod( - a: _DualArrayLike[np.dtype[np.bool], int], + a: _DualArrayLike[np.dtype[np.bool | np.integer], int], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -2125,9 +2227,9 @@ def prod( initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[np.int_]: ... -@overload # bool_, keepdims=True +@overload # bool | np.integer, keepdims=True def prod[ShapeT: _Shape]( - a: np.ndarray[ShapeT, np.dtype[np.bool]], + a: np.ndarray[ShapeT, np.dtype[np.bool | np.integer]], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -2316,8 +2418,8 @@ def sum( initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[np.complex128]: ... -@overload # ~number | timedelta64 -def sum[ScalarT: np.number | np.timedelta64]( +@overload # ~inexact | timedelta64 +def sum[ScalarT: np.inexact | np.timedelta64]( a: _ArrayLike[ScalarT], axis: None = None, dtype: None = None, @@ -2326,8 +2428,8 @@ def sum[ScalarT: np.number | np.timedelta64]( initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ScalarT: ... -@overload # ~number | timedelta64 | object_, axis: -def sum[ScalarT: np.number | np.timedelta64 | np.object_]( +@overload # ~inexact | timedelta64 | object_, axis: +def sum[ScalarT: np.inexact | np.timedelta64 | np.object_]( a: _ArrayLike[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -2337,7 +2439,7 @@ def sum[ScalarT: np.number | np.timedelta64 | np.object_]( where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[ScalarT]: ... @overload # ~number | timedelta64 | object_, keepdims=True -def sum[ArrayT: NDArray[np.number | np.timedelta64 | np.object_]]( +def sum[ArrayT: NDArray[np.inexact | np.timedelta64 | np.object_]]( a: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, @@ -2347,9 +2449,9 @@ def sum[ArrayT: NDArray[np.number | np.timedelta64 | np.object_]]( initial: _NumberLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> ArrayT: ... -@overload # bool_ | +builtins.int +@overload # bool | integer | +builtins.int def sum( - a: _DualArrayLike[np.dtype[np.bool], int], + a: _DualArrayLike[np.dtype[np.bool | np.integer], int], axis: None = None, dtype: None = None, out: None = None, @@ -2357,9 +2459,9 @@ def sum( initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> np.int_: ... -@overload # bool_ | +builtins.int, axis: +@overload # bool | integer | +builtins.int, axis: def sum( - a: _DualArrayLike[np.dtype[np.bool], int], + a: _DualArrayLike[np.dtype[np.bool | np.integer], int], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -2367,9 +2469,9 @@ def sum( initial: _IntLike_co | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[np.int_]: ... -@overload # bool_, keepdims=True +@overload # bool | integer, keepdims=True def sum[ShapeT: _Shape]( - a: np.ndarray[ShapeT, np.dtype[np.bool]], + a: np.ndarray[ShapeT, np.dtype[np.bool | np.integer]], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -2657,10 +2759,10 @@ def mean( where: _ArrayLikeBool_co | _NoValueType = ..., ) -> NDArray[Any]: ... -# keep in sync with `mean` above -@overload # +integer | ~object_ | +builtins.float +# +@overload # ~complex128 | +integer | ~object_ | +builtins.float def std( - a: _DualArrayLike[np.dtype[np.integer | np.bool | np.object_], float], + a: _DualArrayLike[np.dtype[np.complex128 | np.integer | np.bool | np.object_], complex], axis: None = None, dtype: None = None, out: None = None, @@ -2671,9 +2773,9 @@ def std( mean: _ArrayLikeFloat_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> np.float64: ... -@overload # +integer | +builtins.float, axis: +@overload # ~complex128 | +integer | +builtins.float, axis: def std( - a: _DualArrayLike[np.dtype[np.integer | np.bool], float], + a: _DualArrayLike[np.dtype[np.complex128 | np.integer | np.bool], complex], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -2686,7 +2788,7 @@ def std( ) -> NDArray[np.float64]: ... @overload # +integer, keepdims=True def std[ShapeT: _Shape]( - a: np.ndarray[ShapeT, np.dtype[np.integer | np.bool]], + a: np.ndarray[ShapeT, np.dtype[np.complex128 | np.integer | np.bool]], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -2697,21 +2799,8 @@ def std[ShapeT: _Shape]( mean: _ArrayLikeComplex_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> np.ndarray[ShapeT, np.dtype[np.float64]]: ... -@overload # ~complex (`list` ensures invariance to avoid overlap with the previous overload) -def std( - a: _NestedSequence[list[complex]] | list[complex], - axis: None = None, - dtype: None = None, - out: None = None, - ddof: float = 0, - keepdims: Literal[False] | _NoValueType = ..., - *, - where: _ArrayLikeBool_co | _NoValueType = ..., - mean: _ArrayLikeComplex_co | _NoValueType = ..., - correction: float | _NoValueType = ..., -) -> np.complex128: ... -@overload # ~inexact | timedelta64 -def std[ScalarT: np.inexact | np.timedelta64]( +@overload # ~floating | timedelta64 +def std[ScalarT: np.floating | np.timedelta64]( a: _ArrayLike[ScalarT], axis: None = None, dtype: None = None, @@ -2723,8 +2812,8 @@ def std[ScalarT: np.inexact | np.timedelta64]( mean: _ArrayLikeComplex_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> ScalarT: ... -@overload # ~inexact | timedelta64 | object_, axis: -def std[ScalarT: np.inexact | np.timedelta64 | np.object_]( +@overload # ~floating | timedelta64 | object_, axis: +def std[ScalarT: np.floating | np.timedelta64 | np.object_]( a: _ArrayLike[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -2736,8 +2825,8 @@ def std[ScalarT: np.inexact | np.timedelta64 | np.object_]( mean: _ArrayLikeComplex_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> NDArray[ScalarT]: ... -@overload # ~inexact | timedelta64 | object_, keepdims=True -def std[ArrayT: NDArray[np.inexact | np.timedelta64 | np.object_]]( +@overload # ~floating | timedelta64 | object_, keepdims=True +def std[ArrayT: NDArray[np.floating | np.timedelta64 | np.object_]]( a: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, @@ -2868,9 +2957,9 @@ def std( ) -> NDArray[Any]: ... # keep in sync with `std` above -@overload # +integer | ~object_ | +builtins.float +@overload # ~complex128 | +integer | ~object_ | +builtins.float def var( - a: _DualArrayLike[np.dtype[np.integer | np.bool | np.object_], float], + a: _DualArrayLike[np.dtype[np.complex128 | np.integer | np.bool | np.object_], complex], axis: None = None, dtype: None = None, out: None = None, @@ -2881,9 +2970,9 @@ def var( mean: _ArrayLikeFloat_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> np.float64: ... -@overload # +integer | +builtins.float, axis: +@overload # ~complex128 | +integer | +builtins.float, axis: def var( - a: _DualArrayLike[np.dtype[np.integer | np.bool], float], + a: _DualArrayLike[np.dtype[np.complex128 | np.integer | np.bool], complex], axis: int | tuple[int, ...], dtype: None = None, out: None = None, @@ -2896,7 +2985,7 @@ def var( ) -> NDArray[np.float64]: ... @overload # +integer, keepdims=True def var[ShapeT: _Shape]( - a: np.ndarray[ShapeT, np.dtype[np.integer | np.bool]], + a: np.ndarray[ShapeT, np.dtype[np.complex128 | np.integer | np.bool]], axis: int | tuple[int, ...] | None = None, dtype: None = None, out: None = None, @@ -2907,21 +2996,8 @@ def var[ShapeT: _Shape]( mean: _ArrayLikeComplex_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> np.ndarray[ShapeT, np.dtype[np.float64]]: ... -@overload # ~complex (`list` ensures invariance to avoid overlap with the previous overload) -def var( - a: _NestedSequence[list[complex]] | list[complex], - axis: None = None, - dtype: None = None, - out: None = None, - ddof: float = 0, - keepdims: Literal[False] | _NoValueType = ..., - *, - where: _ArrayLikeBool_co | _NoValueType = ..., - mean: _ArrayLikeComplex_co | _NoValueType = ..., - correction: float | _NoValueType = ..., -) -> np.complex128: ... -@overload # ~inexact | timedelta64 -def var[ScalarT: np.inexact | np.timedelta64]( +@overload # ~floating | timedelta64 +def var[ScalarT: np.floating | np.timedelta64]( a: _ArrayLike[ScalarT], axis: None = None, dtype: None = None, @@ -2933,8 +3009,8 @@ def var[ScalarT: np.inexact | np.timedelta64]( mean: _ArrayLikeComplex_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> ScalarT: ... -@overload # ~inexact | timedelta64 | object_, axis: -def var[ScalarT: np.inexact | np.timedelta64 | np.object_]( +@overload # ~floating | timedelta64 | object_, axis: +def var[ScalarT: np.floating | np.timedelta64 | np.object_]( a: _ArrayLike[ScalarT], axis: int | tuple[int, ...], dtype: None = None, @@ -2946,8 +3022,8 @@ def var[ScalarT: np.inexact | np.timedelta64 | np.object_]( mean: _ArrayLikeComplex_co | _NoValueType = ..., correction: float | _NoValueType = ..., ) -> NDArray[ScalarT]: ... -@overload # ~inexact | timedelta64 | object_, keepdims=True -def var[ArrayT: NDArray[np.inexact | np.timedelta64 | np.object_]]( +@overload # ~floating | timedelta64 | object_, keepdims=True +def var[ArrayT: NDArray[np.floating | np.timedelta64 | np.object_]]( a: ArrayT, axis: int | tuple[int, ...] | None = None, dtype: None = None, diff --git a/numpy/_core/function_base.py b/numpy/_core/function_base.py index 30ce0197ba42..c792621f8cfb 100644 --- a/numpy/_core/function_base.py +++ b/numpy/_core/function_base.py @@ -137,8 +137,19 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, else: integer_dtype = _nx.issubdtype(dtype, _nx.integer) - # Use `dtype=type(dt)` to enforce a floating point evaluation: - delta = np.subtract(stop, start, dtype=type(dt)) + # Use `dtype=type(dt)` to enforce a floating point evaluation. + # Equal endpoints (including equal infinities) must produce a zero step, + # so skip the subtraction there: `inf - inf` would otherwise yield a + # spurious nan and an "invalid value" warning. Using `where=` avoids the + # bad element entirely rather than suppressing warnings globally, so + # genuine invalid operations (e.g. mixed infinities) still warn. + equal = start == stop + if equal.ndim == 0: + delta = dt.type(0) if equal else np.subtract( + stop, start, dtype=type(dt)) + else: + delta = np.subtract(stop, start, dtype=type(dt), where=~equal, + out=np.zeros(equal.shape, dtype=type(dt))) y = _nx.arange( 0, num, dtype=dt, device=device ).reshape((-1,) + (1,) * ndim(delta)) diff --git a/numpy/_core/include/numpy/ndarraytypes.h b/numpy/_core/include/numpy/ndarraytypes.h index 92062bc52a3c..5434df33bd97 100644 --- a/numpy/_core/include/numpy/ndarraytypes.h +++ b/numpy/_core/include/numpy/ndarraytypes.h @@ -1547,6 +1547,14 @@ typedef struct { #define _PyDataType_GET_ITEM_DATA(descr) ((PyArray_Descr_fields *)(descr)) #undef _PyArray_LegacyDescr_GET_ITEM_DATA #define _PyArray_LegacyDescr_GET_ITEM_DATA(descr) ((_PyArray_LegacyDescr_fields *)(descr)) +#undef _PyDatetimeScalarObject_GetMetadata +#define _PyDatetimeScalarObject_GetMetadata(self) ((PyDatetimeScalarObject *)self)->obmeta +#undef _PyTimedeltaScalarObject_GetMetadata +#define _PyTimedeltaScalarObject_GetMetadata(self) ((PyTimedeltaScalarObject *)self)->obmeta +#undef _PyDatetimeScalarObject_GetValue +#define _PyDatetimeScalarObject_GetValue(self) ((PyDatetimeScalarObject *)self)->obval +#undef _PyTimedeltaScalarObject_GetValue +#define _PyTimedeltaScalarObject_GetValue(self) (((PyTimedeltaScalarObject *)self)->obval) #endif /* diff --git a/numpy/_core/src/common/raii_utils.hpp b/numpy/_core/src/common/raii_utils.hpp index e92d0eae9269..7e95eb62357c 100644 --- a/numpy/_core/src/common/raii_utils.hpp +++ b/numpy/_core/src/common/raii_utils.hpp @@ -148,7 +148,7 @@ class NpyStringAcquireAllocator } ~NpyStringAcquireAllocator() { - NpyString_release_allocator(_allocator); + release(); } NpyStringAcquireAllocator(const NpyStringAcquireAllocator&) = delete; @@ -159,6 +159,18 @@ class NpyStringAcquireAllocator npy_string_allocator *allocator() { return _allocator; } + + // Release the allocator lock early, before this object goes out of scope. + // Use this when a scope must drop the lock partway through, e.g. before + // running Python C API code that must not hold the allocator lock (which + // could otherwise deadlock on re-entrancy). Idempotent: the destructor + // also calls it, so an explicit call never double-releases. + void release() { + if (_allocator != NULL) { + NpyString_release_allocator(_allocator); + _allocator = NULL; + } + } }; }} // namespace np { namespace raii { diff --git a/numpy/_core/src/multiarray/arrayobject.c b/numpy/_core/src/multiarray/arrayobject.c index 23bba6a43d51..ba8efe2f9209 100644 --- a/numpy/_core/src/multiarray/arrayobject.c +++ b/numpy/_core/src/multiarray/arrayobject.c @@ -1325,24 +1325,28 @@ _PyArrayNeighborhoodIter_GET_ITEM_DATA(const PyArrayNeighborhoodIterObject *iter { return (PyArrayNeighborhoodIterObject_fields *)(((char *)iter) + sizeof(PyObject)); } +#undef _PyDatetimeScalarObject_GetMetadata /*NUMPY_API*/ NPY_NO_EXPORT PyArray_DatetimeMetaData _PyDatetimeScalarObject_GetMetadata(PyObject *self) { return ((PyDatetimeScalarObject *)self)->obmeta; } +#undef _PyTimedeltaScalarObject_GetMetadata /*NUMPY_API*/ NPY_NO_EXPORT PyArray_DatetimeMetaData _PyTimedeltaScalarObject_GetMetadata(PyObject *self) { return ((PyTimedeltaScalarObject *)self)->obmeta; } +#undef _PyDatetimeScalarObject_GetValue /*NUMPY_API*/ NPY_NO_EXPORT npy_datetime _PyDatetimeScalarObject_GetValue(PyObject *self) { return ((PyDatetimeScalarObject *)self)->obval; } +#undef _PyTimedeltaScalarObject_GetValue /*NUMPY_API*/ NPY_NO_EXPORT npy_timedelta _PyTimedeltaScalarObject_GetValue(PyObject *self) diff --git a/numpy/_core/src/multiarray/datetime.c b/numpy/_core/src/multiarray/datetime.c index a15ad5703658..be6f8ca03669 100644 --- a/numpy/_core/src/multiarray/datetime.c +++ b/numpy/_core/src/multiarray/datetime.c @@ -167,12 +167,23 @@ static npy_int64 days_to_yearsdays(npy_int64 *days_) { const npy_int64 days_per_400years = (400*365 + 100 - 4 + 1); + const npy_int64 days_offset = (365*30 + 7); /* Adjust so it's relative to the year 2000 (divisible by 400) */ - npy_int64 days = (*days_) - (365*30 + 7); + npy_int64 days; npy_int64 year; + if (*days_ < NPY_MIN_INT64 + days_offset) { + /* Extract 400-year cycles first to reduce magnitude */ + days = *days_ + (days_per_400years - days_offset); + year = 400 * extract_unit_64(&days, days_per_400years); + year -= 400; + } + else { + days = (*days_) - days_offset; + year = 400 * extract_unit_64(&days, days_per_400years); + } + /* Break down the 400 year cycle to get the year and day within the year */ - year = 400 * extract_unit_64(&days, days_per_400years); /* Work out the year/day within the 400 year cycle */ if (days >= 366) { diff --git a/numpy/_core/src/multiarray/npy_static_data.c b/numpy/_core/src/multiarray/npy_static_data.c index 09c1fc713f30..78c6f6875a9c 100644 --- a/numpy/_core/src/multiarray/npy_static_data.c +++ b/numpy/_core/src/multiarray/npy_static_data.c @@ -180,6 +180,12 @@ initialize_static_globals(void) return -1; } + npy_static_pydata.legacy_resolver_promoting = + PyContextVar_New("numpy._legacy_resolver_promoting", Py_False); + if (npy_static_pydata.legacy_resolver_promoting == NULL) { + return -1; + } + npy_static_pydata.kwnames_is_copy = PyTuple_Pack(1, npy_interned_str.copy); if (npy_static_pydata.kwnames_is_copy == NULL) { return -1; diff --git a/numpy/_core/src/multiarray/npy_static_data.h b/numpy/_core/src/multiarray/npy_static_data.h index 3536dac8f1ea..c8c730d3dc28 100644 --- a/numpy/_core/src/multiarray/npy_static_data.h +++ b/numpy/_core/src/multiarray/npy_static_data.h @@ -132,6 +132,13 @@ typedef struct npy_static_pydata_struct { PyObject *os_fspath; PyObject *format_options; + /* + * Context variable set to True while the legacy ufunc type resolvers + * run for promotion, to suppress their deprecation warnings (the + * resolution step warns on every call). + */ + PyObject *legacy_resolver_promoting; + /* * Used in the __array__ internals to avoid building a tuple inline */ diff --git a/numpy/_core/src/multiarray/stringdtype/casts.cpp b/numpy/_core/src/multiarray/stringdtype/casts.cpp index aa7004f83a98..f8cc1486876b 100644 --- a/numpy/_core/src/multiarray/stringdtype/casts.cpp +++ b/numpy/_core/src/multiarray/stringdtype/casts.cpp @@ -147,6 +147,9 @@ string_to_string_resolve_descriptors(PyObject *NPY_UNUSED(self), { if (given_descrs[1] == NULL) { loop_descrs[1] = stringdtype_finalize_descr(given_descrs[0]); + if (loop_descrs[1] == NULL) { + return (NPY_CASTING)-1; + } } else { Py_INCREF(given_descrs[1]); @@ -852,9 +855,13 @@ make_type2s_name(NPY_TYPES typenum) { size_t nlen = strlen(type_name); const char suffix[] = "_to_StringDType"; - size_t slen = sizeof(prefix)/sizeof(char) - 1; + size_t slen = sizeof(suffix)/sizeof(char) - 1; char *buf = (char *)PyMem_RawCalloc(sizeof(char), plen + nlen + slen + 1); + if (buf == NULL) { + npy_gil_error(PyExc_MemoryError, "Failed allocate memory for cast"); + return NULL; + } // memcpy instead of strcpy/strncat to avoid stringop-truncation warning, // since we are not including the trailing null character @@ -1873,7 +1880,7 @@ void_to_string(PyArrayMethod_Context *context, char *const data[], npy_intp out_stride = strides[1]; while(N--) { - size_t out_num_bytes = utf8_buffer_size(in, max_in_size); + Py_ssize_t out_num_bytes = utf8_buffer_size(in, max_in_size); if (out_num_bytes < 0) { npy_gil_error(PyExc_TypeError, "Invalid UTF-8 bytes found, cannot convert to UTF-8"); @@ -1881,7 +1888,7 @@ void_to_string(PyArrayMethod_Context *context, char *const data[], } npy_static_string out_ss = {0, NULL}; if (load_new_string((npy_packed_static_string *)out, - &out_ss, out_num_bytes, allocator, + &out_ss, (size_t)out_num_bytes, allocator, "void to string cast") == -1) { goto fail; } @@ -1918,8 +1925,6 @@ string_to_bytes(PyArrayMethod_Context *context, char *const data[], NpyAuxData *NPY_UNUSED(auxdata)) { PyArray_StringDTypeObject *descr = (PyArray_StringDTypeObject *)context->descriptors[0]; - np::raii::NpyStringAcquireAllocator alloc(descr); - int has_null = descr->na_object != NULL; int has_string_na = descr->has_string_na; const npy_static_string *default_string = &descr->default_string; @@ -1931,6 +1936,8 @@ string_to_bytes(PyArrayMethod_Context *context, char *const data[], npy_intp out_stride = strides[1]; size_t max_out_size = context->descriptors[1]->elsize; + np::raii::NpyStringAcquireAllocator alloc(descr); + while (N--) { const npy_packed_static_string *ps = (npy_packed_static_string *)in; npy_static_string s = {0, NULL}; @@ -1940,11 +1947,29 @@ string_to_bytes(PyArrayMethod_Context *context, char *const data[], return -1; } - for (size_t i=0; i 127) { + // Building the UnicodeEncodeError needs the GIL and must not + // run while the allocator is held (re-entrant Python could + // deadlock on it), so copy the offending bytes out, release the + // allocator, then raise. + char *bad = (char *)PyMem_RawMalloc(s.size); + if (bad == NULL) { + alloc.release(); + npy_gil_error(PyExc_MemoryError, + "Failed to allocate memory for unicode " + "encoding error"); + return -1; + } + memcpy(bad, s.buf, s.size); + size_t bad_size = s.size; + size_t bad_pos = i; + alloc.release(); + np::raii::EnsureGIL ensure_gil{}; - PyObject *str = PyUnicode_FromStringAndSize(s.buf, s.size); + PyObject *str = PyUnicode_FromStringAndSize(bad, bad_size); + PyMem_RawFree(bad); if (str == NULL) { PyErr_SetString( @@ -1958,8 +1983,8 @@ string_to_bytes(PyArrayMethod_Context *context, char *const data[], "sOnns", "ascii", str, - (Py_ssize_t)i, - (Py_ssize_t)(i+1), + (Py_ssize_t)bad_pos, + (Py_ssize_t)(bad_pos + 1), "ordinal not in range(128)" ); diff --git a/numpy/_core/src/multiarray/stringdtype/dtype.c b/numpy/_core/src/multiarray/stringdtype/dtype.c index ef63c1684c30..c5d47b2b8278 100644 --- a/numpy/_core/src/multiarray/stringdtype/dtype.c +++ b/numpy/_core/src/multiarray/stringdtype/dtype.c @@ -230,7 +230,7 @@ common_instance(PyArray_StringDTypeObject *dtype1, PyArray_StringDTypeObject *dt } return (PyArray_StringDTypeObject *)new_stringdtype_instance( - out_na_object, dtype1->coerce && dtype1->coerce); + out_na_object, dtype1->coerce && dtype2->coerce); } /* @@ -266,6 +266,13 @@ as_pystring(PyObject *scalar, int coerce) Py_INCREF(scalar); return scalar; } + // str subclasses (e.g. np.str_) are string data even when coercion is + // disabled; convert to an exact str, preserving embedded and trailing + // nulls that a round-trip through a fixed-width unicode descriptor + // would strip + if (PyUnicode_Check(scalar)) { + return PyUnicode_FromObject(scalar); + } if (coerce == 0) { PyErr_SetString(PyExc_ValueError, "StringDType only allows string data when " @@ -315,62 +322,55 @@ stringdtype_setitem(PyArray_StringDTypeObject *descr, PyObject *obj, char **data { npy_packed_static_string *sdata = (npy_packed_static_string *)dataptr; - // borrow reference + // borrowed reference PyObject *na_object = descr->na_object; - // We need the result of the comparison after acquiring the allocator, but - // cannot use functions requiring the GIL when the allocator is acquired, - // so we do the comparison before acquiring the allocator. - + // We need the result of the comparison before packing below, but cannot + // use functions requiring the GIL when the allocator is acquired. int na_cmp = na_eq_cmp(obj, na_object); if (na_cmp == -1) { return -1; } - npy_string_allocator *allocator = NpyString_acquire_allocator(descr); - - if (na_object != NULL) { - if (na_cmp) { - if (NpyString_pack_null(allocator, sdata) < 0) { - PyErr_SetString(PyExc_MemoryError, - "Failed to pack null string during StringDType " - "setitem"); - goto fail; - } - goto success; + if (na_object != NULL && na_cmp) { + npy_string_allocator *allocator = NpyString_acquire_allocator(descr); + int pack_status = NpyString_pack_null(allocator, sdata); + NpyString_release_allocator(allocator); + if (pack_status < 0) { + PyErr_SetString(PyExc_MemoryError, + "Failed to pack null string during StringDType " + "setitem"); + return -1; } + return 0; } + PyObject *val_obj = as_pystring(obj, descr->coerce); if (val_obj == NULL) { - goto fail; + return -1; } Py_ssize_t length = 0; const char *val = PyUnicode_AsUTF8AndSize(val_obj, &length); if (val == NULL) { Py_DECREF(val_obj); - goto fail; + return -1; } - if (NpyString_pack(allocator, sdata, val, length) < 0) { + npy_string_allocator *allocator = NpyString_acquire_allocator(descr); + int pack_status = NpyString_pack(allocator, sdata, val, length); + NpyString_release_allocator(allocator); + Py_DECREF(val_obj); + + if (pack_status < 0) { PyErr_SetString(PyExc_MemoryError, "Failed to pack string during StringDType " "setitem"); - Py_DECREF(val_obj); - goto fail; + return -1; } - Py_DECREF(val_obj); - -success: - NpyString_release_allocator(allocator); return 0; - -fail: - NpyString_release_allocator(allocator); - - return -1; } static PyObject * @@ -642,6 +642,11 @@ stringdtype_is_known_scalar_type(PyArray_DTypeMeta *cls, { return 1; } + // otherwise np.str_ discovers its fixed-width 'U' descriptor, whose + // cast into StringDType strips trailing NULs setitem would preserve + else if (pytype == &PyUnicodeArrType_Type) { + return 1; + } return 0; } @@ -661,6 +666,9 @@ stringdtype_finalize_descr(PyArray_Descr *dtype) NpyString_release_allocator(allocator); PyArray_StringDTypeObject *ret = (PyArray_StringDTypeObject *)new_stringdtype_instance( sdtype->na_object, sdtype->coerce); + if (ret == NULL) { + return NULL; + } ret->array_owned = 1; return (PyArray_Descr *)ret; } diff --git a/numpy/_core/src/multiarray/stringdtype/static_string.c b/numpy/_core/src/multiarray/stringdtype/static_string.c index 1f2ed81f2f0e..a8396d96075c 100644 --- a/numpy/_core/src/multiarray/stringdtype/static_string.c +++ b/numpy/_core/src/multiarray/stringdtype/static_string.c @@ -229,6 +229,37 @@ arena_free(npy_string_arena *arena, _npy_static_string_u *str) static npy_string_arena NEW_ARENA = {0, 0, NULL}; +static void +acquire_allocator_lock(npy_string_allocator *allocator) +{ +#if PY_VERSION_HEX < 0x30d00b3 + if (!PyThread_acquire_lock(allocator->allocator_lock, NOWAIT_LOCK)) { + if (PyGILState_Check()) { + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(allocator->allocator_lock, WAIT_LOCK); + Py_END_ALLOW_THREADS + } + else { + PyThread_acquire_lock(allocator->allocator_lock, WAIT_LOCK); + } + } +#else + PyMutex_Lock(&allocator->allocator_lock); +#endif +} + +static int +allocator_seen(npy_string_allocator *allocator, + npy_string_allocator *allocators[], size_t end) +{ + for (size_t i = 0; i < end; i++) { + if (allocators[i] == allocator) { + return 1; + } + } + return 0; +} + NPY_NO_EXPORT npy_string_allocator * NpyString_new_allocator(npy_string_malloc_func m, npy_string_free_func f, npy_string_realloc_func r) @@ -281,18 +312,13 @@ NpyString_free_allocator(npy_string_allocator *allocator) * by this function exactly once. * * Note that functions requiring the GIL should not be called while the - * allocator mutex is held, as doing so may cause deadlocks. + * allocator mutex is held and reentrant calls are possible, as doing so may + * cause deadlocks. */ NPY_NO_EXPORT npy_string_allocator * NpyString_acquire_allocator(const PyArray_StringDTypeObject *descr) { -#if PY_VERSION_HEX < 0x30d00b3 - if (!PyThread_acquire_lock(descr->allocator->allocator_lock, NOWAIT_LOCK)) { - PyThread_acquire_lock(descr->allocator->allocator_lock, WAIT_LOCK); - } -#else - PyMutex_Lock(&descr->allocator->allocator_lock); -#endif + acquire_allocator_lock(descr->allocator); return descr->allocator; } @@ -309,42 +335,64 @@ NpyString_acquire_allocator(const PyArray_StringDTypeObject *descr) * ignored. A buffer overflow will happen if the *descrs* array does not * contain n_descriptors elements. * - * If pointers to the same descriptor are passed multiple times, only acquires + * If pointers to the same allocator are passed multiple times, only acquires * the allocator mutex once but sets identical allocator pointers appropriately. + * Distinct allocator mutexes are acquired in ascending allocator address order + * to avoid lock-ordering deadlocks when callers pass different descriptor + * orders. * * The allocator mutexes must be released after this function returns, see * NpyString_release_allocators. * * Note that functions requiring the GIL should not be called while the - * allocator mutex is held, as doing so may cause deadlocks. + * allocator mutex is held and reentrant calls are possible, as doing so may + * cause deadlocks. */ NPY_NO_EXPORT void NpyString_acquire_allocators(size_t n_descriptors, PyArray_Descr *const descrs[], npy_string_allocator *allocators[]) { - for (size_t i=0; iallocator; + } + + uintptr_t last_locked_addr = 0; + int have_last_locked_addr = 0; + + for (;;) { + npy_string_allocator *next_allocator = NULL; + uintptr_t next_addr = UINTPTR_MAX; + + for (size_t i = 0; i < n_descriptors; i++) { + npy_string_allocator *allocator = allocators[i]; + + if (allocator == NULL || allocator_seen(allocator, allocators, i)) { continue; } - if (((PyArray_StringDTypeObject *)descrs[i])->allocator == - ((PyArray_StringDTypeObject *)descrs[j])->allocator) - { - allocators[i] = allocators[j]; - allocators_match = 1; - break; + + uintptr_t addr = (uintptr_t)allocator; + + if (have_last_locked_addr && addr <= last_locked_addr) { + continue; + } + if (next_allocator == NULL || addr < next_addr) { + next_allocator = allocator; + next_addr = addr; } } - if (!allocators_match) { - allocators[i] = NpyString_acquire_allocator( - (PyArray_StringDTypeObject *)descrs[i]); + + if (next_allocator == NULL) { + break; } + + acquire_allocator_lock(next_allocator); + last_locked_addr = next_addr; + have_last_locked_addr = 1; } } diff --git a/numpy/_core/src/multiarray/unique.cpp b/numpy/_core/src/multiarray/unique.cpp index c3028d183555..213ecd441039 100644 --- a/numpy/_core/src/multiarray/unique.cpp +++ b/numpy/_core/src/multiarray/unique.cpp @@ -357,8 +357,8 @@ unique_vstring(PyArrayObject *self, npy_bool equal_nan) { PyArray_StringDTypeObject *descr = reinterpret_cast(PyArray_DESCR(self)); - np::raii::NpyStringAcquireAllocator alloc(descr); np::raii::SaveThreadState save_thread_state{}; + np::raii::NpyStringAcquireAllocator alloc(descr); char *idata = PyArray_BYTES(self); npy_intp istride = PyArray_STRIDES(self)[0]; @@ -387,8 +387,8 @@ unique_vstring(PyArrayObject *self, npy_bool equal_nan) { PyArray_StringDTypeObject *res_descr = reinterpret_cast(PyArray_DESCR(res_obj)); - np::raii::NpyStringAcquireAllocator alloc(res_descr); np::raii::SaveThreadState save_thread_state{}; + np::raii::NpyStringAcquireAllocator alloc(res_descr); char *odata = PyArray_BYTES(res_obj); npy_intp ostride = PyArray_STRIDES(res_obj)[0]; diff --git a/numpy/_core/src/umath/dispatching.cpp b/numpy/_core/src/umath/dispatching.cpp index 340c0bd7f8e2..afe0d911e494 100644 --- a/numpy/_core/src/umath/dispatching.cpp +++ b/numpy/_core/src/umath/dispatching.cpp @@ -735,9 +735,18 @@ legacy_promote_using_legacy_type_resolver(PyUFuncObject *ufunc, * difference. Whether the actual operands can be casts must be checked * during the type resolution step (which may _also_ calls this!). */ - if (ufunc->type_resolver(ufunc, + PyObject *promotion_token = npy_begin_legacy_resolver_promotion(); + if (promotion_token == NULL) { + Py_XDECREF(type_tuple); + return -1; + } + int resolver_result = ufunc->type_resolver(ufunc, NPY_UNSAFE_CASTING, (PyArrayObject **)ops, type_tuple, - out_descrs) < 0) { + out_descrs); + if (npy_end_legacy_resolver_promotion(promotion_token) < 0) { + resolver_result = -1; + } + if (resolver_result < 0) { Py_XDECREF(type_tuple); /* Not all legacy resolvers clean up on failures: */ for (int i = 0; i < nargs; i++) { diff --git a/numpy/_core/src/umath/stringdtype_ufuncs.cpp b/numpy/_core/src/umath/stringdtype_ufuncs.cpp index 79e662cb9f38..64b99644c971 100644 --- a/numpy/_core/src/umath/stringdtype_ufuncs.cpp +++ b/numpy/_core/src/umath/stringdtype_ufuncs.cpp @@ -248,7 +248,7 @@ binary_resolve_descriptors(struct PyArrayMethodObject_tag *NPY_UNUSED(method), { PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0]; PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1]; - int out_coerce = descr1->coerce && descr1->coerce; + int out_coerce = descr1->coerce && descr2->coerce; PyObject *out_na_object = NULL; if (stringdtype_compatible_na( diff --git a/numpy/_core/src/umath/ufunc_object.c b/numpy/_core/src/umath/ufunc_object.c index 88e2cd641d9d..73ce23992b57 100644 --- a/numpy/_core/src/umath/ufunc_object.c +++ b/numpy/_core/src/umath/ufunc_object.c @@ -6423,8 +6423,8 @@ py_resolve_dtypes_generic(PyUFuncObject *ufunc, npy_bool return_context, DTypes[i] = NPY_DTYPE(descr); Py_INCREF(DTypes[i]); } - /* Explicitly allow int, float, and complex for the "weak" types. */ - else if (descr_obj == (PyObject *)&PyLong_Type) { + /* Explicitly allow int, float, and complex for the "weak" types. */ + else if (descr_obj == (PyObject *)&PyLong_Type && i < ufunc -> nin) { descr = PyArray_DescrFromType(NPY_INTP); dummy_arrays[i] = (PyArrayObject *)PyArray_Empty(0, NULL, descr, 0); if (dummy_arrays[i] == NULL) { @@ -6435,7 +6435,7 @@ py_resolve_dtypes_generic(PyUFuncObject *ufunc, npy_bool return_context, DTypes[i] = &PyArray_PyLongDType; promoting_pyscalars = NPY_TRUE; } - else if (descr_obj == (PyObject *)&PyFloat_Type) { + else if (descr_obj == (PyObject *)&PyFloat_Type && i < ufunc -> nin) { descr = PyArray_DescrFromType(NPY_DOUBLE); dummy_arrays[i] = (PyArrayObject *)PyArray_Empty(0, NULL, descr, 0); if (dummy_arrays[i] == NULL) { @@ -6446,7 +6446,7 @@ py_resolve_dtypes_generic(PyUFuncObject *ufunc, npy_bool return_context, DTypes[i] = &PyArray_PyFloatDType; promoting_pyscalars = NPY_TRUE; } - else if (descr_obj == (PyObject *)&PyComplex_Type) { + else if (descr_obj == (PyObject *)&PyComplex_Type && i < ufunc -> nin) { descr = PyArray_DescrFromType(NPY_CDOUBLE); dummy_arrays[i] = (PyArrayObject *)PyArray_Empty(0, NULL, descr, 0); if (dummy_arrays[i] == NULL) { @@ -6465,6 +6465,11 @@ py_resolve_dtypes_generic(PyUFuncObject *ufunc, npy_bool return_context, goto finish; } } + else if (i >= ufunc -> nin) { + PyErr_SetString(PyExc_TypeError, + "Output descriptors must be NumPy dtypes or None."); + goto finish; + } else { PyErr_SetString(PyExc_TypeError, "Provided dtype must be a valid NumPy dtype, " diff --git a/numpy/_core/src/umath/ufunc_type_resolution.c b/numpy/_core/src/umath/ufunc_type_resolution.c index fb90dac1d0ca..7fb02558810b 100644 --- a/numpy/_core/src/umath/ufunc_type_resolution.c +++ b/numpy/_core/src/umath/ufunc_type_resolution.c @@ -38,6 +38,7 @@ #include "numpy/ndarraytypes.h" #include "numpy/ufuncobject.h" #include "npy_import.h" +#include "npy_static_data.h" #include "ufunc_type_resolution.h" #include "ufunc_object.h" #include "common.h" @@ -72,6 +73,51 @@ npy_casting_to_py_object(NPY_CASTING casting) } +/* + * The dispatching code calls the legacy type resolvers a second time to + * promote (`legacy_promote_using_legacy_type_resolver`); that result is + * cached while the type-resolution call happens on every ufunc invocation. + * The promotion call is bracketed with this context variable so each + * invocation warns exactly once, no matter the promotion cache state. + * The warnings machinery cannot deduplicate the two emissions for us: + * under `simplefilter("always")` it delivers everything by design. + */ +NPY_NO_EXPORT PyObject * +npy_begin_legacy_resolver_promotion(void) +{ + return PyContextVar_Set( + npy_static_pydata.legacy_resolver_promoting, Py_True); +} + +NPY_NO_EXPORT int +npy_end_legacy_resolver_promotion(PyObject *token) +{ + int result = PyContextVar_Reset( + npy_static_pydata.legacy_resolver_promoting, token); + Py_DECREF(token); + return result; +} + +static int +deprecate_integer_datetime_operation(void) +{ + PyObject *promoting; + if (PyContextVar_Get(npy_static_pydata.legacy_resolver_promoting, + Py_False, &promoting) < 0) { + return -1; + } + int skip = (promoting == Py_True); + Py_DECREF(promoting); + if (skip) { + return 0; + } + return DEPRECATE( + "The 'generic' unit for NumPy timedelta is deprecated, " + "and will raise an error in the future. " + "Please convert the integer with an explicit unit."); +} + + /** * Always returns -1 to indicate the exception was raised, for convenience */ @@ -818,10 +864,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, /* m8[] + int => m8[] + m8[] */ else if (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2)) { - if (DEPRECATE( - "The 'generic' unit for NumPy timedelta is deprecated, " - "and will raise an error in the future. " - "Please convert the integer with an explicit unit.") < 0) { + if (deprecate_integer_datetime_operation() < 0) { return -1; } out_dtypes[0] = NPY_DT_CALL_ensure_canonical( @@ -861,10 +904,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, /* M8[] + int => M8[] + m8[] */ else if (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2)) { - if (DEPRECATE( - "The 'generic' unit for NumPy timedelta is deprecated, " - "and will raise an error in the future. " - "Please convert the integer with an explicit unit.") < 0) { + if (deprecate_integer_datetime_operation() < 0) { return -1; } out_dtypes[0] = NPY_DT_CALL_ensure_canonical( @@ -892,10 +932,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) { /* int + m8[] => m8[] + m8[] */ if (type_num2 == NPY_TIMEDELTA) { - if (DEPRECATE( - "The 'generic' unit for NumPy timedelta is deprecated, " - "and will raise an error in the future. " - "Please convert the integer with an explicit unit.") < 0) { + if (deprecate_integer_datetime_operation() < 0) { return -1; } out_dtypes[0] = NPY_DT_CALL_ensure_canonical( @@ -911,10 +948,7 @@ PyUFunc_AdditionTypeResolver(PyUFuncObject *ufunc, type_num1 = NPY_TIMEDELTA; } else if (type_num2 == NPY_DATETIME) { - if (DEPRECATE( - "The 'generic' unit for NumPy timedelta is deprecated, " - "and will raise an error in the future. " - "Please convert the integer with an explicit unit.") < 0) { + if (deprecate_integer_datetime_operation() < 0) { return -1; } /* Make a new NPY_TIMEDELTA, and copy type2's metadata */ @@ -1015,10 +1049,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, /* m8[] - int => m8[] - m8[] */ else if (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2)) { - if (DEPRECATE( - "The 'generic' unit for NumPy timedelta is deprecated, " - "and will raise an error in the future. " - "Please convert the integer with an explicit unit.") < 0) { + if (deprecate_integer_datetime_operation() < 0) { return -1; } out_dtypes[0] = NPY_DT_CALL_ensure_canonical( @@ -1058,10 +1089,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, /* M8[] - int => M8[] - m8[] */ else if (PyTypeNum_ISINTEGER(type_num2) || PyTypeNum_ISBOOL(type_num2)) { - if (DEPRECATE( - "The 'generic' unit for NumPy timedelta is deprecated, " - "and will raise an error in the future. " - "Please convert the integer with an explicit unit.") < 0) { + if (deprecate_integer_datetime_operation() < 0) { return -1; } out_dtypes[0] = NPY_DT_CALL_ensure_canonical( @@ -1105,10 +1133,7 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, else if (PyTypeNum_ISINTEGER(type_num1) || PyTypeNum_ISBOOL(type_num1)) { /* int - m8[] => m8[] - m8[] */ if (type_num2 == NPY_TIMEDELTA) { - if (DEPRECATE( - "The 'generic' unit for NumPy timedelta is deprecated, " - "and will raise an error in the future. " - "Please convert the integer with an explicit unit.") < 0) { + if (deprecate_integer_datetime_operation() < 0) { return -1; } out_dtypes[0] = NPY_DT_CALL_ensure_canonical( diff --git a/numpy/_core/src/umath/ufunc_type_resolution.h b/numpy/_core/src/umath/ufunc_type_resolution.h index 531e9267afa2..77cd5165dd39 100644 --- a/numpy/_core/src/umath/ufunc_type_resolution.h +++ b/numpy/_core/src/umath/ufunc_type_resolution.h @@ -5,6 +5,17 @@ extern "C" { #endif +/* + * Bracket legacy type resolver calls made (only) for promotion, so they + * do not emit deprecation warnings. `begin` returns a context variable + * token (or NULL with an error set); `end` consumes the token. + */ +NPY_NO_EXPORT PyObject * +npy_begin_legacy_resolver_promotion(void); + +NPY_NO_EXPORT int +npy_end_legacy_resolver_promotion(PyObject *token); + NPY_NO_EXPORT int PyUFunc_SimpleBinaryComparisonTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, diff --git a/numpy/_core/tests/examples/limited_api/limited_api_opaque.c b/numpy/_core/tests/examples/limited_api/limited_api.c similarity index 77% rename from numpy/_core/tests/examples/limited_api/limited_api_opaque.c rename to numpy/_core/tests/examples/limited_api/limited_api.c index 86a934fd1622..72a8c7476387 100644 --- a/numpy/_core/tests/examples/limited_api/limited_api_opaque.c +++ b/numpy/_core/tests/examples/limited_api/limited_api.c @@ -1,13 +1,28 @@ -#ifndef Py_TARGET_ABI3T -#error "This file must be compiled with -DPy_TARGET_ABI3T" +#ifndef MODULE_NAME +#error "MODULE_NAME must be defined before including this file" +#endif + +#ifndef Py_LIMITED_API +#error "Py_LIMITED_API must be defined before including this file" #endif #include #include #include #include +#include +#include + + +#define MODULE_NAME_STR NPY_TOSTRING(MODULE_NAME) + +#ifdef Py_TARGET_ABI3T +#define ENTRY_FUNC_NAME NPY_CAT(PyModExport_, MODULE_NAME) +#else +#define ENTRY_FUNC_NAME NPY_CAT(PyInit_, MODULE_NAME) +#endif -static PyObject *limited_api_opaque_nonzero(PyObject *mod, PyArrayObject *self) +static PyObject *limited_api_nonzero(PyObject *mod, PyArrayObject *self) { PyArray_NonzeroFunc* nonzero = PyDataType_GetArrFuncs(PyArray_DESCR(self))->nonzero; @@ -91,7 +106,7 @@ static PyObject *limited_api_opaque_nonzero(PyObject *mod, PyArrayObject *self) * legacy iterator macros. */ static PyObject * -limited_api_opaque_iter_next(PyObject *mod, PyArrayObject *self) +limited_api_iter_next(PyObject *mod, PyArrayObject *self) { PyObject *iter_obj = PyArray_IterNew((PyObject *)self); if (iter_obj == NULL) { @@ -110,7 +125,7 @@ limited_api_opaque_iter_next(PyObject *mod, PyArrayObject *self) * Test PyArray_ITER_GOTO1D by accessing a specific flat index. */ static PyObject * -limited_api_opaque_iter_goto1d(PyObject *mod, PyObject *args) +limited_api_iter_goto1d(PyObject *mod, PyObject *args) { PyArrayObject *arr; npy_intp index; @@ -132,7 +147,7 @@ limited_api_opaque_iter_goto1d(PyObject *mod, PyObject *args) * Returns the sum from the second pass (should equal the first). */ static PyObject * -limited_api_opaque_iter_reset(PyObject *mod, PyArrayObject *self) +limited_api_iter_reset(PyObject *mod, PyArrayObject *self) { PyObject *iter_obj = PyArray_IterNew((PyObject *)self); if (iter_obj == NULL) { @@ -160,7 +175,7 @@ limited_api_opaque_iter_reset(PyObject *mod, PyArrayObject *self) * Returns the total sum of (a + b) for all broadcast elements. */ static PyObject * -limited_api_opaque_multi_iter_next(PyObject *mod, PyObject *args) +limited_api_multi_iter_next(PyObject *mod, PyObject *args) { PyArrayObject *a, *b; if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &a, @@ -200,7 +215,7 @@ limited_api_opaque_multi_iter_next(PyObject *mod, PyObject *args) * Test PyArray_ITER_GOTO by jumping to a coordinate and reading the value. */ static PyObject * -limited_api_opaque_iter_goto(PyObject *mod, PyObject *args) +limited_api_iter_goto(PyObject *mod, PyObject *args) { PyArrayObject *arr; PyObject *coord_tuple; @@ -235,7 +250,7 @@ limited_api_opaque_iter_goto(PyObject *mod, PyObject *args) * and returning (a_val, b_val) at that position. */ static PyObject * -limited_api_opaque_multi_iter_goto(PyObject *mod, PyObject *args) +limited_api_multi_iter_goto(PyObject *mod, PyObject *args) { PyArrayObject *a, *b; PyObject *coord_tuple; @@ -274,7 +289,7 @@ limited_api_opaque_multi_iter_goto(PyObject *mod, PyObject *args) * and returning (a_val, b_val) at that position. */ static PyObject * -limited_api_opaque_multi_iter_goto1d(PyObject *mod, PyObject *args) +limited_api_multi_iter_goto1d(PyObject *mod, PyObject *args) { PyArrayObject *a, *b; npy_intp index; @@ -298,7 +313,7 @@ limited_api_opaque_multi_iter_goto1d(PyObject *mod, PyObject *args) * and returning its data pointer value after N steps. */ static PyObject * -limited_api_opaque_multi_iter_nexti(PyObject *mod, PyObject *args) +limited_api_multi_iter_nexti(PyObject *mod, PyObject *args) { PyArrayObject *a, *b; int steps; @@ -318,46 +333,87 @@ limited_api_opaque_multi_iter_nexti(PyObject *mod, PyObject *args) return PyFloat_FromDouble(val); } -static PyMethodDef limited_api_opaque_methods[] = { - {"nonzero", (PyCFunction)limited_api_opaque_nonzero, METH_O, +static PyObject * +limited_api_datetime_metadata(PyObject *mod, PyArrayObject *arr) +{ + PyArray_Descr *descr = PyArray_DESCR(arr); + npy_uint64 flags = PyDataType_FLAGS(descr); + PyArray_DatetimeDTypeMetaData *dt_meta = + (PyArray_DatetimeDTypeMetaData *)PyDataType_C_METADATA(descr); + if (dt_meta == NULL) { + PyErr_SetString( + PyExc_RuntimeError, + "PyDataType_C_METADATA() returned NULL for a datetime descriptor"); + return NULL; + } + return Py_BuildValue("Kii", (unsigned long long)flags, + (int)dt_meta->meta.base, dt_meta->meta.num); +} + +static PyMethodDef limited_api_methods[] = { + {"nonzero", (PyCFunction)limited_api_nonzero, METH_O, "Count the number of non-zero elements in the array."}, - {"iter_next", (PyCFunction)limited_api_opaque_iter_next, METH_O, + {"iter_next", (PyCFunction)limited_api_iter_next, METH_O, "Sum array elements using PyArray_ITER_NEXT."}, - {"iter_goto1d", (PyCFunction)limited_api_opaque_iter_goto1d, METH_VARARGS, + {"iter_goto1d", (PyCFunction)limited_api_iter_goto1d, METH_VARARGS, "Get element at flat index using PyArray_ITER_GOTO1D."}, - {"iter_reset", (PyCFunction)limited_api_opaque_iter_reset, METH_O, + {"iter_reset", (PyCFunction)limited_api_iter_reset, METH_O, "Sum array elements after reset using PyArray_ITER_RESET."}, - {"multi_iter_next", (PyCFunction)limited_api_opaque_multi_iter_next, + {"multi_iter_next", (PyCFunction)limited_api_multi_iter_next, METH_VARARGS, "Sum broadcast (a+b) using PyArray_MultiIter_NEXT."}, - {"iter_goto", (PyCFunction)limited_api_opaque_iter_goto, METH_VARARGS, + {"iter_goto", (PyCFunction)limited_api_iter_goto, METH_VARARGS, "Get element at coordinate using PyArray_ITER_GOTO."}, - {"multi_iter_goto", (PyCFunction)limited_api_opaque_multi_iter_goto, + {"multi_iter_goto", (PyCFunction)limited_api_multi_iter_goto, METH_VARARGS, "Get (a, b) at coordinate using PyArray_MultiIter_GOTO."}, - {"multi_iter_goto1d", (PyCFunction)limited_api_opaque_multi_iter_goto1d, + {"multi_iter_goto1d", (PyCFunction)limited_api_multi_iter_goto1d, METH_VARARGS, "Get (a, b) at flat index using PyArray_MultiIter_GOTO1D."}, - {"multi_iter_nexti", (PyCFunction)limited_api_opaque_multi_iter_nexti, + {"multi_iter_nexti", (PyCFunction)limited_api_multi_iter_nexti, METH_VARARGS, "Advance only iter 0 N steps using PyArray_MultiIter_NEXTi."}, + {"datetime_metadata", (PyCFunction)limited_api_datetime_metadata, + METH_O, + "Get (flags, unit base, unit num) from a datetime64/timedelta64 array."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; +#ifdef Py_TARGET_ABI3T + PyABIInfo_VAR(abi_info); -static PySlot limited_api_opaque_slots[] = { +static PySlot limited_api_slots[] = { PySlot_STATIC_DATA(Py_mod_abi, &abi_info), - PySlot_STATIC_DATA(Py_mod_name, "limited_api_opaque"), - PySlot_STATIC_DATA(Py_mod_methods, limited_api_opaque_methods), + PySlot_STATIC_DATA(Py_mod_name, MODULE_NAME_STR), + PySlot_STATIC_DATA(Py_mod_methods, limited_api_methods), PySlot_STATIC_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), PySlot_END, }; PyMODEXPORT_FUNC -PyModExport_limited_api_opaque(void) +ENTRY_FUNC_NAME(void) { import_array(); import_umath(); - return limited_api_opaque_slots; + return limited_api_slots; } + +#else + +static PyModuleDef moduledef = { + .m_base = PyModuleDef_HEAD_INIT, + .m_name = MODULE_NAME_STR, + .m_size = -1, + .m_methods = limited_api_methods, +}; + +PyMODINIT_FUNC +ENTRY_FUNC_NAME(void) +{ + import_array(); + import_umath(); + return PyModule_Create(&moduledef); +} + +#endif diff --git a/numpy/_core/tests/examples/limited_api/limited_api1.c b/numpy/_core/tests/examples/limited_api/limited_api1.c deleted file mode 100644 index 115a3f3a6835..000000000000 --- a/numpy/_core/tests/examples/limited_api/limited_api1.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include - -static PyModuleDef moduledef = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "limited_api1" -}; - -PyMODINIT_FUNC PyInit_limited_api1(void) -{ - import_array(); - import_umath(); - return PyModule_Create(&moduledef); -} diff --git a/numpy/_core/tests/examples/limited_api/limited_api2.pyx b/numpy/_core/tests/examples/limited_api/limited_api2.pyx deleted file mode 100644 index 327d5b038c6c..000000000000 --- a/numpy/_core/tests/examples/limited_api/limited_api2.pyx +++ /dev/null @@ -1,11 +0,0 @@ -#cython: language_level=3 - -""" -Make sure cython can compile in limited API mode (see meson.build) -""" - -cdef extern from "numpy/arrayobject.h": - pass -cdef extern from "numpy/arrayscalars.h": - pass - diff --git a/numpy/_core/tests/examples/limited_api/limited_api_cython.pyx b/numpy/_core/tests/examples/limited_api/limited_api_cython.pyx new file mode 100644 index 000000000000..740dbef03214 --- /dev/null +++ b/numpy/_core/tests/examples/limited_api/limited_api_cython.pyx @@ -0,0 +1,155 @@ +#cython: language_level=3 + +""" +Make sure cython can compile using the NumPy C-API in limited API mode +(see meson.build). + +""" + +cimport numpy as cnp + +cnp.import_array() + + +def nonzero(cnp.ndarray arr): + """Count non-zero elements of a float64 array using the NpyIter API.""" + cdef cnp.NpyIter *it + cdef cnp.NpyIter_IterNextFunc iternext + cdef char **dataptr + cdef cnp.npy_intp *strideptr + cdef cnp.npy_intp *innersizeptr + cdef cnp.npy_intp nonzero_count = 0 + cdef cnp.npy_intp stride, count + cdef char *data + + if cnp.PyArray_SIZE(arr) == 0: + return 0 + + it = cnp.NpyIter_New( + arr, + cnp.NPY_ITER_READONLY | cnp.NPY_ITER_EXTERNAL_LOOP | cnp.NPY_ITER_REFS_OK, + cnp.NPY_KEEPORDER, cnp.NPY_NO_CASTING, NULL) + if it == NULL: + raise RuntimeError("NpyIter_New failed") + iternext = cnp.NpyIter_GetIterNext(it, NULL) + if iternext == NULL: + cnp.NpyIter_Deallocate(it) + raise RuntimeError("NpyIter_GetIterNext failed") + dataptr = cnp.NpyIter_GetDataPtrArray(it) + strideptr = cnp.NpyIter_GetInnerStrideArray(it) + innersizeptr = cnp.NpyIter_GetInnerLoopSizePtr(it) + + while True: + data = dataptr[0] + stride = strideptr[0] + count = innersizeptr[0] + while count > 0: + if (data)[0] != 0.0: + nonzero_count += 1 + data += stride + count -= 1 + if not iternext(it): + break + + cnp.NpyIter_Deallocate(it) + return nonzero_count + + +def iter_next(cnp.ndarray arr): + """Sum float64 array elements using PyArray_ITER_NEXT / _DATA / _NOTDONE.""" + cdef cnp.flatiter it = cnp.PyArray_IterNew(arr) + cdef double total = 0.0 + while cnp.PyArray_ITER_NOTDONE(it): + total += (cnp.PyArray_ITER_DATA(it))[0] + cnp.PyArray_ITER_NEXT(it) + return total + + +def iter_goto1d(cnp.ndarray arr, cnp.npy_intp index): + """Get element at a flat index using PyArray_ITER_GOTO1D.""" + cdef cnp.flatiter it = cnp.PyArray_IterNew(arr) + cnp.PyArray_ITER_GOTO1D(it, index) + return (cnp.PyArray_ITER_DATA(it))[0] + + +def iter_reset(cnp.ndarray arr): + """Sum float64 array elements after PyArray_ITER_RESET.""" + cdef cnp.flatiter it = cnp.PyArray_IterNew(arr) + cdef double total = 0.0 + while cnp.PyArray_ITER_NOTDONE(it): + cnp.PyArray_ITER_NEXT(it) + cnp.PyArray_ITER_RESET(it) + while cnp.PyArray_ITER_NOTDONE(it): + total += (cnp.PyArray_ITER_DATA(it))[0] + cnp.PyArray_ITER_NEXT(it) + return total + + +def iter_goto(cnp.ndarray arr, tuple coord): + """Get element at a coordinate using PyArray_ITER_GOTO.""" + cdef int nd = cnp.PyArray_NDIM(arr) + cdef cnp.npy_intp destination[32] + cdef cnp.flatiter it + cdef int i + if len(coord) != nd: + raise ValueError("coordinate length mismatch") + for i in range(nd): + destination[i] = coord[i] + it = cnp.PyArray_IterNew(arr) + cnp.PyArray_ITER_GOTO(it, destination) + return (cnp.PyArray_ITER_DATA(it))[0] + + +def multi_iter_next(cnp.ndarray a, cnp.ndarray b): + """Sum broadcast (a + b) using PyArray_MultiIter_NEXT / _DATA.""" + cdef cnp.broadcast multi = cnp.PyArray_MultiIterNew2(a, b) + cdef double total = 0.0 + while cnp.PyArray_MultiIter_NOTDONE(multi): + total += (cnp.PyArray_MultiIter_DATA(multi, 0))[0] + total += (cnp.PyArray_MultiIter_DATA(multi, 1))[0] + cnp.PyArray_MultiIter_NEXT(multi) + return total + + +def multi_iter_goto1d(cnp.ndarray a, cnp.ndarray b, cnp.npy_intp index): + """Get (a, b) at a flat index using PyArray_MultiIter_GOTO1D.""" + cdef cnp.broadcast multi = cnp.PyArray_MultiIterNew2(a, b) + cdef double va, vb + cnp.PyArray_MultiIter_GOTO1D(multi, index) + va = (cnp.PyArray_MultiIter_DATA(multi, 0))[0] + vb = (cnp.PyArray_MultiIter_DATA(multi, 1))[0] + return va, vb + + +def multi_iter_nexti(cnp.ndarray a, cnp.ndarray b, int steps): + """Advance only iterator 0 by N steps using PyArray_MultiIter_NEXTi.""" + cdef cnp.broadcast multi = cnp.PyArray_MultiIterNew2(a, b) + cdef int _i + for _i in range(steps): + cnp.PyArray_MultiIter_NEXTi(multi, 0) + return (cnp.PyArray_MultiIter_DATA(multi, 0))[0] + + +def get_datetime_value(obj): + """Underlying int64 of a datetime64 scalar via get_datetime64_value.""" + return cnp.get_datetime64_value(obj) + + +def get_timedelta_value(obj): + """Underlying int64 of a timedelta64 scalar via get_timedelta64_value.""" + return cnp.get_timedelta64_value(obj) + + +def get_datetime_unit(obj): + """Unit (NPY_DATETIMEUNIT base) of a datetime64 scalar.""" + return cnp.get_datetime64_unit(obj) + + +def is_datetime64(obj): + """Cython is_datetime64_object (isinstance-like check).""" + return cnp.is_datetime64_object(obj) + + +def is_timedelta64(obj): + """Cython is_timedelta64_object (isinstance-like check).""" + return cnp.is_timedelta64_object(obj) diff --git a/numpy/_core/tests/examples/limited_api/limited_api_latest.c b/numpy/_core/tests/examples/limited_api/limited_api_latest.c deleted file mode 100644 index 92d83ea977a1..000000000000 --- a/numpy/_core/tests/examples/limited_api/limited_api_latest.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#include - -#if Py_LIMITED_API != PY_VERSION_HEX & 0xffff0000 - # error "Py_LIMITED_API not defined to Python major+minor version" -#endif - -static PyModuleDef moduledef = { - .m_base = PyModuleDef_HEAD_INIT, - .m_name = "limited_api_latest" -}; - -PyMODINIT_FUNC PyInit_limited_api_latest(void) -{ - import_array(); - import_umath(); - return PyModule_Create(&moduledef); -} diff --git a/numpy/_core/tests/examples/limited_api/meson.build b/numpy/_core/tests/examples/limited_api/meson.build index d657af274e08..2973fd3ab629 100644 --- a/numpy/_core/tests/examples/limited_api/meson.build +++ b/numpy/_core/tests/examples/limited_api/meson.build @@ -28,40 +28,52 @@ npy_path = run_command(py, [ # where cython may not find the right __init__.pyd file. add_project_arguments('-I', npy_path, language : 'cython') -if py.get_variable('Py_GIL_DISABLED', 0) == 0 - py.extension_module( - 'limited_api1', - 'limited_api1.c', - c_args: [ - '-DNPY_NO_DEPRECATED_API=NPY_1_21_API_VERSION', - ], - include_directories: [npy_include_path], - limited_api: '3.9', - ) - - py.extension_module( - 'limited_api_latest', - 'limited_api_latest.c', - c_args: [ - '-DNPY_NO_DEPRECATED_API=NPY_1_21_API_VERSION', - ], - include_directories: [npy_include_path], - limited_api: py.language_version(), - ) +# Build limited API extension modules for all supported Python and NumPy versions +# The _PY_ABI3_VERSIONS and _NPY_TARGET_VERSIONS lists should be kept in sync +# with the lists defined in test_limited_api.py, and the test should be updated if new versions are added here. +# The special 'default' entry builds without defining NPY_TARGET_VERSION at all, +# which exercises the path where numpyconfig.h picks the current API version. +_PY_ABI3_VERSIONS = ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.15'] +_NPY_TARGET_VERSIONS = ['2.0', '2.1', '2.2', '2.3', '2.4', '2.5', 'default'] - py.extension_module( - 'limited_api2', - 'limited_api2.pyx', - install: false, - c_args: [ - '-DNPY_NO_DEPRECATED_API=0', - # Require 1.25+ to test datetime additions - '-DNPY_TARGET_VERSION=NPY_2_0_API_VERSION', - '-DCYTHON_LIMITED_API=1', - ], - include_directories: [npy_include_path], - limited_api: '3.9', -) +if py.get_variable('Py_GIL_DISABLED', 0) == 0 + foreach py_ver : _PY_ABI3_VERSIONS + # skip abi3 version higher than the current interpreter version + if py.language_version().version_compare('>=' + py_ver) + foreach npy_ver : _NPY_TARGET_VERSIONS + # 'default' leaves NPY_TARGET_VERSION undefined + if npy_ver == 'default' + npy_target_args = [] + else + npy_macro = 'NPY_' + npy_ver.replace('.', '_') + '_API_VERSION' + npy_target_args = ['-DNPY_TARGET_VERSION=' + npy_macro] + endif + modname = 'limited_api_' + py_ver.replace('.', '_') + '_npy' + npy_ver.replace('.', '_') + py.extension_module( + modname, + 'limited_api.c', + c_args: [ + '-DNPY_NO_DEPRECATED_API=NPY_1_21_API_VERSION', + '-DMODULE_NAME=' + modname, + ] + npy_target_args, + include_directories: [npy_include_path], + limited_api: py_ver, + ) + modname = 'limited_api_cython_' + py_ver.replace('.', '_') + '_npy' + npy_ver.replace('.', '_') + py.extension_module( + modname, + 'limited_api_cython.pyx', + install: false, + cython_args: ['--module-name', modname], + c_args: [ + '-DNPY_NO_DEPRECATED_API=NPY_1_21_API_VERSION', + ] + npy_target_args, + include_directories: [npy_include_path], + limited_api: py_ver, + ) + endforeach + endif + endforeach endif if ( @@ -70,8 +82,9 @@ if ( ) py.extension_module( 'limited_api_opaque', - 'limited_api_opaque.c', + 'limited_api.c', c_args: [ + '-DMODULE_NAME=limited_api_opaque', '-DNPY_TARGET_VERSION=NPY_2_5_API_VERSION', '-DPy_TARGET_ABI3T=0x030F0000', ], diff --git a/numpy/_core/tests/test_cython.py b/numpy/_core/tests/test_cython.py index c5752253409a..e4269efb2a89 100644 --- a/numpy/_core/tests/test_cython.py +++ b/numpy/_core/tests/test_cython.py @@ -21,7 +21,7 @@ from numpy._utils import _pep440 # Note: keep in sync with the one in pyproject.toml - required_version = "3.0.6" + required_version = "3.1.0" if _pep440.parse(cython_version) < _pep440.Version(required_version): # too old or wrong cython, skip the test cython = None diff --git a/numpy/_core/tests/test_datetime.py b/numpy/_core/tests/test_datetime.py index 16e0265e6afa..ab94706a1cf0 100644 --- a/numpy/_core/tests/test_datetime.py +++ b/numpy/_core/tests/test_datetime.py @@ -1265,6 +1265,16 @@ def test_arithmetic_valid_boundary(self): assert small * np.int64(7) == np.timedelta64(21, "s") assert np.int64(7) * small == np.timedelta64(21, "s") + def test_datetime64_item_int64_min_edge_case(self): + info = np.iinfo(np.int64) + + for offset in [1, 2, 100, 1000, 10000]: + dt64 = np.datetime64(info.min + offset, "D") + result = dt64.item() + # expected integer value + assert result == info.min + offset + assert isinstance(result, int) + def test_pyobject_roundtrip(self): # All datetime types should be able to roundtrip through object a = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/numpy/_core/tests/test_deprecations.py b/numpy/_core/tests/test_deprecations.py index 70eedde7003a..c9d1561d46c9 100644 --- a/numpy/_core/tests/test_deprecations.py +++ b/numpy/_core/tests/test_deprecations.py @@ -5,6 +5,8 @@ """ import contextlib import re +import sys +import textwrap import warnings from collections.abc import Callable @@ -12,7 +14,8 @@ import numpy as np from numpy._core._multiarray_tests import fromstring_null_term_c_api # noqa: F401 -from numpy.testing import assert_raises +from numpy.testing import IS_WASM, assert_raises +from numpy.testing._private.utils import run_subprocess class _DeprecationTestCase: @@ -519,6 +522,26 @@ def test_non_associative_case_warns(self): self.assert_deprecated(np.add, num=None, args=(a, b)) self.assert_deprecated(np.add, num=None, args=(b, c)) + @pytest.mark.skipif(IS_WASM, reason="wasm doesn't have support for subprocess") + def test_first_call_warns_once(self): + # The legacy type resolver used to also warn when invoked to populate + # the ufunc promotion cache, so the first call in a process emitted + # two warnings and later calls one. Use a fresh process to test with + # a cold cache. + code = textwrap.dedent(""" + import warnings + import numpy as np + + for args in [(np.timedelta64(3, "s"), 5), + (np.array([3], dtype="m8[s]"), np.array([5]))]: + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + np.subtract(*args) + assert len(w) == 1, [str(x.message) for x in w] + """) + p = run_subprocess([sys.executable, "-c", code]) + assert p.returncode == 0, p.stderr + class TestTriDeprecationWithNonInteger(_DeprecationTestCase): # Deprecation in NumPy 2.5, 2026-03 diff --git a/numpy/_core/tests/test_function_base.py b/numpy/_core/tests/test_function_base.py index b78c79a6f032..7ea1b7a4876c 100644 --- a/numpy/_core/tests/test_function_base.py +++ b/numpy/_core/tests/test_function_base.py @@ -1,5 +1,6 @@ import platform import sys +import warnings import pytest @@ -21,6 +22,7 @@ from numpy._core import sctypes from numpy._core.function_base import add_newdoc from numpy.testing import ( + IS_WASM, assert_, assert_allclose, assert_array_equal, @@ -482,6 +484,58 @@ def test_any_step_zero_and_not_mult_inplace(self): y = linspace(start, stop, 3) assert_array_equal(y, array([[0.0, 1.0], [1.0, 1.0], [2.0, 1.0]])) + def test_inf_equal_endpoints(self): + # Regression test for gh-26699, equal infinite endpoint were returning NaN. + # Equal endpoints skip the `inf - inf` subtraction entirely, so no + # spurious "invalid value" RuntimeWarning must be emitted (the fix does + # not rely on suppressing warnings via errstate). + with warnings.catch_warnings(): + warnings.simplefilter("error", RuntimeWarning) + for inf in [np.inf, -np.inf]: + assert_array_equal(linspace(inf, inf, 0), array([])) + assert_array_equal(linspace(inf, inf, 1), array([inf])) + for num in [2, 5, 50]: + assert_array_equal(linspace(inf, inf, num), np.full(num, inf)) + + assert_array_equal(linspace(np.inf, np.inf, 5, endpoint=False), + np.full(5, np.inf)) + + y, step = linspace(np.inf, np.inf, 2, retstep=True) + assert_array_equal(y, array([np.inf, np.inf])) + assert_equal(step, 0.0) + + start = array([np.inf, 1.0]) + stop = array([np.inf, 3.0]) + result = linspace(start, stop, 3) + assert_array_equal(result, + array([[np.inf, 1.0], [np.inf, 2.0], + [np.inf, 3.0]])) + + # Complex infinities + for cinf in [complex(np.inf, 0), complex(0, np.inf), + complex(np.inf, np.inf), complex(-np.inf, -np.inf)]: + assert_array_equal(linspace(cinf, cinf, 3), np.full(3, cinf)) + + @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm") + def test_inf_mixed_endpoints_warns(self): + # Mixed infinite endpoints produce NaN interior points and should warn. + cases = [(np.inf, -np.inf, -np.inf), (-np.inf, np.inf, np.inf)] + for num in [3, 5]: + for start, stop, expected_stop in cases: + with pytest.warns(RuntimeWarning, match="invalid value"): + mixed = linspace(start, stop, num) + assert_(isnan(mixed[:-1]).all()) + assert_equal(mixed[-1], expected_stop) + + def test_nan_endpoints(self): + # NaN inputs: behavior unchanged by gh-26699 fix (nan != nan so no + # replacement occurs; delta stays nan and propagates normally). + assert_(isnan(linspace(np.nan, np.nan, 5)).all()) + assert_(isnan(linspace(np.nan, np.nan, 1)).all()) + result = linspace(np.nan, 1.0, 5) + assert_(isnan(result[:-1]).all()) + assert_equal(result[-1], 1.0) + class TestAdd_newdoc: diff --git a/numpy/_core/tests/test_limited_api.py b/numpy/_core/tests/test_limited_api.py index c052f34c1ba4..07d2bab7341d 100644 --- a/numpy/_core/tests/test_limited_api.py +++ b/numpy/_core/tests/test_limited_api.py @@ -1,3 +1,4 @@ +import importlib import os import shutil import subprocess @@ -6,7 +7,8 @@ import pytest -from numpy.testing import IS_EDITABLE, IS_WASM, NOGIL_BUILD +import numpy as np +from numpy.testing import IS_64BIT, IS_EDITABLE, IS_WASM, NOGIL_BUILD from numpy.testing._private.utils import run_subprocess # This import is copied from random.tests.test_extending @@ -19,7 +21,7 @@ from numpy._utils import _pep440 # Note: keep in sync with the one in pyproject.toml - required_version = "3.0.6" + required_version = "3.1.0" if _pep440.parse(cython_version) < _pep440.Version(required_version): # too old or wrong cython, skip the test cython = None @@ -82,6 +84,125 @@ def install_temp(tmpdir_factory): sys.path.append(str(build_dir)) +def _check_api_module(mod, cython=False): + arr = np.ones((200, 200)) + assert mod.nonzero(arr) == 200 * 200 + + # Legacy single-array iterator: PyArray_ITER_NEXT / _DATA / _NOTDONE. + arr = np.arange(12.0).reshape(3, 4) + assert mod.iter_next(arr) == 66.0 + assert mod.iter_goto1d(arr, 5) == 5.0 + assert mod.iter_goto1d(arr, -1) == 11.0 + assert mod.iter_reset(arr) == 66.0 + assert mod.iter_goto(arr, (1, 2)) == 6.0 + assert mod.iter_goto(arr, (2, 3)) == 11.0 + + # Broadcasting multi-iterator. + a = np.arange(3.0).reshape(3, 1) + b = np.arange(4.0).reshape(1, 4) + assert mod.multi_iter_next(a, b) == float(np.sum(a + b)) + if not cython: + va, vb = mod.multi_iter_goto(a, b, (1, 2)) + assert va == 1.0 and vb == 2.0 + va, vb = mod.multi_iter_goto1d(a, b, 6) + assert va == 1.0 and vb == 2.0 + + a = np.arange(6.0).reshape(2, 3) + b = np.zeros((2, 3)) + assert mod.multi_iter_nexti(a, b, 3) == 3.0 + + if cython: + # Datetime / timedelta scalar accessors (.pxd helpers). + dt = np.datetime64("2021-01-01", "D") + assert mod.get_datetime_value(dt) == dt.astype("int64") + assert mod.get_datetime_unit(dt) == 4 + assert mod.is_datetime64(dt) + assert not mod.is_timedelta64(dt) + + # A plain seconds timedelta: base NPY_FR_s, unit multiplier 1. + td = np.timedelta64(5, "s") + assert mod.get_timedelta_value(td) == 5 + assert mod.is_timedelta64(td) + assert not mod.is_datetime64(td) + + # A non-unit multiplier exercises the metadata `num` field. + td = np.timedelta64(1000, "ms").astype("timedelta64[10ms]") + assert mod.get_timedelta_value(td) == td.astype("int64") + else: + # PyDataType_FLAGS / PyDataType_C_METADATA on datetime descriptors. + dt = np.array(["2021-01-01"], dtype="datetime64[D]") + flags, base, num = mod.datetime_metadata(dt) + assert flags == dt.dtype.flags + assert base == 4 # NPY_FR_D + assert num == 1 + + # A plain seconds timedelta: base NPY_FR_s, unit multiplier 1. + td = np.array([5], dtype="timedelta64[s]") + flags, base, num = mod.datetime_metadata(td) + assert flags == td.dtype.flags + assert base == 7 # NPY_FR_s + assert num == 1 + + # A non-unit multiplier exercises the metadata `num` field. + td = np.array([1000], dtype="timedelta64[ms]").astype( + "timedelta64[10ms]") + flags, base, num = mod.datetime_metadata(td) + assert flags == td.dtype.flags + assert base == 8 # NPY_FR_ms + assert num == 10 + + # Non-datetime descriptors have no c_metadata and are rejected. + with pytest.raises(RuntimeError): + mod.datetime_metadata(np.arange(3)) + + +# Test limited API extension modules for all supported Python and NumPy versions +# The _PY_ABI3_VERSIONS and _NPY_TARGET_VERSIONS lists should be kept in sync +# with the lists defined in meson.build, and the test should be updated +# if new versions are added here. +# The special "default" entry builds without defining NPY_TARGET_VERSION at all, +# which exercises the path where numpyconfig.h picks the current API version. +_PY_ABI3_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] +_NPY_TARGET_VERSIONS = ["2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "default"] + +def _module_names(prefix, abi3_versions): + names = [] + for py_ver in abi3_versions: + if sys.version_info < tuple(map(int, py_ver.split('.'))): + continue + py = py_ver.replace('.', '_') + for npy_ver in _NPY_TARGET_VERSIONS: + npy = npy_ver.replace('.', '_') + names.append(f"{prefix}_{py}_npy{npy}") + return names + + +def limited_api_module_names(): + return _module_names("limited_api", _PY_ABI3_VERSIONS) + + +def limited_api_cython_module_names(): + return _module_names("limited_api_cython", _PY_ABI3_VERSIONS) + + +@pytest.mark.skipif(IS_WASM, reason="Can't start subprocess") +@pytest.mark.xfail( + sysconfig.get_config_var("Py_DEBUG"), + reason=( + "Py_LIMITED_API is incompatible with Py_DEBUG, Py_TRACE_REFS, " + "and Py_REF_DEBUG" + ), +) +@pytest.mark.xfail( + NOGIL_BUILD, + reason="Py_GIL_DISABLED builds do not support abi3", +) +@pytest.mark.parametrize("module_name", limited_api_module_names()) +def test_limited_api_abi3(install_temp, module_name): + mod = importlib.import_module(module_name) + _check_api_module(mod) + + @pytest.mark.skipif(IS_WASM, reason="Can't start subprocess") @pytest.mark.xfail( sysconfig.get_config_var("Py_DEBUG"), @@ -92,20 +213,20 @@ def install_temp(tmpdir_factory): ) @pytest.mark.xfail( NOGIL_BUILD, - reason="Py_GIL_DISABLED builds do not currently support abi3", + reason="Py_GIL_DISABLED builds do not support abi3", ) -def test_limited_api_abi3(install_temp): - """Test building a third-party C extension with the limited API - and building a cython extension with the limited API - """ +@pytest.mark.parametrize("module_name", limited_api_cython_module_names()) +def test_limited_api_cython(install_temp, module_name): + mod = importlib.import_module(module_name) + _check_api_module(mod, cython=True) - import limited_api1 # Earliest (3.6) # noqa: F401 - import limited_api2 # cython # noqa: F401 - import limited_api_latest # Latest version (current Python) # noqa: F401 @pytest.mark.skipif( sys.version_info < (3, 15), reason="opaque PyObject requires Python 3.15+" ) +@pytest.mark.skipif( + not IS_64BIT, reason="opaque abi3t extensions are broken on 32-bit" +) @pytest.mark.skipif( sys.platform == "win32" and not sysconfig.get_config_var('Py_GIL_DISABLED'), reason=("Meson does not yet support building abi3t extensions on the " @@ -114,46 +235,4 @@ def test_limited_api_abi3(install_temp): def test_limited_opaque(install_temp): import limited_api_opaque - import numpy as np - arr = np.ones((200, 200)) - assert limited_api_opaque.nonzero(arr) == 200 * 200 - - # Test PyArray_ITER_NEXT / PyArray_ITER_DATA / PyArray_ITER_NOTDONE - arr = np.arange(12.0).reshape(3, 4) - assert limited_api_opaque.iter_next(arr) == 66.0 - - # Test PyArray_ITER_GOTO1D - assert limited_api_opaque.iter_goto1d(arr, 5) == 5.0 - assert limited_api_opaque.iter_goto1d(arr, -1) == 11.0 - - # Test PyArray_ITER_RESET - assert limited_api_opaque.iter_reset(arr) == 66.0 - - # Test PyArray_MultiIter_NEXT / RESET / DATA with broadcasting - a = np.arange(3.0).reshape(3, 1) # shape (3, 1) - b = np.arange(4.0).reshape(1, 4) # shape (1, 4) - # Each broadcast element is a[i] + b[j], total sum: - expected = float(np.sum(a + b)) - assert limited_api_opaque.multi_iter_next(a, b) == expected - - # Test PyArray_ITER_GOTO - arr = np.arange(12.0).reshape(3, 4) - assert limited_api_opaque.iter_goto(arr, (1, 2)) == 6.0 - assert limited_api_opaque.iter_goto(arr, (2, 3)) == 11.0 - - # Test PyArray_MultiIter_GOTO - a = np.arange(3.0).reshape(3, 1) - b = np.arange(4.0).reshape(1, 4) - va, vb = limited_api_opaque.multi_iter_goto(a, b, (1, 2)) - assert va == 1.0 and vb == 2.0 - - # Test PyArray_MultiIter_GOTO1D - # flat index 6 in (3,4) broadcast → row 1, col 2 - va, vb = limited_api_opaque.multi_iter_goto1d(a, b, 6) - assert va == 1.0 and vb == 2.0 - - # Test PyArray_MultiIter_NEXTi - a = np.arange(6.0).reshape(2, 3) - b = np.zeros((2, 3)) - # Advance iter 0 by 3 steps → flat index 3 → value 3.0 - assert limited_api_opaque.multi_iter_nexti(a, b, 3) == 3.0 + _check_api_module(limited_api_opaque) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index 6c2c7d214cf2..1866d4efdc10 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -1,4 +1,8 @@ import concurrent.futures +import inspect +import subprocess +import sys +import textwrap import threading import pytest @@ -7,7 +11,7 @@ from numpy._core import _rational_tests from numpy._core.tests.test_stringdtype import random_unicode_string_list from numpy.testing import IS_64BIT, IS_WASM -from numpy.testing._private.utils import run_threaded +from numpy.testing._private.utils import run_subprocess, run_threaded if IS_WASM: pytest.skip(allow_module_level=True, reason="no threading support in wasm") @@ -474,3 +478,226 @@ def func(b): x.__buffer__(flags[i % 2]) run_threaded(func, max_workers=8, pass_barrier=True) + + +def assert_no_deadlock(workload, *, args=(), helpers=(), timeout=30, + reason="deadlock"): + """Run ``workload`` in a fresh subprocess; fail if it does not finish in time. + + ``workload`` is a self-contained function: its source is extracted with + ``inspect.getsource`` and executed in a clean interpreter. It must do its + own imports and may only reference arguments and helpers explicitly passed + to this function. + + For regression tests whose failure mode is a hang. Delegates to + ``run_subprocess`` (which folds the child's output into the failure on a + nonzero exit); this only adds the timeout watchdog, reporting a likely + ``reason`` if the child is killed. + + """ + source = "\n".join( + textwrap.dedent(inspect.getsource(helper)) for helper in helpers + ) + source += "\n" + textwrap.dedent(inspect.getsource(workload)) + script = ( + "import faulthandler\n" + f"faulthandler.dump_traceback_later({timeout}, exit=True)\n" + f"{source}\n" + f"{workload.__name__}(*{args!r})\n" + "faulthandler.cancel_dump_traceback_later()\n" + ) + try: + run_subprocess([sys.executable, "-c", script], timeout=timeout + 15) + except subprocess.TimeoutExpired: + raise AssertionError( + f"subprocess did not finish within {timeout}s -- likely {reason}" + ) from None + + +def threaded_deadlock_reproducer(operation, nworkers, niters, stall): + import faulthandler + import os + import sys + import threading + import time + + progress = [0] * nworkers + errors = [] + barrier = threading.Barrier(nworkers) + + def worker(idx): + try: + barrier.wait() + for _ in range(niters): + operation(idx) + progress[idx] += 1 + except BaseException as exc: + errors.append(exc) + raise + + workers = [threading.Thread(target=worker, args=(i,), daemon=True) + for i in range(nworkers)] + for t in workers: + t.start() + + last_total, last_change = 0, time.monotonic() + while any(t.is_alive() for t in workers): + time.sleep(0.05) + if errors: + raise AssertionError(f"worker failed: {errors[0]!r}") from errors[0] + total = sum(progress) + now = time.monotonic() + if total != last_total: + last_total, last_change = total, now + elif now - last_change > stall: + # dump the wedged threads' tracebacks, then os._exit to avoid a + # shutdown hang on the daemon threads. + sys.stderr.write("Probably deadlocked!\n") + faulthandler.dump_traceback() + sys.stderr.flush() + os._exit(1) + if errors: + raise AssertionError(f"worker failed: {errors[0]!r}") from errors[0] + + +def allocator_lock_order_workload(case_): + import numpy as np + + N = 1_000 + NWORKERS = 8 + NITERS = 1000 + STALL = 2.0 + + if case_ == "two-allocator": + a = np.array(["alpha"] * N, dtype="T") + b = np.array(["bravo"] * N, dtype="T") + + def operation(idx): + if idx % 2 == 0: + np.less(a, b) # locks alloc(a) then alloc(b) + else: + np.less(b, a) # locks alloc(b) then alloc(a) + + elif case_ == "three-allocator": + a = np.array(["alpha"] * N, dtype="T") + b = np.array(["bravo"] * N, dtype="T") + c = np.array(["charlie"] * N, dtype="T") + + def operation(idx): + order = idx % 3 + if order == 0: + np.maximum(a, b, out=c) # locks alloc(a), alloc(b), alloc(c) + elif order == 1: + np.maximum(b, c, out=a) # locks alloc(b), alloc(c), alloc(a) + else: + np.maximum(c, a, out=b) # locks alloc(c), alloc(a), alloc(b) + + elif case_ == "four-allocator": + from numpy._core.umath import _replace + + a = np.array(["a"] * N, dtype="T") + b = np.array(["b"] * N, dtype="T") + c = np.array(["c"] * N, dtype="T") + d = np.array(["d"] * N, dtype="T") + counts = np.ones(N, dtype=np.int64) + + def operation(idx): + order = idx % 4 + if order == 0: + _replace(a, b, c, counts, out=d) + elif order == 1: + _replace(b, c, d, counts, out=a) + elif order == 2: + _replace(c, d, a, counts, out=b) + else: + _replace(d, a, b, counts, out=c) + + elif case_ == "duplicate-allocator": + from numpy._core.umath import _replace + + a = np.array(["same"] * N, dtype="T") + b = np.array(["other"] * N, dtype="T") + c = np.array(["out"] * N, dtype="T") + counts = np.ones(N, dtype=np.int64) + + def operation(idx): + if idx % 2 == 0: + np.maximum(a, a, out=b) + else: + _replace(a, a, b, counts, out=c) + + else: + raise ValueError(f"unknown allocator deadlock case: {case_}") + + threaded_deadlock_reproducer( + operation, NWORKERS, NITERS, stall=STALL, + ) + + +def test_setitem_reentrant_no_deadlock(): + def workload(): + import numpy as np + + arr = np.empty(2, dtype="T") + + class Reenter: + def __str__(self): + # re-enters stringdtype_setitem and re-acquires the allocator + arr[1] = "inner" + return "outer" + + arr[0] = Reenter() + assert arr[0] == "outer" + assert arr[1] == "inner" + + assert_no_deadlock( + workload, + reason="re-entrant allocator acquire in stringdtype_setitem", + ) + + +@pytest.mark.parametrize( + "case_", + ["two-allocator", "three-allocator", "four-allocator", + "duplicate-allocator"], +) +def test_concurrent_allocator_acquire_no_deadlock(case_): + # NpyString_acquire_allocators must lock distinct allocators in a + # canonical order, otherwise deadlocks are possible from ABBA cycles + assert_no_deadlock( + allocator_lock_order_workload, + args=(case_,), + helpers=(threaded_deadlock_reproducer,), + reason=f"allocator lock-ordering in {case_}", + ) + + +def unique_deadlock_workload(): + import numpy as np + from numpy._core._multiarray_umath import _unique_hash + + NWORKERS = 8 + NITERS = 200 + STALL = 2.0 + + # _unique_hash (the engine under np.unique) rather than np.unique itself: + # it spends almost all its time in the GIL-released, allocator-locked load + # loop, so workers reliably overlap there. A single shared array with many + # duplicates concentrates contention on one allocator. + data = np.array([f"v{i % 128}" for i in range(50_000)], dtype="T") + + def operation(idx): + _unique_hash(data, equal_nan=False) + + threaded_deadlock_reproducer(operation, NWORKERS, NITERS, stall=STALL) + + +def test_concurrent_unique_no_deadlock(): + # Concurrent unique on a shared array deadlocks the allocator lock against + # the GIL unless unique_vstring releases the GIL before locking. Only + # reproduces on Python <= 3.12; PyMutex detaches on 3.13+. + assert_no_deadlock( + unique_deadlock_workload, + helpers=(threaded_deadlock_reproducer,), + reason="unique allocator lock vs GIL", + ) diff --git a/numpy/_core/tests/test_stringdtype.py b/numpy/_core/tests/test_stringdtype.py index 81fb18d03ebe..ea90bb81071c 100644 --- a/numpy/_core/tests/test_stringdtype.py +++ b/numpy/_core/tests/test_stringdtype.py @@ -144,6 +144,35 @@ def test_create_with_na(dtype): assert arr[1] is dtype.na_object +def test_create_with_failing_na_comparison(): + # An error raised while re-creating an array-owned descriptor used to + # crash with a NULL dereference in stringdtype_finalize_descr instead + # of propagating. + class Flaky: + def __init__(self): + self.calls = 0 + + def __eq__(self, other): + return self is other + + def __ne__(self, other): + self.calls += 1 + if self.calls > 1: + raise RuntimeError("boom") + return False + + def __hash__(self): + return 0 + + dt = StringDType(na_object=Flaky()) + # the first array takes ownership of the descriptor + np.array(["x"], dtype=dt) + # the second array creation re-creates the descriptor, re-running the + # na object classification, which fails + with pytest.raises(RuntimeError, match="boom"): + np.array(["y"], dtype=dt) + + @pytest.mark.parametrize("i", list(range(5))) def test_set_replace_na(i): # Test strings of various lengths can be set to NaN and then replaced. @@ -168,6 +197,18 @@ def test_null_roundtripping(): assert data[1] == arr[1] +@pytest.mark.parametrize("coerce", [True, False]) +def test_np_str_trailing_nul_preserved(coerce): + dtype = StringDType(coerce=coerce) + value = np.str_("q\x00") + arr = np.empty(1, dtype=dtype) + arr[0] = value + assert arr[0] == "q\x00" + arr[0:1] = [value] + assert arr[0] == "q\x00" + assert np.array([value], dtype=dtype)[0] == "q\x00" + + @pytest.mark.parametrize( "op, pyop", [ @@ -311,6 +352,17 @@ def test_self_casts(dtype, dtype2, strings): assert_array_equal(arr[:-1], newarr[:-1]) +def test_cast_method_names(): + get_castingimpl = np._core._multiarray_umath._get_castingimpl + string_DT = type(StringDType()) + for other_DT, name in [(np.dtypes.BoolDType, "bool"), + (np.dtypes.Float64DType, "double")]: + to_string = get_castingimpl(other_DT, string_DT) + assert f"cast_{name}_to_StringDType" in repr(to_string) + from_string = get_castingimpl(string_DT, other_DT) + assert f"cast_StringDType_to_{name}" in repr(from_string) + + @pytest.mark.parametrize( ("strings"), [ @@ -892,6 +944,34 @@ def test_float_nan_cast_na_object(): assert arr[0] == '1.2' +def test_string_to_bytes_invalid_ascii_error(): + # The cast builds this UnicodeEncodeError only after releasing the allocator + # lock and copying the offending bytes out of the arena; check the reported + # character and position survive that. + arr = np.array(["abc", "café", "xy"], dtype="T") + with pytest.raises(UnicodeEncodeError) as excinfo: + arr.astype("S10") + exc = excinfo.value + assert exc.encoding == "ascii" + assert exc.object == "café" + assert exc.start == 3 + assert exc.end == 4 + assert exc.reason == "ordinal not in range(128)" + assert_array_equal( + np.array(["abc", "xy"], dtype="T").astype("S3"), + np.array([b"abc", b"xy"], dtype="S3"), + ) + + +def test_void_to_string_invalid_utf8_error(): + # invalid UTF-8 in a void buffer used to surface as a confusing + # MemoryError because the error return of utf8_buffer_size was + # stored in an unsigned variable + varr = np.array([b"\xff\xff\xff\xff"], dtype="V4") + with pytest.raises(TypeError, match="Invalid UTF-8"): + varr.astype(StringDType()) + + @pytest.mark.parametrize( "typename", [ @@ -1740,10 +1820,13 @@ def test_unset_na_coercion(): StringDType(na_object=None)]: if op_dtype is None: op = "2" + expected = StringDType(na_object=None) # coerce defaults to True else: op = np.array("2", dtype=op_dtype) + expected = StringDType(na_object=None, coerce=op_dtype.coerce) res = arr + op assert_array_equal(res, ["hello2", "world2"]) + assert res.dtype == expected # dtype instances with distinct explicitly set NA objects are incompatible for op_dtype in [StringDType(na_object=pd_NA), StringDType(na_object="")]: @@ -1767,6 +1850,17 @@ def test_unset_na_coercion(): arr == op +def test_coerce_promotion_commutative(): + # promoting a coerce=False instance with a coerce=True instance + # used to depend on the argument order + dt = StringDType() + dt_no_coerce = StringDType(coerce=False) + assert np.promote_types(dt, dt_no_coerce) == dt_no_coerce + assert np.promote_types(dt_no_coerce, dt) == dt_no_coerce + assert np.promote_types(dt, dt) == dt + assert np.promote_types(dt_no_coerce, dt_no_coerce) == dt_no_coerce + + def test_repeat(string_array): res = string_array.repeat(1000) # Create an empty array with expanded dimension, and fill it. Then, diff --git a/numpy/_core/tests/test_ufunc.py b/numpy/_core/tests/test_ufunc.py index a078d6fa18d3..dd5c1c8853f5 100644 --- a/numpy/_core/tests/test_ufunc.py +++ b/numpy/_core/tests/test_ufunc.py @@ -3253,6 +3253,13 @@ def test_resolve_dtypes_basic(self): with pytest.raises(TypeError): np.add.resolve_dtypes((i4, f4, None), casting="no") + def test_resolve_dtypes_unary_weak_scalar(self): + assert np.sin.resolve_dtypes((int, None)) == ( + np.dtype("f8"), np.dtype("f8")) + with pytest.raises(TypeError, + match="Output descriptors must be NumPy dtypes or None."): + np.sin.resolve_dtypes((int, int)) + def test_resolve_dtypes_comparison(self): i4 = np.dtype("i4") i8 = np.dtype("i8") diff --git a/numpy/_core/umath.pyi b/numpy/_core/umath.pyi index 600955316949..061cea1c4bb4 100644 --- a/numpy/_core/umath.pyi +++ b/numpy/_core/umath.pyi @@ -12,6 +12,7 @@ from typing import ( Self, TypedDict, Unpack, + final, overload, override, type_check_only, @@ -26,7 +27,6 @@ from numpy import ( add, arctan2, bitwise_and, - bitwise_count, bitwise_or, bitwise_xor, conj, @@ -34,39 +34,27 @@ from numpy import ( divide, divmod, e, - equal, euler_gamma, float_power, floor_divide, fmax, fmin, fmod, - frexp, frompyfunc, gcd, - greater, - greater_equal, heaviside, hypot, lcm, - ldexp, left_shift, - less, - less_equal, logaddexp, logaddexp2, - logical_and, - logical_or, - logical_xor, matmul, matvec, maximum, minimum, mod, - modf, multiply, nextafter, - not_equal, pi, power, remainder, @@ -89,10 +77,12 @@ from numpy._typing import ( _CharLike_co, _DTypeLike, _DTypeLikeBool, + _DTypeLikeObject, _FloatLike_co, _IntLike_co, _NestedSequence, _NumberLike_co, + _ScalarLike_co, _Shape, ) from numpy._typing._array_like import _DualArrayLike @@ -204,6 +194,8 @@ type _Array1D[ScalarT: np.generic] = np.ndarray[tuple[int], np.dtype[ScalarT]] type _Array2D[ScalarT: np.generic] = np.ndarray[tuple[int, int], np.dtype[ScalarT]] type _Array3D[ScalarT: np.generic] = np.ndarray[tuple[int, int, int], np.dtype[ScalarT]] +type _tuple2[T] = tuple[T, T] + type _ErrKind = Literal["ignore", "warn", "raise", "call", "print", "log"] type _ErrCall = Callable[[str, int], Any] | SupportsWrite[str] @@ -213,10 +205,18 @@ type _to_number = np.number | np.bool type _to_numeric = _to_number | np.timedelta64 type _numeric = np.number | np.timedelta64 type _time = np.datetime64 | np.timedelta64 +type _non_object = _to_number | _time | np.flexible # np.generic - np.object_ + +type _as_f64 = np.int64 | np.uint64 | np.int32 | np.uint32 +type _as_f32 = np.int16 | np.uint16 +type _as_f16 = np.int8 | np.uint8 | np.bool type _ArrayLikeNumericObj = _DualArrayLike[np.dtype[_numeric | np.object_], complex] type _ArrayLikeNumericObj_co = _DualArrayLike[np.dtype[_to_numeric | np.object_], complex] +type _ArrayUnlikeObject = _DualArrayLike[np.dtype[_non_object] | np.dtypes.StringDType, complex | bytes | str] +type _ScalarUnlikeObject = complex | bytes | str | np.generic # bare `np.object_` don't exist + @type_check_only class _ExtOjbDict(TypedDict, total=False): divide: _ErrKind @@ -227,15 +227,55 @@ class _ExtOjbDict(TypedDict, total=False): bufsize: int type _Signature1 = tuple[str | None, str | None] | str +type _Signature2 = tuple[str | None, str | None, str | None] | str + +# TODO(@jorenham): make these `__array_ufunc__` protocols generic on `ufunc` so that +# implementing types can structurally determine the signature of the calling `ufunc`. @type_check_only class _CanUfuncCall1[OutT](Protocol): - def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["__call__"], /, *args: Any, **kwargs: Any) -> OutT: ... + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["__call__"], x: Self, /) -> OutT: ... @type_check_only class _CanUfuncAt1[IxT, OutT](Protocol): def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["at"], a: Self, indices: IxT, /) -> OutT: ... +@type_check_only +class _CanUfuncCall2L[OtherT, OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["__call__"], lhs: Self, rhs: OtherT, /) -> OutT: ... + +@type_check_only +class _CanUfuncCall2R[OtherT, OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["__call__"], lhs: OtherT, rhs: Self, /) -> OutT: ... + +@type_check_only +class _CanUfuncOuterL[OtherT, OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["outer"], lhs: Self, rhs: OtherT, /) -> OutT: ... + +@type_check_only +class _CanUfuncOuterR[OtherT, OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["outer"], lhs: OtherT, rhs: Self, /) -> OutT: ... + +@type_check_only +class _CanUfuncAt2L[OtherT, IxT, OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["at"], a: Self, indices: IxT, b: OtherT, /) -> OutT: ... + +@type_check_only +class _CanUfuncAt2R[OtherT, IxT, OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["at"], a: OtherT, indices: IxT, b: Self, /) -> OutT: ... + +@type_check_only +class _CanUfuncAccumulate[OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["accumulate"], a: Self, /) -> OutT: ... + +@type_check_only +class _CanUfuncReduce[OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["reduce"], a: Self, /) -> OutT: ... + +@type_check_only +class _CanUfuncReduceAt[IxT, OutT](Protocol): + def __array_ufunc__(self, ufunc: np.ufunc, method: Literal["reduceat"], a: Self, indices: IxT, /) -> OutT: ... + ### # ufunc specializations @@ -251,6 +291,22 @@ class _Kwargs11(TypedDict, total=False): subok: bool # = True, signature: _Signature1 +@type_check_only +class _Kwargs12(TypedDict, total=False): + where: _ArrayLikeBool_co # = True + casting: _CastingKind # = "same_kind" + order: _OrderKACF # = "K", + subok: bool # = True, + signature: _Signature2 + +@type_check_only +class _Kwargs21(TypedDict, total=False): + where: _ArrayLikeBool_co # = True + casting: _CastingKind # = "same_kind" + order: _OrderKACF # = "K", + subok: bool # = True, + signature: _Signature2 + _IdT_co = TypeVar("_IdT_co", covariant=True, default=None) @type_check_only @@ -281,6 +337,59 @@ class _ufunc_11(np.ufunc, Generic[_IdT_co]): # type: ignore[misc] @override def outer(self, A: Never, B: Never, /) -> Never: ... # pyrefly:ignore[bad-override] +@type_check_only +class _ufunc_12(np.ufunc): # type: ignore[misc] + @property + @override + def identity(self) -> None: ... + @property + @override + def nin(self) -> Literal[1]: ... + @property + @override + def nout(self) -> Literal[2]: ... + @property + @override + def nargs(self) -> Literal[3]: ... + @property + @override + def signature(self) -> None: ... + + # + @final + @override + def at(self, a: Never, indices: Never, /) -> Never: ... # pyrefly:ignore[bad-override] + @final + @override + def accumulate(self, array: Never, /) -> Never: ... # pyrefly:ignore[bad-override] + @final + @override + def reduce(self, array: Never, /) -> Never: ... # pyrefly:ignore[bad-override] + @final + @override + def reduceat(self, array: Never, /, indices: Never) -> Never: ... # pyrefly:ignore[bad-override] + @final + @override + def outer(self, A: Never, B: Never, /) -> Never: ... # pyrefly:ignore[bad-override] + +@type_check_only +class _ufunc_21(np.ufunc, Generic[_IdT_co]): # type: ignore[misc] + @property + @override + def identity(self) -> _IdT_co: ... + @property + @override + def nin(self) -> Literal[2]: ... + @property + @override + def nout(self) -> Literal[1]: ... + @property + @override + def nargs(self) -> Literal[3]: ... + @property + @override + def signature(self) -> None: ... + # Mm => ? @type_check_only class _ufunc_11_m_b(_ufunc_11): # type: ignore[misc] @@ -659,6 +768,108 @@ class _ufunc_11_bifgco_bo(_ufunc_11): # type: ignore[misc] @overload def at[IxT, OutT](self, a: _CanUfuncAt1[IxT, OutT], indices: IxT, /) -> OutT: ... +# bBhHiIlLqQO => BO, where bBhHiIlLqQ => B and O => O +@type_check_only +class _ufunc_11_io(_ufunc_11): # type: ignore[misc] + @override + @overload # Nd, known shape + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_to_integer]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.uint8]]: ... + @overload # Nd object, known shape + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[np.object_]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.object_]]: ... + @overload # scalar + def __call__( + self, + x: int | _to_integer, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.uint8: ... + @overload # scalar, out=... + def __call__( + self, + x: int | _to_integer, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> _Array0D[np.uint8]: ... + @overload # 1d + def __call__( + self, + x: Sequence[int | _to_integer], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> _Array1D[np.uint8]: ... + @overload # 2d + def __call__( + self, + x: Sequence[Sequence[int | _to_integer]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> _Array2D[np.uint8]: ... + @overload # 3d + def __call__( + self, + x: Sequence[Sequence[Sequence[int | _to_integer]]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> _Array3D[np.uint8]: ... + @overload # out= + def __call__[OutT: np.ndarray]( + self, + x: _ArrayLikeInt_co, + /, + out: OutT, + *, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> OutT: ... + @overload # x.__array_ufunc__(...) -> OutT + def __call__[OutT]( + self, + x: _CanUfuncCall1[OutT], + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs11], + ) -> OutT: ... + + # + @override + @overload + def at(self, a: np.ndarray[_Shape, np.dtype[_to_integer]], indices: _ArrayLikeInt, /) -> None: ... # pyrefly:ignore[bad-override] + @overload + def at[IxT, OutT](self, a: _CanUfuncAt1[IxT, OutT], indices: IxT, /) -> OutT: ... + # efdg => efdg @type_check_only class _ufunc_11_f(_ufunc_11): # type: ignore[misc] @@ -676,23 +887,63 @@ class _ufunc_11_f(_ufunc_11): # type: ignore[misc] @overload # Nd, +f64 def __call__[ShapeT: _Shape]( self, - x: np.ndarray[ShapeT, np.dtype[_to_integer]], + x: np.ndarray[ShapeT, np.dtype[_as_f64]], /, *, out: EllipsisType | None = None, dtype: None = None, **kwargs: Unpack[_Kwargs11], ) -> np.ndarray[ShapeT, np.dtype[np.float64]]: ... + @overload # Nd, +f32 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f32]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.float32]]: ... + @overload # Nd, +f16 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f16]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.float16]]: ... @overload # scalar, float | +f64 def __call__( self, - x: float | _to_integer, + x: float | _as_f64, /, *, out: None = None, dtype: None = None, **kwargs: Unpack[_Kwargs11], ) -> np.float64: ... + @overload # scalar, +f32 + def __call__( + self, + x: _as_f32, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.float32: ... + @overload # scalar, +f16 + def __call__( + self, + x: _as_f16, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.float16: ... @overload # 1d, +float def __call__( self, @@ -828,23 +1079,63 @@ class _ufunc_11_fo(_ufunc_11): # type: ignore[misc] @overload # Nd, +f64 def __call__[ShapeT: _Shape]( self, - x: np.ndarray[ShapeT, np.dtype[np.integer | np.bool]], + x: np.ndarray[ShapeT, np.dtype[_as_f64]], /, *, out: EllipsisType | None = None, dtype: None = None, **kwargs: Unpack[_Kwargs11], ) -> np.ndarray[ShapeT, np.dtype[np.float64]]: ... + @overload # Nd, +f32 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f32]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.float32]]: ... + @overload # Nd, +f16 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f16]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.float16]]: ... @overload # scalar, float | +f64 def __call__( self, - x: float | np.integer | np.bool, + x: float | _as_f64, /, *, out: None = None, dtype: None = None, **kwargs: Unpack[_Kwargs11], ) -> np.float64: ... + @overload # scalar, +f32 + def __call__( + self, + x: _as_f32, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.float32: ... + @overload # scalar, +f16 + def __call__( + self, + x: _as_f16, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.float16: ... @overload # 1d, +float def __call__( self, @@ -980,23 +1271,63 @@ class _ufunc_11_fco(_ufunc_11): # type: ignore[misc] @overload # Nd, +f64 def __call__[ShapeT: _Shape]( self, - x: np.ndarray[ShapeT, np.dtype[np.integer | np.bool]], + x: np.ndarray[ShapeT, np.dtype[_as_f64]], /, *, out: EllipsisType | None = None, dtype: None = None, **kwargs: Unpack[_Kwargs11], ) -> np.ndarray[ShapeT, np.dtype[np.float64]]: ... + @overload # Nd, +f32 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f32]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.float32]]: ... + @overload # Nd, +f16 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f16]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.ndarray[ShapeT, np.dtype[np.float16]]: ... @overload # scalar, float | +f64 def __call__( self, - x: float | np.integer | np.bool, + x: float | _as_f64, /, *, out: None = None, dtype: None = None, **kwargs: Unpack[_Kwargs11], ) -> np.float64: ... + @overload # scalar, +f32 + def __call__( + self, + x: _as_f32, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.float32: ... + @overload # scalar, +f16 + def __call__( + self, + x: _as_f16, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs11], + ) -> np.float16: ... @overload # 1d, +float def __call__( self, @@ -2784,7 +3115,6 @@ class _ufunc_11_sut_i(_ufunc_11[Literal[0]]): # type: ignore[misc] def at[IxT, OutT](self, a: _CanUfuncAt1[IxT, OutT], indices: IxT, /) -> OutT: ... isnat: Final[_ufunc_11_m_b] = ... - signbit: Final[_ufunc_11_f_b] = ... isfinite: Final[_ufunc_11_bifgcm_b] = ... @@ -2793,6 +3123,8 @@ isnan: Final[_ufunc_11_bifgcm_b] = ... # TODO: StringDType[?] support logical_not: Final[_ufunc_11_bifgco_bo] = ... +bitwise_count: Final[_ufunc_11_io] = ... + spacing: Final[_ufunc_11_f] = ... cbrt: Final[_ufunc_11_fo] = ... @@ -2854,6 +3186,1801 @@ isupper: Final[_ufunc_11_sut_b] = ... str_len: Final[_ufunc_11_sut_i] = ... +# efdg => (efdg, i) +@type_check_only +class _ufunc_12_frexp(_ufunc_12): # type: ignore[misc] + @override + @overload # Nd, known dtype + def __call__[ShapeT: _Shape, DTypeT: np.dtype[np.floating]]( + self, + x: np.ndarray[ShapeT, DTypeT], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[np.ndarray[ShapeT, DTypeT], np.ndarray[ShapeT, np.dtype[np.int32]]]: ... + @overload # Nd, +f64 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f64]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[np.ndarray[ShapeT, np.dtype[np.float64]], np.ndarray[ShapeT, np.dtype[np.int32]]]: ... + @overload # Nd, +f32 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f32]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[np.ndarray[ShapeT, np.dtype[np.float32]], np.ndarray[ShapeT, np.dtype[np.int32]]]: ... + @overload # Nd, +f16 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f16]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[np.ndarray[ShapeT, np.dtype[np.float16]], np.ndarray[ShapeT, np.dtype[np.int32]]]: ... + @overload # scalar, known dtype + def __call__[ScalarT: np.floating]( + self, + x: ScalarT, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[ScalarT, np.int32]: ... + @overload # scalar, float | +f64 + def __call__( + self, + x: float | _as_f64, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[np.float64, np.int32]: ... + @overload # scalar, +f32 + def __call__( + self, + x: _as_f32, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[np.float32, np.int32]: ... + @overload # scalar, +f16 + def __call__( + self, + x: _as_f16, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[np.float16, np.int32]: ... + @overload # 1d, +float + def __call__( + self, + x: Sequence[float], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[_Array1D[np.float64], _Array1D[np.int32]]: ... + @overload # 2d, +float + def __call__( + self, + x: Sequence[Sequence[float]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[_Array2D[np.float64], _Array2D[np.int32]]: ... + @overload # ?d, unknown dtype + def __call__( + self, + x: _ArrayLikeFloat_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[Any]: ... + @overload # out= + def __call__[OutT1: np.ndarray, OutT2: np.ndarray]( + self, + x: _ArrayLikeFloat_co, + /, + out: tuple[OutT1, OutT2], + *, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[OutT1, OutT2]: ... + @overload # out= + def __call__[OutT]( + self, + x: _CanUfuncCall1[OutT], + /, + out: object | None = None, + *, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs12], + ) -> OutT: ... + +# efdg => (efdg, efdg) +@type_check_only +class _ufunc_12_modf(_ufunc_12): # type: ignore[misc] + @override + @overload # known shape, known scalar/array + def __call__[T: np.floating | npt.NDArray[np.floating]]( + self, + x: T, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[T]: ... + @overload # Nd, +f64 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f64]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.ndarray[ShapeT, np.dtype[np.float64]]]: ... + @overload # Nd, +f32 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f32]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.ndarray[ShapeT, np.dtype[np.float32]]]: ... + @overload # Nd, +f16 + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_as_f16]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.ndarray[ShapeT, np.dtype[np.float16]]]: ... + @overload # scalar, float | +f64 + def __call__( + self, + x: float | _as_f64, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.float64]: ... + @overload # scalar, +f32 + def __call__( + self, + x: _as_f32, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.float32]: ... + @overload # scalar, +f16 + def __call__( + self, + x: _as_f16, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.float16]: ... + @overload # 1d, +float + def __call__( + self, + x: Sequence[float], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[_Array1D[np.float64]]: ... + @overload # 2d, +float + def __call__( + self, + x: Sequence[Sequence[float]], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[_Array2D[np.float64]]: ... + @overload # scalar, dtype= + def __call__[ScalarT: np.floating]( + self, + x: _FloatLike_co, + /, + *, + out: None = None, + dtype: _DTypeLike[ScalarT], + **kwargs: Unpack[_Kwargs11], + ) -> _tuple2[ScalarT]: ... + @overload # Nd, dtype= + def __call__[ShapeT: _Shape, ScalarT: np.floating]( + self, + x: np.ndarray[ShapeT, np.dtype[_to_floating]], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLike[ScalarT], + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.ndarray[ShapeT, np.dtype[ScalarT]]]: ... + @overload # Nd, dtype= + def __call__[ShapeT: _Shape]( + self, + x: np.ndarray[ShapeT, np.dtype[_to_floating]], + /, + *, + out: EllipsisType | None = None, + dtype: npt.DTypeLike, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.ndarray[ShapeT]]: ... + @overload # Nd, dtype= + def __call__[ScalarT: np.floating]( + self, + x: _NestedSequence[float], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLike[ScalarT], + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[npt.NDArray[ScalarT]]: ... + @overload # Nd, dtype= + def __call__( + self, + x: _NestedSequence[float], + /, + *, + out: EllipsisType | None = None, + dtype: npt.DTypeLike, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[np.ndarray]: ... + @overload # ?d, dtype= + def __call__[ScalarT: np.floating]( + self, + x: _ArrayLikeFloat_co, + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLike[ScalarT], + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[npt.NDArray[ScalarT] | Any]: ... # `| Any` because of overlap + @overload # ?d, dtype= + def __call__( + self, + x: _ArrayLikeFloat_co, + /, + *, + out: EllipsisType | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs12], + ) -> _tuple2[Any]: ... + @overload # out= + def __call__[OutT1: np.ndarray, OutT2: np.ndarray]( + self, + x: _ArrayLikeFloat_co, + /, + out: tuple[OutT1, OutT2], + *, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs12], + ) -> tuple[OutT1, OutT2]: ... + @overload # out= + def __call__[OutT]( + self, + x: _CanUfuncCall1[OutT], + /, + out: object | None = None, + *, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs12], + ) -> OutT: ... + +frexp: Final[_ufunc_12_frexp] = ... +modf: Final[_ufunc_12_modf] = ... + +# ?bBhHiIlLqQefdgFDGOSUVT, ?bBhHiIlLqQefdgFDGOSUVT => ?O; (also supports `mM` input) +@type_check_only +class _ufunc_21_logical[IdT: bool](_ufunc_21[IdT]): # type: ignore[misc] + # TODO(@jorenham): shape-typing + + @override + @overload # 0d, 0d + def __call__( + self, + x1: _ScalarUnlikeObject, + x2: _ScalarUnlikeObject, + /, + *, + out: None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.bool: ... + @overload # >0d, >=0d + def __call__( + self, + x1: np.ndarray[Any, np.dtype[_non_object] | np.dtypes.StringDType] | _NestedSequence[_ScalarUnlikeObject], + x2: _ArrayUnlikeObject, + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >=0d, >0d + def __call__( + self, + x1: _ArrayUnlikeObject, + x2: np.ndarray[Any, np.dtype[_non_object] | np.dtypes.StringDType] | _NestedSequence[_ScalarUnlikeObject], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >0d object_, >=0d + def __call__( + self, + x1: npt.NDArray[np.object_], + x2: npt.ArrayLike, + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.object_]: ... + @overload # >=0d, >0d object_ + def __call__( + self, + x1: npt.ArrayLike, + x2: npt.NDArray[np.object_], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.object_]: ... + @overload # >=0d, >=0d, out=... + def __call__( + self, + x1: _ArrayUnlikeObject, + x2: _ArrayUnlikeObject, + /, + *, + out: EllipsisType, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # out= + def __call__[OutT: np.ndarray]( + self, + x1: npt.ArrayLike, + x2: npt.ArrayLike, + /, + out: OutT, + *, + dtype: _DTypeLikeBool | _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x1.__array_ufunc__(self, "__call__", x1, x2, ...) + def __call__[OtherT, OutT]( + self, + x1: _CanUfuncCall2L[OtherT, OutT], + x2: OtherT, + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x2.__array_ufunc__(self, "__call__", x1, x2, ...) + def __call__[OtherT, OutT]( + self, + x1: OtherT, + x2: _CanUfuncCall2R[OtherT, OutT], + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # +object, +object (overlaps with, well, everything...) + def __call__( + self, + x1: object, + x2: object, + /, + *, + out: None = None, + dtype: _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> Any: ... + + # + @override + @overload # 0d, 0d + def outer( + self, + x1: _ScalarUnlikeObject, + x2: _ScalarUnlikeObject, + /, + *, + out: None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.bool: ... + @overload # >0d, >=0d + def outer( + self, + x1: np.ndarray[Any, np.dtype[_non_object] | np.dtypes.StringDType] | _NestedSequence[_ScalarUnlikeObject], + x2: _ArrayUnlikeObject, + /, + *, + out: None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >=0d, >0d + def outer( + self, + x1: _ArrayUnlikeObject, + x2: np.ndarray[Any, np.dtype[_non_object] | np.dtypes.StringDType] | _NestedSequence[_ScalarUnlikeObject], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >0d object, >=0d + def outer( + self, + x1: npt.NDArray[np.object_], + x2: npt.ArrayLike, + /, + *, + out: None = None, + dtype: _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.object_]: ... + @overload # >=0d, >0d object + def outer( + self, + x1: npt.ArrayLike, + x2: npt.NDArray[np.object_], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.object_]: ... + @overload # >=0d, >=0d, out= + def outer[OutT: np.ndarray]( + self, + x1: npt.ArrayLike, + x2: npt.ArrayLike, + /, + *, + out: OutT, + dtype: _DTypeLikeBool | _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # >=0d, >=0d (fallback) + def outer( + self, + x1: _ArrayUnlikeObject, + x2: _ArrayUnlikeObject, + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool] | Any: ... + @overload # x1.__array_ufunc__(self, "outer", x1, x2, ...) + def outer[OtherT, OutT]( + self, + x1: _CanUfuncOuterL[OtherT, OutT], + x2: OtherT, + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x2.__array_ufunc__(self, "outer", x1, x2, ...) + def outer[OtherT, OutT]( + self, + x1: OtherT, + x2: _CanUfuncOuterR[OtherT, OutT], + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # +object, +object (overlaps with everything...) + def outer( + self, + x1: object, + x2: object, + /, + *, + out: None = None, + dtype: _DTypeLikeObject | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> Any: ... + + # + @override + @overload + def at(self, a: np.ndarray, indices: _ArrayLikeInt, b: npt.ArrayLike, /) -> None: ... # pyrefly:ignore[bad-override] + @overload + def at[OtherT, IxT, OutT](self, a: _CanUfuncAt2L[OtherT, IxT, OutT], indices: IxT, b: OtherT, /) -> OutT: ... + @overload + def at[OtherT, IxT, OutT](self, a: OtherT, indices: IxT, b: _CanUfuncAt2R[OtherT, IxT, OutT], /) -> OutT: ... + + # + @override + @overload # unknown shape, not object_ + def reduce( # pyrefly:ignore[bad-override] + self, + array: _ArrayUnlikeObject, + /, + *, + axis: int | tuple[int, ...] = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.bool] | Any: ... + @overload # unknown shape, not object_, axis=None + def reduce( + self, + array: _ArrayUnlikeObject, + /, + *, + axis: None, + dtype: _DTypeLikeBool | None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> np.bool: ... + @overload # unknown shape, not object_ keepdims=True + def reduce( + self, + array: _ArrayUnlikeObject, + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + keepdims: Literal[True], + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.bool]: ... + @overload # unknown shape, not object_, out=... + def reduce( + self, + array: _ArrayUnlikeObject, + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeBool | None = None, + out: EllipsisType, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.bool]: ... + @overload # unknown shape, object_ + def reduce( + self, + array: npt.NDArray[np.object_], + /, + *, + axis: int | tuple[int, ...] = 0, + dtype: _DTypeLikeObject | None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.object_] | Any: ... + @overload # unknown shape, object_, axis=None + def reduce( + self, + array: npt.NDArray[np.object_], + /, + *, + axis: None, + dtype: _DTypeLikeObject | None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> Any: ... + @overload # unknown shape, not object_ keepdims=True + def reduce( + self, + array: npt.NDArray[np.object_], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeObject | None = None, + out: None = None, + keepdims: Literal[True], + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.object_]: ... + @overload # unknown shape, object_, out=... + def reduce( + self, + array: npt.NDArray[np.object_], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeBool | None = None, + out: EllipsisType, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.object_]: ... + @overload # out= + def reduce[OutT: np.ndarray]( + self, + array: npt.ArrayLike, + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeBool | _DTypeLikeObject | None = None, + out: OutT, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> OutT: ... + @overload # array.__array_ufunc__(self, "reduceat", array, ...) + def reduce[OutT]( + self, + array: _CanUfuncReduce[OutT], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: npt.DTypeLike | None = None, + out: np.ndarray | EllipsisType | None = None, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> OutT: ... + + # + @override + @overload # known shape + def reduceat[ShapeT: _Shape]( # pyrefly:ignore[bad-override] + self, + array: np.ndarray[ShapeT, np.dtype[_non_object] | np.dtypes.StringDType], + /, + indices: tuple[int, ...], + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> np.ndarray[ShapeT, np.dtype[np.bool]]: ... + @overload # known shape, object_ + def reduceat[ShapeT: _Shape]( # pyrefly:ignore[bad-override] + self, + array: np.ndarray[ShapeT, np.dtype[np.object_]], + /, + indices: tuple[int, ...], + *, + axis: int = 0, + dtype: _DTypeLikeObject | None = None, + out: None = None, + ) -> np.ndarray[ShapeT, np.dtype[np.object_]]: ... + @overload # unknown shape + def reduceat( + self, + array: _ArrayUnlikeObject, + /, + indices: tuple[int, ...], + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> npt.NDArray[np.bool]: ... + @overload # out= + def reduceat[OutT: np.ndarray]( + self, + array: npt.ArrayLike, + /, + indices: tuple[int, ...], + *, + axis: int = 0, + dtype: _DTypeLikeBool | _DTypeLikeObject | None = None, + out: OutT, + ) -> OutT: ... + @overload # array.__array_ufunc__(self, "reduceat", array, ...) + def reduceat[IxT, OutT]( + self, + array: _CanUfuncReduceAt[IxT, OutT], + /, + indices: IxT, + *, + axis: int = 0, + dtype: npt.DTypeLike | None = None, + out: np.ndarray | None = None, + ) -> OutT: ... + + # TODO + @override + @overload # known shape + def accumulate[ShapeT: _Shape]( # pyrefly:ignore[bad-override] + self, + array: np.ndarray[ShapeT, np.dtype[_non_object] | np.dtypes.StringDType], + /, + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> np.ndarray[ShapeT, np.dtype[np.bool]]: ... + @overload # known shape, OBJECT_ + def accumulate[ShapeT: _Shape]( + self, + array: np.ndarray[ShapeT, np.dtype[np.object_]], + /, + *, + axis: int = 0, + dtype: _DTypeLikeObject | None = None, + out: None = None, + ) -> np.ndarray[ShapeT, np.dtype[np.object_]]: ... + @overload # unknown shape + def accumulate( + self, + array: _ArrayUnlikeObject, + /, + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> npt.NDArray[np.bool]: ... + @overload # out= + def accumulate[OutT: np.ndarray]( + self, + array: npt.ArrayLike, + /, + *, + axis: int = 0, + dtype: _DTypeLikeBool | _DTypeLikeObject | None = None, + out: OutT, + ) -> OutT: ... + @overload # array.__array_ufunc__(self, "accumulate", array, ...) + def accumulate[OutT]( + self, + array: _CanUfuncAccumulate[OutT], + /, + *, + axis: int = 0, + dtype: npt.DTypeLike | None = None, + out: np.ndarray | None = None, + ) -> OutT: ... + +# ?bBhHiIlLqQefdgFDGMmOSUT => ?O +# NOTE: These theoretically also supports O => O but that's an unlikely usecase because +# that'd require something incompatible with `bool` or `np.bool` to be returned from the +# respective "rich" comparison dunder methods of the underlying python objects. +# So for the sake of simplicity we assume that all `np.generic` are accepted and that +# the output dtype is always `np.bool`. +@type_check_only +class _ufunc_21_cmp(_ufunc_21[None]): # type: ignore[misc] + # TODO(@jorenham): shape-typing + + @override + @overload # 0d, 0d + def __call__( + self, + x1: _ScalarLike_co, + x2: _ScalarLike_co, + /, + *, + out: None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.bool: ... + @overload # >0d, >=0d + def __call__( + self, + x1: np.ndarray | _NestedSequence[_ScalarLike_co], + x2: npt.ArrayLike, + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >=0d, >0d + def __call__( + self, + x1: npt.ArrayLike, + x2: np.ndarray | _NestedSequence[_ScalarLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >=0d, >=0d, out=... + def __call__( + self, + x1: npt.ArrayLike, + x2: npt.ArrayLike, + /, + *, + out: EllipsisType, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # out= + def __call__[OutT: np.ndarray]( + self, + x1: npt.ArrayLike, + x2: npt.ArrayLike, + /, + out: OutT, + *, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x1.__array_ufunc__(self, "__call__", x1, x2, ...) + def __call__[OtherT, OutT]( + self, + x1: _CanUfuncCall2L[OtherT, OutT], + x2: OtherT, + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x2.__array_ufunc__(self, "__call__", x1, x2, ...) + def __call__[OtherT, OutT]( + self, + x1: OtherT, + x2: _CanUfuncCall2R[OtherT, OutT], + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + + # + @override + @overload # 0d, 0d + def outer( + self, + x1: _ScalarLike_co, + x2: _ScalarLike_co, + /, + *, + out: None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.bool: ... + @overload # >0d, >=0d + def outer( + self, + x1: np.ndarray | _NestedSequence[_ScalarLike_co], + x2: npt.ArrayLike, + /, + *, + out: None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >=0d, >0d + def outer( + self, + x1: npt.ArrayLike, + x2: np.ndarray | _NestedSequence[_ScalarLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool]: ... + @overload # >=0d, >=0d, out= + def outer[OutT: np.ndarray]( + self, + x1: npt.ArrayLike, + x2: npt.ArrayLike, + /, + *, + out: OutT, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # >=0d, >=0d (fallback) + def outer( + self, + x1: npt.ArrayLike, + x2: npt.ArrayLike, + /, + *, + out: None = None, + dtype: _DTypeLikeBool | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.bool] | Any: ... + @overload # x1.__array_ufunc__(self, "outer", x1, x2, ...) + def outer[OtherT, OutT]( + self, + x1: _CanUfuncOuterL[OtherT, OutT], + x2: OtherT, + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x2.__array_ufunc__(self, "outer", x1, x2, ...) + def outer[OtherT, OutT]( + self, + x1: OtherT, + x2: _CanUfuncOuterR[OtherT, OutT], + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + + # + @override + @overload + def at(self, a: np.ndarray, indices: _ArrayLikeInt, b: npt.ArrayLike, /) -> None: ... # pyrefly:ignore[bad-override] + @overload + def at[OtherT, IxT, OutT](self, a: _CanUfuncAt2L[OtherT, IxT, OutT], indices: IxT, b: OtherT, /) -> OutT: ... + @overload + def at[OtherT, IxT, OutT](self, a: OtherT, indices: IxT, b: _CanUfuncAt2R[OtherT, IxT, OutT], /) -> OutT: ... + + # + @override + @overload # unknown shape + def reduce( # pyrefly:ignore[bad-override] + self, + array: _ArrayLikeBool_co, + /, + *, + axis: int | tuple[int, ...] = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.bool] | np.bool: ... + @overload # unknown shape, axis=None + def reduce( + self, + array: _ArrayLikeBool_co, + /, + *, + axis: None, + dtype: _DTypeLikeBool | None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> np.bool: ... + @overload # unknown shape, keepdims=True + def reduce( + self, + array: _ArrayLikeBool_co, + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + keepdims: Literal[True], + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.bool]: ... + @overload # unknown shape, out=... + def reduce( + self, + array: _ArrayLikeBool_co, + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeBool | None = None, + out: EllipsisType, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.bool]: ... + @overload # out= + def reduce[OutT: np.ndarray]( + self, + array: _ArrayLikeBool_co, + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: _DTypeLikeBool | None = None, + out: OutT, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> OutT: ... + @overload # array.__array_ufunc__(self, "reduceat", array, ...) + def reduce[OutT]( + self, + array: _CanUfuncReduce[OutT], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: npt.DTypeLike | None = None, + out: np.ndarray | EllipsisType | None = None, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> OutT: ... + + # + @override + @overload # known shape + def reduceat[ShapeT: _Shape]( # pyrefly:ignore[bad-override] + self, + array: np.ndarray[ShapeT, np.dtype[np.bool]], + /, + indices: tuple[int, ...], + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> np.ndarray[ShapeT, np.dtype[np.bool]]: ... + @overload # unknown shape + def reduceat( + self, + array: _ArrayLikeBool_co, + /, + indices: tuple[int, ...], + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> npt.NDArray[np.bool]: ... + @overload # out= + def reduceat[OutT: np.ndarray]( + self, + array: _ArrayLikeBool_co, + /, + indices: tuple[int, ...], + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: OutT, + ) -> OutT: ... + @overload # array.__array_ufunc__(self, "reduceat", array, ...) + def reduceat[IxT, OutT]( + self, + array: _CanUfuncReduceAt[IxT, OutT], + /, + indices: IxT, + *, + axis: int = 0, + dtype: npt.DTypeLike | None = None, + out: np.ndarray | None = None, + ) -> OutT: ... + + # + @override + @overload # known shape + def accumulate[ShapeT: _Shape]( # pyrefly:ignore[bad-override] + self, + array: np.ndarray[ShapeT, np.dtype[np.bool]], + /, + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> np.ndarray[ShapeT, np.dtype[np.bool]]: ... + @overload # unknown shape + def accumulate( + self, + array: _ArrayLikeBool_co, + /, + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: None = None, + ) -> npt.NDArray[np.bool]: ... + @overload # out= + def accumulate[OutT: np.ndarray]( + self, + array: _ArrayLikeBool_co, + /, + *, + axis: int = 0, + dtype: _DTypeLikeBool | None = None, + out: OutT, + ) -> OutT: ... + @overload # array.__array_ufunc__(self, "accumulate", array, ...) + def accumulate[OutT]( + self, + array: _CanUfuncAccumulate[OutT], + /, + *, + axis: int = 0, + dtype: npt.DTypeLike | None = None, + out: np.ndarray | None = None, + ) -> OutT: ... + +# efdg, il => efdg +@type_check_only +class _ufunc_21_ldexp(_ufunc_21[None]): # type: ignore[misc] + # TODO(@jorenham): shape-typing + + @override + @overload # 0d T@floating, 0d + def __call__[ScalarT: np.floating]( + self, + x1: ScalarT, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> ScalarT: ... + @overload # 0d float | +f64, 0d + def __call__( + self, + x1: float | _as_f64, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.float64: ... + @overload # 0d +f32, 0d + def __call__( + self, + x1: _as_f32, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.float32: ... + @overload # 0d +f16, 0d + def __call__( + self, + x1: _as_f16, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.float16: ... + @overload # >0d T@floating, >=0d + def __call__[ScalarT: np.floating]( + self, + x1: npt.NDArray[ScalarT] | _NestedSequence[ScalarT], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[ScalarT]: ... + @overload # >0d +f64, >=0d + def __call__( + self, + x1: npt.NDArray[_as_f64] | _NestedSequence[float | _as_f64], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float64]: ... + @overload # >0d +f32, >=0d + def __call__( + self, + x1: npt.NDArray[_as_f32] | _NestedSequence[_as_f32], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float32]: ... + @overload # >0d +f16, >=0d + def __call__( + self, + x1: npt.NDArray[_as_f16] | _NestedSequence[_as_f16], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float16]: ... + @overload # >=0d T@floating, >0d + def __call__[ScalarT: np.floating]( + self, + x1: _ArrayLike[ScalarT], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[ScalarT]: ... + @overload # >=0d +f64, >0d + def __call__( + self, + x1: _DualArrayLike[np.dtype[_as_f64], float], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float64]: ... + @overload # >=0d +f32, >0d + def __call__( + self, + x1: _ArrayLike[_as_f32], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float32]: ... + @overload # >=0d +f32, >0d + def __call__( + self, + x1: _ArrayLike[_as_f16], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float16]: ... + @overload # >=0d T@floating, >=0d, out=... + def __call__[ScalarT: np.floating]( + self, + x1: _ArrayLike[ScalarT], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[ScalarT]: ... + @overload # >=0d +f64, >=0d, out=... + def __call__( + self, + x1: _DualArrayLike[np.dtype[_as_f64], float], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float64]: ... + @overload # >=0d +f32, >=0d, out=... + def __call__( + self, + x1: _ArrayLike[_as_f32], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float32]: ... + @overload # >=0d +f16, >=0d, out=... + def __call__( + self, + x1: _ArrayLike[_as_f16], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float16]: ... + @overload # out= + def __call__[OutT: np.ndarray]( + self, + x1: _ArrayLikeFloat_co, + x2: _ArrayLikeInt_co, + /, + out: OutT, + *, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x1.__array_ufunc__(self, "__call__", x1, x2, ...) + def __call__[OtherT, OutT]( + self, + x1: _CanUfuncCall2L[OtherT, OutT], + x2: OtherT, + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x2.__array_ufunc__(self, "__call__", x1, x2, ...) + def __call__[OtherT, OutT]( + self, + x1: OtherT, + x2: _CanUfuncCall2R[OtherT, OutT], + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + + # + @override + @overload # 0d T@floating, 0d + def outer[ScalarT: np.floating]( # pyrefly:ignore[bad-override] + self, + x1: ScalarT, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> ScalarT: ... + @overload # 0d float | +f64, 0d + def outer( + self, + x1: float | _as_f64, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.float64: ... + @overload # 0d +f32, 0d + def outer( + self, + x1: _as_f32, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.float32: ... + @overload # 0d +f16, 0d + def outer( + self, + x1: _as_f16, + x2: _IntLike_co, + /, + *, + out: None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> np.float16: ... + @overload # >0d T@floating, >=0d + def outer[ScalarT: np.floating]( + self, + x1: npt.NDArray[ScalarT] | _NestedSequence[ScalarT], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[ScalarT]: ... + @overload # >0d +f64, >=0d + def outer( + self, + x1: npt.NDArray[_as_f64] | _NestedSequence[float | _as_f64], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float64]: ... + @overload # >0d +f32, >=0d + def outer( + self, + x1: npt.NDArray[_as_f32] | _NestedSequence[_as_f32], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float32]: ... + @overload # >0d +f16, >=0d + def outer( + self, + x1: npt.NDArray[_as_f16] | _NestedSequence[_as_f16], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float16]: ... + @overload # >=0d T@floating, >0d + def outer[ScalarT: np.floating]( + self, + x1: _ArrayLike[ScalarT], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[ScalarT]: ... + @overload # >=0d +f64, >0d + def outer( + self, + x1: _DualArrayLike[np.dtype[_as_f64], float], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float64]: ... + @overload # >=0d +f32, >0d + def outer( + self, + x1: _ArrayLike[_as_f32], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float32]: ... + @overload # >=0d +f16, >0d + def outer( + self, + x1: _ArrayLike[_as_f16], + x2: npt.NDArray[_to_integer] | _NestedSequence[_IntLike_co], + /, + *, + out: EllipsisType | None = None, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float16]: ... + @overload # >=0d T@floating, >=0d, out=... + def outer[ScalarT: np.floating]( + self, + x1: _ArrayLike[ScalarT], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[ScalarT]: ... + @overload # >=0d +f64, >=0d, out=... + def outer( + self, + x1: _DualArrayLike[np.dtype[_as_f64], float], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float64]: ... + @overload # >=0d +f32, >=0d, out=... + def outer( + self, + x1: _ArrayLike[_as_f32], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float32]: ... + @overload # >=0d +f16, >=0d, out=... + def outer( + self, + x1: _ArrayLike[_as_f16], + x2: _ArrayLikeInt_co, + /, + *, + out: EllipsisType, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> npt.NDArray[np.float16]: ... + @overload # out= + def outer[OutT: np.ndarray]( + self, + x1: _ArrayLikeFloat_co, + x2: _ArrayLikeInt_co, + /, + out: OutT, + *, + dtype: None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x1.__array_ufunc__(self, "outer", x1, x2, ...) + def outer[OtherT, OutT]( + self, + x1: _CanUfuncCall2L[OtherT, OutT], + x2: OtherT, + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + @overload # x2.__array_ufunc__(self, "outer", x1, x2, ...) + def outer[OtherT, OutT]( + self, + x1: OtherT, + x2: _CanUfuncCall2R[OtherT, OutT], + /, + *, + out: object | None = None, + dtype: npt.DTypeLike | None = None, + **kwargs: Unpack[_Kwargs21], + ) -> OutT: ... + + # + @override + @overload + def at(self, a: npt.NDArray[_to_floating], indices: _ArrayLikeInt, b: _ArrayLikeInt_co, /) -> None: ... # pyrefly:ignore[bad-override] + @overload + def at[OtherT, IxT, OutT](self, a: _CanUfuncAt2L[OtherT, IxT, OutT], indices: IxT, b: OtherT, /) -> OutT: ... + @overload + def at[OtherT, IxT, OutT](self, a: OtherT, indices: IxT, b: _CanUfuncAt2R[OtherT, IxT, OutT], /) -> OutT: ... + + # + @override + @overload # unknown shape, +f64 + def reduce( # pyrefly:ignore[bad-override] + self, + array: _DualArrayLike[np.dtype[_as_f64], float], + /, + *, + axis: int | tuple[int, ...] = 0, + dtype: None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float64] | np.float64: ... + @overload # unknown shape, +f64, axis=None + def reduce( + self, + array: _DualArrayLike[np.dtype[_as_f64], float], + /, + *, + axis: None, + dtype: None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> np.float64: ... + @overload # unknown shape, +f64, keepdims=True + def reduce( + self, + array: _DualArrayLike[np.dtype[_as_f64], float], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: None = None, + out: None = None, + keepdims: Literal[True], + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float64]: ... + @overload # unknown shape, +f64, out=... + def reduce( + self, + array: _DualArrayLike[np.dtype[_as_f64], float], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: None = None, + out: EllipsisType, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float64]: ... + @overload # unknown shape, +f32 + def reduce( + self, + array: _ArrayLike[_as_f32], + /, + *, + axis: int | tuple[int, ...] = 0, + dtype: None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float32] | np.float32: ... + @overload # unknown shape, +f32, axis=None + def reduce( + self, + array: _ArrayLike[_as_f32], + /, + *, + axis: None, + dtype: None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> np.float32: ... + @overload # unknown shape, +f32, keepdims=True + def reduce( + self, + array: _ArrayLike[_as_f32], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: None = None, + out: None = None, + keepdims: Literal[True], + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float32]: ... + @overload # unknown shape, +f32, out=... + def reduce( + self, + array: _ArrayLike[_as_f32], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: None = None, + out: EllipsisType, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float32]: ... + @overload # unknown shape, +f16 + def reduce( + self, + array: _ArrayLike[_as_f16], + /, + *, + axis: int | tuple[int, ...] = 0, + dtype: None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float16] | np.float16: ... + @overload # unknown shape, +f16, axis=None + def reduce( + self, + array: _ArrayLike[_as_f16], + /, + *, + axis: None, + dtype: None = None, + out: None = None, + keepdims: Literal[False] = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> np.float16: ... + @overload # unknown shape, +f16, keepdims=True + def reduce( + self, + array: _ArrayLike[_as_f16], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: None = None, + out: None = None, + keepdims: Literal[True], + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float16]: ... + @overload # unknown shape, +f16, out=... + def reduce( + self, + array: _ArrayLike[_as_f16], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: None = None, + out: EllipsisType, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> npt.NDArray[np.float16]: ... + @overload # out= + def reduce[OutT: np.ndarray]( + self, + array: _ArrayLikeInt, + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: None = None, + out: OutT, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> OutT: ... + @overload # array.__array_ufunc__(self, "reduce", array, ...) + def reduce[OutT]( + self, + array: _CanUfuncReduce[OutT], + /, + *, + axis: int | tuple[int, ...] | None = 0, + dtype: npt.DTypeLike | None = None, + out: np.ndarray | EllipsisType | None = None, + keepdims: bool = False, + initial: bool | np.bool = ..., + where: _ArrayLikeBool_co = True, + ) -> OutT: ... + + @override + def reduceat(self, array: Never, /, indices: Never) -> Never: ... # pyrefly:ignore[bad-override] + @override + def accumulate(self, array: Never, /) -> Never: ... # pyrefly:ignore[bad-override] + +logical_and: Final[_ufunc_21_logical[Literal[True]]] = ... +logical_or: Final[_ufunc_21_logical[Literal[False]]] = ... +logical_xor: Final[_ufunc_21_logical[Literal[False]]] = ... + +equal: Final[_ufunc_21_cmp] = ... +greater: Final[_ufunc_21_cmp] = ... +greater_equal: Final[_ufunc_21_cmp] = ... +less: Final[_ufunc_21_cmp] = ... +less_equal: Final[_ufunc_21_cmp] = ... +not_equal: Final[_ufunc_21_cmp] = ... + +ldexp: Final[_ufunc_21_ldexp] = ... + ### # re-exports from `_core._multiarray_umath` that are used by `_core._ufunc_config` diff --git a/numpy/_typing/__init__.py b/numpy/_typing/__init__.py index f69368a95e10..aebd9cc80615 100644 --- a/numpy/_typing/__init__.py +++ b/numpy/_typing/__init__.py @@ -140,8 +140,6 @@ # from ._ufunc import ( _GUFunc_Nin2_Nout1 as _GUFunc_Nin2_Nout1, - _UFunc_Nin1_Nout1 as _UFunc_Nin1_Nout1, - _UFunc_Nin1_Nout2 as _UFunc_Nin1_Nout2, _UFunc_Nin2_Nout1 as _UFunc_Nin2_Nout1, _UFunc_Nin2_Nout2 as _UFunc_Nin2_Nout2, ) diff --git a/numpy/_typing/_ufunc.py b/numpy/_typing/_ufunc.py index db52a1fdb318..7f90b906c541 100644 --- a/numpy/_typing/_ufunc.py +++ b/numpy/_typing/_ufunc.py @@ -1,7 +1,5 @@ from numpy import ufunc -_UFunc_Nin1_Nout1 = ufunc _UFunc_Nin2_Nout1 = ufunc -_UFunc_Nin1_Nout2 = ufunc _UFunc_Nin2_Nout2 = ufunc _GUFunc_Nin2_Nout1 = ufunc diff --git a/numpy/_typing/_ufunc.pyi b/numpy/_typing/_ufunc.pyi index 5ace7d90d00a..1890bea74b50 100644 --- a/numpy/_typing/_ufunc.pyi +++ b/numpy/_typing/_ufunc.pyi @@ -74,75 +74,6 @@ class _ReduceKwargs(TypedDict, total=False): # pyright: reportIncompatibleMethodOverride=false -@type_check_only -class _UFunc_Nin1_Nout1[NameT: LiteralString, NTypesT: int, IdentT](ufunc): # type: ignore[misc] - @property - def __name__(self) -> NameT: ... - @property - def __qualname__(self) -> NameT: ... # pyright: ignore[reportIncompatibleVariableOverride] - @property - def ntypes(self) -> NTypesT: ... - @property - def identity(self) -> IdentT: ... - @property - def nin(self) -> Literal[1]: ... - @property - def nout(self) -> Literal[1]: ... - @property - def nargs(self) -> Literal[2]: ... - @property - def signature(self) -> None: ... - - @overload - def __call__( - self, - x1: _ScalarLike_co, - /, - out: None = None, - *, - dtype: DTypeLike | None = None, - where: _ArrayLikeBool_co | None = True, - casting: _CastingKind = ..., - order: _OrderKACF = ..., - subok: bool = ..., - signature: str | _2Tuple[str | None] = ..., - ) -> Incomplete: ... - @overload - def __call__( - self, - x1: ArrayLike, - /, - out: np.ndarray | tuple[np.ndarray] | EllipsisType | None = None, - *, - dtype: DTypeLike | None = None, - where: _ArrayLikeBool_co | None = True, - casting: _CastingKind = ..., - order: _OrderKACF = ..., - subok: bool = ..., - signature: str | _2Tuple[str | None] = ..., - ) -> NDArray[Incomplete]: ... - @overload - def __call__( - self, - x1: _SupportsArrayUFunc, - /, - out: np.ndarray | tuple[np.ndarray] | EllipsisType | None = None, - *, - dtype: DTypeLike | None = None, - where: _ArrayLikeBool_co | None = True, - casting: _CastingKind = ..., - order: _OrderKACF = ..., - subok: bool = ..., - signature: str | _2Tuple[str | None] = ..., - ) -> Incomplete: ... - - def accumulate(self, array: Never, /) -> NoReturn: ... # type: ignore[override] - def reduce(self, array: Never, /) -> NoReturn: ... # type: ignore[override] - def reduceat(self, array: Never, /, indices: Never) -> NoReturn: ... # type: ignore[override] - def outer(self, A: Never, B: Never, /) -> NoReturn: ... # type: ignore[override] - - def at(self, a: np.ndarray | _SupportsArrayUFunc, indices: _ArrayLikeInt_co, /) -> None: ... # type: ignore[override] - @type_check_only class _UFunc_Nin2_Nout1[NameT: LiteralString, NTypesT: int, IdentT](ufunc): # type: ignore[misc] @property @@ -342,80 +273,6 @@ class _UFunc_Nin2_Nout1[NameT: LiteralString, NTypesT: int, IdentT](ufunc): # t /, ) -> None: ... -@type_check_only -class _UFunc_Nin1_Nout2[NameT: LiteralString, NTypesT: int, IdentT](ufunc): # type: ignore[misc] - @property - def __name__(self) -> NameT: ... - @property - def __qualname__(self) -> NameT: ... # pyright: ignore[reportIncompatibleVariableOverride] - @property - def ntypes(self) -> NTypesT: ... - @property - def identity(self) -> IdentT: ... - @property - def nin(self) -> Literal[1]: ... - @property - def nout(self) -> Literal[2]: ... - @property - def nargs(self) -> Literal[3]: ... - @property - def signature(self) -> None: ... - - @overload - def __call__( - self, - x1: _ScalarLike_co, - out1: EllipsisType | None = ..., - out2: None = None, - /, - *, - out: EllipsisType | None = ..., - dtype: DTypeLike | None = None, - where: _ArrayLikeBool_co | None = True, - casting: _CastingKind = ..., - order: _OrderKACF = ..., - subok: bool = ..., - signature: str | _3Tuple[str | None] = ..., - ) -> _2Tuple[Incomplete]: ... - @overload - def __call__( - self, - x1: ArrayLike, - out1: np.ndarray | EllipsisType | None = ..., - out2: np.ndarray | None = ..., - /, - *, - out: _2Tuple[np.ndarray] | EllipsisType = ..., - dtype: DTypeLike | None = None, - where: _ArrayLikeBool_co | None = True, - casting: _CastingKind = ..., - order: _OrderKACF = ..., - subok: bool = ..., - signature: str | _3Tuple[str | None] = ..., - ) -> _2Tuple[NDArray[Incomplete]]: ... - @overload - def __call__( - self, - x1: _SupportsArrayUFunc, - out1: np.ndarray | EllipsisType | None = ..., - out2: np.ndarray | None = ..., - /, - *, - out: _2Tuple[np.ndarray] | EllipsisType = ..., - dtype: DTypeLike | None = None, - where: _ArrayLikeBool_co | None = True, - casting: _CastingKind = ..., - order: _OrderKACF = ..., - subok: bool = ..., - signature: str | _3Tuple[str | None] = ..., - ) -> _2Tuple[Incomplete]: ... - - def accumulate(self, array: Never, /) -> NoReturn: ... # type: ignore[override] - def reduce(self, array: Never, /) -> NoReturn: ... # type: ignore[override] - def reduceat(self, array: Never, /, indices: Never) -> NoReturn: ... # type: ignore[override] - def outer(self, A: Never, B: Never, /) -> NoReturn: ... # type: ignore[override] - def at(self, a: Never, indices: Never, /) -> NoReturn: ... # type: ignore[override] - @type_check_only class _UFunc_Nin2_Nout2[NameT: LiteralString, NTypesT: int, IdentT](ufunc): # type: ignore[misc] @property diff --git a/numpy/lib/_format_impl.py b/numpy/lib/_format_impl.py index 51b16ce0de48..5c79947cb3a1 100644 --- a/numpy/lib/_format_impl.py +++ b/numpy/lib/_format_impl.py @@ -519,6 +519,9 @@ def read_array_header_1_0(fp, max_header_size=_MAX_HEADER_SIZE): ---------- fp : filelike object A file object or something with a `.read()` method like a file. + Must be positioned at the start of the header, just after the + magic string and version bytes. This can be done by calling + `read_magic` on ``fp`` first. Returns ------- @@ -557,6 +560,9 @@ def read_array_header_2_0(fp, max_header_size=_MAX_HEADER_SIZE): ---------- fp : filelike object A file object or something with a `.read()` method like a file. + Must be positioned at the start of the header, just after the + magic string and version bytes. This can be done by calling + `read_magic` on ``fp`` first. max_header_size : int, optional Maximum allowed size of the header. Large headers may not be safe to load securely and thus require explicitly passing a larger value. diff --git a/numpy/lib/_function_base_impl.py b/numpy/lib/_function_base_impl.py index 18b3ce783c8f..aaac4abea546 100644 --- a/numpy/lib/_function_base_impl.py +++ b/numpy/lib/_function_base_impl.py @@ -5562,6 +5562,15 @@ def insert(arr, obj, values, axis=None): # Can safely cast the empty list to intp indices = indices.astype(intp) + if indices.size > 0: + min_idx = indices.min() + max_idx = indices.max() + if min_idx < -N or max_idx > N: + oob = min_idx if min_idx < -N else max_idx + raise IndexError( + f"index {oob} is out of bounds " + f"for axis {axis} with size {N}" + ) indices[indices < 0] += N numnew = len(indices) diff --git a/numpy/lib/_function_base_impl.pyi b/numpy/lib/_function_base_impl.pyi index 5887d7d496ce..96f3cb70b5b1 100644 --- a/numpy/lib/_function_base_impl.pyi +++ b/numpy/lib/_function_base_impl.pyi @@ -470,6 +470,12 @@ def asarray_chkfinite(a: object, dtype: DTypeLike | None = None, order: _OrderKA # `[condlist]`. And even though the documentation says these should be boolean, in # practice anything that `np.array(condlist, dtype=bool)` accepts will work, i.e. any # array-like. +@overload # this overload is required to avoid pyright rejecting all-scalar `funclist` +def piecewise[ShapeT: _Shape, ScalarT: np.generic]( + x: _Array[ShapeT, ScalarT], + condlist: ArrayLike, + funclist: _SizedIterable[_ScalarLike_co], +) -> _Array[ShapeT, ScalarT]: ... @overload def piecewise[ShapeT: _Shape, ScalarT: np.generic, **Tss]( x: _Array[ShapeT, ScalarT], diff --git a/numpy/lib/_nanfunctions_impl.py b/numpy/lib/_nanfunctions_impl.py index 44110dd751e4..252f9cc83f8c 100644 --- a/numpy/lib/_nanfunctions_impl.py +++ b/numpy/lib/_nanfunctions_impl.py @@ -751,7 +751,7 @@ def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, the product of the flattened array. dtype : data-type, optional The type of the returned array and of the accumulator in which the - elements are summed. By default, the dtype of `a` is used. An + elements are multiplied. By default, the dtype of `a` is used. An exception is when `a` has an integer type with less precision than the platform (u)intp. In that case, the default will be either (u)int32 or (u)int64 depending on whether the platform is 32 or 64 diff --git a/numpy/lib/_twodim_base_impl.pyi b/numpy/lib/_twodim_base_impl.pyi index af8e3d72c4d8..f6c08b6a5d21 100644 --- a/numpy/lib/_twodim_base_impl.pyi +++ b/numpy/lib/_twodim_base_impl.pyi @@ -40,6 +40,8 @@ type _Int_co = np.integer | np.bool type _Float_co = np.floating | _Int_co type _Number_co = np.number | np.bool +type _AtLeast2D = tuple[int, int, *tuple[int, ...]] # input only + type _Array1D[ScalarT: np.generic] = np.ndarray[tuple[int], np.dtype[ScalarT]] type _Array2D[ScalarT: np.generic] = np.ndarray[tuple[int, int], np.dtype[ScalarT]] # Workaround for mypy's and pyright's lack of compliance with the typing spec for @@ -192,7 +194,9 @@ def tri( # keep in sync with `triu` @overload -def tril[ArrayT: np.ndarray](m: ArrayT, k: int = 0) -> ArrayT: ... +def tril[ArrayT: np.ndarray[_AtLeast2D]](m: ArrayT, k: int = 0) -> ArrayT: ... +@overload +def tril[DTypeT: np.dtype](m: np.ndarray[tuple[int], DTypeT], k: int = 0) -> np.ndarray[tuple[int, int], DTypeT]: ... @overload def tril[ScalarT: np.generic](m: _ArrayLike[ScalarT], k: int = 0) -> NDArray[ScalarT]: ... @overload @@ -200,7 +204,9 @@ def tril(m: ArrayLike, k: int = 0) -> NDArray[Any]: ... # keep in sync with `tril` @overload -def triu[ArrayT: np.ndarray](m: ArrayT, k: int = 0) -> ArrayT: ... +def triu[ArrayT: np.ndarray[_AtLeast2D]](m: ArrayT, k: int = 0) -> ArrayT: ... +@overload +def triu[DTypeT: np.dtype](m: np.ndarray[tuple[int], DTypeT], k: int = 0) -> np.ndarray[tuple[int, int], DTypeT]: ... @overload def triu[ScalarT: np.generic](m: _ArrayLike[ScalarT], k: int = 0) -> NDArray[ScalarT]: ... @overload diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index cd3d34893cbb..9440ec3f1c5f 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -698,10 +698,14 @@ def test_index_floats(self): with pytest.raises(IndexError): np.insert([0, 1, 2], np.array([], dtype=float), []) - @pytest.mark.parametrize('idx', [4, -4]) - def test_index_out_of_bounds(self, idx): + @pytest.mark.parametrize('idx,values', [ + ([4], [3, 4]), + ([-4], [3, 4]), + ([-6, 0], [9, 8]), + ]) + def test_index_out_of_bounds(self, idx, values): with pytest.raises(IndexError, match='out of bounds'): - np.insert([0, 1, 2], [idx], [3, 4]) + np.insert([0, 1, 2], idx, values) class TestAmax: diff --git a/numpy/linalg/_linalg.pyi b/numpy/linalg/_linalg.pyi index 8eb3e57cf1e2..1f3f79faecfc 100644 --- a/numpy/linalg/_linalg.pyi +++ b/numpy/linalg/_linalg.pyi @@ -196,8 +196,8 @@ class SVDResult(NamedTuple, Generic[_FloatingT_co, _InexactT_co]): Vh: NDArray[_InexactT_co] class SlogdetResult(NamedTuple, Generic[_FloatingOrArrayT_co, _InexactOrArrayT_co]): - sign: _FloatingOrArrayT_co - logabsdet: _InexactOrArrayT_co + sign: _InexactOrArrayT_co + logabsdet: _FloatingOrArrayT_co # keep in sync with `solve` @overload # ~float64, +float64 @@ -962,43 +962,49 @@ def diagonal(x: ArrayLike, /, *, offset: SupportsIndex = 0) -> np.ndarray: ... def trace( x: _SupportsArray[_JustAnyShape, np.dtype[_to_complex]], /, *, offset: SupportsIndex = 0, dtype: DTypeLike | None = None ) -> Any: ... -@overload # 2d known dtype, dtype=None -def trace[ScalarT: _to_complex](x: _ArrayLike2D[ScalarT], /, *, offset: SupportsIndex = 0, dtype: None = None) -> ScalarT: ... +@overload # 2d known dtype +def trace[ScalarT: np.inexact](x: _ArrayLike2D[ScalarT], /, *, offset: SupportsIndex = 0, dtype: None = None) -> ScalarT: ... @overload # 2d, dtype= -def trace[ScalarT: _to_complex]( - x: _ToArrayComplex_2d, /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT] -) -> ScalarT: ... -@overload # 2d bool -def trace(x: _Sequence2D[bool], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.bool: ... -@overload # 2d int -def trace(x: Sequence[list[int]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.int_: ... -@overload # 2d float +def trace[ScalarT: np.number](x: _ToArrayComplex_2d, /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT]) -> ScalarT: ... +@overload # 2d +int +def trace( + x: _ArrayLike2D[np.integer | np.bool] | Sequence[Sequence[int]], /, *, offset: SupportsIndex = 0, dtype: None = None +) -> np.int_: ... +@overload # 2d ~float def trace(x: Sequence[list[float]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.float64: ... -@overload # 2d complex +@overload # 2d ~complex def trace(x: Sequence[list[complex]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> np.complex128: ... -@overload # 3d known dtype, dtype=None -def trace[DTypeT: np.dtype[_to_complex]]( +@overload # 3d known dtype +def trace[DTypeT: np.dtype[np.inexact]]( x: _SupportsArray[tuple[int, int, int], DTypeT], /, *, offset: SupportsIndex = 0, dtype: None = None ) -> np.ndarray[tuple[int], DTypeT]: ... +@overload # 3d +int +def trace( + x: _SupportsArray[tuple[int, int, int], np.dtype[np.integer | np.bool]], /, *, offset: SupportsIndex = 0, dtype: None = None +) -> np.ndarray[tuple[int], np.dtype[np.int_]]: ... @overload # 3d, dtype= -def trace[ScalarT: _to_complex]( +def trace[ScalarT: np.number]( x: _ToArrayComplex_3d, /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT] ) -> _Array1D[ScalarT]: ... -@overload # 3d+ known dtype, dtype=None -def trace[DTypeT: np.dtype[_to_complex]]( +@overload # 3d+ known dtype +def trace[DTypeT: np.dtype[np.inexact]]( x: _SupportsArray[_AtLeast3D, DTypeT], /, *, offset: SupportsIndex = 0, dtype: None = None ) -> np.ndarray[tuple[int, *tuple[Any, ...]], DTypeT]: ... @overload # 3d+, dtype= -def trace[ScalarT: _to_complex]( +def trace[ScalarT: np.inexact]( x: _ArrayLike3ND[_to_complex] | _Sequence3ND[complex], /, *, offset: SupportsIndex = 0, dtype: _DTypeLike[ScalarT] ) -> np.ndarray[tuple[int, *tuple[Any, ...]], np.dtype[ScalarT]]: ... -@overload # 3d+ bool -def trace(x: _Sequence3ND[bool], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.bool]: ... -@overload # 3d+ int -def trace(x: _Sequence2ND[list[int]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.int_]: ... -@overload # 3d+ float +@overload # 3d+ +integer +def trace( + x: _SupportsArray[_AtLeast3D, np.dtype[np.integer | np.bool]] | _Sequence2ND[Sequence[int]], + /, + *, + offset: SupportsIndex = 0, + dtype: None = None, +) -> NDArray[np.int_]: ... +@overload # 3d+ ~float def trace(x: _Sequence2ND[list[float]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.float64]: ... -@overload # 3d+ complex +@overload # 3d+ ~complex def trace(x: _Sequence2ND[list[complex]], /, *, offset: SupportsIndex = 0, dtype: None = None) -> NDArray[np.complex128]: ... @overload # fallback def trace(x: _ArrayLikeComplex_co, /, *, offset: SupportsIndex = 0, dtype: DTypeLike | None = None) -> Any: ... diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 7ec99eb30fb0..d2cdd5c8e42a 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -8451,6 +8451,8 @@ def convolve(a, v, mode='full', propagate_mask=True): See Also -------- numpy.convolve : Equivalent function in the top-level NumPy module. + numpy.ma.correlate : Cross-correlation of two 1-D sequences; see its + examples for ``propagate_mask`` behaviour. """ return _convolve_or_correlate(np.convolve, a, v, mode, propagate_mask) diff --git a/numpy/ma/core.pyi b/numpy/ma/core.pyi index c7b189f2e230..0e1260e30d65 100644 --- a/numpy/ma/core.pyi +++ b/numpy/ma/core.pyi @@ -2073,7 +2073,7 @@ class MaskedArray(ndarray[_ShapeT_co, _DTypeT_co]): # Keep in sync with `ndarray.cumprod` @override # type: ignore[override] @overload - def cumprod[DTypeT: dtype[number | object_]]( + def cumprod[DTypeT: dtype[inexact | object_]]( self: MaskedArray[Any, DTypeT], axis: None = None, dtype: None = None, @@ -2081,7 +2081,7 @@ class MaskedArray(ndarray[_ShapeT_co, _DTypeT_co]): ) -> MaskedArray[tuple[int], DTypeT]: ... @overload # bool_ def cumprod( - self: _MaskedArray[np.bool], + self: _MaskedArray[np.integer | np.bool], axis: None = None, dtype: None = None, out: None = None, @@ -2117,15 +2117,15 @@ class MaskedArray(ndarray[_ShapeT_co, _DTypeT_co]): out: None = None, ) -> _Masked1D[Any]: ... @overload # axis: - def cumprod[ArrayT: _MaskedArray[number | object_]]( + def cumprod[ArrayT: _MaskedArray[np.inexact | object_]]( self: ArrayT, axis: SupportsIndex, dtype: None = None, out: None = None, ) -> ArrayT: ... - @overload # bool_, axis: + @overload # +integer, axis: def cumprod[ShapeT: _Shape]( - self: MaskedArray[ShapeT, np.dtype[np.bool]], + self: MaskedArray[ShapeT, np.dtype[np.integer | np.bool]], axis: SupportsIndex, dtype: None = None, out: None = None, @@ -2163,7 +2163,7 @@ class MaskedArray(ndarray[_ShapeT_co, _DTypeT_co]): # Keep in sync with `ndarray.cumsum` @override # type: ignore[override] @overload - def cumsum[DTypeT: dtype[number | timedelta64 | object_]]( + def cumsum[DTypeT: dtype[inexact | timedelta64 | object_]]( self: MaskedArray[Any, DTypeT], axis: None = None, dtype: None = None, @@ -2171,7 +2171,7 @@ class MaskedArray(ndarray[_ShapeT_co, _DTypeT_co]): ) -> MaskedArray[tuple[int], DTypeT]: ... @overload # bool_ def cumsum( - self: _MaskedArray[np.bool], + self: _MaskedArray[np.integer | np.bool], axis: None = None, dtype: None = None, out: None = None, @@ -2213,9 +2213,9 @@ class MaskedArray(ndarray[_ShapeT_co, _DTypeT_co]): dtype: None = None, out: None = None, ) -> ArrayT: ... - @overload # bool_, axis: + @overload # +integer, axis: def cumsum[ShapeT: _Shape]( - self: MaskedArray[ShapeT, np.dtype[np.bool]], + self: MaskedArray[ShapeT, np.dtype[np.integer | np.bool]], axis: SupportsIndex, dtype: None = None, out: None = None, diff --git a/numpy/ma/tests/test_mrecords.py b/numpy/ma/tests/test_mrecords.py index b4070df0f9a3..0cd4aa62ec32 100644 --- a/numpy/ma/tests/test_mrecords.py +++ b/numpy/ma/tests/test_mrecords.py @@ -384,6 +384,24 @@ def test_view_flexible_type(self): assert_equal(test.dtype, np.dtype(alttype)) assert_(test._fill_value is None) + def test_view_ndarray_subclass_preserves_dtype(self): + mrec = self._create_data()[0] + + class MySub(np.ndarray): + pass + + test = mrec.view(MySub) + assert_(isinstance(test, MySub)) + assert_equal(test.dtype, mrec.dtype) + + def test_view_maskedarray_preserves_fill_value(self): + mrec = self._create_data()[0] + original_fv = mrec.fill_value + + test = mrec.view(ma.MaskedArray) + assert_(isinstance(test, ma.MaskedArray)) + assert_equal(test.fill_value, original_fv) + ############################################################################## class TestMRecordsImport: diff --git a/numpy/random/_generator.pyi b/numpy/random/_generator.pyi index 634aaf68912c..e45990c1db65 100644 --- a/numpy/random/_generator.pyi +++ b/numpy/random/_generator.pyi @@ -688,17 +688,28 @@ class Generator: def permuted[ArrayT: np.ndarray](self, /, x: ArrayLike, *, axis: int | None = None, out: ArrayT) -> ArrayT: ... # - @overload # >=0d int, size=None (default) + @overload # 0d int, size=None (default) def choice( self, /, - a: int | _NestedSequence[int], + a: int, size: None = None, replace: bool = True, p: _ArrayLikeFloat_co | None = None, axis: int = 0, shuffle: bool = True, ) -> int: ... + @overload # >0d int, size=None (default) + def choice( + self, + /, + a: _NestedSequence[int], + size: None = None, + replace: bool = True, + p: _ArrayLikeFloat_co | None = None, + axis: int = 0, + shuffle: bool = True, + ) -> np.int_: ... @overload # >=0d known, size=None (default) def choice[ScalarT: np.generic]( self, diff --git a/numpy/random/bit_generator.pyx b/numpy/random/bit_generator.pyx index 676f95e5ad70..614b33726a34 100644 --- a/numpy/random/bit_generator.pyx +++ b/numpy/random/bit_generator.pyx @@ -142,9 +142,16 @@ def _coerce_to_uint32_array(x): else: if len(x) == 0: return np.array([], dtype=np.uint32) + # Should be a sequence of interpretable-as-ints. Convert each one to # a uint32 array and concatenate. - subseqs = [_coerce_to_uint32_array(v) for v in x] + subseqs = [] + for v in x: + if hasattr(v, '__len__') and not isinstance(v, str): + raise TypeError("SeedSequence does not accept nested sequences.") + + subseqs.append(_coerce_to_uint32_array(v)) + return np.concatenate(subseqs) diff --git a/numpy/random/mtrand.pyi b/numpy/random/mtrand.pyi index 066a56545f23..116364284e0d 100644 --- a/numpy/random/mtrand.pyi +++ b/numpy/random/mtrand.pyi @@ -93,13 +93,13 @@ class RandomState: def __reduce__(self) -> tuple[Callable[[BitGenerator], RandomState], tuple[BitGenerator], dict[str, Any]]: ... # - def seed(self, seed: _ArrayLikeFloat_co | None = None) -> None: ... + def seed(self, seed: _ArrayLikeInt_co | None = None) -> None: ... # @overload - def get_state(self, legacy: Literal[False] = False) -> dict[str, Any]: ... + def get_state(self, legacy: Literal[False]) -> dict[str, Any]: ... @overload - def get_state(self, legacy: Literal[True] = True) -> dict[str, Any] | tuple[str, NDArray[np.uint32], int, int, float]: ... + def get_state(self, legacy: Literal[True] = True) -> tuple[str, NDArray[np.uint32], int, int, float]: ... # def set_state(self, state: dict[str, Any] | tuple[str, NDArray[np.uint32], int, int, float]) -> None: ... diff --git a/numpy/random/tests/test_extending.py b/numpy/random/tests/test_extending.py index 23a82f8a3b32..64f3288ae9b7 100644 --- a/numpy/random/tests/test_extending.py +++ b/numpy/random/tests/test_extending.py @@ -38,7 +38,7 @@ else: from numpy._utils import _pep440 # Note: keep in sync with the one in pyproject.toml - required_version = '3.0.6' + required_version = '3.1.0' if _pep440.parse(cython_version) < _pep440.Version(required_version): # too old or wrong cython, skip the test cython = None diff --git a/numpy/random/tests/test_seed_sequence.py b/numpy/random/tests/test_seed_sequence.py index 87ae4ff72139..7e91244c0fed 100644 --- a/numpy/random/tests/test_seed_sequence.py +++ b/numpy/random/tests/test_seed_sequence.py @@ -1,6 +1,11 @@ import numpy as np from numpy.random import SeedSequence -from numpy.testing import assert_array_compare, assert_array_equal +from numpy.testing import ( + assert_array_compare, + assert_array_equal, + assert_raises, + assert_raises_regex, +) def test_reference_data(): @@ -77,3 +82,22 @@ def test_zero_padding(): np.not_equal, SeedSequence(42, spawn_key=(0,)).generate_state(4), expected42) + + +def test_seedsequence_rejects_nested_sequence(): + with assert_raises(TypeError): + SeedSequence(SeedSequence(42)) + + # Prevents infinite recursion (Issue #28822) and rejects + # invalid types (Issue #27380) + match_str = "SeedSequence does not accept nested sequences." + + # Test standard nested lists + with assert_raises_regex(TypeError, match_str): + SeedSequence([[1, 2], [3, 4]]) + + # Test self-referencing/cyclic lists + with assert_raises_regex(TypeError, match_str): + cyclic_seed = [] + cyclic_seed.append(cyclic_seed) + SeedSequence(cyclic_seed) diff --git a/numpy/typing/tests/data/fail/lib_function_base.pyi b/numpy/typing/tests/data/fail/lib_function_base.pyi index d7be993bf04c..6dd26af01307 100644 --- a/numpy/typing/tests/data/fail/lib_function_base.pyi +++ b/numpy/typing/tests/data/fail/lib_function_base.pyi @@ -24,8 +24,8 @@ np.vectorize(1) # type: ignore[arg-type] np.place(AR_f8, slice(None), 5) # type: ignore[arg-type] np.piecewise(AR_f8, True, [fn_ar_i], "wrong") # type: ignore[call-overload] -np.piecewise(AR_f8, AR_b_list, [fn_none_i]) # type: ignore[call-overload] -np.piecewise(AR_f8, AR_b_list, [fn_ar_i]) # type: ignore[call-overload] +np.piecewise(AR_f8, AR_b_list, [fn_none_i]) # type: ignore[list-item] +np.piecewise(AR_f8, AR_b_list, [fn_ar_i]) # type: ignore[list-item] np.piecewise(AR_f8, AR_b_list, [fn_ar_i], 3.14) # type: ignore[call-overload] np.piecewise(AR_f8, AR_b_list, [fn_ar_i], 42, None) # type: ignore[call-overload] np.piecewise(AR_f8, AR_b_list, [fn_ar_i], 42, _=None) # type: ignore[list-item] diff --git a/numpy/typing/tests/data/fail/ufuncs.pyi b/numpy/typing/tests/data/fail/ufuncs.pyi index 1b1628d7da44..1e6ec7ed15f6 100644 --- a/numpy/typing/tests/data/fail/ufuncs.pyi +++ b/numpy/typing/tests/data/fail/ufuncs.pyi @@ -12,6 +12,3 @@ np.add(1, 1, 1) # type: ignore[call-overload] np.add(1, 1, axis=0) # type: ignore[call-overload] np.matmul(AR_f8, AR_f8, where=True) # type: ignore[call-overload] - -np.frexp(AR_f8, out=None) # type: ignore[call-overload] -np.frexp(AR_f8, out=AR_f8) # type: ignore[call-overload] diff --git a/numpy/typing/tests/data/reveal/fromnumeric.pyi b/numpy/typing/tests/data/reveal/fromnumeric.pyi index af473fd41305..9b84a8987054 100644 --- a/numpy/typing/tests/data/reveal/fromnumeric.pyi +++ b/numpy/typing/tests/data/reveal/fromnumeric.pyi @@ -13,7 +13,9 @@ AR_f4: npt.NDArray[np.float32] AR_f4_1d: np.ndarray[tuple[int], np.dtype[np.float32]] AR_f4_2d: np.ndarray[tuple[int, int], np.dtype[np.float32]] AR_f4_3d: np.ndarray[tuple[int, int, int], np.dtype[np.float32]] +AR_c8: npt.NDArray[np.complex64] AR_c16: npt.NDArray[np.complex128] +AR_i1: npt.NDArray[np.int8] AR_u8: npt.NDArray[np.uint64] AR_i8: npt.NDArray[np.int64] AR_O: npt.NDArray[np.object_] @@ -242,22 +244,26 @@ assert_type(np.any(AR_f4_2d, keepdims=True), np.ndarray[tuple[int, int], np.dtyp assert_type(np.any(AR_f4_3d, keepdims=True), np.ndarray[tuple[int, int, int], np.dtype[np.bool]]) assert_type(np.any(AR_f4, out=AR_subclass), NDArraySubclass) -assert_type(np.cumsum(b), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.cumsum(b), np.ndarray[tuple[int], np.dtype[np.int_]]) assert_type(np.cumsum(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumsum(f), np.ndarray[tuple[int]]) -assert_type(np.cumsum(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) -assert_type(np.cumsum(AR_b, axis=0), npt.NDArray[np.bool]) +assert_type(np.cumsum(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumsum(AR_b, axis=0), npt.NDArray[np.int_]) +assert_type(np.cumsum(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumsum(AR_i1, axis=0), npt.NDArray[np.int_]) assert_type(np.cumsum(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumsum(AR_f4, axis=0), npt.NDArray[np.float32]) assert_type(np.cumsum(f, dtype=float), np.ndarray[tuple[int]]) assert_type(np.cumsum(f, dtype=np.float64), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(np.cumsum(AR_f4, out=AR_subclass), NDArraySubclass) -assert_type(np.cumulative_sum(b), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.cumulative_sum(b), np.ndarray[tuple[int], np.dtype[np.int_]]) assert_type(np.cumulative_sum(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumulative_sum(f), np.ndarray[tuple[int]]) -assert_type(np.cumulative_sum(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) -assert_type(np.cumulative_sum(AR_b, axis=0), npt.NDArray[np.bool]) +assert_type(np.cumulative_sum(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumulative_sum(AR_b, axis=0), npt.NDArray[np.int_]) +assert_type(np.cumulative_sum(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumulative_sum(AR_i1, axis=0), npt.NDArray[np.int_]) assert_type(np.cumulative_sum(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumulative_sum(AR_f4, axis=0), npt.NDArray[np.float32]) assert_type(np.cumulative_sum(f, dtype=float), np.ndarray[tuple[int]]) @@ -342,22 +348,26 @@ assert_type(np.amin(_dtype_list), Any) assert_type(np.amin(_dtype_list, axis=1), npt.NDArray[Any]) assert_type(np.amin(_dtype_list, keepdims=True), npt.NDArray[Any]) -assert_type(np.cumprod(b), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.cumprod(b), np.ndarray[tuple[int], np.dtype[np.int_]]) assert_type(np.cumprod(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumprod(f), np.ndarray[tuple[int]]) -assert_type(np.cumprod(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) -assert_type(np.cumprod(AR_b, axis=0), npt.NDArray[np.bool]) +assert_type(np.cumprod(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumprod(AR_b, axis=0), npt.NDArray[np.int_]) +assert_type(np.cumprod(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumprod(AR_i1, axis=0), npt.NDArray[np.int_]) assert_type(np.cumprod(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumprod(AR_f4, axis=0), npt.NDArray[np.float32]) assert_type(np.cumprod(f, dtype=float), np.ndarray[tuple[int]]) assert_type(np.cumprod(f, dtype=np.float64), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(np.cumprod(AR_f4, out=AR_subclass), NDArraySubclass) -assert_type(np.cumulative_prod(b), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.cumulative_prod(b), np.ndarray[tuple[int], np.dtype[np.int_]]) assert_type(np.cumulative_prod(f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumulative_prod(f), np.ndarray[tuple[int]]) -assert_type(np.cumulative_prod(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) -assert_type(np.cumulative_prod(AR_b, axis=0), npt.NDArray[np.bool]) +assert_type(np.cumulative_prod(AR_b), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumulative_prod(AR_b, axis=0), npt.NDArray[np.int_]) +assert_type(np.cumulative_prod(AR_i1), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(np.cumulative_prod(AR_i1, axis=0), npt.NDArray[np.int_]) assert_type(np.cumulative_prod(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cumulative_prod(AR_f4, axis=0), npt.NDArray[np.float32]) assert_type(np.cumulative_prod(f, dtype=float), np.ndarray[tuple[int]]) @@ -389,10 +399,14 @@ assert_type(np.around(AR_f4_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.prod(AR_nd), Any) assert_type(np.prod(AR_b), np.int_) -assert_type(np.prod(AR_i8), np.int64) -assert_type(np.prod(AR_i8, axis=0), npt.NDArray[np.int64]) -assert_type(np.prod(AR_i8, keepdims=True), npt.NDArray[np.int64]) -assert_type(np.prod(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int64]) +assert_type(np.prod(AR_i1), np.int_) +assert_type(np.prod(AR_i1, axis=0), npt.NDArray[np.int_]) +assert_type(np.prod(AR_i1, keepdims=True), npt.NDArray[np.int_]) +assert_type(np.prod(AR_i1, axis=0, keepdims=True), npt.NDArray[np.int_]) +assert_type(np.prod(AR_i8), np.int_) +assert_type(np.prod(AR_i8, axis=0), npt.NDArray[np.int_]) +assert_type(np.prod(AR_i8, keepdims=True), npt.NDArray[np.int_]) +assert_type(np.prod(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int_]) assert_type(np.prod(AR_f4), np.float32) assert_type(np.prod(AR_c16), np.complex128) assert_type(np.prod(AR_O), Any) @@ -418,10 +432,14 @@ assert_type(np.prod(AR_f4, out=AR_subclass), NDArraySubclass) # same as above (but with `timedelta64`) assert_type(np.sum(AR_nd), Any) assert_type(np.sum(AR_b), np.int_) -assert_type(np.sum(AR_i8), np.int64) -assert_type(np.sum(AR_i8, axis=0), npt.NDArray[np.int64]) -assert_type(np.sum(AR_i8, keepdims=True), npt.NDArray[np.int64]) -assert_type(np.sum(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int64]) +assert_type(np.sum(AR_i1), np.int_) +assert_type(np.sum(AR_i1, axis=0), npt.NDArray[np.int_]) +assert_type(np.sum(AR_i1, keepdims=True), npt.NDArray[np.int_]) +assert_type(np.sum(AR_i1, axis=0, keepdims=True), npt.NDArray[np.int_]) +assert_type(np.sum(AR_i8), np.int_) +assert_type(np.sum(AR_i8, axis=0), npt.NDArray[np.int_]) +assert_type(np.sum(AR_i8, keepdims=True), npt.NDArray[np.int_]) +assert_type(np.sum(AR_i8, axis=0, keepdims=True), npt.NDArray[np.int_]) assert_type(np.sum(AR_f4), np.float32) assert_type(np.sum(AR_c16), np.complex128) assert_type(np.sum(AR_O), Any) @@ -485,7 +503,12 @@ assert_type(np.std(AR_i8, axis=0), npt.NDArray[np.float64]) assert_type(np.std(AR_i8, keepdims=True), npt.NDArray[np.float64]) assert_type(np.std(AR_i8, axis=0, keepdims=True), npt.NDArray[np.float64]) assert_type(np.std(AR_f4), np.float32) -assert_type(np.std(AR_c16), np.complex128) +assert_type(np.std(AR_c8), Any) +assert_type(np.std(AR_c8, axis=0), npt.NDArray[Any]) +assert_type(np.std(AR_c8, keepdims=True), npt.NDArray[Any]) +assert_type(np.std(AR_c16), np.float64) +assert_type(np.std(AR_c16, axis=0), npt.NDArray[np.float64]) +assert_type(np.std(AR_c16, keepdims=True), npt.NDArray[np.float64]) assert_type(np.std(AR_O), np.float64) assert_type(np.std(AR_O, axis=0), npt.NDArray[np.object_]) assert_type(np.std(AR_O, keepdims=True), npt.NDArray[np.object_]) @@ -516,7 +539,12 @@ assert_type(np.var(AR_i8, axis=0), npt.NDArray[np.float64]) assert_type(np.var(AR_i8, keepdims=True), npt.NDArray[np.float64]) assert_type(np.var(AR_i8, axis=0, keepdims=True), npt.NDArray[np.float64]) assert_type(np.var(AR_f4), np.float32) -assert_type(np.var(AR_c16), np.complex128) +assert_type(np.var(AR_c8), Any) +assert_type(np.var(AR_c8, axis=0), npt.NDArray[Any]) +assert_type(np.var(AR_c8, keepdims=True), npt.NDArray[Any]) +assert_type(np.var(AR_c16), np.float64) +assert_type(np.var(AR_c16, axis=0), npt.NDArray[np.float64]) +assert_type(np.var(AR_c16, keepdims=True), npt.NDArray[np.float64]) assert_type(np.var(AR_O), np.float64) assert_type(np.var(AR_O, axis=0), npt.NDArray[np.object_]) assert_type(np.var(AR_O, keepdims=True), npt.NDArray[np.object_]) diff --git a/numpy/typing/tests/data/reveal/lib_function_base.pyi b/numpy/typing/tests/data/reveal/lib_function_base.pyi index bab43106d1c5..e288237e3bf9 100644 --- a/numpy/typing/tests/data/reveal/lib_function_base.pyi +++ b/numpy/typing/tests/data/reveal/lib_function_base.pyi @@ -103,6 +103,7 @@ assert_type(np.asarray_chkfinite(AR_f8, dtype=np.float64), npt.NDArray[np.float6 assert_type(np.asarray_chkfinite(AR_f8, dtype=float), np.ndarray) # piecewise +assert_type(np.piecewise(AR_f8_1d, AR_b, [-1.0, 1.0]), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(np.piecewise(AR_f8_1d, AR_b, [func]), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(np.piecewise(AR_f8, AR_b, [func]), npt.NDArray[np.float64]) assert_type(np.piecewise(AR_f8, AR_b, [func_f8]), npt.NDArray[np.float64]) diff --git a/numpy/typing/tests/data/reveal/linalg.pyi b/numpy/typing/tests/data/reveal/linalg.pyi index a1155e2bb5ed..d58204a5b1e5 100644 --- a/numpy/typing/tests/data/reveal/linalg.pyi +++ b/numpy/typing/tests/data/reveal/linalg.pyi @@ -16,8 +16,10 @@ type _Array3D[ScalarT: np.generic] = np.ndarray[tuple[int, int, int], np.dtype[S bool_list_1d: list[bool] bool_list_2d: list[list[bool]] +bool_list_3d: list[list[list[bool]]] int_list_1d: list[int] int_list_2d: list[list[int]] +int_list_3d: list[list[list[int]]] float_list_1d: list[float] float_list_2d: list[list[float]] float_list_3d: list[list[list[float]]] @@ -59,6 +61,8 @@ AR_f8_2d: _Array2D[np.float64] AR_f8_3d: _Array3D[np.float64] AR_f8_4d: np.ndarray[tuple[int, int, int, int], np.dtype[np.float64]] +AR_i1_2d: _Array2D[np.int8] +AR_i1_3d: _Array3D[np.int8] AR_f2_2d: _Array2D[np.float16] AR_f4_1d: _Array1D[np.float32] AR_f4_2d: _Array2D[np.float32] @@ -388,15 +392,19 @@ assert_type(np.linalg.diagonal(str_list_2d), npt.NDArray[np.str_]) assert_type(np.linalg.trace(AR_any), Any) assert_type(np.linalg.trace(AR_f4), Any) +assert_type(np.linalg.trace(AR_i1_2d), np.int_) +assert_type(np.linalg.trace(AR_i1_3d), _Array1D[np.int_]) assert_type(np.linalg.trace(AR_f4_2d), np.float32) assert_type(np.linalg.trace(AR_f8_2d), np.float64) assert_type(np.linalg.trace(AR_f4_3d), _Array1D[np.float32]) assert_type(np.linalg.trace(AR_f8_3d), _Array1D[np.float64]) assert_type(np.linalg.trace(AR_f8_4d), np.ndarray[tuple[int, *tuple[Any, ...]], np.dtype[np.float64]]) -assert_type(np.linalg.trace(bool_list_2d), np.bool) +assert_type(np.linalg.trace(bool_list_2d), np.int_) assert_type(np.linalg.trace(int_list_2d), np.int_) assert_type(np.linalg.trace(float_list_2d), np.float64) assert_type(np.linalg.trace(complex_list_2d), np.complex128) +assert_type(np.linalg.trace(bool_list_3d), npt.NDArray[np.int_]) +assert_type(np.linalg.trace(int_list_3d), npt.NDArray[np.int_]) assert_type(np.linalg.trace(float_list_3d), npt.NDArray[np.float64]) assert_type(np.linalg.outer(bool_list_1d, bool_list_1d), _Array2D[np.bool]) diff --git a/numpy/typing/tests/data/reveal/ndarray_misc.pyi b/numpy/typing/tests/data/reveal/ndarray_misc.pyi index ba718421af88..868fb5731710 100644 --- a/numpy/typing/tests/data/reveal/ndarray_misc.pyi +++ b/numpy/typing/tests/data/reveal/ndarray_misc.pyi @@ -32,6 +32,7 @@ AR_f8: npt.NDArray[np.float64] AR_i8: npt.NDArray[np.int64] AR_u1: npt.NDArray[np.uint8] AR_c8: npt.NDArray[np.complex64] +AR_c16: npt.NDArray[np.complex128] AR_m: npt.NDArray[np.timedelta64] AR_U: npt.NDArray[np.str_] AR_V: npt.NDArray[np.void] @@ -119,8 +120,10 @@ assert_type(f8.conjugate(), np.float64) assert_type(AR_f8.conjugate(), npt.NDArray[np.float64]) assert_type(B.conjugate(), SubClass) -assert_type(AR_i8.cumprod(), np.ndarray[tuple[int], np.dtype[np.int64]]) -assert_type(AR_i8.cumprod(axis=0), npt.NDArray[np.int64]) +assert_type(AR_u1.cumprod(), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(AR_u1.cumprod(axis=0), npt.NDArray[np.int_]) +assert_type(AR_i8.cumprod(), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(AR_i8.cumprod(axis=0), npt.NDArray[np.int_]) assert_type(AR_f8.cumprod(), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(AR_f8.cumprod(axis=0), npt.NDArray[np.float64]) assert_type(AR_f8.cumprod(dtype=np.float32), np.ndarray[tuple[int], np.dtype[np.float32]]) @@ -133,8 +136,10 @@ assert_type(AR_f8_2d.cumprod(dtype=np.float32, axis=0), np.ndarray[tuple[int, in assert_type(AR_any.cumprod(), np.ndarray[tuple[int]]) assert_type(AR_any.cumprod(axis=0), np.ndarray) -assert_type(AR_i8.cumsum(), np.ndarray[tuple[int], np.dtype[np.int64]]) -assert_type(AR_i8.cumsum(axis=0), npt.NDArray[np.int64]) +assert_type(AR_u1.cumsum(), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(AR_u1.cumsum(axis=0), npt.NDArray[np.int_]) +assert_type(AR_i8.cumsum(), np.ndarray[tuple[int], np.dtype[np.int_]]) +assert_type(AR_i8.cumsum(axis=0), npt.NDArray[np.int_]) assert_type(AR_f8.cumsum(), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(AR_f8.cumsum(axis=0), npt.NDArray[np.float64]) assert_type(AR_f8.cumsum(dtype=np.float32), np.ndarray[tuple[int], np.dtype[np.float32]]) @@ -151,10 +156,14 @@ assert_type(AR_any.cumsum(axis=0), np.ndarray) assert_type(b1.prod(), np.int_) assert_type(i8.prod(), np.int64) assert_type(f8.prod(), np.float64) -assert_type(AR_i8.prod(), np.int64) -assert_type(AR_i8.prod(keepdims=True), npt.NDArray[np.int64]) -assert_type(AR_i8.prod(axis=0), npt.NDArray[np.int64]) -assert_type(AR_i8.prod(axis=0, keepdims=True), npt.NDArray[np.int64]) +assert_type(AR_u1.prod(), np.int_) +assert_type(AR_u1.prod(keepdims=True), npt.NDArray[np.int_]) +assert_type(AR_u1.prod(axis=0), npt.NDArray[np.int_]) +assert_type(AR_u1.prod(axis=0, keepdims=True), npt.NDArray[np.int_]) +assert_type(AR_i8.prod(), np.int_) +assert_type(AR_i8.prod(keepdims=True), npt.NDArray[np.int_]) +assert_type(AR_i8.prod(axis=0), npt.NDArray[np.int_]) +assert_type(AR_i8.prod(axis=0, keepdims=True), npt.NDArray[np.int_]) assert_type(AR_f8.prod(), np.float64) assert_type(AR_f8.prod(keepdims=True), npt.NDArray[np.float64]) assert_type(AR_f8.prod(axis=0), npt.NDArray[np.float64]) @@ -181,10 +190,14 @@ assert_type(f8.sum(), np.float64) assert_type(m8_ns.sum(), np.timedelta64[int]) assert_type(m8_ms.sum(), np.timedelta64[dt.timedelta]) assert_type(m8_na.sum(), np.timedelta64[None]) -assert_type(AR_i8.sum(), np.int64) -assert_type(AR_i8.sum(keepdims=True), npt.NDArray[np.int64]) -assert_type(AR_i8.sum(axis=0), npt.NDArray[np.int64]) -assert_type(AR_i8.sum(axis=0, keepdims=True), npt.NDArray[np.int64]) +assert_type(AR_u1.sum(), np.int_) +assert_type(AR_u1.sum(keepdims=True), npt.NDArray[np.int_]) +assert_type(AR_u1.sum(axis=0), npt.NDArray[np.int_]) +assert_type(AR_u1.sum(axis=0, keepdims=True), npt.NDArray[np.int_]) +assert_type(AR_i8.sum(), np.int_) +assert_type(AR_i8.sum(keepdims=True), npt.NDArray[np.int_]) +assert_type(AR_i8.sum(axis=0), npt.NDArray[np.int_]) +assert_type(AR_i8.sum(axis=0, keepdims=True), npt.NDArray[np.int_]) assert_type(AR_f8.sum(), np.float64) assert_type(AR_f8.sum(keepdims=True), npt.NDArray[np.float64]) assert_type(AR_f8.sum(axis=0), npt.NDArray[np.float64]) @@ -224,12 +237,16 @@ assert_type(AR_f8_2d.mean(dtype=np.float32, axis=0), npt.NDArray[np.float32]) assert_type(AR_f8_2d.mean(dtype=np.float32, keepdims=True), np.ndarray[tuple[int, int], np.dtype[np.float32]]) assert_type(AR_f8_2d.mean(dtype=np.float32, axis=0, keepdims=True), np.ndarray[tuple[int, int], np.dtype[np.float32]]) -# same as above +# as above, but returns floating for complexfloating input assert_type(f8.std(), Any) assert_type(AR_f8.std(), np.float64) assert_type(AR_f8.std(keepdims=True), npt.NDArray[np.float64]) assert_type(AR_f8.std(axis=0), npt.NDArray[np.float64]) assert_type(AR_f8.std(axis=0, keepdims=True), npt.NDArray[np.float64]) +assert_type(AR_c16.std(), np.float64) +assert_type(AR_c16.std(keepdims=True), npt.NDArray[np.float64]) +assert_type(AR_c16.std(axis=0), npt.NDArray[np.float64]) +assert_type(AR_c16.std(axis=0, keepdims=True), npt.NDArray[np.float64]) assert_type(AR_f8.std(dtype=np.float32), np.float32) assert_type(AR_f8.std(dtype=np.float32, axis=0), npt.NDArray[np.float32]) assert_type(AR_f8.std(dtype=np.float32, keepdims=True), npt.NDArray[np.float32]) @@ -250,6 +267,10 @@ assert_type(AR_f8.var(), np.float64) assert_type(AR_f8.var(keepdims=True), npt.NDArray[np.float64]) assert_type(AR_f8.var(axis=0), npt.NDArray[np.float64]) assert_type(AR_f8.var(axis=0, keepdims=True), npt.NDArray[np.float64]) +assert_type(AR_c16.var(), np.float64) +assert_type(AR_c16.var(keepdims=True), npt.NDArray[np.float64]) +assert_type(AR_c16.var(axis=0), npt.NDArray[np.float64]) +assert_type(AR_c16.var(axis=0, keepdims=True), npt.NDArray[np.float64]) assert_type(AR_f8.var(dtype=np.float32), np.float32) assert_type(AR_f8.var(dtype=np.float32, axis=0), npt.NDArray[np.float32]) assert_type(AR_f8.var(dtype=np.float32, keepdims=True), npt.NDArray[np.float32]) diff --git a/numpy/typing/tests/data/reveal/random.pyi b/numpy/typing/tests/data/reveal/random.pyi index 72f8c62f79e0..37b920a1b5e2 100644 --- a/numpy/typing/tests/data/reveal/random.pyi +++ b/numpy/typing/tests/data/reveal/random.pyi @@ -700,6 +700,7 @@ assert_type(def_gen.bit_generator, np.random.BitGenerator) assert_type(def_gen.bytes(2), bytes) assert_type(def_gen.choice(5), int) +assert_type(def_gen.choice([1, 2]), np.int_) assert_type(def_gen.choice(5, 3), npt.NDArray[np.int64]) assert_type(def_gen.choice(5, 3, replace=True), npt.NDArray[np.int64]) assert_type(def_gen.choice(5, 3, p=[1 / 5] * 5), npt.NDArray[np.int64]) @@ -1335,7 +1336,7 @@ assert_type(random_st.seed([0, 1]), None) random_st_get_state = random_st.get_state() assert_type(random_st_state, dict[str, Any]) random_st_get_state_legacy = random_st.get_state(legacy=True) -assert_type(random_st_get_state_legacy, dict[str, Any] | tuple[str, npt.NDArray[np.uint32], int, int, float]) +assert_type(random_st_get_state_legacy, tuple[str, npt.NDArray[np.uint32], int, int, float]) assert_type(random_st.set_state(random_st_get_state), None) assert_type(random_st.rand(), float) diff --git a/numpy/typing/tests/data/reveal/scalars.pyi b/numpy/typing/tests/data/reveal/scalars.pyi index c56c8e88092c..2ad0c8932e43 100644 --- a/numpy/typing/tests/data/reveal/scalars.pyi +++ b/numpy/typing/tests/data/reveal/scalars.pyi @@ -1,3 +1,4 @@ +import datetime as dt from typing import Any, Literal, assert_type import numpy as np @@ -49,8 +50,6 @@ assert_type(u8[()], np.uint64) assert_type(f8[()], np.float64) assert_type(c8[()], np.complex64) assert_type(c16[()], np.complex128) -assert_type(U[()], np.str_) -assert_type(S[()], np.bytes_) assert_type(V[()], np.void) assert_type(b[...], np.ndarray[tuple[()], np.dtype[np.bool]]) @@ -65,10 +64,6 @@ assert_type(c8[...], np.ndarray[tuple[()], np.dtype[np.complex64]]) assert_type(c8[(...,)], np.ndarray[tuple[()], np.dtype[np.complex64]]) assert_type(c16[...], np.ndarray[tuple[()], np.dtype[np.complex128]]) assert_type(c16[(...,)], np.ndarray[tuple[()], np.dtype[np.complex128]]) -assert_type(U[...], np.ndarray[tuple[()], np.dtype[np.str_]]) -assert_type(U[(...,)], np.ndarray[tuple[()], np.dtype[np.str_]]) -assert_type(S[...], np.ndarray[tuple[()], np.dtype[np.bytes_]]) -assert_type(S[(...,)], np.ndarray[tuple[()], np.dtype[np.bytes_]]) assert_type(V[...], np.ndarray[tuple[()], np.dtype[np.void]]) assert_type(V[(...,)], np.ndarray[tuple[()], np.dtype[np.void]]) @@ -113,17 +108,15 @@ assert_type(c16[None2], np.ndarray[tuple[int, int], np.dtype[np.complex128]]) assert_type(c16[None3], np.ndarray[tuple[int, int, int], np.dtype[np.complex128]]) assert_type(c16[None4], np.ndarray[tuple[Any, ...], np.dtype[np.complex128]]) -assert_type(U[None], np.ndarray[tuple[int], np.dtype[np.str_]]) -assert_type(U[None1], np.ndarray[tuple[int], np.dtype[np.str_]]) -assert_type(U[None2], np.ndarray[tuple[int, int], np.dtype[np.str_]]) -assert_type(U[None3], np.ndarray[tuple[int, int, int], np.dtype[np.str_]]) -assert_type(U[None4], np.ndarray[tuple[Any, ...], np.dtype[np.str_]]) +assert_type(U[0], str) +assert_type(U[:1], str) +assert_type(U[1:], str) +assert_type(U[:], str) -assert_type(S[None], np.ndarray[tuple[int], np.dtype[np.bytes_]]) -assert_type(S[None1], np.ndarray[tuple[int], np.dtype[np.bytes_]]) -assert_type(S[None2], np.ndarray[tuple[int, int], np.dtype[np.bytes_]]) -assert_type(S[None3], np.ndarray[tuple[int, int, int], np.dtype[np.bytes_]]) -assert_type(S[None4], np.ndarray[tuple[Any, ...], np.dtype[np.bytes_]]) +assert_type(S[0], int) +assert_type(S[:1], bytes) +assert_type(S[1:], bytes) +assert_type(S[:], bytes) assert_type(V[None], np.ndarray[tuple[int], np.dtype[np.void]]) assert_type(V[None1], np.ndarray[tuple[int], np.dtype[np.void]]) @@ -258,6 +251,24 @@ assert_type(int(O), int) assert_type(float(O), float) assert_type(complex(O), complex) +assert_type(np.datetime64(), np.datetime64[None]) +assert_type(np.datetime64("now", "ns"), np.datetime64[int]) +assert_type(np.datetime64("now", "s"), np.datetime64[dt.datetime]) +assert_type(np.datetime64("now", "Y"), np.datetime64[dt.date]) +# unit-swapping +assert_type(np.datetime64(np.datetime64(), "ns"), np.datetime64[int]) +assert_type(np.datetime64(np.datetime64("now", "ns"), "ns"), np.datetime64[int]) +assert_type(np.datetime64(np.datetime64("now", "s"), "ns"), np.datetime64[int]) +assert_type(np.datetime64(np.datetime64("now", "Y"), "ns"), np.datetime64[int]) +assert_type(np.datetime64(np.datetime64(), "s"), np.datetime64[dt.datetime]) +assert_type(np.datetime64(np.datetime64("now", "ns"), "s"), np.datetime64[dt.datetime]) +assert_type(np.datetime64(np.datetime64("now", "s"), "s"), np.datetime64[dt.datetime]) +assert_type(np.datetime64(np.datetime64("now", "Y"), "s"), np.datetime64[dt.datetime]) +assert_type(np.datetime64(np.datetime64(), "Y"), np.datetime64[dt.date]) +assert_type(np.datetime64(np.datetime64("now", "ns"), "Y"), np.datetime64[dt.date]) +assert_type(np.datetime64(np.datetime64("now", "s"), "Y"), np.datetime64[dt.date]) +assert_type(np.datetime64(np.datetime64("now", "Y"), "Y"), np.datetime64[dt.date]) + # These fail fail because of a mypy __new__ bug: # https://github.com/python/mypy/issues/15182 # According to the typing spec, the following statements are valid, see diff --git a/numpy/typing/tests/data/reveal/strings.pyi b/numpy/typing/tests/data/reveal/strings.pyi index 357568c232b5..08694cf07b9d 100644 --- a/numpy/typing/tests/data/reveal/strings.pyi +++ b/numpy/typing/tests/data/reveal/strings.pyi @@ -11,29 +11,29 @@ AR_U: npt.NDArray[np.str_] AR_S: npt.NDArray[np.bytes_] AR_T: AR_T_alias -assert_type(np.strings.equal(AR_U, AR_U), np.ndarray) -assert_type(np.strings.equal(AR_S, AR_S), np.ndarray) -assert_type(np.strings.equal(AR_T, AR_T), np.ndarray) +assert_type(np.strings.equal(AR_U, AR_U), npt.NDArray[np.bool]) +assert_type(np.strings.equal(AR_S, AR_S), npt.NDArray[np.bool]) +assert_type(np.strings.equal(AR_T, AR_T), npt.NDArray[np.bool]) -assert_type(np.strings.not_equal(AR_U, AR_U), np.ndarray) -assert_type(np.strings.not_equal(AR_S, AR_S), np.ndarray) -assert_type(np.strings.not_equal(AR_T, AR_T), np.ndarray) +assert_type(np.strings.not_equal(AR_U, AR_U), npt.NDArray[np.bool]) +assert_type(np.strings.not_equal(AR_S, AR_S), npt.NDArray[np.bool]) +assert_type(np.strings.not_equal(AR_T, AR_T), npt.NDArray[np.bool]) -assert_type(np.strings.greater_equal(AR_U, AR_U), np.ndarray) -assert_type(np.strings.greater_equal(AR_S, AR_S), np.ndarray) -assert_type(np.strings.greater_equal(AR_T, AR_T), np.ndarray) +assert_type(np.strings.greater_equal(AR_U, AR_U), npt.NDArray[np.bool]) +assert_type(np.strings.greater_equal(AR_S, AR_S), npt.NDArray[np.bool]) +assert_type(np.strings.greater_equal(AR_T, AR_T), npt.NDArray[np.bool]) -assert_type(np.strings.less_equal(AR_U, AR_U), np.ndarray) -assert_type(np.strings.less_equal(AR_S, AR_S), np.ndarray) -assert_type(np.strings.less_equal(AR_T, AR_T), np.ndarray) +assert_type(np.strings.less_equal(AR_U, AR_U), npt.NDArray[np.bool]) +assert_type(np.strings.less_equal(AR_S, AR_S), npt.NDArray[np.bool]) +assert_type(np.strings.less_equal(AR_T, AR_T), npt.NDArray[np.bool]) -assert_type(np.strings.greater(AR_U, AR_U), np.ndarray) -assert_type(np.strings.greater(AR_S, AR_S), np.ndarray) -assert_type(np.strings.greater(AR_T, AR_T), np.ndarray) +assert_type(np.strings.greater(AR_U, AR_U), npt.NDArray[np.bool]) +assert_type(np.strings.greater(AR_S, AR_S), npt.NDArray[np.bool]) +assert_type(np.strings.greater(AR_T, AR_T), npt.NDArray[np.bool]) -assert_type(np.strings.less(AR_U, AR_U), np.ndarray) -assert_type(np.strings.less(AR_S, AR_S), np.ndarray) -assert_type(np.strings.less(AR_T, AR_T), np.ndarray) +assert_type(np.strings.less(AR_U, AR_U), npt.NDArray[np.bool]) +assert_type(np.strings.less(AR_S, AR_S), npt.NDArray[np.bool]) +assert_type(np.strings.less(AR_T, AR_T), npt.NDArray[np.bool]) assert_type(np.strings.add(AR_U, AR_U), np.ndarray) assert_type(np.strings.add(AR_S, AR_S), np.ndarray) diff --git a/numpy/typing/tests/data/reveal/twodim_base.pyi b/numpy/typing/tests/data/reveal/twodim_base.pyi index d8c45afe44ab..1abe90e0d7ad 100644 --- a/numpy/typing/tests/data/reveal/twodim_base.pyi +++ b/numpy/typing/tests/data/reveal/twodim_base.pyi @@ -5,6 +5,7 @@ import numpy.typing as npt type _1D = tuple[int] type _2D = tuple[int, int] +type _3D = tuple[int, int, int] type _ND = tuple[Any, ...] type _Indices2D = tuple[ @@ -17,6 +18,7 @@ type _Indices2D = tuple[ _nd_bool: np.ndarray[_ND, np.dtype[np.bool]] _1d_bool: np.ndarray[_1D, np.dtype[np.bool]] _2d_bool: np.ndarray[_2D, np.dtype[np.bool]] +_3d_bool: np.ndarray[_3D, np.dtype[np.bool]] _nd_u64: np.ndarray[_ND, np.dtype[np.uint64]] _nd_i64: np.ndarray[_ND, np.dtype[np.int64]] _nd_f64: np.ndarray[_ND, np.dtype[np.float64]] @@ -89,12 +91,18 @@ assert_type(np.tril(_nd_bool), np.ndarray[_ND, np.dtype[np.bool]]) assert_type(np.tril(_to_nd_bool, k=0), np.ndarray) assert_type(np.tril(_to_1d_bool, k=0), np.ndarray) assert_type(np.tril(_to_2d_bool, k=0), np.ndarray) +assert_type(np.tril(_1d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) +assert_type(np.tril(_2d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) +assert_type(np.tril(_3d_bool, k=0), np.ndarray[_3D, np.dtype[np.bool]]) # triu assert_type(np.triu(_nd_bool), np.ndarray[_ND, np.dtype[np.bool]]) assert_type(np.triu(_to_nd_bool, k=0), np.ndarray) assert_type(np.triu(_to_1d_bool, k=0), np.ndarray) assert_type(np.triu(_to_2d_bool, k=0), np.ndarray) +assert_type(np.triu(_1d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) +assert_type(np.triu(_2d_bool, k=0), np.ndarray[_2D, np.dtype[np.bool]]) +assert_type(np.triu(_3d_bool, k=0), np.ndarray[_3D, np.dtype[np.bool]]) # vander assert_type(np.vander(_nd_bool), np.ndarray[_2D, np.dtype[np.int_]]) diff --git a/numpy/typing/tests/data/reveal/ufuncs.pyi b/numpy/typing/tests/data/reveal/ufuncs.pyi index 8ec60c516efb..1878ddd8825b 100644 --- a/numpy/typing/tests/data/reveal/ufuncs.pyi +++ b/numpy/typing/tests/data/reveal/ufuncs.pyi @@ -25,17 +25,6 @@ assert_type(np.add.reduceat(AR_f8, AR_i8), npt.NDArray[Any]) assert_type(np.add.outer(f8, f8), Any) assert_type(np.add.outer(AR_f8, f8), npt.NDArray[Any]) -assert_type(np.frexp.__name__, Literal["frexp"]) -assert_type(np.frexp.__qualname__, Literal["frexp"]) -assert_type(np.frexp.ntypes, Literal[4]) -assert_type(np.frexp.identity, None) -assert_type(np.frexp.nin, Literal[1]) -assert_type(np.frexp.nout, Literal[2]) -assert_type(np.frexp.nargs, Literal[3]) -assert_type(np.frexp.signature, None) -assert_type(np.frexp(f8), tuple[Any, Any]) -assert_type(np.frexp(AR_f8), tuple[npt.NDArray[Any], npt.NDArray[Any]]) - assert_type(np.divmod.__name__, Literal["divmod"]) assert_type(np.divmod.__qualname__, Literal["divmod"]) assert_type(np.divmod.ntypes, Literal[15]) @@ -70,18 +59,6 @@ assert_type(np.vecdot.signature, Literal["(n),(n)->()"]) assert_type(np.vecdot.identity, None) assert_type(np.vecdot(AR_f8, AR_f8), Any) -assert_type(np.bitwise_count.__name__, Literal["bitwise_count"]) -assert_type(np.bitwise_count.__qualname__, Literal["bitwise_count"]) -assert_type(np.bitwise_count.ntypes, Literal[11]) -assert_type(np.bitwise_count.identity, None) -assert_type(np.bitwise_count.nin, Literal[1]) -assert_type(np.bitwise_count.nout, Literal[1]) -assert_type(np.bitwise_count.nargs, Literal[2]) -assert_type(np.bitwise_count.signature, None) -assert_type(np.bitwise_count.identity, None) -assert_type(np.bitwise_count(i8), Any) -assert_type(np.bitwise_count(AR_i8), npt.NDArray[Any]) - def test_absolute_outer_invalid() -> None: assert_type(np.absolute.outer(AR_f8, AR_f8), NoReturn) # type: ignore[arg-type] def test_frexp_outer_invalid() -> None: @@ -152,6 +129,10 @@ _i16_0d: np.int16 _i16_1d: np.ndarray[tuple[int], np.dtype[np.int16]] _i16_2d: np.ndarray[tuple[int, int], np.dtype[np.int16]] _i16_nd: npt.NDArray[np.int16] +_i32_0d: np.int32 +_i32_1d: np.ndarray[tuple[int], np.dtype[np.int32]] +_i32_2d: np.ndarray[tuple[int, int], np.dtype[np.int32]] +_i32_nd: npt.NDArray[np.int32] _f32_0d: np.float32 _f32_1d: np.ndarray[tuple[int], np.dtype[np.float32]] _f32_2d: np.ndarray[tuple[int, int], np.dtype[np.float32]] @@ -291,6 +272,27 @@ assert_type(np.isinf(_td_ns_nd), npt.NDArray[np.bool]) assert_type(np.isinf(_py_i_2d, out=_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) +# _ufunc_11_io +# (bitwise_count) + +assert_type(np.bitwise_count(_py_b_0d), np.uint8) +assert_type(np.bitwise_count(_py_b_1d), np.ndarray[tuple[int], np.dtype[np.uint8]]) +assert_type(np.bitwise_count(_py_b_2d), np.ndarray[tuple[int, int], np.dtype[np.uint8]]) +assert_type(np.bitwise_count(_py_i_0d), np.uint8) +assert_type(np.bitwise_count(_py_i_1d), np.ndarray[tuple[int], np.dtype[np.uint8]]) +assert_type(np.bitwise_count(_py_i_2d), np.ndarray[tuple[int, int], np.dtype[np.uint8]]) + +assert_type(np.bitwise_count(_bool_0d), np.uint8) +assert_type(np.bitwise_count(_bool_1d), np.ndarray[tuple[int], np.dtype[np.uint8]]) +assert_type(np.bitwise_count(_bool_2d), np.ndarray[tuple[int, int], np.dtype[np.uint8]]) +assert_type(np.bitwise_count(_bool_nd), npt.NDArray[np.uint8]) +assert_type(np.bitwise_count(_i16_0d), np.uint8) +assert_type(np.bitwise_count(_i16_1d), np.ndarray[tuple[int], np.dtype[np.uint8]]) +assert_type(np.bitwise_count(_i16_2d), np.ndarray[tuple[int, int], np.dtype[np.uint8]]) +assert_type(np.bitwise_count(_i16_nd), npt.NDArray[np.uint8]) + +assert_type(np.bitwise_count(_py_i_2d, out=_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) + # _ufunc_11_f # (spacing) @@ -301,10 +303,18 @@ assert_type(np.spacing(_py_f_0d), np.float64) assert_type(np.spacing(_py_f_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(np.spacing(_py_f_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) -assert_type(np.spacing(_i16_0d), np.float64) -assert_type(np.spacing(_i16_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) -assert_type(np.spacing(_i16_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) -assert_type(np.spacing(_i16_nd), npt.NDArray[np.float64]) +assert_type(np.spacing(_bool_0d), np.float16) +assert_type(np.spacing(_bool_1d), np.ndarray[tuple[int], np.dtype[np.float16]]) +assert_type(np.spacing(_bool_2d), np.ndarray[tuple[int, int], np.dtype[np.float16]]) +assert_type(np.spacing(_bool_nd), npt.NDArray[np.float16]) +assert_type(np.spacing(_i16_0d), np.float32) +assert_type(np.spacing(_i16_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) +assert_type(np.spacing(_i16_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) +assert_type(np.spacing(_i16_nd), npt.NDArray[np.float32]) +assert_type(np.spacing(_i32_0d), np.float64) +assert_type(np.spacing(_i32_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) +assert_type(np.spacing(_i32_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) +assert_type(np.spacing(_i32_nd), npt.NDArray[np.float64]) assert_type(np.spacing(_f32_0d), np.float32) assert_type(np.spacing(_f32_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.spacing(_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) @@ -330,10 +340,18 @@ assert_type(np.cbrt(_py_f_0d), np.float64) assert_type(np.cbrt(_py_f_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) assert_type(np.cbrt(_py_f_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) -assert_type(np.cbrt(_i16_0d), np.float64) -assert_type(np.cbrt(_i16_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) -assert_type(np.cbrt(_i16_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) -assert_type(np.cbrt(_i16_nd), npt.NDArray[np.float64]) +assert_type(np.cbrt(_bool_0d), np.float16) +assert_type(np.cbrt(_bool_1d), np.ndarray[tuple[int], np.dtype[np.float16]]) +assert_type(np.cbrt(_bool_2d), np.ndarray[tuple[int, int], np.dtype[np.float16]]) +assert_type(np.cbrt(_bool_nd), npt.NDArray[np.float16]) +assert_type(np.cbrt(_i16_0d), np.float32) +assert_type(np.cbrt(_i16_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) +assert_type(np.cbrt(_i16_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) +assert_type(np.cbrt(_i16_nd), npt.NDArray[np.float32]) +assert_type(np.cbrt(_i32_0d), np.float64) +assert_type(np.cbrt(_i32_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) +assert_type(np.cbrt(_i32_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) +assert_type(np.cbrt(_i32_nd), npt.NDArray[np.float64]) assert_type(np.cbrt(_f32_0d), np.float32) assert_type(np.cbrt(_f32_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.cbrt(_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) @@ -364,10 +382,14 @@ assert_type(np.sin(_py_c_0d), np.complex128 | Any) # `complex` overlaps with `f assert_type(np.sin(_py_c_1d), np.ndarray[tuple[int], np.dtype[np.complex128]]) assert_type(np.sin(_py_c_2d), np.ndarray[tuple[int, int], np.dtype[np.complex128]]) -assert_type(np.sin(_i16_0d), np.float64) -assert_type(np.sin(_i16_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) -assert_type(np.sin(_i16_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) -assert_type(np.sin(_i16_nd), npt.NDArray[np.float64]) +assert_type(np.sin(_i16_0d), np.float32) +assert_type(np.sin(_i16_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) +assert_type(np.sin(_i16_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) +assert_type(np.sin(_i16_nd), npt.NDArray[np.float32]) +assert_type(np.sin(_i32_0d), np.float64) +assert_type(np.sin(_i32_1d), np.ndarray[tuple[int], np.dtype[np.float64]]) +assert_type(np.sin(_i32_2d), np.ndarray[tuple[int, int], np.dtype[np.float64]]) +assert_type(np.sin(_i32_nd), npt.NDArray[np.float64]) assert_type(np.sin(_f32_0d), np.float32) assert_type(np.sin(_f32_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) assert_type(np.sin(_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) @@ -662,3 +684,369 @@ assert_type(np.abs(_u8_2d, dtype="f4"), np.ndarray[tuple[int, int]]) assert_type(np.abs(_py_b_1d, out=_i16_1d), np.ndarray[tuple[int], np.dtype[np.int16]]) assert_type(np.abs(_py_i_2d, out=_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) + +# _ufunc_12_frexp + +assert_type(np.frexp(_py_i_0d), tuple[np.float64, np.int32]) +assert_type( + np.frexp(_py_i_1d), + tuple[ + np.ndarray[tuple[int], np.dtype[np.float64]], + np.ndarray[tuple[int], np.dtype[np.int32]], + ], +) +assert_type( + np.frexp(_py_i_2d), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float64]], + np.ndarray[tuple[int, int], np.dtype[np.int32]], + ], +) +assert_type(np.frexp(_py_f_0d), tuple[np.float64, np.int32]) +assert_type( + np.frexp(_py_f_1d), + tuple[ + np.ndarray[tuple[int], np.dtype[np.float64]], + np.ndarray[tuple[int], np.dtype[np.int32]], + ], +) +assert_type( + np.frexp(_py_f_2d), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float64]], + np.ndarray[tuple[int, int], np.dtype[np.int32]], + ], +) + +assert_type(np.frexp(_bool_0d), tuple[np.float16, np.int32]) +assert_type( + np.frexp(_bool_1d), + tuple[ + np.ndarray[tuple[int], np.dtype[np.float16]], + np.ndarray[tuple[int], np.dtype[np.int32]], + ], +) +assert_type( + np.frexp(_bool_2d), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float16]], + np.ndarray[tuple[int, int], np.dtype[np.int32]], + ], +) +assert_type(np.frexp(_bool_nd), tuple[npt.NDArray[np.float16], npt.NDArray[np.int32]]) +assert_type(np.frexp(_i16_0d), tuple[np.float32, np.int32]) +assert_type( + np.frexp(_i16_1d), + tuple[ + np.ndarray[tuple[int], np.dtype[np.float32]], + np.ndarray[tuple[int], np.dtype[np.int32]], + ], +) +assert_type( + np.frexp(_i16_2d), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float32]], + np.ndarray[tuple[int, int], np.dtype[np.int32]], + ], +) +assert_type(np.frexp(_i16_nd), tuple[npt.NDArray[np.float32], npt.NDArray[np.int32]]) +assert_type(np.frexp(_i32_0d), tuple[np.float64, np.int32]) +assert_type( + np.frexp(_i32_1d), + tuple[ + np.ndarray[tuple[int], np.dtype[np.float64]], + np.ndarray[tuple[int], np.dtype[np.int32]], + ], +) +assert_type( + np.frexp(_i32_2d), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float64]], + np.ndarray[tuple[int, int], np.dtype[np.int32]], + ], +) +assert_type(np.frexp(_i32_nd), tuple[npt.NDArray[np.float64], npt.NDArray[np.int32]]) +assert_type(np.frexp(_f32_0d), tuple[np.float32, np.int32]) +assert_type( + np.frexp(_f32_1d), + tuple[ + np.ndarray[tuple[int], np.dtype[np.float32]], + np.ndarray[tuple[int], np.dtype[np.int32]], + ], +) +assert_type( + np.frexp(_f32_2d), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float32]], + np.ndarray[tuple[int, int], np.dtype[np.int32]], + ], +) +assert_type(np.frexp(_f32_nd), tuple[npt.NDArray[np.float32], npt.NDArray[np.int32]]) + +assert_type( + np.frexp(_py_i_2d, out=(_f32_2d, _i16_2d)), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float32]], + np.ndarray[tuple[int, int], np.dtype[np.int16]], + ] +) + +# _ufunc_12_modf + +type _tuple2[T] = tuple[T, T] + +assert_type(np.modf(_py_i_0d), _tuple2[np.float64]) +assert_type(np.modf(_py_i_1d), _tuple2[np.ndarray[tuple[int], np.dtype[np.float64]]]) +assert_type(np.modf(_py_i_2d), _tuple2[np.ndarray[tuple[int, int], np.dtype[np.float64]]]) +assert_type(np.modf(_py_f_0d), _tuple2[np.float64]) +assert_type(np.modf(_py_f_1d), _tuple2[np.ndarray[tuple[int], np.dtype[np.float64]]]) +assert_type(np.modf(_py_f_2d), _tuple2[np.ndarray[tuple[int, int], np.dtype[np.float64]]]) + +assert_type(np.modf(_bool_0d), _tuple2[np.float16]) +assert_type(np.modf(_bool_1d), _tuple2[np.ndarray[tuple[int], np.dtype[np.float16]]]) +assert_type(np.modf(_bool_2d), _tuple2[np.ndarray[tuple[int, int], np.dtype[np.float16]]]) +assert_type(np.modf(_bool_nd), _tuple2[npt.NDArray[np.float16]]) +assert_type(np.modf(_i16_0d), _tuple2[np.float32]) +assert_type(np.modf(_i16_1d), _tuple2[np.ndarray[tuple[int], np.dtype[np.float32]]]) +assert_type(np.modf(_i16_2d), _tuple2[np.ndarray[tuple[int, int], np.dtype[np.float32]]]) +assert_type(np.modf(_i16_nd), _tuple2[npt.NDArray[np.float32]]) +assert_type(np.modf(_i32_0d), _tuple2[np.float64]) +assert_type(np.modf(_i32_1d), _tuple2[np.ndarray[tuple[int], np.dtype[np.float64]]]) +assert_type(np.modf(_i32_2d), _tuple2[np.ndarray[tuple[int, int], np.dtype[np.float64]]]) +assert_type(np.modf(_i32_nd), _tuple2[npt.NDArray[np.float64]]) +assert_type(np.modf(_f32_0d), _tuple2[np.float32]) +assert_type(np.modf(_f32_1d), _tuple2[np.ndarray[tuple[int], np.dtype[np.float32]]]) +assert_type(np.modf(_f32_2d), _tuple2[np.ndarray[tuple[int, int], np.dtype[np.float32]]]) +assert_type(np.modf(_f32_nd), _tuple2[npt.NDArray[np.float32]]) + +assert_type(np.modf(_py_i_0d, dtype=np.float32), _tuple2[np.float32]) +assert_type(np.modf(_py_i_1d, dtype=np.float32), _tuple2[npt.NDArray[np.float32]]) +assert_type(np.modf(_i16_2d, dtype=np.float32), _tuple2[np.ndarray[tuple[int, int], np.dtype[np.float32]]]) + +assert_type(np.modf(_py_i_0d, dtype="f4"), _tuple2[Any]) +assert_type(np.modf(_py_i_1d, dtype="f4"), _tuple2[np.ndarray]) +assert_type(np.modf(_i16_2d, dtype="f4"), _tuple2[np.ndarray[tuple[int, int]]]) + +assert_type( + np.modf(_py_i_2d, out=(_f32_2d, _i16_2d)), + tuple[ + np.ndarray[tuple[int, int], np.dtype[np.float32]], + np.ndarray[tuple[int, int], np.dtype[np.int16]], + ] +) + +# _ufunc_21_logical +# (logical_and, logical_or, logical_xor) + +assert_type(np.logical_or(_py_b_0d, _py_b_0d), np.bool) +assert_type(np.logical_or(_py_b_1d, _py_b_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_py_b_2d, _py_b_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_py_i_0d, _py_i_0d), np.bool) +assert_type(np.logical_or(_py_i_1d, _py_i_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_py_i_2d, _py_i_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_py_f_0d, _py_f_0d), np.bool) +assert_type(np.logical_or(_py_f_1d, _py_f_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_py_f_2d, _py_f_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_py_c_0d, _py_c_0d), np.bool) +assert_type(np.logical_or(_py_c_1d, _py_c_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_py_c_2d, _py_c_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(object(), object()), Any) + +assert_type(np.logical_or(_bool_0d, _bool_0d), np.bool) +assert_type(np.logical_or(_bool_1d, _bool_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_bool_2d, _bool_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_bool_nd, _bool_nd), npt.NDArray[np.bool]) +assert_type(np.logical_or(_i16_0d, _i16_0d), np.bool) +assert_type(np.logical_or(_i16_1d, _i16_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_i16_2d, _i16_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_i16_nd, _i16_nd), npt.NDArray[np.bool]) +assert_type(np.logical_or(_f32_0d, _f32_0d), np.bool) +assert_type(np.logical_or(_f32_1d, _f32_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_f32_2d, _f32_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_f32_nd, _f32_nd), npt.NDArray[np.bool]) +assert_type(np.logical_or(_c64_0d, _c64_0d), np.bool) +assert_type(np.logical_or(_c64_1d, _c64_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_c64_2d, _c64_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_c64_nd, _c64_nd), npt.NDArray[np.bool]) +assert_type(np.logical_or(_dt_ns_0d, _dt_ns_0d), np.bool) +assert_type(np.logical_or(_dt_ns_1d, _dt_ns_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_dt_ns_2d, _dt_ns_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_dt_ns_nd, _dt_ns_nd), npt.NDArray[np.bool]) +assert_type(np.logical_or(_td_ns_0d, _td_ns_0d), np.bool) +assert_type(np.logical_or(_td_ns_1d, _td_ns_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_td_ns_2d, _td_ns_2d), npt.NDArray[np.bool]) +assert_type(np.logical_or(_td_ns_nd, _td_ns_nd), npt.NDArray[np.bool]) +assert_type(np.logical_or(_obj_nd, _bool_nd), npt.NDArray[np.object_]) +assert_type(np.logical_or(_bool_nd, _obj_nd), npt.NDArray[np.object_]) +assert_type(np.logical_or(_obj_nd, _obj_nd), npt.NDArray[np.object_]) + +assert_type(np.logical_or(_py_i_2d, _py_i_2d, out=_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) + +assert_type(np.logical_or.outer(_f32_0d, _c64_0d), np.bool) +assert_type(np.logical_or.outer(_f32_1d, _c64_0d), npt.NDArray[np.bool]) +assert_type(np.logical_or.outer(_f32_0d, _c64_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or.outer(_f32_1d, _c64_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or.outer(_obj_1d, _obj_1d), npt.NDArray[np.object_]) +assert_type(np.logical_or.outer(_obj_2d, _obj_1d), npt.NDArray[np.object_]) +assert_type(np.logical_or.outer(_obj_1d, _obj_2d), npt.NDArray[np.object_]) +assert_type(np.logical_or.outer(_obj_2d, _obj_2d), npt.NDArray[np.object_]) +assert_type(np.logical_or.outer(object(), object()), Any) + +assert_type(np.logical_or.at(_c64_2d, 1, _py_c_1d), None) +assert_type(np.logical_or.at(_obj_2d, 1, _obj_1d), None) + +assert_type(np.logical_or.accumulate(_dt_ns_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.logical_or.accumulate(_dt_ns_2d), np.ndarray[tuple[int, int], np.dtype[np.bool]]) +assert_type(np.logical_or.accumulate(_obj_1d), np.ndarray[tuple[int], np.dtype[np.object_]]) +assert_type(np.logical_or.accumulate(_obj_2d), np.ndarray[tuple[int, int], np.dtype[np.object_]]) +assert_type(np.logical_or.accumulate(_py_f_1d), npt.NDArray[np.bool]) +assert_type(np.logical_or.accumulate(_py_f_1d, out=_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) + +assert_type(np.logical_or.reduce(_i16_1d), npt.NDArray[np.bool] | Any) +assert_type(np.logical_or.reduce(_i16_2d), npt.NDArray[np.bool] | Any) +assert_type(np.logical_or.reduce(_i16_1d, axis=None), np.bool) +assert_type(np.logical_or.reduce(_i16_1d, keepdims=True), npt.NDArray[np.bool]) +assert_type(np.logical_or.reduce(_i16_1d, out=...), npt.NDArray[np.bool]) +assert_type(np.logical_or.reduce(_i16_1d, out=_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.logical_or.reduce(_obj_1d), npt.NDArray[np.object_] | Any) +assert_type(np.logical_or.reduce(_obj_2d), npt.NDArray[np.object_] | Any) +assert_type(np.logical_or.reduce(_obj_1d, axis=None), Any) +assert_type(np.logical_or.reduce(_obj_1d, keepdims=True), npt.NDArray[np.object_]) +assert_type(np.logical_or.reduce(_obj_1d, out=...), npt.NDArray[np.object_]) +assert_type(np.logical_or.reduce(_obj_1d, out=_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) + +assert_type(np.logical_or.reduceat(_td_ns_1d, (0,)), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.logical_or.reduceat(_td_ns_2d, (0,)), np.ndarray[tuple[int, int], np.dtype[np.bool]]) +assert_type(np.logical_or.reduceat(_obj_1d, (0,)), np.ndarray[tuple[int], np.dtype[np.object_]]) +assert_type(np.logical_or.reduceat(_obj_2d, (0,)), np.ndarray[tuple[int, int], np.dtype[np.object_]]) +assert_type(np.logical_or.reduceat(_py_c_1d, (0,)), npt.NDArray[np.bool]) +assert_type(np.logical_or.reduceat(_py_c_1d, (0,), out=_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) + +# _ufunc_21_cmp +# (equal, greater, greater_equal, less, less_equal, not_equal) + +assert_type(np.less(_py_b_0d, _py_b_0d), np.bool) +assert_type(np.less(_py_b_1d, _py_b_1d), npt.NDArray[np.bool]) +assert_type(np.less(_py_b_2d, _py_b_2d), npt.NDArray[np.bool]) +assert_type(np.less(_py_i_0d, _py_i_0d), np.bool) +assert_type(np.less(_py_i_1d, _py_i_1d), npt.NDArray[np.bool]) +assert_type(np.less(_py_i_2d, _py_i_2d), npt.NDArray[np.bool]) +assert_type(np.less(_py_f_0d, _py_f_0d), np.bool) +assert_type(np.less(_py_f_1d, _py_f_1d), npt.NDArray[np.bool]) +assert_type(np.less(_py_f_2d, _py_f_2d), npt.NDArray[np.bool]) +assert_type(np.less(_py_c_0d, _py_c_0d), np.bool) +assert_type(np.less(_py_c_1d, _py_c_1d), npt.NDArray[np.bool]) +assert_type(np.less(_py_c_2d, _py_c_2d), npt.NDArray[np.bool]) + +assert_type(np.less(_bool_0d, _bool_0d), np.bool) +assert_type(np.less(_bool_1d, _bool_1d), npt.NDArray[np.bool]) +assert_type(np.less(_bool_2d, _bool_2d), npt.NDArray[np.bool]) +assert_type(np.less(_bool_nd, _bool_nd), npt.NDArray[np.bool]) +assert_type(np.less(_i16_0d, _i16_0d), np.bool) +assert_type(np.less(_i16_1d, _i16_1d), npt.NDArray[np.bool]) +assert_type(np.less(_i16_2d, _i16_2d), npt.NDArray[np.bool]) +assert_type(np.less(_i16_nd, _i16_nd), npt.NDArray[np.bool]) +assert_type(np.less(_f32_0d, _f32_0d), np.bool) +assert_type(np.less(_f32_1d, _f32_1d), npt.NDArray[np.bool]) +assert_type(np.less(_f32_2d, _f32_2d), npt.NDArray[np.bool]) +assert_type(np.less(_f32_nd, _f32_nd), npt.NDArray[np.bool]) +assert_type(np.less(_c64_0d, _c64_0d), np.bool) +assert_type(np.less(_c64_1d, _c64_1d), npt.NDArray[np.bool]) +assert_type(np.less(_c64_2d, _c64_2d), npt.NDArray[np.bool]) +assert_type(np.less(_c64_nd, _c64_nd), npt.NDArray[np.bool]) +assert_type(np.less(_dt_ns_0d, _dt_ns_0d), np.bool) +assert_type(np.less(_dt_ns_1d, _dt_ns_1d), npt.NDArray[np.bool]) +assert_type(np.less(_dt_ns_2d, _dt_ns_2d), npt.NDArray[np.bool]) +assert_type(np.less(_dt_ns_nd, _dt_ns_nd), npt.NDArray[np.bool]) +assert_type(np.less(_td_ns_0d, _td_ns_0d), np.bool) +assert_type(np.less(_td_ns_1d, _td_ns_1d), npt.NDArray[np.bool]) +assert_type(np.less(_td_ns_2d, _td_ns_2d), npt.NDArray[np.bool]) +assert_type(np.less(_td_ns_nd, _td_ns_nd), npt.NDArray[np.bool]) + +assert_type(np.less(_py_i_2d, _py_i_2d, out=_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) + +assert_type(np.less.outer(_py_c_0d, _py_c_0d), np.bool) +assert_type(np.less.outer(_py_c_1d, _py_c_0d), npt.NDArray[np.bool]) +assert_type(np.less.outer(_py_c_0d, _py_c_1d), npt.NDArray[np.bool]) +assert_type(np.less.outer(_py_c_1d, _py_c_1d), npt.NDArray[np.bool]) + +assert_type(np.less.at(_c64_1d, 1, _py_c_1d), None) + +assert_type(np.less.accumulate(_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.less.accumulate(_bool_2d), np.ndarray[tuple[int, int], np.dtype[np.bool]]) +assert_type(np.less.accumulate(_py_b_1d), npt.NDArray[np.bool]) +assert_type(np.less.accumulate(_py_b_1d, out=_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) + +assert_type(np.less.reduce(_bool_1d), npt.NDArray[np.bool] | np.bool) +assert_type(np.less.reduce(_bool_2d), npt.NDArray[np.bool] | np.bool) +assert_type(np.less.reduce(_bool_1d, axis=None), np.bool) +assert_type(np.less.reduce(_bool_1d, keepdims=True), npt.NDArray[np.bool]) +assert_type(np.less.reduce(_bool_1d, out=...), npt.NDArray[np.bool]) +assert_type(np.less.reduce(_bool_1d, out=_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) + +assert_type(np.less.reduceat(_bool_1d, (0,)), np.ndarray[tuple[int], np.dtype[np.bool]]) +assert_type(np.less.reduceat(_bool_2d, (0,)), np.ndarray[tuple[int, int], np.dtype[np.bool]]) +assert_type(np.less.reduceat(_py_b_1d, (0,)), npt.NDArray[np.bool]) +assert_type(np.less.reduceat(_py_b_1d, (0,), out=_bool_1d), np.ndarray[tuple[int], np.dtype[np.bool]]) + +# _ufunc_21_ldexp +# (ldexp) + +assert_type(np.ldexp(_py_b_0d, _py_b_0d), np.float64) +assert_type(np.ldexp(_py_b_1d, _py_b_1d), npt.NDArray[np.float64]) +assert_type(np.ldexp(_py_b_2d, _py_b_2d), npt.NDArray[np.float64]) +assert_type(np.ldexp(_py_i_0d, _py_i_0d), np.float64) +assert_type(np.ldexp(_py_i_1d, _py_i_1d), npt.NDArray[np.float64]) +assert_type(np.ldexp(_py_i_2d, _py_i_2d), npt.NDArray[np.float64]) +assert_type(np.ldexp(_py_f_0d, _py_i_0d), np.float64) +assert_type(np.ldexp(_py_f_1d, _py_i_1d), npt.NDArray[np.float64]) +assert_type(np.ldexp(_py_f_2d, _py_i_2d), npt.NDArray[np.float64]) + +assert_type(np.ldexp(_bool_0d, _bool_0d), np.float16) +assert_type(np.ldexp(_bool_1d, _bool_1d), npt.NDArray[np.float16]) +assert_type(np.ldexp(_bool_2d, _bool_2d), npt.NDArray[np.float16]) +assert_type(np.ldexp(_bool_nd, _bool_nd), npt.NDArray[np.float16]) +assert_type(np.ldexp(_i16_0d, _i16_0d), np.float32) +assert_type(np.ldexp(_i16_1d, _i16_1d), npt.NDArray[np.float32]) +assert_type(np.ldexp(_i16_2d, _i16_2d), npt.NDArray[np.float32]) +assert_type(np.ldexp(_i16_nd, _i16_nd), npt.NDArray[np.float32]) +assert_type(np.ldexp(_i32_0d, _i32_0d), np.float64) +assert_type(np.ldexp(_i32_1d, _i32_1d), npt.NDArray[np.float64]) +assert_type(np.ldexp(_i32_2d, _i32_2d), npt.NDArray[np.float64]) +assert_type(np.ldexp(_i32_nd, _i32_nd), npt.NDArray[np.float64]) +assert_type(np.ldexp(_f32_0d, _i16_0d), np.float32) +assert_type(np.ldexp(_f32_1d, _i16_1d), npt.NDArray[np.float32]) +assert_type(np.ldexp(_f32_2d, _i16_2d), npt.NDArray[np.float32]) +assert_type(np.ldexp(_f32_nd, _i16_nd), npt.NDArray[np.float32]) + +assert_type(np.ldexp(_py_i_2d, _py_i_2d, out=_f32_2d), np.ndarray[tuple[int, int], np.dtype[np.float32]]) + +assert_type(np.ldexp.outer(_py_f_0d, _py_i_0d), np.float64) +assert_type(np.ldexp.outer(_py_f_1d, _py_i_0d), npt.NDArray[np.float64]) +assert_type(np.ldexp.outer(_py_f_0d, _py_i_1d), npt.NDArray[np.float64]) +assert_type(np.ldexp.outer(_py_f_1d, _py_i_1d), npt.NDArray[np.float64]) +assert_type(np.ldexp.outer(_f32_0d, _py_i_0d), np.float32) +assert_type(np.ldexp.outer(_f32_1d, _py_i_0d), npt.NDArray[np.float32]) +assert_type(np.ldexp.outer(_f32_0d, _py_i_1d), npt.NDArray[np.float32]) +assert_type(np.ldexp.outer(_f32_1d, _py_i_1d), npt.NDArray[np.float32]) + +assert_type(np.ldexp.at(_f32_2d, 1, _py_i_1d), None) + +assert_type(np.ldexp.reduce(_bool_1d), npt.NDArray[np.float16] | np.float16) +assert_type(np.ldexp.reduce(_bool_2d), npt.NDArray[np.float16] | np.float16) +assert_type(np.ldexp.reduce(_bool_1d, axis=None), np.float16) +assert_type(np.ldexp.reduce(_bool_1d, keepdims=True), npt.NDArray[np.float16]) +assert_type(np.ldexp.reduce(_bool_1d, out=...), npt.NDArray[np.float16]) +assert_type(np.ldexp.reduce(_i16_1d), npt.NDArray[np.float32] | np.float32) +assert_type(np.ldexp.reduce(_i16_2d), npt.NDArray[np.float32] | np.float32) +assert_type(np.ldexp.reduce(_i16_1d, axis=None), np.float32) +assert_type(np.ldexp.reduce(_i16_1d, keepdims=True), npt.NDArray[np.float32]) +assert_type(np.ldexp.reduce(_i16_1d, out=...), npt.NDArray[np.float32]) +assert_type(np.ldexp.reduce(_i32_1d), npt.NDArray[np.float64] | np.float64) +assert_type(np.ldexp.reduce(_i32_2d), npt.NDArray[np.float64] | np.float64) +assert_type(np.ldexp.reduce(_i32_1d, axis=None), np.float64) +assert_type(np.ldexp.reduce(_i32_1d, keepdims=True), npt.NDArray[np.float64]) +assert_type(np.ldexp.reduce(_i32_1d, out=...), npt.NDArray[np.float64]) +assert_type(np.ldexp.reduce(_i32_1d, out=_f32_1d), np.ndarray[tuple[int], np.dtype[np.float32]]) + +# ldexp doesn't support reduceat / accumulate diff --git a/pyproject.toml b/pyproject.toml index 418bdb1ee757..ef24b3fe0709 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ build-backend = "mesonpy" requires = [ "meson-python>=0.18.0", - "Cython>=3.0.6", # keep in sync with version check in meson.build + "Cython>=3.1.0", # keep in sync with version check in meson.build ] [project] diff --git a/requirements/build_requirements.txt b/requirements/build_requirements.txt index a571c57c68cb..fb0fc6f9863e 100644 --- a/requirements/build_requirements.txt +++ b/requirements/build_requirements.txt @@ -1,5 +1,5 @@ -meson-python>=0.20.0 -Cython>=3.2.5 +meson-python>=0.18.0 +Cython>=3.1.0 ninja spin build diff --git a/requirements/ci32_requirements.txt b/requirements/ci32_requirements.txt index df42864374a4..85593f420cc5 100644 --- a/requirements/ci32_requirements.txt +++ b/requirements/ci32_requirements.txt @@ -1,3 +1,3 @@ spin # Keep this in sync with ci_requirements.txt -scipy-openblas32==0.3.33.112.0 +scipy-openblas32==0.3.33.201.0 diff --git a/requirements/ci_requirements.txt b/requirements/ci_requirements.txt index c33fc3274070..1aa588d42b7e 100644 --- a/requirements/ci_requirements.txt +++ b/requirements/ci_requirements.txt @@ -1,4 +1,4 @@ spin # Keep this in sync with ci32_requirements.txt -scipy-openblas32==0.3.33.112.0 -scipy-openblas64==0.3.33.112.0 +scipy-openblas32==0.3.33.201.0 +scipy-openblas64==0.3.33.201.0 diff --git a/requirements/doc_requirements.txt b/requirements/doc_requirements.txt index 471d9d410e5d..f55a119056e2 100644 --- a/requirements/doc_requirements.txt +++ b/requirements/doc_requirements.txt @@ -1,14 +1,14 @@ # doxygen required, use apt-get or dnf sphinx==8.2.3 numpydoc==1.10.0 -pydata-sphinx-theme>=0.16.1 +pydata-sphinx-theme sphinx-copybutton sphinx-design scipy -matplotlib>=3.10.8 +matplotlib pandas -breathe>=4.36.0 -ipython>=9.12.0 +breathe +ipython # Needed for ipython>=8.17 # https://github.com/ipython/ipython/issues/14237 pickleshare @@ -17,10 +17,10 @@ pickleshare towncrier toml -scipy-doctest>=2.2.0 +scipy-doctest # interactive documentation utilities # see https://github.com/jupyterlite/pyodide-kernel#compatibility -jupyterlite-sphinx>=0.22.1 +jupyterlite-sphinx # Works with Pyodide 0.27.1 jupyterlite-pyodide-kernel==0.5.2 diff --git a/requirements/emscripten_test_requirements.txt b/requirements/emscripten_test_requirements.txt index f405bb00d884..a375dd0cfd7d 100644 --- a/requirements/emscripten_test_requirements.txt +++ b/requirements/emscripten_test_requirements.txt @@ -1,4 +1,4 @@ -hypothesis==6.152.1 -pytest==9.1.0 +hypothesis==6.156.1 +pytest==9.1.1 tzdata pytest-xdist diff --git a/requirements/linter_requirements.txt b/requirements/linter_requirements.txt index 27284b68b9da..5cb0cff1b3c6 100644 --- a/requirements/linter_requirements.txt +++ b/requirements/linter_requirements.txt @@ -1,5 +1,5 @@ # keep in sync with `environment.yml` cython-lint -ruff==0.15.17 -GitPython>=3.1.50 +ruff==0.15.20 +GitPython==3.1.50 spin diff --git a/requirements/release_requirements.txt b/requirements/release_requirements.txt index 9bbefcd01dbc..1e2acd619d3c 100644 --- a/requirements/release_requirements.txt +++ b/requirements/release_requirements.txt @@ -3,7 +3,7 @@ # changelog.py pygithub -gitpython>=3.1.50 +gitpython==3.1.50 # uploading release documentation packaging diff --git a/requirements/test_requirements.txt b/requirements/test_requirements.txt index 958b541066c8..b8684cba9592 100644 --- a/requirements/test_requirements.txt +++ b/requirements/test_requirements.txt @@ -1,6 +1,6 @@ Cython -hypothesis==6.152.1 -pytest==9.1.0 +hypothesis==6.156.1 +pytest==9.1.1 pytest-cov==7.1.0 meson ninja; sys_platform != "emscripten"