Skip to content

LocalStore doesn't create non-existent directories #2334

Description

@TomAugspurger

Zarr version

v3

Numcodecs version

na

Python Version

na

Operating System

na

Installation

na

Description

zarr v3 doesn't create a directory for a LocalStore if it doesn't exist. Should it?

Steps to reproduce

In [3]: zarr.open_group(zarr.storage.LocalStore("/tmp/zarr-python-v3-not-a-directory", mode="w"))
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[3], line 1
----> 1 zarr.open_group(zarr.storage.LocalStore("/tmp/zarr-python-v3-not-a-directory", mode="w"))

File ~/gh/zarr-developers/zarr-python/src/zarr/_compat.py:43, in _deprecate_positional_args.<locals>._inner_deprecate_positional_args.<locals>.inner_f(*args, **kwargs)
     41 extra_args = len(args) - len(all_args)
     42 if extra_args <= 0:
---> 43     return f(*args, **kwargs)
     45 # extra_args > 0
     46 args_msg = [
     47     f"{name}={arg}"
     48     for name, arg in zip(kwonly_args[:extra_args], args[-extra_args:], strict=False)
     49 ]

File ~/gh/zarr-developers/zarr-python/src/zarr/api/synchronous.py:216, in open_group(store, mode, cache_attrs, synchronizer, path, chunk_store, storage_options, zarr_version, zarr_format, meta_array, attributes, use_consolidated)
    199 @_deprecate_positional_args
    200 def open_group(
    201     store: StoreLike | None = None,
   (...)
    213     use_consolidated: bool | str | None = None,
    214 ) -> Group:
    215     return Group(
--> 216         sync(
    217             async_api.open_group(
    218                 store=store,
    219                 mode=mode,
    220                 cache_attrs=cache_attrs,
    221                 synchronizer=synchronizer,
    222                 path=path,
    223                 chunk_store=chunk_store,
    224                 storage_options=storage_options,
    225                 zarr_version=zarr_version,
    226                 zarr_format=zarr_format,
    227                 meta_array=meta_array,
    228                 attributes=attributes,
    229                 use_consolidated=use_consolidated,
    230             )
    231         )
    232     )

File ~/gh/zarr-developers/zarr-python/src/zarr/core/sync.py:91, in sync(coro, loop, timeout)
     88 return_result = next(iter(finished)).result()
     90 if isinstance(return_result, BaseException):
---> 91     raise return_result
     92 else:
     93     return return_result

File ~/gh/zarr-developers/zarr-python/src/zarr/core/sync.py:50, in _runner(coro)
     45 """
     46 Await a coroutine and return the result of running it. If awaiting the coroutine raises an
     47 exception, the exception will be returned.
     48 """
     49 try:
---> 50     return await coro
     51 except Exception as ex:
     52     return ex

File ~/gh/zarr-developers/zarr-python/src/zarr/api/asynchronous.py:699, in open_group(store, mode, cache_attrs, synchronizer, path, chunk_store, storage_options, zarr_version, zarr_format, meta_array, attributes, use_consolidated)
    696 if chunk_store is not None:
    697     warnings.warn("chunk_store is not yet implemented", RuntimeWarning, stacklevel=2)
--> 699 store_path = await make_store_path(store, mode=mode, storage_options=storage_options)
    700 if path is not None:
    701     store_path = store_path / path

File ~/gh/zarr-developers/zarr-python/src/zarr/storage/common.py:99, in make_store_path(store_like, mode, storage_options)
     97     if mode is not None and mode != store_like.mode.str:
     98         store_like = store_like.with_mode(mode)
---> 99     await store_like._ensure_open()
    100     result = StorePath(store_like)
    101 elif store_like is None:
    102     # mode = "w" is an exception to the default mode = 'r'

File ~/gh/zarr-developers/zarr-python/src/zarr/abc/store.py:80, in Store._ensure_open(self)
     78 async def _ensure_open(self) -> None:
     79     if not self._is_open:
---> 80         await self._open()

File ~/gh/zarr-developers/zarr-python/src/zarr/abc/store.py:73, in Store._open(self)
     71     raise ValueError("store is already open")
     72 if self.mode.str == "w":
---> 73     await self.clear()
     74 elif self.mode.str == "w-" and not await self.empty():
     75     raise FileExistsError("Store already exists")

File ~/gh/zarr-developers/zarr-python/src/zarr/storage/local.py:98, in LocalStore.clear(self)
     96 async def clear(self) -> None:
     97     self._check_writable()
---> 98     shutil.rmtree(self.root)
     99     self.root.mkdir()

File ~/mambaforge/envs/python=3.12/lib/python3.12/shutil.py:759, in rmtree(path, ignore_errors, onerror, onexc, dir_fd)
    757 try:
    758     while stack:
--> 759         _rmtree_safe_fd(stack, onexc)
    760 finally:
    761     # Close any file descriptors still on the stack.
    762     while stack:

File ~/mambaforge/envs/python=3.12/lib/python3.12/shutil.py:703, in _rmtree_safe_fd(stack, onexc)
    701 except OSError as err:
    702     err.filename = path
--> 703     onexc(func, path, err)

File ~/mambaforge/envs/python=3.12/lib/python3.12/shutil.py:669, in _rmtree_safe_fd(stack, onexc)
    667 assert func is os.lstat
    668 if orig_entry is None:
--> 669     orig_st = os.lstat(name, dir_fd=dirfd)
    670 else:
    671     orig_st = orig_entry.stat(follow_symlinks=False)

FileNotFoundError: [Errno 2] No such file or directory: PosixPath('/tmp/zarr-python-v3-not-a-directory')

In zarr-python 2.x it created the directory:

In [2]: zarr.open_group("/tmp/zarr-python-v2-not-a-directory")
Out[2]: <zarr.hierarchy.Group '/'>

Additional output

No response

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugPotential issues with the zarr-python library

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions