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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugPotential issues with the zarr-python library

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions