Skip to content

fix: accept universal-pathlib UPath as a StoreLike value - #4265

Open
d-v-b wants to merge 19 commits into
zarr-developers:mainfrom
d-v-b:claude/zarr-python-issue-4244-3863cc
Open

fix: accept universal-pathlib UPath as a StoreLike value#4265
d-v-b wants to merge 19 commits into
zarr-developers:mainfrom
d-v-b:claude/zarr-python-issue-4244-3863cc

Conversation

@d-v-b

@d-v-b d-v-b commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

closes #4244

Get UPath working again as a StoreLike value. This broke because we only handled UPath instances implicitly via isinstance checks against a parent class, and a recent release of UPath changed the inheritance structure of those objects.

claude wrote this, source PR is here: d-v-b#280

Author attestation

  • I am a human, these are my changes, and I have reviewed and understood every change and can explain why each is correct.

TODO

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/user-guide/*.md
  • Changes documented as a new file in changes/
  • GitHub Actions have all passed
  • Test coverage is 100% (Codecov passes)

dependabot Bot and others added 15 commits May 31, 2026 19:28
…#176)

Bumps the actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi) | `0.9.5` | `0.9.6` |
| [codecov/codecov-action](https://github.com/codecov/codecov-action) | `6.0.0` | `6.0.1` |
| [github/issue-metrics](https://github.com/github/issue-metrics) | `4.2.2` | `4.2.7` |
| [j178/prek-action](https://github.com/j178/prek-action) | `2.0.3` | `2.0.4` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `7.0.0` | `7.0.1` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `7.0.0` | `8.0.1` |
| [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) | `1.13.0` | `1.14.0` |
| [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) | `0.5.3` | `0.5.6` |



Updates `prefix-dev/setup-pixi` from 0.9.5 to 0.9.6
- [Release notes](https://github.com/prefix-dev/setup-pixi/releases)
- [Commits](prefix-dev/setup-pixi@1b2de7f...5185adf)

Updates `codecov/codecov-action` from 6.0.0 to 6.0.1
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](codecov/codecov-action@57e3a13...e79a696)

Updates `github/issue-metrics` from 4.2.2 to 4.2.7
- [Release notes](https://github.com/github/issue-metrics/releases)
- [Commits](github-community-projects/issue-metrics@c9e9838...1e38d5e)

Updates `j178/prek-action` from 2.0.3 to 2.0.4
- [Release notes](https://github.com/j178/prek-action/releases)
- [Commits](j178/prek-action@6ad8027...bdca6f1)

Updates `actions/upload-artifact` from 7.0.0 to 7.0.1
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v7...043fb46)

Updates `actions/download-artifact` from 7.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@v7...3e5f45b)

Updates `pypa/gh-action-pypi-publish` from 1.13.0 to 1.14.0
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](pypa/gh-action-pypi-publish@v1.13.0...cef2210)

Updates `zizmorcore/zizmor-action` from 0.5.3 to 0.5.6
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](zizmorcore/zizmor-action@b1d7e1f...5f14fd0)

---
updated-dependencies:
- dependency-name: prefix-dev/setup-pixi
  dependency-version: 0.9.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: codecov/codecov-action
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/issue-metrics
  dependency-version: 4.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: j178/prek-action
  dependency-version: 2.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.5.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
A remote UPath now creates an FsspecStore from the filesystem and storage
options the UPath already carries, and a local UPath creates a LocalStore so
that UPath("/data") and Path("/data") agree.

This previously worked only by accident. In universal-pathlib < 0.3 every
UPath subclassed pathlib.Path and implemented __fspath__, so a remote path was
either converted to a URI string by the caller (xarray does this) or wrapped
in a LocalStore that happened to dispatch through fsspec. Since
universal-pathlib 0.3 remote paths do neither, and passing one raised
TypeError: Unsupported type for store_like.

The UPath branch is checked before the Path branch so that routing is
identical on both universal-pathlib 0.2 and 0.3.

FsspecStore.from_upath now converts the UPath's filesystem to async mode
instead of raising TypeError for synchronous filesystems and warning for
sync-mode instances of async ones.

The memory:// routing test wraps a synchronous MemoryFileSystem, which needs
fsspec's AsyncFileSystemWrapper, so it is skipped below fsspec 2024.12.0 as
test_wrap_sync_filesystem already does.

Closes zarr-developers#4244

Assisted-by: ClaudeCode:claude-fable-5
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.12%. Comparing base (f4a239c) to head (f65b05f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4265   +/-   ##
=======================================
  Coverage   94.12%   94.12%           
=======================================
  Files          92       92           
  Lines       12826    12830    +4     
=======================================
+ Hits        12072    12076    +4     
  Misses        754      754           
Files with missing lines Coverage Δ
src/zarr/storage/_common.py 93.13% <100.00%> (+0.13%) ⬆️
src/zarr/storage/_fsspec.py 91.17% <ø> (ø)
src/zarr/storage/_utils.py 96.42% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

d-v-b added 3 commits August 14, 2026 13:55
The changelog fragment was named for 4245, which towncrier renders as a link
to an unrelated issue ("Link Checker Report"). Rename it to the PR number.

test_make_store_upath only asserted the store type, so a mangled local path
would still produce a LocalStore and pass. Assert the root as well.

Silence PLC0414 on the UPath re-export: the alias looks redundant but is the
explicit re-export mypy requires under strict mode, and ruff 0.16.0 enables
the rule by default.

Assisted-by: ClaudeCode:claude-opus-4.8
@d-v-b d-v-b changed the title fix: accept universal-pathlib UPath as a StoreLike value - #280 fix: accept universal-pathlib UPath as a StoreLike value Aug 14, 2026
The sentence ended in a colon introducing an example of passing
storage_options alongside a UPath, but the snippet shows the working case and
never passes storage_options. Worse, the `anon=True` on the UPath sits right
after the sentence and reads as the thing being called out, when it is the
recommended way to supply the option.

State the rule as prose and let the example just show normal usage.

Assisted-by: ClaudeCode:claude-opus-4.8
@d-v-b
d-v-b marked this pull request as ready for review August 14, 2026 14:31
@d-v-b

d-v-b commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@mark-boer could you have a look

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.

Universal-pathlib support for zarr.open

1 participant