From 50c62da29a57108b3a9e58656e901b6ce619a8dc Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Thu, 18 Jun 2026 23:56:06 -0400 Subject: [PATCH 1/9] Wrap parametrize itertools.product in tuple pytest 9.1 raises PytestRemovedIn10Warning for non-Collection iterables passed to parametrize, which becomes an error under filterwarnings=error and aborts test collection. Wrap in tuple() to match main. Co-Authored-By: Claude Opus 4.8 (cherry picked from commit 15803df58f33b4d621810b1b3aa9fd506400871c) --- numcodecs/tests/test_checksum32.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numcodecs/tests/test_checksum32.py b/numcodecs/tests/test_checksum32.py index 9bdc25cb1..8af409454 100644 --- a/numcodecs/tests/test_checksum32.py +++ b/numcodecs/tests/test_checksum32.py @@ -54,12 +54,12 @@ ) -@pytest.mark.parametrize(("codec", "arr"), itertools.product(codecs, arrays)) +@pytest.mark.parametrize(("codec", "arr"), tuple(itertools.product(codecs, arrays))) def test_encode_decode(codec, arr): check_encode_decode(arr, codec) -@pytest.mark.parametrize(("codec", "arr"), itertools.product(codecs, arrays)) +@pytest.mark.parametrize(("codec", "arr"), tuple(itertools.product(codecs, arrays))) def test_errors(codec, arr): enc = codec.encode(arr) with pytest.raises(RuntimeError): From a78c0e59e4bc5f815e1fcaa152b12fad2b93f0f7 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Fri, 19 Jun 2026 00:12:39 -0400 Subject: [PATCH 2/9] Cap zarr to <3.1 in CI The numcodecs.zarr3 module in this branch targets the zarr 3.0.x API. zarr 3.1+ moved these codecs into zarr itself and changed internal APIs (e.g. the new dtype system), breaking test_zarr3.py. The previous `--pre zarr>=3.0.0b2` install resolved to zarr 3.2.1, causing 26 failures on Python 3.12/3.13. Pin the test dependency to a compatible zarr release. Co-Authored-By: Claude Opus 4.8 (cherry picked from commit c4360702acf5df1dce46c7a1d3b6a23c5048add3) --- .github/workflows/ci.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a2a62c448..cfeab3cce 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -56,8 +56,9 @@ jobs: # Since zarr v3 requires numpy >= 1.25, on Python 3.11 leave it out # so we can have some tests of our minimum version of numpy (1.24) if: matrix.python-version != '3.11' - # TODO: remove --pre option when zarr v3 is out - run: python -m pip install --pre zarr>=3.0.0b2 + # numcodecs.zarr3 in this branch targets the zarr 3.0.x API; zarr 3.1+ + # moved these codecs into zarr itself and changed internal APIs. + run: python -m pip install "zarr>=3.0,<3.1" - name: List installed packages run: python -m pip list From 5a9123b4f706aa225747750a1606fbc40b352767 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Fri, 19 Jun 2026 00:26:21 -0400 Subject: [PATCH 3/9] Swap macos-13 runner for macos-15-large in CI macos-13 runners are stalling. main already moved to macos-15-large (still an Intel runner, preserving x86 coverage). Update the matrix and the clang-install conditional to match. Co-Authored-By: Claude Opus 4.8 (cherry picked from commit 64eade51d9ed4a95f00001036fc947559c19f96f) --- .github/workflows/ci.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cfeab3cce..25e43f16a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,8 +13,8 @@ jobs: fail-fast: false matrix: python-version: ["3.11", "3.12", "3.13"] - # macos-13 is an intel runner, macos-14 is a arm64 runner - platform: [ubuntu-latest, windows-latest, macos-13, macos-14] + # macos-15-large is an intel runner, macos-14 is a arm64 runner + platform: [ubuntu-latest, windows-latest, macos-15-large, macos-14] defaults: run: @@ -38,7 +38,7 @@ jobs: run: conda install -y c-compiler cxx-compiler - name: Install clang - if: matrix.platform == 'macos-13' + if: matrix.platform == 'macos-15-large' run: conda install -y 'clang>=12.0.1,<17' - name: Show conda environment info From fcc528cb5173d68866c1476cac691d3da5c5414b Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Fri, 19 Jun 2026 07:19:02 -0400 Subject: [PATCH 4/9] Strip duplicate LC_RPATH from extensions on macOS The conda-forge compilers inject -Wl,-rpath,$PREFIX/lib three times per link, producing duplicate LC_RPATH load commands. macOS 15's dyld rejects duplicate LC_RPATH at load time, so the freshly switched macos-15-large runners failed every test with an ImportError on dlopen. macos-14's older dyld tolerated the duplicates. Add a macOS-only post-build step that uses install_name_tool to remove the redundant LC_RPATH entries from numcodecs/*.so. Co-Authored-By: Claude Opus 4.8 (cherry picked from commit 7ae0f4b3af1f2aad650f4519f04986ac26b96dfa) --- .github/workflows/ci.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 25e43f16a..ed0bb5488 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -52,6 +52,22 @@ jobs: export DISABLE_NUMCODECS_AVX2="" python -m pip install -v -e .[test,test_extras,msgpack,crc32c,pcodec,zfpy] + - name: Deduplicate LC_RPATH entries + # The conda-forge compilers inject -Wl,-rpath,$PREFIX/lib multiple + # times, producing duplicate LC_RPATH load commands. macOS 15's dyld + # rejects duplicate LC_RPATH at load time, so strip the extras. + if: runner.os == 'macOS' + run: | + rpaths() { otool -l "$1" | awk '/ LC_RPATH$/{getline; getline; print $2}'; } + for so in numcodecs/*.so; do + for rp in $(rpaths "$so" | sort | uniq -d); do + while [ "$(rpaths "$so" | grep -cx "$rp")" -gt 1 ]; do + echo "Removing duplicate LC_RPATH '$rp' from $so" + install_name_tool -delete_rpath "$rp" "$so" + done + done + done + - name: Install zarr-python # Since zarr v3 requires numpy >= 1.25, on Python 3.11 leave it out # so we can have some tests of our minimum version of numpy (1.24) From c1a32cf63ca23f768bbd513a600efe2178d57355 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Sat, 20 Jun 2026 17:25:38 -0400 Subject: [PATCH 5/9] Fix docs, pre-commit, wheel, and coverage CI for the backport branch - .readthedocs.yaml: bump build image to ubuntu-24.04 (ubuntu-20.04 was removed by Read the Docs) and cap docs zarr to <3.1, matching the 0.15.x compatibility range. - .pre-commit-config.yaml: pin the mypy hook's zarr to >=3.0.0rc1,<3.1 so type checking runs against the zarr 3.0 API that numcodecs.zarr3 targets (the unpinned hook pulled zarr 3.2, whose ZDType broke mypy). - zarr3.py: correct two `# type: ignore[arg-type]` to `[call-overload]` for np.dtype(astype) under current numpy. - wheel.yaml: swap the stalled macos-13 intel runner for macos-15-large, matching ci.yaml. - codecov.yml / pyproject.toml: exclude test files from coverage (ignore: tests/** and [tool.coverage.run] omit), matching main. - test_zarr3.py: cover AsType.evolve_from_array_spec when decode_dtype is unset, bringing zarr3.py to 100%. Co-Authored-By: Claude Opus 4.8 (cherry picked from commit dd3c1e346e8d66cf6d2dbe746127108e252040f4) --- .github/codecov.yml | 2 ++ .github/workflows/wheel.yaml | 4 ++-- .pre-commit-config.yaml | 2 +- .readthedocs.yaml | 4 ++-- numcodecs/tests/test_zarr3.py | 2 ++ numcodecs/zarr3.py | 4 ++-- pyproject.toml | 3 +++ 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index e9b99c821..c910cfa5e 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -7,6 +7,8 @@ coverage: patch: default: target: 100 + ignore: + - "tests/**" comment: layout: "diff, files" behavior: default diff --git a/.github/workflows/wheel.yaml b/.github/workflows/wheel.yaml index 6746ffb8f..8227cfa90 100644 --- a/.github/workflows/wheel.yaml +++ b/.github/workflows/wheel.yaml @@ -13,8 +13,8 @@ jobs: strategy: fail-fast: false matrix: - # macos-13 is an intel runner, macos-14 is a arm64 runner - os: [ubuntu-latest, windows-latest, macos-13, macos-14] + # macos-15-large is an intel runner, macos-14 is an arm64 runner + os: [ubuntu-latest, windows-latest, macos-15-large, macos-14] env: CIBW_TEST_COMMAND: python -c "import numcodecs" CIBW_BUILD: "cp311-* cp312-* cp313-*" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5728b2fb3..88ba122b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,4 +30,4 @@ repos: hooks: - id: mypy args: [--config-file, pyproject.toml] - additional_dependencies: [numpy, pytest, crc32c, zfpy, 'zarr>=3.0.0rc1'] + additional_dependencies: [numpy, pytest, crc32c, zfpy, 'zarr>=3.0.0rc1,<3.1'] diff --git a/.readthedocs.yaml b/.readthedocs.yaml index fa14bf340..5d81f8d34 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -4,12 +4,12 @@ submodules: include: all build: - os: ubuntu-20.04 + os: ubuntu-24.04 tools: python: "3.12" jobs: post_install: - - python -m pip install --pre 'zarr>=3.0.0b2' + - python -m pip install --pre 'zarr>=3.0.0b2,<3.1' sphinx: configuration: docs/conf.py diff --git a/numcodecs/tests/test_zarr3.py b/numcodecs/tests/test_zarr3.py index 0d8ecc74f..eaf8634e9 100644 --- a/numcodecs/tests/test_zarr3.py +++ b/numcodecs/tests/test_zarr3.py @@ -92,12 +92,14 @@ def test_generic_compressor( (numcodecs.zarr3.FixedScaleOffset, {"offset": 0, "scale": 25.5}), (numcodecs.zarr3.FixedScaleOffset, {"offset": 0, "scale": 51, "astype": "uint16"}), (numcodecs.zarr3.AsType, {"encode_dtype": "float32", "decode_dtype": "float32"}), + (numcodecs.zarr3.AsType, {"encode_dtype": "float32"}), ], ids=[ "delta", "fixedscaleoffset", "fixedscaleoffset2", "astype", + "astype_no_decode_dtype", ], ) def test_generic_filter( diff --git a/numcodecs/zarr3.py b/numcodecs/zarr3.py index f1743ffbc..43684c3dd 100644 --- a/numcodecs/zarr3.py +++ b/numcodecs/zarr3.py @@ -286,7 +286,7 @@ def __init__(self, **codec_config: dict[str, JSON]) -> None: def resolve_metadata(self, chunk_spec: ArraySpec) -> ArraySpec: if astype := self.codec_config.get("astype"): - return replace(chunk_spec, dtype=np.dtype(astype)) # type: ignore[arg-type] + return replace(chunk_spec, dtype=np.dtype(astype)) # type: ignore[call-overload] return chunk_spec @@ -304,7 +304,7 @@ def __init__(self, **codec_config: JSON) -> None: def resolve_metadata(self, chunk_spec: ArraySpec) -> ArraySpec: if astype := self.codec_config.get("astype"): - return replace(chunk_spec, dtype=np.dtype(astype)) # type: ignore[arg-type] + return replace(chunk_spec, dtype=np.dtype(astype)) # type: ignore[call-overload] return chunk_spec def evolve_from_array_spec(self, array_spec: ArraySpec) -> FixedScaleOffset: diff --git a/pyproject.toml b/pyproject.toml index 29ef0bf0d..957f176f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,6 +117,9 @@ write_to = "numcodecs/version.py" skip = "./.git,fixture" ignore-words-list = "ba, compiletime, hist, nd, unparseable" +[tool.coverage.run] +omit = ["tests/*"] + [tool.coverage.report] exclude_lines = [ "pragma: no cover", From a06d86e74389575efaefce5256a763826b8fa81e Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Sat, 20 Jun 2026 17:57:54 -0400 Subject: [PATCH 6/9] Fix coverage-ignore and docs zarr pin for this branch's layout The previous commit copied main's config verbatim, but this branch differs from main in two ways that broke CI: - Tests live at numcodecs/tests/ here (main moved them to top-level tests/), so codecov `ignore: tests/**` and coverage `omit = ["tests/*"]` matched nothing. Point both at numcodecs/tests so the subprocess/skipped-test lines stop counting against the 100% project target. - Read the Docs strips the quotes around post_install commands, so the `<` in 'zarr>=3.0.0b2,<3.1' was parsed as a shell input redirection ("cannot open 3.1"). Use 'zarr~=3.0.0' instead, which pins to zarr 3.0.x with no shell metacharacters. Co-Authored-By: Claude Opus 4.8 (cherry picked from commit 02dae089b4d6dd60bec70c6e4823433ad3c77108) --- .github/codecov.yml | 2 +- .readthedocs.yaml | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index c910cfa5e..e9b2ef7cd 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -8,7 +8,7 @@ coverage: default: target: 100 ignore: - - "tests/**" + - "numcodecs/tests/**" comment: layout: "diff, files" behavior: default diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 5d81f8d34..5e124a8fc 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ build: python: "3.12" jobs: post_install: - - python -m pip install --pre 'zarr>=3.0.0b2,<3.1' + - python -m pip install --pre 'zarr~=3.0.0' sphinx: configuration: docs/conf.py diff --git a/pyproject.toml b/pyproject.toml index 957f176f2..6bb09a67b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,7 +118,7 @@ skip = "./.git,fixture" ignore-words-list = "ba, compiletime, hist, nd, unparseable" [tool.coverage.run] -omit = ["tests/*"] +omit = ["numcodecs/tests/*"] [tool.coverage.report] exclude_lines = [ From fdfe33cbb9134bde2034d9be782b72de06ec914d Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Mon, 5 Oct 2026 20:38:24 +0200 Subject: [PATCH 7/9] ci: build with GCC 14 on Linux and unpin clang on Intel macOS GCC 15 defaults to C23, where bool is a keyword, so it rejects the 'typedef _Bool bool' in the c-blosc version bundled on this branch. Pin the conda-forge compilers to GCC 14 on Linux rather than bumping the bundled library on a maintenance line. Also drop the clang<17 pin on macos-15-large. It downgraded the compiler to clang 14, which cannot parse the macOS 15 SDK headers (math.h: '_Float16 is not supported on this target'). Assisted-by: ClaudeCode:claude-fable-5.1 --- .github/workflows/ci.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ed0bb5488..bca3a1fdc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,9 +37,11 @@ jobs: - name: Install compilers run: conda install -y c-compiler cxx-compiler - - name: Install clang - if: matrix.platform == 'macos-15-large' - run: conda install -y 'clang>=12.0.1,<17' + - name: Install GCC 14 + # GCC 15 defaults to C23, where `bool` is a keyword, and the c-blosc + # version bundled on this branch still defines `bool` with a typedef. + if: runner.os == 'Linux' + run: conda install -y 'gcc<15' 'gxx<15' - name: Show conda environment info run: | From ca0d488f0524a2db9c436a1308233ea48a6716c4 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Mon, 5 Oct 2026 20:39:33 +0200 Subject: [PATCH 8/9] ci(wheels): only install NumPy from wheels when building Linux wheels The Linux wheels are built in the manylinux2014 image. NumPy 2.3 and later publish only manylinux_2_28 wheels, so pip fell back to building the newest NumPy from source, and NumPy 2.5 refuses the image's GCC ('NumPy requires GCC >= 10.3'), which failed the cp312 build. Setting PIP_ONLY_BINARY=numpy makes pip pick the newest NumPy that has a manylinux2014 wheel, keeping the wheels' platform tag unchanged. Assisted-by: ClaudeCode:claude-fable-5.1 --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 6bb09a67b..af7efb93f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -159,6 +159,10 @@ filterwarnings = [ [tool.cibuildwheel] environment = { DISABLE_NUMCODECS_AVX2=1 } +[tool.cibuildwheel.linux] +# Only install NumPy from wheels when building. Recent NumPy releases have no manylinux2014 +# wheels, and building them from source fails with the GCC in the manylinux2014 image. +environment = { DISABLE_NUMCODECS_AVX2=1, PIP_ONLY_BINARY="numpy" } [tool.cibuildwheel.macos] environment = { MACOSX_DEPLOYMENT_TARGET=10.9, DISABLE_NUMCODECS_AVX2=1, CFLAGS="$CFLAGS -Wno-implicit-function-declaration" } [[tool.cibuildwheel.overrides]] From 922b5e73a854680c4b0d7ca44dbe28ce5777acbc Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Mon, 5 Oct 2026 20:54:29 +0200 Subject: [PATCH 9/9] ci: only disable AVX2 on x86_64 runners setup.py on this branch appends -mno-avx2 whenever DISABLE_NUMCODECS_AVX2 is set, regardless of architecture. clang 21 from conda-forge rejects that flag on arm64 ('unsupported option -mno-avx2 for target arm64-apple-darwin'), which failed every macos-14 build. Set the variable only on x86_64 runners. Assisted-by: ClaudeCode:claude-fable-5.1 --- .github/workflows/ci.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bca3a1fdc..61f8a6f7e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -51,7 +51,11 @@ jobs: - name: Install numcodecs run: | - export DISABLE_NUMCODECS_AVX2="" + # On this branch setup.py adds -mno-avx2 whenever this variable is set, and + # recent clang rejects that flag on arm64, so only set it on x86_64. + if [[ "$(uname -m)" == "x86_64" ]]; then + export DISABLE_NUMCODECS_AVX2="" + fi python -m pip install -v -e .[test,test_extras,msgpack,crc32c,pcodec,zfpy] - name: Deduplicate LC_RPATH entries