From 601e88cb3b7685797ed082cd3b6042564b2ef8a3 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 30 Sep 2026 14:38:44 +0000 Subject: [PATCH 01/11] matscipy: Add version 1.2.0 --- .github/workflows/build-matscipy.yml | 101 +++++++++++++++++++++++++++ docs/packages/matscipy.yaml | 5 ++ 2 files changed, 106 insertions(+) create mode 100644 .github/workflows/build-matscipy.yml create mode 100644 docs/packages/matscipy.yaml diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml new file mode 100644 index 00000000000..6e5bab1fb7c --- /dev/null +++ b/.github/workflows/build-matscipy.yml @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on the `build` job of +# https://github.com/libAtoms/matscipy/blob/v1.2.0/.github/workflows/wheels.yml +# and the `pytest` step of +# https://github.com/libAtoms/matscipy/blob/v1.2.0/.github/workflows/tests.yml +name: Build matscipy wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/matscipy.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-matscipy.yml' + - 'docs/packages/matscipy.yaml' + push: + branches: [main] + paths: + - '.github/workflows/build-matscipy.yml' + - 'docs/packages/matscipy.yaml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: matscipy + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build matscipy ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 180 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + python: ["cp312", "cp313", "cp314"] + + env: + MATSCIPY_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout matscipy v${{ env.MATSCIPY_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: libAtoms/matscipy + ref: v${{ env.MATSCIPY_VERSION }} + fetch-depth: 0 + persist-credentials: false + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + only: ${{ matrix.python }}-manylinux_riscv64 + env: + CIBW_BUILD_FRONTEND: build + CIBW_BEFORE_BUILD: pip install meson meson-python ninja + CIBW_ENVIRONMENT: >- + PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + PIP_ONLY_BINARY=numpy,scipy,matplotlib,contourpy,kiwisolver,pillow + # Upstream's wheel job only imports the extension; run its tests.yml suite instead. + CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy + CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: matscipy-${{ env.MATSCIPY_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: ./wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish matscipy ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: matscipy-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/matscipy.yaml b/docs/packages/matscipy.yaml new file mode 100644 index 00000000000..67ee66b3565 --- /dev/null +++ b/docs/packages/matscipy.yaml @@ -0,0 +1,5 @@ +package-name: matscipy +source-code: https://github.com/libAtoms/matscipy +license: LGPL-2.1-only +versions: +- version: 1.2.0 From b743b4ac7348f7054f403a95d3541d5a4864534f Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 30 Sep 2026 18:37:22 +0200 Subject: [PATCH 02/11] build-matscipy: cap ase below 3.28 for the test run 29 tests failed and 12 modules errored on riscv64 with AttributeError: module 'ase.constraints' has no attribute 'UnitCellFilter' and 'Log' object has no attribute 'close', plus ImportErrors in test_crack.py, test_dislocation.py and others. Not a riscv64 bug: ase 3.28.0 removed the whole ase.constraints module (confirmed by downloading the ase 3.25.0-3.29.0 wheels and diffing their contents -- 3.25.0-3.27.0 still ship ase/constraints.py, 3.28.0/3.29.0 don't). matscipy v1.2.0's own tests/ still import from the old location; its pyproject.toml floor of ase>=3.26.0 doesn't prevent pip from picking today's 3.29.0, which no longer has it. Pin the *test* dependency only -- matscipy's own install_requires is left untouched, since widening it is upstream's call, not this port's. Signed-off-by: Ludovic Henry --- .github/workflows/build-matscipy.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 6e5bab1fb7c..7e9ccf3cfc2 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -74,7 +74,10 @@ jobs: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ PIP_ONLY_BINARY=numpy,scipy,matplotlib,contourpy,kiwisolver,pillow # Upstream's wheel job only imports the extension; run its tests.yml suite instead. - CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy + # ase<3.28: 3.28.0 removed the whole ase.constraints module (UnitCellFilter and + # friends) that this v1.2.0 tag's own tests/ still imports from; matscipy's + # pyproject.toml floor (ase>=3.26.0) doesn't cap the other end. + CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy "ase<3.28" CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From bc857a43f8b31b4cb091b2783615cacf076e5e70 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 30 Sep 2026 20:37:26 +0200 Subject: [PATCH 03/11] build-matscipy: fix the ase<3.28 pin, quoted in CIBW_TEST_REQUIRES did nothing The rerun on the previous fix (b743b4ac) failed identically: 29 failed, 12 errors, same AttributeErrors on ase.constraints.UnitCellFilter and Log.close(). The quoted token "ase<3.28" inside the space-separated CIBW_TEST_REQUIRES string is a YAML plain scalar, so the quote characters are literal, not stripped -- pip (or cibuildwheel's splitting of the value) never saw a valid `ase<3.28` requirement. Switch to PIP_CONSTRAINT via CIBW_TEST_ENVIRONMENT instead: a constraints file written on the host, referenced by an env var pip honors on every invocation in the test phase regardless of install order or how the requirement string gets tokenized. Signed-off-by: Ludovic Henry --- .github/workflows/build-matscipy.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 7e9ccf3cfc2..1746d51ac32 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -63,6 +63,17 @@ jobs: fetch-depth: 0 persist-credentials: false + # ase 3.28.0 removed the whole ase.constraints module (UnitCellFilter and friends) + # that this v1.2.0 tag's own tests/ still imports from; matscipy's pyproject.toml + # floor (ase>=3.26.0) doesn't cap the other end, so an unconstrained resolve picks + # today's ase and breaks 29 tests / errors on 12 modules. A PIP_CONSTRAINT file + # (below, via CIBW_TEST_ENVIRONMENT) caps it for every pip invocation in the test + # phase, however cibuildwheel orders installs -- unlike a version spec embedded + # directly in CIBW_TEST_REQUIRES, which a prior commit tried and which silently did + # nothing (the quoted token was a literal string, not a parsed requirement). + - name: Constrain ase for the test run + run: echo 'ase<3.28' > "${{ github.workspace }}/ase-constraint.txt" + - name: Build wheels uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 with: @@ -74,10 +85,8 @@ jobs: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ PIP_ONLY_BINARY=numpy,scipy,matplotlib,contourpy,kiwisolver,pillow # Upstream's wheel job only imports the extension; run its tests.yml suite instead. - # ase<3.28: 3.28.0 removed the whole ase.constraints module (UnitCellFilter and - # friends) that this v1.2.0 tag's own tests/ still imports from; matscipy's - # pyproject.toml floor (ase>=3.26.0) doesn't cap the other end. - CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy "ase<3.28" + CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy + CIBW_TEST_ENVIRONMENT: PIP_CONSTRAINT=${{ github.workspace }}/ase-constraint.txt CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From f4d62cbf7ad28ef5bd8f37b883cd88ff87e49d22 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 30 Sep 2026 22:36:59 +0200 Subject: [PATCH 04/11] build-matscipy: write the ase constraint file to runner.temp, not github.workspace The rerun failed immediately with: ERROR: Could not open constraint file: [Errno 2] No such file or directory: '/home/runner/work/python-wheels/python-wheels/ase-constraint.txt' cibuildwheel's manylinux test phase runs pip inside the same docker container as the build; github.workspace's host path isn't reliably what's mounted there on this self-hosted riscv64 runner. build-kivy.yml already proved runner.temp *is* visible in that same test-phase environment (its own PIP_CONSTRAINT fix changed kivy's failure mode exactly as expected once switched to it). Use the same path here. Signed-off-by: Ludovic Henry --- .github/workflows/build-matscipy.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 1746d51ac32..20e6d335da7 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -71,8 +71,13 @@ jobs: # phase, however cibuildwheel orders installs -- unlike a version spec embedded # directly in CIBW_TEST_REQUIRES, which a prior commit tried and which silently did # nothing (the quoted token was a literal string, not a parsed requirement). + # runner.temp, not github.workspace: cibuildwheel's manylinux test phase runs pip + # inside the same docker container as the build, and github.workspace's host path + # isn't what's mounted there on this self-hosted runner (confirmed by a failed + # rerun); runner.temp is (build-kivy.yml's own PIP_CONSTRAINT fix proved it visible + # in that same test-phase environment). - name: Constrain ase for the test run - run: echo 'ase<3.28' > "${{ github.workspace }}/ase-constraint.txt" + run: echo 'ase<3.28' > "${{ runner.temp }}/ase-constraint.txt" - name: Build wheels uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 @@ -86,7 +91,7 @@ jobs: PIP_ONLY_BINARY=numpy,scipy,matplotlib,contourpy,kiwisolver,pillow # Upstream's wheel job only imports the extension; run its tests.yml suite instead. CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy - CIBW_TEST_ENVIRONMENT: PIP_CONSTRAINT=${{ github.workspace }}/ase-constraint.txt + CIBW_TEST_ENVIRONMENT: PIP_CONSTRAINT=${{ runner.temp }}/ase-constraint.txt CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From cd4dcc022ea573719e11be7814519c632fbdfca1 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 1 Oct 2026 00:42:09 +0200 Subject: [PATCH 05/11] matscipy: write the ase constraint inside the cibuildwheel container CIBW_TEST_COMMAND runs matscipy's whole build+test lifecycle inside the manylinux Docker container; neither github.workspace nor runner.temp is a path that container ever sees; a host-side step writing to either one is invisible to it, which is why both prior attempts hit the same "Could not open constraint file" error. before-build/build/repair/test all run in that same container instance per identifier, so writing the constraint file from CIBW_BEFORE_BUILD instead puts it on a path the container actually has. --- .github/workflows/build-matscipy.yml | 35 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 20e6d335da7..4d44282f9b9 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -63,35 +63,34 @@ jobs: fetch-depth: 0 persist-credentials: false - # ase 3.28.0 removed the whole ase.constraints module (UnitCellFilter and friends) - # that this v1.2.0 tag's own tests/ still imports from; matscipy's pyproject.toml - # floor (ase>=3.26.0) doesn't cap the other end, so an unconstrained resolve picks - # today's ase and breaks 29 tests / errors on 12 modules. A PIP_CONSTRAINT file - # (below, via CIBW_TEST_ENVIRONMENT) caps it for every pip invocation in the test - # phase, however cibuildwheel orders installs -- unlike a version spec embedded - # directly in CIBW_TEST_REQUIRES, which a prior commit tried and which silently did - # nothing (the quoted token was a literal string, not a parsed requirement). - # runner.temp, not github.workspace: cibuildwheel's manylinux test phase runs pip - # inside the same docker container as the build, and github.workspace's host path - # isn't what's mounted there on this self-hosted runner (confirmed by a failed - # rerun); runner.temp is (build-kivy.yml's own PIP_CONSTRAINT fix proved it visible - # in that same test-phase environment). - - name: Constrain ase for the test run - run: echo 'ase<3.28' > "${{ runner.temp }}/ase-constraint.txt" - - name: Build wheels uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 with: only: ${{ matrix.python }}-manylinux_riscv64 env: CIBW_BUILD_FRONTEND: build - CIBW_BEFORE_BUILD: pip install meson meson-python ninja + # ase 3.28.0 removed the whole ase.constraints module (UnitCellFilter and + # friends) that this v1.2.0 tag's own tests/ still imports from; matscipy's + # pyproject.toml floor (ase>=3.26.0) doesn't cap the other end, so an + # unconstrained resolve picks today's ase and breaks 29 tests / errors on 12 + # modules. A version spec embedded directly in CIBW_TEST_REQUIRES silently + # does nothing (the quoted token is a literal string, not a parsed + # requirement), so this pins it via a PIP_CONSTRAINT file instead. + # CIBW_TEST_COMMAND runs matscipy's whole build+test lifecycle inside the + # manylinux Docker container, and neither github.workspace nor runner.temp + # -- both host paths -- is visible there (two prior attempts confirmed + # each one the hard way). before-build/build/repair/test share one container + # instance per identifier, so write the file here, from inside that + # container, instead of from a host-side step. + CIBW_BEFORE_BUILD: | + pip install meson meson-python ninja + echo 'ase<3.28' > /tmp/ase-constraint.txt CIBW_ENVIRONMENT: >- PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ PIP_ONLY_BINARY=numpy,scipy,matplotlib,contourpy,kiwisolver,pillow # Upstream's wheel job only imports the extension; run its tests.yml suite instead. CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy - CIBW_TEST_ENVIRONMENT: PIP_CONSTRAINT=${{ runner.temp }}/ase-constraint.txt + CIBW_TEST_ENVIRONMENT: PIP_CONSTRAINT=/tmp/ase-constraint.txt CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From aeb900ed7667e87ba756bf8b72ef79213ee81a73 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 1 Oct 2026 02:37:19 +0200 Subject: [PATCH 06/11] matscipy: pin ase via a bare CIBW_TEST_REQUIRES token, not PIP_CONSTRAINT The PIP_CONSTRAINT file (written from CIBW_BEFORE_BUILD into /tmp) didn't error this run, but it also didn't take: the test venv still picked an ase recent enough to hit NumPy 2.5 deprecation warnings inside ase/atoms.py itself, well past 3.28, and all 29 failures are still the removed ase.constraints.UnitCellFilter/ExpCellFilter. Dropping the constraint file entirely and adding `ase<3.28` as its own space-separated token in CIBW_TEST_REQUIRES - a plain version spec, no quote characters anywhere in the YAML scalar to be literal or stripped - puts the pin in the same `pip install` invocation that installs the wheel, resolved together with matscipy's own `ase>=3.26.0` floor. --- .github/workflows/build-matscipy.yml | 29 +++++++++++----------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 4d44282f9b9..204347a9759 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -69,28 +69,21 @@ jobs: only: ${{ matrix.python }}-manylinux_riscv64 env: CIBW_BUILD_FRONTEND: build - # ase 3.28.0 removed the whole ase.constraints module (UnitCellFilter and - # friends) that this v1.2.0 tag's own tests/ still imports from; matscipy's - # pyproject.toml floor (ase>=3.26.0) doesn't cap the other end, so an - # unconstrained resolve picks today's ase and breaks 29 tests / errors on 12 - # modules. A version spec embedded directly in CIBW_TEST_REQUIRES silently - # does nothing (the quoted token is a literal string, not a parsed - # requirement), so this pins it via a PIP_CONSTRAINT file instead. - # CIBW_TEST_COMMAND runs matscipy's whole build+test lifecycle inside the - # manylinux Docker container, and neither github.workspace nor runner.temp - # -- both host paths -- is visible there (two prior attempts confirmed - # each one the hard way). before-build/build/repair/test share one container - # instance per identifier, so write the file here, from inside that - # container, instead of from a host-side step. - CIBW_BEFORE_BUILD: | - pip install meson meson-python ninja - echo 'ase<3.28' > /tmp/ase-constraint.txt + CIBW_BEFORE_BUILD: pip install meson meson-python ninja CIBW_ENVIRONMENT: >- PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ PIP_ONLY_BINARY=numpy,scipy,matplotlib,contourpy,kiwisolver,pillow # Upstream's wheel job only imports the extension; run its tests.yml suite instead. - CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy - CIBW_TEST_ENVIRONMENT: PIP_CONSTRAINT=/tmp/ase-constraint.txt + # ase 3.28.0 removed the whole ase.constraints module (UnitCellFilter and + # friends) that this v1.2.0 tag's own tests/ still imports from; matscipy's + # pyproject.toml floor (ase>=3.26.0) doesn't cap the other end, so an + # unconstrained resolve picks today's ase and breaks 29 tests / errors on 12 + # modules. ase<3.28 is its own space-separated token here (no quote + # characters anywhere in this YAML scalar), resolved together with + # matscipy's own floor by the same `pip install` that installs the wheel. + # A PIP_CONSTRAINT file written from CIBW_BEFORE_BUILD into /tmp was tried + # first and didn't error, but also didn't actually cap the resolve. + CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy ase<3.28 CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From 456293528d1c783d89ddfe2f3b2c83db132baef9 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 1 Oct 2026 04:39:39 +0200 Subject: [PATCH 07/11] matscipy: the ase ceiling was off by one version (<3.27, not <3.28) The CIBW_TEST_REQUIRES pin DID work this run - pip correctly resolved and installed ase-3.27.0, downgrading from the unconstrained 3.29.0 - but the build still failed with the exact same AttributeError. Checked upstream ase directly (gitlab.com/ase/ase, not the stale GitHub mirror this repo's other ase diagnosis apparently checked): 3.26.0's ase/constraints.py still has UnitCellFilter as a @deprecated subclass re-exported from ase.filters; that re-export is gone in 3.27.0's constraints.py entirely. The removal this v1.2.0 tag's tests/ can't survive landed in 3.27.0, not 3.28.0. --- .github/workflows/build-matscipy.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 204347a9759..375274fbedf 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -74,16 +74,15 @@ jobs: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ PIP_ONLY_BINARY=numpy,scipy,matplotlib,contourpy,kiwisolver,pillow # Upstream's wheel job only imports the extension; run its tests.yml suite instead. - # ase 3.28.0 removed the whole ase.constraints module (UnitCellFilter and - # friends) that this v1.2.0 tag's own tests/ still imports from; matscipy's - # pyproject.toml floor (ase>=3.26.0) doesn't cap the other end, so an - # unconstrained resolve picks today's ase and breaks 29 tests / errors on 12 - # modules. ase<3.28 is its own space-separated token here (no quote - # characters anywhere in this YAML scalar), resolved together with - # matscipy's own floor by the same `pip install` that installs the wheel. - # A PIP_CONSTRAINT file written from CIBW_BEFORE_BUILD into /tmp was tried - # first and didn't error, but also didn't actually cap the resolve. - CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy ase<3.28 + # ase.constraints.UnitCellFilter (and friends), which this v1.2.0 tag's tests/ + # still imports, was a @deprecated re-export from ase.filters through 3.26.0; + # 3.27.0 drops the re-export entirely (checked gitlab.com/ase/ase directly, + # tag by tag). matscipy's pyproject.toml floor (ase>=3.26.0) doesn't cap the + # other end, so an unconstrained resolve picks today's ase and breaks 29 + # tests / errors on 12 modules. ase<3.27 is its own space-separated token + # here (no quote characters anywhere in this YAML scalar), resolved together + # with matscipy's own floor by the same `pip install` that installs the wheel. + CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy ase<3.27 CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From 7db94f61f676546ab49d585ccfff4648008bd1ae Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 1 Oct 2026 06:37:12 +0200 Subject: [PATCH 08/11] matscipy: patch write_eam's int()/float() on 1-element arrays Down to 10 failures after the ase fix, all unrelated to ase: 8 in test_cauchy_born_corrector.py (not yet root-caused) and test_eam_io.py::test_eam_read_write. The latter is a clean one - write_eam's own "# FIXME: atomic numbers etc are now arrays, and not scalars" comment already names the bug; NumPy 2.x turned the implicit scalar conversion it relied on into a hard TypeError. Patch indexes [0] before int()/float(), exactly as the FIXME describes. --- .github/workflows/build-matscipy.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 375274fbedf..84b6bb6fb9b 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -19,11 +19,13 @@ on: paths: - '.github/workflows/build-matscipy.yml' - 'docs/packages/matscipy.yaml' + - 'patches/matscipy/**' push: branches: [main] paths: - '.github/workflows/build-matscipy.yml' - 'docs/packages/matscipy.yaml' + - 'patches/matscipy/**' concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -63,6 +65,15 @@ jobs: fetch-depth: 0 persist-credentials: false + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch matscipy source + run: git apply python-wheels/patches/matscipy/${{ env.MATSCIPY_VERSION }}/*.patch + - name: Build wheels uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 with: From 0a399df7c2023811bd4d4bf1335a5fe02d7b1807 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 1 Oct 2026 06:37:26 +0200 Subject: [PATCH 09/11] matscipy: patch write_eam's int()/float() on 1-element arrays --- ...ex-1-element-arrays-before-int-float.patch | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch diff --git a/patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch b/patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch new file mode 100644 index 00000000000..a40c0d630d0 --- /dev/null +++ b/patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Thu, 01 Oct 2026 00:00:00 +0000 +Subject: [PATCH] eam/io: index 1-element arrays before int()/float() + +write_eam's "eam" (DYNAMO funcfl) branch calls int(atomic_numbers) and +float(atomic_masses)/float(lattice_parameters) directly on the +EAMParameters fields, which read_eam builds as 1-element numpy arrays +(np.array((int(words[0]),), dtype=int), etc.), not scalars - the +function's own "# FIXME: atomic numbers etc are now arrays, and not +scalars" comment already says as much. NumPy 2.x turned the implicit +conversion of a non-0-d array into a hard TypeError: "only +0-dimensional arrays can be converted to Python scalars", so +test_eam_io.py::test_eam_read_write's round-trip through this branch +fails outright. Indexing [0] before the int()/float() calls is what +the FIXME already describes as the fix. + +Upstream-Status: To upstream [not yet submitted; a NumPy-2.x correctness fix, not riscv64-specific] + +Signed-off-by: Ludovic Henry +--- +--- a/matscipy/calculators/eam/io.py ++++ b/matscipy/calculators/eam/io.py +@@ -548,7 +548,7 @@ + # parameters unpacked + # FIXME: atomic numbers etc are now arrays, and not scalars + crystal_structures_str = ' '.join(s for s in crystal_structures) +- atline = f"{int(atomic_numbers)} {float(atomic_masses)} {float(lattice_parameters)} {crystal_structures_str}" ++ atline = f"{int(atomic_numbers[0])} {float(atomic_masses[0])} {float(lattice_parameters[0])} {crystal_structures_str}" + parameterline = f'{int(Nrho)}\t{float(drho):.16e}\t{int(Nr)}\t{float(dr):.16e}\t{float(cutoff):.10e}' + potheader = f"# EAM potential from : # {source} \n {atline} \n {parameterline}" + # --- Writing new EAM alloy pot file --- # From cb685dffbd50a5436ec02498533fa33432580e14 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 1 Oct 2026 07:35:52 +0200 Subject: [PATCH 10/11] matscipy: hide the patched file from meson-python's dirty-version check meson-python derives the wheel version from git describe on the checked- out matscipy tree; patching a tracked file makes that tree dirty, so the detected version became "1.2.0.dirty" and packaging.version.Version rejected it outright (InvalidVersion) before the build even reached compilation - a fast, unmissable failure, but a build-breaking one. Same fix as build-ssh2-python.yml's own patch step (gotcha 315): git update-index --skip-worktree on the one file the patch touches keeps it out of dirty-detection without touching the patch itself. --- .github/workflows/build-matscipy.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index 84b6bb6fb9b..f1d397f6f45 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -72,7 +72,13 @@ jobs: persist-credentials: false - name: Patch matscipy source - run: git apply python-wheels/patches/matscipy/${{ env.MATSCIPY_VERSION }}/*.patch + run: | + git apply python-wheels/patches/matscipy/${{ env.MATSCIPY_VERSION }}/*.patch + # meson-python reads the wheel version from `git describe` on this tree; a + # dirty tree gives "1.2.0.dirty", which packaging.version.Version rejects + # outright (InvalidVersion), failing the build before compilation even + # starts. Hide just the patched file from dirty-detection instead. + git update-index --skip-worktree matscipy/calculators/eam/io.py - name: Build wheels uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 From 902795b2079b96aa3079ec99e998347d19e5ac01 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 1 Oct 2026 17:45:56 +0000 Subject: [PATCH 11/11] matscipy: backport upstream's SciPy 1.18 and Python 3.14 fixes, raise test timeout All three legs of run 36820543557 failed in the test phase: - cp312/cp313/cp314: the nine TestPredictCauchyBornShifts tests raise "TypeError: sqrtm() got an unexpected keyword argument 'disp'" - scipy 1.18.1 removed sqrtm's deprecated disp= keyword. Backport upstream 75f077ff (in 1.3.0), which drops it. - cp314 only: 20 collection/setup errors from "ValueError: cannot resize an array that may be referenced by another object" in Molecules.__init__ and LAMMPSData (numpy >= 2.4 on Python 3.14, gotcha 593). Backport upstream libAtoms/matscipy#312 (1b006eb4, 3a1ec1dc, in 1.3.0), which replaces the resizes with np.empty. - cp312 only: test_bop.py::test_crystal_elastic_constants[par2-a0=5.2] hit upstream's x86-sized --timeout=600 at 599.99s, with several siblings at 520-596s on every leg. Raise it to 1800 (gotcha 38). The 0001 eam/io patch was also fixed upstream (255201ab, in 1.3.0), so its Upstream-Status becomes Backport. The skip-worktree step now hides every patched file from meson-python's dirty-version check, not just the first one. --- .github/workflows/build-matscipy.yml | 7 +- ...ex-1-element-arrays-before-int-float.patch | 5 +- ...born-drop-removed-sqrtm-disp-keyword.patch | 38 ++++++++ ...ata-replace-ndarray-resize-for-py314.patch | 88 +++++++++++++++++++ 4 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 patches/matscipy/1.2.0/0002-cauchy_born-drop-removed-sqrtm-disp-keyword.patch create mode 100644 patches/matscipy/1.2.0/0003-molecules-lammpsdata-replace-ndarray-resize-for-py314.patch diff --git a/.github/workflows/build-matscipy.yml b/.github/workflows/build-matscipy.yml index f1d397f6f45..0afaa146933 100644 --- a/.github/workflows/build-matscipy.yml +++ b/.github/workflows/build-matscipy.yml @@ -77,8 +77,8 @@ jobs: # meson-python reads the wheel version from `git describe` on this tree; a # dirty tree gives "1.2.0.dirty", which packaging.version.Version rejects # outright (InvalidVersion), failing the build before compilation even - # starts. Hide just the patched file from dirty-detection instead. - git update-index --skip-worktree matscipy/calculators/eam/io.py + # starts. Hide just the patched files from dirty-detection instead. + git diff --name-only | xargs git update-index --skip-worktree - name: Build wheels uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 @@ -100,7 +100,8 @@ jobs: # here (no quote characters anywhere in this YAML scalar), resolved together # with matscipy's own floor by the same `pip install` that installs the wheel. CIBW_TEST_REQUIRES: pytest pytest-subtests pytest-timeout pytest-xdist sympy ase<3.27 - CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=600 -n auto + # Upstream's --timeout=600 is sized for x86; test_bop/test_dislocation cases take 350-600s here. + CIBW_TEST_COMMAND: cd {project}/tests && pytest -v --durations=20 --timeout=1800 -n auto - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: diff --git a/patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch b/patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch index a40c0d630d0..2095d3a1252 100644 --- a/patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch +++ b/patches/matscipy/1.2.0/0001-eam-io-index-1-element-arrays-before-int-float.patch @@ -13,9 +13,10 @@ conversion of a non-0-d array into a hard TypeError: "only 0-dimensional arrays can be converted to Python scalars", so test_eam_io.py::test_eam_read_write's round-trip through this branch fails outright. Indexing [0] before the int()/float() calls is what -the FIXME already describes as the fix. +the FIXME already describes as the fix; upstream made the same change +in 255201ab (released in 1.3.0). -Upstream-Status: To upstream [not yet submitted; a NumPy-2.x correctness fix, not riscv64-specific] +Upstream-Status: Backport [https://github.com/libAtoms/matscipy/commit/255201aba4d827bb97dead8964c80e7276161603] Signed-off-by: Ludovic Henry --- diff --git a/patches/matscipy/1.2.0/0002-cauchy_born-drop-removed-sqrtm-disp-keyword.patch b/patches/matscipy/1.2.0/0002-cauchy_born-drop-removed-sqrtm-disp-keyword.patch new file mode 100644 index 00000000000..e65d73032a3 --- /dev/null +++ b/patches/matscipy/1.2.0/0002-cauchy_born-drop-removed-sqrtm-disp-keyword.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Thu, 01 Oct 2026 00:00:00 +0000 +Subject: [PATCH] cauchy_born: drop the removed sqrtm(disp=) keyword + +SciPy 1.18 removed the deprecated disp= keyword of scipy.linalg.sqrtm, +so CubicCauchyBorn's two sqrtm(Usqr, disp=True) calls raise +"TypeError: sqrtm() got an unexpected keyword argument 'disp'" and all +nine test_cauchy_born_corrector.py::TestPredictCauchyBornShifts tests +fail. disp=True was the default, so sqrtm(Usqr) is unchanged on older +SciPy. Upstream made the same change in 75f077ff (released in 1.3.0). + +Upstream-Status: Backport [https://github.com/libAtoms/matscipy/commit/75f077ff7de629fc65814b760d6ebd225b630d64] + +Signed-off-by: Ludovic Henry +--- +diff --git a/matscipy/cauchy_born.py b/matscipy/cauchy_born.py +index c84a2534..ad973424 100644 +--- a/matscipy/cauchy_born.py ++++ b/matscipy/cauchy_born.py +@@ -268,7 +268,7 @@ class CubicCauchyBorn: + # get U^2 + Usqr = 2 * E + np.eye(3) + # square root matrix to get U +- U = sqrtm(Usqr, disp=True) ++ U = sqrtm(Usqr) + + # this is just the symmetric stretch tensor, exactly what we need. + x = U +@@ -1330,7 +1330,7 @@ class CubicCauchyBorn: + # get U^2 + Usqr = 2 * E + np.eye(3) + # square root matrix +- U = sqrtm(Usqr, disp=True) ++ U = sqrtm(Usqr) + + # this is just the symmetric stretch tensor, exactly what we need. + x = U diff --git a/patches/matscipy/1.2.0/0003-molecules-lammpsdata-replace-ndarray-resize-for-py314.patch b/patches/matscipy/1.2.0/0003-molecules-lammpsdata-replace-ndarray-resize-for-py314.patch new file mode 100644 index 00000000000..930360d3d63 --- /dev/null +++ b/patches/matscipy/1.2.0/0003-molecules-lammpsdata-replace-ndarray-resize-for-py314.patch @@ -0,0 +1,88 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Thu, 01 Oct 2026 00:00:00 +0000 +Subject: [PATCH] molecules, io/lammpsdata: replace ndarray.resize for Python 3.14 + +From NumPy 2.4 on, ndarray.resize(refcheck=True) on Python >= 3.14 uses +PyUnstable_Object_IsUniquelyReferenced, which an attribute-held array +never satisfies, so Molecules.__init__ and LAMMPSData's setter/reader +raise "ValueError: cannot resize an array that may be referenced by +another object" on cp314 only (test_newmb.py, test_neighbours.py, +test_io.py collection errors, test_manybody_molecules.py setup errors). +Upstream replaced every such resize with a fresh np.empty of the same +dtype in libAtoms/matscipy#312 (commits 1b006eb4 and 3a1ec1dc, released +in 1.3.0); this is those two hunks unchanged. + +Upstream-Status: Backport [https://github.com/libAtoms/matscipy/pull/312] + +Signed-off-by: Ludovic Henry +--- +diff --git a/matscipy/io/lammpsdata.py b/matscipy/io/lammpsdata.py +index 4525a786..e7200f0a 100644 +--- a/matscipy/io/lammpsdata.py ++++ b/matscipy/io/lammpsdata.py +@@ -193,10 +193,15 @@ class LAMMPSData: + """Set data component.""" + if name in self._type_names: + name = self._type_names[name] +- self.__data[name].resize(len(value)) ++ old = self.__data[name] ++ new_arr = np.empty(len(value), dtype=self._dtypes[name]) ++ n = min(len(old), len(value)) ++ if n > 0: ++ new_arr[:n] = old[:n] ++ self.__data[name] = new_arr + self.__data[name]['type'] = value + elif name in self._data_names: +- self.__data[name].resize(len(value)) ++ self.__data[name] = np.empty(len(value), dtype=self._dtypes[name]) + data = self.__data[name] + try: + data[data.dtype.names[-1]] = np.array(value) +@@ -309,16 +314,15 @@ class LAMMPSData: + for linum, line in enumerate(fd): + if 'Masses' in line: + ntypes = type_counts['atom types'] +- self['masses'].resize(ntypes) +- self['masses'][:] = \ ++ self.__data['masses'] = \ + np.genfromtxt(fd, skip_header=1, +- max_rows=ntypes, usecols=(1,)) ++ max_rows=ntypes, usecols=(1,), dtype=self._dtypes['masses']) + + else: + for label in self._data_names: + if self.__headers[label] in line: + nlines = data_counts[label] +- self[label].resize(nlines) ++ self.__data[label] = np.empty(nlines, dtype=self._dtypes[label]) + dtype = self[label].dtype + + raw_dtype = np.dtype([('num', np.int32)] + [ +diff --git a/matscipy/molecules.py b/matscipy/molecules.py +index 4657da62..d9a88996 100644 +--- a/matscipy/molecules.py ++++ b/matscipy/molecules.py +@@ -72,19 +72,19 @@ class Molecules: + self.__dict__[data] = np.array([], dtype=dtype) + + if bonds_connectivity is not None: +- self.bonds.resize(len(bonds_connectivity)) ++ self.bonds = np.empty(len(bonds_connectivity), dtype=self._dtypes["bonds"]) + self.bonds["atoms"][:] = bonds_connectivity + self.bonds["type"][:] = bonds_types \ + if bonds_types is not None else default_type + + if angles_connectivity is not None: +- self.angles.resize(len(angles_connectivity)) ++ self.angles = np.empty(len(angles_connectivity), dtype=self._dtypes["angles"]) + self.angles["atoms"][:] = angles_connectivity + self.angles["type"][:] = angles_types \ + if angles_types is not None else default_type + + if dihedrals_connectivity is not None: +- self.dihedrals.resize(len(dihedrals_connectivity)) ++ self.dihedrals = np.empty(len(dihedrals_connectivity), dtype=self._dtypes["dihedrals"]) + self.dihedrals["atoms"][:] = dihedrals_connectivity + self.dihedrals["type"][:] = dihedrals_types \ + if dihedrals_types is not None else default_type