Skip to content

feat: Support Python 3.14. - #131

Open
joshuarobinson wants to merge 1 commit into
mainfrom
fea-python-3.14-support-v2
Open

feat: Support Python 3.14.#131
joshuarobinson wants to merge 1 commit into
mainfrom
fea-python-3.14-support-v2

Conversation

@joshuarobinson

@joshuarobinson joshuarobinson commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Adds Python 3.14 to the supported matrix without waiting on the Zarr 3.x migration.

Zarr 2.x cannot run on Python 3.14: zarr 2.18.7 caps numcodecs <0.16, but numcodecs only ships Python 3.14 wheels from 0.16.4 on, and forcing 0.16.x breaks Zarr 2.x at import (numcodecs.blosc.cbuffer_sizes was removed). Zarr 3.x does support Python 3.14 but is a breaking API change (no zarr.storage.BaseStore, async store ABC) and requires Python 3.11+, rising to 3.12+ in recent releases, so it cannot cover our Python 3.10 support either. Rather than maintain parallel Zarr 2.x/3.x store implementations for the few months until Python 3.10 EOL (October 2026), the zarr extra is gated off on Python 3.14 and everything else ships now.

Dependency floors are raised only where Python 3.14 needs them, using markers so Python 3.10-3.13 consumers keep their existing floors. pyyaml 6.0.3, xattr 1.3.0, numpy 2.3.2 and oci 2.182 (2.181 pins crc32c 2.7.1) are each the first release carrying cp314 wheels, and neither pyyaml 6.0.1 nor xattr 1.1.4 builds from its sdist on 3.14. Scoping the bumps avoids forcing an upgrade on downstream pins for a constraint their interpreter doesn't have, and keeps the PyPy wheels that xattr dropped in 1.3.0.

Because a marker-gated extra resolves empty rather than failing, pip install "multi-storage-client[zarr]" succeeds but the zarr extra contributes nothing on Python 3.14. The contrib getattr now translates an unusable optional dependency into a message naming the package and the extra. It catches ImportError rather than ModuleNotFoundError, because an installed Zarr 3.x fails on a missing name (zarr.storage.BaseStore) rather than a missing module, and it resolves the Zarr cases from the attribute being imported rather than from the raised error, because contrib.zarr imports numpy before zarr and would otherwise name the wrong package on 3.14. Unrecognized ImportErrors propagate untouched so unrelated failures aren't masked. This also fixes a pre-existing papercut on every Python version: installing the xarray extra without the zarr extra previously surfaced a bare "No module named 'zarr'".

xattr 1.3.0 gave xattr.xattr.get a sentinel default of object(), widening the inferred return type of the module-level getxattr from bytes to object and failing pyright at every .decode() call site on every Python version. multistorageclient._xattr restores the bytes contract in one place. contrib/zarr.py's zarr imports carry reportMissingImports suppressions so pyright also passes in a Python 3.14 environment, where the extra installs nothing; they are inert wherever Zarr is installed.

Verified: full unit suite passes on Python 3.14 (1072 passed, 5 skipped) and Python 3.10 (1074 passed, 3 skipped); the difference is the two zarr-dependent contrib tests skipping. uv sync --all-extras --locked succeeds on Python 3.14 and maturin produces a cp314 wheel. ruff check, ruff format --check and uv run pyright are clean, the last both with and without Zarr installed. pyyaml 6.0.1 and xattr 1.1.4 were confirmed to fail their sdist builds on 3.14 and to install cleanly on 3.10, and the Zarr 3 path was exercised against a package exposing zarr.storage without BaseStore.

Description

Change description.

{Relates to/Closes} {Task ID}.

Checklist

  • Development PR
    • .release_notes/.unreleased.md
      • Notable changes to the client (i.e. not related to tooling, CI/CD, etc.) from this PR have been added.
  • Release PR
    • CI/CD
      • The default branch pipelines are passing in both GitHub + GitLab (latter for SwiftStack E2E tests).
    • multi-storage-client/pyproject.toml
      • The package version has been bumped.
    • .release_notes/.unreleased.md
      • This file's contents have been moved into a .release_notes/{bumped package version}.md file.

Summary by CodeRabbit

  • New Features
    • Added Python 3.14 support for installation, development, and automated compatibility testing.
    • Improved diagnostics for missing optional integrations, including actionable installation guidance.
  • Bug Fixes
    • Improved extended-attribute handling for cache and POSIX file metadata operations.
    • Clarified incompatibilities involving Zarr versions and Python 3.14.
  • Documentation
    • Documented Zarr limitations on Python 3.14, with workarounds using Python 3.13 or separately installed Zarr 3.x.

@joshuarobinson
joshuarobinson requested a review from a team August 6, 2026 16:10
@copy-pr-bot

copy-pr-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6903abbb-ade3-457f-9f40-a32f80bae584

📥 Commits

Reviewing files that changed from the base of the PR and between 712d11f and 45cbf0d.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .github/workflows/.beta.yml
  • .github/workflows/.source.yml
  • .gitlab/pipelines/.beta.yml
  • .gitlab/pipelines/.source.yml
  • .release_notes/.unreleased.md
  • multi-storage-client-docs/src/user_guide/installation.rst
  • multi-storage-client/pyproject.toml
  • multi-storage-client/src/multistorageclient/__init__.py
  • multi-storage-client/src/multistorageclient/_xattr.py
  • multi-storage-client/src/multistorageclient/cache.py
  • multi-storage-client/src/multistorageclient/contrib/zarr.py
  • multi-storage-client/src/multistorageclient/providers/posix_file.py
  • multi-storage-client/tests/test_multistorageclient/unit/contrib/test_xarray.py
  • multi-storage-client/tests/test_multistorageclient/unit/contrib/test_zarr.py
  • multi-storage-client/tests/test_multistorageclient/unit/sync/test_worker.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_cache.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_lazy_import.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_partial_file_caching.py
  • nix/overlays/development/multi-storage-client/devShells/python3.14.nix
🚧 Files skipped from review as they are similar to previous changes (19)
  • .github/workflows/.source.yml
  • multi-storage-client/tests/test_multistorageclient/unit/sync/test_worker.py
  • multi-storage-client-docs/src/user_guide/installation.rst
  • multi-storage-client/src/multistorageclient/_xattr.py
  • .gitlab/pipelines/.source.yml
  • .github/workflows/.beta.yml
  • nix/overlays/development/multi-storage-client/devShells/python3.14.nix
  • multi-storage-client/tests/test_multistorageclient/unit/contrib/test_xarray.py
  • .gitlab/pipelines/.beta.yml
  • multi-storage-client/src/multistorageclient/contrib/zarr.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_partial_file_caching.py
  • multi-storage-client/tests/test_multistorageclient/unit/contrib/test_zarr.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_cache.py
  • .release_notes/.unreleased.md
  • multi-storage-client/src/multistorageclient/providers/posix_file.py
  • multi-storage-client/pyproject.toml
  • multi-storage-client/tests/test_multistorageclient/unit/test_lazy_import.py
  • multi-storage-client/src/multistorageclient/cache.py
  • multi-storage-client/src/multistorageclient/init.py

📝 Walkthrough

Walkthrough

The change adds Python 3.14 support across packaging, CI, documentation, and development tooling. It adds optional dependency diagnostics, handles Zarr constraints, and centralizes extended-attribute reads through an internal wrapper.

Changes

Python 3.14 and dependency support

Layer / File(s) Summary
Python 3.14 packaging and CI
multi-storage-client/pyproject.toml, .github/workflows/*, .gitlab/pipelines/*, multi-storage-client-docs/..., .release_notes/..., nix/...
Packaging constraints, CI matrices, documentation, release notes, and the development shell now include Python 3.14. Zarr remains restricted to Python 3.10–3.13.
Contrib import diagnostics
multi-storage-client/src/multistorageclient/__init__.py, multi-storage-client/src/multistorageclient/contrib/zarr.py, multi-storage-client/tests/.../contrib/*, multi-storage-client/tests/.../test_lazy_import.py
Lazy imports now provide mapped installation guidance, explicit Python 3.14 and Zarr 3 diagnostics, chained import errors, and coverage for missing metadata and unrelated failures.
Extended-attribute abstraction
multi-storage-client/src/multistorageclient/_xattr.py, multi-storage-client/src/multistorageclient/cache.py, multi-storage-client/src/multistorageclient/providers/posix_file.py, multi-storage-client/tests/...
Extended-attribute reads now use the internal typed wrapper in production code and related tests. Write behavior remains unchanged.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant multistorageclient.__getattr__
  participant OptionalDependency
  participant PackageMetadata
  Caller->>multistorageclient.__getattr__: request contrib module
  multistorageclient.__getattr__->>OptionalDependency: import contrib dependency
  OptionalDependency-->>multistorageclient.__getattr__: module or ImportError
  multistorageclient.__getattr__->>PackageMetadata: inspect installed version
  PackageMetadata-->>multistorageclient.__getattr__: version or metadata failure
  multistorageclient.__getattr__-->>Caller: module or chained guidance error
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding Python 3.14 support.
Description check ✅ Passed The description provides detailed technical context, validation results, and the required template sections, despite retaining placeholder text and unchecked checklist items.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fea-python-3.14-support-v2

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@multi-storage-client/src/multistorageclient/_xattr.py`:
- Around line 19-23: Correct the description around the xattr wrapper behavior:
state that xattr.xattr.get’s implicit _SENTINEL_MISSING widens the static return
type, while module-level xattr.getxattr calls that omit default still raise
OSError for missing attributes. Preserve the existing explanation that the
wrapper restores the bytes contract for callers.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: caec3020-10d7-4e43-bdca-c14c88be112a

📥 Commits

Reviewing files that changed from the base of the PR and between 712d11f and 1e2f895.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .github/workflows/.beta.yml
  • .github/workflows/.source.yml
  • .gitlab/pipelines/.beta.yml
  • .gitlab/pipelines/.source.yml
  • .release_notes/.unreleased.md
  • multi-storage-client-docs/src/user_guide/installation.rst
  • multi-storage-client/pyproject.toml
  • multi-storage-client/src/multistorageclient/__init__.py
  • multi-storage-client/src/multistorageclient/_xattr.py
  • multi-storage-client/src/multistorageclient/cache.py
  • multi-storage-client/src/multistorageclient/contrib/zarr.py
  • multi-storage-client/src/multistorageclient/providers/posix_file.py
  • multi-storage-client/tests/test_multistorageclient/unit/contrib/test_xarray.py
  • multi-storage-client/tests/test_multistorageclient/unit/contrib/test_zarr.py
  • multi-storage-client/tests/test_multistorageclient/unit/sync/test_worker.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_cache.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_lazy_import.py
  • multi-storage-client/tests/test_multistorageclient/unit/test_partial_file_caching.py
  • nix/overlays/development/multi-storage-client/devShells/python3.14.nix

Comment thread multi-storage-client/src/multistorageclient/_xattr.py Outdated
Comment on lines +34 to +42
# PyYAML 6.0.3 and xattr 1.3.0 are the first releases with Python 3.14 wheels, and
# neither older floor builds from its sdist on 3.14. The bumps are scoped to 3.14 so
# older interpreters keep their existing floors: raising them there would force an
# upgrade on downstream pins for no reason, and xattr 1.3.0 ships no PyPy wheels.
"pyyaml>=6.0.1,<7; python_version<'3.14'",
"pyyaml>=6.0.3,<7; python_version>='3.14'",
"opentelemetry-api>=1.24,<2",
"xattr>=1.1.4,<2",
"xattr>=1.1.4,<2; python_version<'3.14'",
"xattr>=1.3.0,<2; python_version>='3.14'",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a good reason to not just use pyyaml>=6.0.3,<7 and xattr>=1.3.0,<2 for all Python versions. The less conditionals we have the better.

Comment on lines +105 to +107
# 2.182 is the first release pinning crc32c 2.8.0, which has Python 3.14 wheels.
"oci>=2.181,<3; python_version<'3.14'",
"oci>=2.182,<3; python_version>='3.14'"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto for oci>=2.182,<3.

Adds Python 3.14 to the supported matrix without waiting on the Zarr 3.x
migration.

Zarr 2.x cannot run on Python 3.14: zarr 2.18.7 caps numcodecs <0.16, but
numcodecs only ships Python 3.14 wheels from 0.16.4 on, and forcing 0.16.x
breaks Zarr 2.x at import (numcodecs.blosc.cbuffer_sizes was removed). Zarr 3.x
does support Python 3.14 but is a breaking API change (no zarr.storage.BaseStore,
async store ABC) and requires Python 3.11+, rising to 3.12+ in recent releases,
so it cannot cover our Python 3.10 support either. Rather than maintain parallel
Zarr 2.x/3.x store implementations for the few months until Python 3.10 EOL
(October 2026), the zarr extra is gated off on Python 3.14 and everything else
ships now.

Dependency floors are raised only where Python 3.14 needs them, using markers so
Python 3.10-3.13 consumers keep their existing floors. pyyaml 6.0.3, xattr 1.3.0,
numpy 2.3.2 and oci 2.182 (2.181 pins crc32c 2.7.1) are each the first release
carrying cp314 wheels, and neither pyyaml 6.0.1 nor xattr 1.1.4 builds from its
sdist on 3.14. Scoping the bumps avoids forcing an upgrade on downstream pins for
a constraint their interpreter doesn't have, and keeps the PyPy wheels that xattr
dropped in 1.3.0.

Because a marker-gated extra resolves empty rather than failing,
`pip install "multi-storage-client[zarr]"` succeeds but the zarr extra
contributes nothing on Python 3.14. The contrib __getattr__ now translates an
unusable optional dependency into a message naming the package and the extra.
It catches ImportError rather than ModuleNotFoundError, because an installed
Zarr 3.x fails on a missing name (zarr.storage.BaseStore) rather than a missing
module, and it resolves the Zarr cases from the attribute being imported rather
than from the raised error, because contrib.zarr imports numpy before zarr and
would otherwise name the wrong package on 3.14. Unrecognized ImportErrors
propagate untouched so unrelated failures aren't masked. This also fixes a
pre-existing papercut on every Python version: installing the xarray extra
without the zarr extra previously surfaced a bare "No module named 'zarr'".

xattr 1.3.0 gave xattr.xattr.get a sentinel default of object(), widening the
inferred return type of the module-level getxattr from bytes to object and
failing pyright at every .decode() call site on every Python version.
multistorageclient._xattr restores the bytes contract in one place.
contrib/zarr.py's zarr imports carry reportMissingImports suppressions so
pyright also passes in a Python 3.14 environment, where the extra installs
nothing; they are inert wherever Zarr is installed.

Verified: full unit suite passes on Python 3.14 (1072 passed, 5 skipped) and
Python 3.10 (1074 passed, 3 skipped); the difference is the two zarr-dependent
contrib tests skipping. `uv sync --all-extras --locked` succeeds on Python 3.14
and maturin produces a cp314 wheel. `ruff check`, `ruff format --check` and
`uv run pyright` are clean, the last both with and without Zarr installed.
pyyaml 6.0.1 and xattr 1.1.4 were confirmed to fail their sdist builds on 3.14
and to install cleanly on 3.10, and the Zarr 3 path was exercised against a
package exposing zarr.storage without BaseStore.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@joshuarobinson
joshuarobinson force-pushed the fea-python-3.14-support-v2 branch from 1e2f895 to 45cbf0d Compare August 6, 2026 18:57
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants