Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paths with leading slashes do bad things #2357

Closed
jhamman opened this issue Oct 14, 2024 · 0 comments · Fixed by #2384
Closed

Paths with leading slashes do bad things #2357

jhamman opened this issue Oct 14, 2024 · 0 comments · Fixed by #2384
Labels
bug Potential issues with the zarr-python library
Milestone

Comments

@jhamman
Copy link
Member

jhamman commented Oct 14, 2024

Zarr version

3.0.0-beta

Numcodecs version

0.13

Python Version

3.11

Operating System

Mac

Installation

pip

Description

In pydata/xarray#9552, we have noticed that setting keys with leading slashes ends poorly for some stores (namely the local store).

We should probably be normalizing keys in the Array/Group APIs before the store even sees them.

Steps to reproduce

import zarr
group = zarr.open_group('/Users/jhamman/workdir/zarr/foo', mode='a')
group.create_group('/foo', attributes={'a': 'b'})
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
Cell In[4], line 1
----> 1 group.create_group('[/foo](http://localhost:8888/foo)', attributes={'a': 'b'})

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py:1480](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py#line=1479), in Group.create_group(self, name, **kwargs)
   1479 def create_group(self, name: str, **kwargs: Any) -> Group:
-> 1480     return Group(self._sync(self._async_group.create_group(name, **kwargs)))

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/sync.py:185](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/sync.py#line=184), in SyncMixin._sync(self, coroutine)
    182 def _sync(self, coroutine: Coroutine[Any, Any, T]) -> T:
    183     # TODO: refactor this to to take *args and **kwargs and pass those to the method
    184     # this should allow us to better type the sync wrapper
--> 185     return sync(
    186         coroutine,
    187         timeout=config.get("async.timeout"),
    188     )

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/sync.py:141](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/sync.py#line=140), in sync(coro, loop, timeout)
    138 return_result = next(iter(finished)).result()
    140 if isinstance(return_result, BaseException):
--> 141     raise return_result
    142 else:
    143     return return_result

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/sync.py:100](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/sync.py#line=99), in _runner(coro)
     95 """
     96 Await a coroutine and return the result of running it. If awaiting the coroutine raises an
     97 exception, the exception will be returned.
     98 """
     99 try:
--> 100     return await coro
    101 except Exception as ex:
    102     return ex

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py:799](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py#line=798), in AsyncGroup.create_group(self, name, exists_ok, attributes)
    791 async def create_group(
    792     self,
    793     name: str,
   (...)
    796     attributes: dict[str, Any] | None = None,
    797 ) -> AsyncGroup:
    798     attributes = attributes or {}
--> 799     return await type(self).from_store(
    800         self.store_path [/](http://localhost:8888/) name,
    801         attributes=attributes,
    802         exists_ok=exists_ok,
    803         zarr_format=self.metadata.zarr_format,
    804     )

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py:413](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py#line=412), in AsyncGroup.from_store(cls, store, attributes, exists_ok, zarr_format)
    408 attributes = attributes or {}
    409 group = cls(
    410     metadata=GroupMetadata(attributes=attributes, zarr_format=zarr_format),
    411     store_path=store_path,
    412 )
--> 413 await group._save_metadata(ensure_parents=True)
    414 return group

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py:745](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/core/group.py#line=744), in AsyncGroup._save_metadata(self, ensure_parents)
    735     for parent in parents:
    736         awaitables.extend(
    737             [
    738                 (parent.store_path [/](http://localhost:8888/) key).set_if_not_exists(value)
   (...)
    742             ]
    743         )
--> 745 await asyncio.gather(*awaitables)

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/abc/store.py:426](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/abc/store.py#line=425), in set_or_delete(byte_setter, value)
    424     await byte_setter.delete()
    425 else:
--> 426     await byte_setter.set(value)

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/common.py:90](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/common.py#line=89), in StorePath.set(self, value, byte_range)
     88 if byte_range is not None:
     89     raise NotImplementedError("Store.set does not have partial writes yet")
---> 90 await self.store.set(self.path, value)

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/local.py:172](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/local.py#line=171), in LocalStore.set(self, key, value)
    170 async def set(self, key: str, value: Buffer) -> None:
    171     # docstring inherited
--> 172     return await self._set(key, value)

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/local.py:189](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/local.py#line=188), in LocalStore._set(self, key, value, exclusive)
    187     raise TypeError("LocalStore.set(): `value` must a Buffer instance")
    188 path = self.root [/](http://localhost:8888/) key
--> 189 await asyncio.to_thread(_put, path, value, start=None, exclusive=exclusive)

File [~/miniforge3/envs/icechunk-demo/lib/python3.12/asyncio/threads.py:25](http://localhost:8888/lab/tree/~/miniforge3/envs/icechunk-demo/lib/python3.12/asyncio/threads.py#line=24), in to_thread(func, *args, **kwargs)
     23 ctx = contextvars.copy_context()
     24 func_call = functools.partial(ctx.run, func, *args, **kwargs)
---> 25 return await loop.run_in_executor(None, func_call)

File [~/miniforge3/envs/icechunk-demo/lib/python3.12/concurrent/futures/thread.py:58](http://localhost:8888/lab/tree/~/miniforge3/envs/icechunk-demo/lib/python3.12/concurrent/futures/thread.py#line=57), in _WorkItem.run(self)
     55     return
     57 try:
---> 58     result = self.fn(*self.args, **self.kwargs)
     59 except BaseException as exc:
     60     self.future.set_exception(exc)

File [~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/local.py:53](http://localhost:8888/lab/tree/~/Library/CloudStorage/Dropbox/src/zarr-python/src/zarr/storage/local.py#line=52), in _put(path, value, start, exclusive)
     47 def _put(
     48     path: Path,
     49     value: Buffer,
     50     start: int | None = None,
     51     exclusive: bool = False,
     52 ) -> int | None:
---> 53     path.parent.mkdir(parents=True, exist_ok=True)
     54     if start is not None:
     55         with path.open("r+b") as f:

File [~/miniforge3/envs/icechunk-demo/lib/python3.12/pathlib.py:1312](http://localhost:8888/lab/tree/~/miniforge3/envs/icechunk-demo/lib/python3.12/pathlib.py#line=1311), in Path.mkdir(self, mode, parents, exist_ok)
   1308 """
   1309 Create a new directory at this given path.
   1310 """
   1311 try:
-> 1312     os.mkdir(self, mode)
   1313 except FileNotFoundError:
   1314     if not parents or self.parent == self:

OSError: [Errno 30] Read-only file system: '[/foo](http://localhost:8888/foo)'

Additional output

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Potential issues with the zarr-python library
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant