Skip to content

Commit 331ea93

Browse files
authored
Merge branch 'main' into refactor/metadata-package
2 parents c90c9a0 + 029c376 commit 331ea93

4 files changed

Lines changed: 48 additions & 2 deletions

File tree

changes/3924.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Apply path normalization to the `path` attribute of `FsspecStore`, ensuring that leading and trailing "/" symbols are removed.

docs/contributing.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,29 @@ The Zarr library is an implementation of a file format standard defined external
311311

312312
If an existing Zarr format version changes, or a new version of the Zarr format is released, then the Zarr library will generally require changes. It is very likely that a new Zarr format will require extensive breaking changes to the Zarr library, and so support for a new Zarr format in the Zarr library will almost certainly come in new `major` release. When the Zarr library adds support for a new Zarr format, there may be a period of accelerated changes as developers refine newly added APIs and deprecate old APIs. In such a transitional phase breaking changes may be more frequent than usual.
313313

314+
315+
## Experimental API policy
316+
317+
The `zarr.experimental` namespace contains features that are under active development and may change without notice. When contributing to or depending on experimental features, please keep the following in mind:
318+
319+
### For contributors
320+
321+
When adding a new feature to `zarr.experimental`:
322+
323+
1. Place the feature under `src/zarr/experimental/` and export it from `src/zarr/experimental/__init__.py`.
324+
2. Document the feature in `docs/user-guide/experimental.md` and note clearly that it is experimental.
325+
3. Add a changelog entry categorized as `feature`.
326+
327+
We aim to either **promote** or **remove** experimental features within **6 months** of their addition. To promote a feature to stable:
328+
329+
1. Move it from `zarr.experimental` to the appropriate stable module.
330+
2. Keep a deprecated re-export in `zarr.experimental` for one minor release.
331+
3. Update the documentation to reflect the stable location.
332+
333+
### For users
334+
335+
Features in `zarr.experimental` carry no stability guarantees. They may be changed or removed in any release, including patch releases. If you depend on an experimental feature, pin your `zarr-python` version accordingly.
336+
314337
## Release procedure
315338

316339
Open an issue on GitHub announcing the release using the release checklist template:

src/zarr/storage/_fsspec.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
)
1717
from zarr.core.buffer import Buffer
1818
from zarr.errors import ZarrUserWarning
19-
from zarr.storage._utils import _join_paths
19+
from zarr.storage._utils import _join_paths, normalize_path
2020

2121
if TYPE_CHECKING:
2222
from collections.abc import AsyncIterator, Iterable
@@ -127,7 +127,7 @@ def __init__(
127127
) -> None:
128128
super().__init__(read_only=read_only)
129129
self.fs = fs
130-
self.path = path
130+
self.path = normalize_path(path)
131131
self.allowed_exceptions = allowed_exceptions
132132

133133
if not self.fs.async_impl:

tests/test_store/test_fsspec.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from zarr.errors import ZarrUserWarning
1818
from zarr.storage import FsspecStore
1919
from zarr.storage._fsspec import _make_async
20+
from zarr.storage._utils import normalize_path
2021
from zarr.testing.store import StoreTests
2122

2223
if TYPE_CHECKING:
@@ -286,6 +287,27 @@ def array_roundtrip(store: FsspecStore) -> None:
286287
np.testing.assert_array_equal(arr[:], data)
287288

288289

290+
@pytest.mark.skipif(
291+
parse_version(fsspec.__version__) < parse_version("2024.12.0"),
292+
reason="No AsyncFileSystemWrapper",
293+
)
294+
@pytest.mark.parametrize("path", ["", "/", "//", "foo", "foo/", "/foo", "/foo/", "//foo//"])
295+
def test_fsspec_store_path_normalization(path: str) -> None:
296+
"""`FsspecStore.path` is normalized to the canonical form, matching
297+
`normalize_path`, regardless of the surface representation the caller
298+
supplies.
299+
300+
Regression test for https://github.com/zarr-developers/zarr-python/issues/3922
301+
-- when a caller passed `path="/"` the leading slash flowed through
302+
unmodified to subsequent `_join_paths([self.path, key])` calls, producing
303+
`"//key"` and missing the underlying object.
304+
"""
305+
sync_fs = fsspec.filesystem("memory")
306+
fs = _make_async(sync_fs)
307+
store = FsspecStore(fs=fs, path=path)
308+
assert store.path == normalize_path(path)
309+
310+
289311
@pytest.mark.skipif(
290312
parse_version(fsspec.__version__) < parse_version("2024.12.0"),
291313
reason="No AsyncFileSystemWrapper",

0 commit comments

Comments
 (0)