|
18 | 18 | from zarr.errors import ContainsArrayAndGroupError, ContainsArrayError, ContainsGroupError |
19 | 19 | from zarr.storage._local import LocalStore |
20 | 20 | from zarr.storage._memory import ManagedMemoryStore, MemoryStore |
21 | | -from zarr.storage._utils import _dereference_path, normalize_path |
| 21 | +from zarr.storage._utils import _dereference_path, normalize_path, parse_store_url |
22 | 22 |
|
23 | 23 | _has_fsspec = importlib.util.find_spec("fsspec") |
24 | 24 | if _has_fsspec: |
@@ -293,14 +293,18 @@ async def make_store( |
293 | 293 | """ |
294 | 294 | from zarr.storage._fsspec import FsspecStore # circular import |
295 | 295 |
|
296 | | - if ( |
297 | | - not (isinstance(store_like, str) and _is_fsspec_uri(store_like)) |
298 | | - and storage_options is not None |
299 | | - ): |
300 | | - raise TypeError( |
301 | | - "'storage_options' was provided but unused. " |
302 | | - "'storage_options' is only used when the store is passed as an FSSpec URI string.", |
303 | | - ) |
| 296 | + # Check if storage_options is valid for this store_like |
| 297 | + if storage_options is not None: |
| 298 | + is_fsspec_uri = False |
| 299 | + if isinstance(store_like, str): |
| 300 | + parsed = parse_store_url(store_like) |
| 301 | + # fsspec URIs have a scheme that's not memory, file, or empty |
| 302 | + is_fsspec_uri = parsed.scheme not in ("", "memory", "file") |
| 303 | + if not is_fsspec_uri: |
| 304 | + raise TypeError( |
| 305 | + "'storage_options' was provided but unused. " |
| 306 | + "'storage_options' is only used when the store is passed as an FSSpec URI string.", |
| 307 | + ) |
304 | 308 |
|
305 | 309 | assert mode in (None, "r", "r+", "a", "w", "w-") |
306 | 310 | _read_only = mode == "r" |
@@ -329,23 +333,19 @@ async def make_store( |
329 | 333 | return await LocalStore.open(root=store_like, mode=mode, read_only=_read_only) |
330 | 334 |
|
331 | 335 | elif isinstance(store_like, str): |
332 | | - # Check for memory:// URLs first |
333 | | - if store_like.startswith("memory://"): |
334 | | - # Parse the URL to extract name and path |
335 | | - url_without_scheme = store_like[len("memory://") :] |
336 | | - parts = url_without_scheme.split("/", 1) |
337 | | - name = parts[0] if parts[0] else None |
338 | | - path = parts[1] if len(parts) > 1 else "" |
339 | | - # Create or get the store - ManagedMemoryStore handles both cases |
340 | | - return ManagedMemoryStore(name=name, path=path, read_only=_read_only) |
341 | | - # Either an FSSpec URI or a local filesystem path |
342 | | - elif _is_fsspec_uri(store_like): |
| 336 | + parsed = parse_store_url(store_like) |
| 337 | + |
| 338 | + if parsed.scheme == "memory": |
| 339 | + # Create or get a ManagedMemoryStore |
| 340 | + return ManagedMemoryStore(name=parsed.name, path=parsed.path, read_only=_read_only) |
| 341 | + elif parsed.scheme == "file" or not parsed.scheme: |
| 342 | + # Local filesystem path |
| 343 | + return await make_store(Path(store_like), mode=mode, storage_options=storage_options) |
| 344 | + else: |
| 345 | + # Assume fsspec can handle it (s3://, gs://, http://, etc.) |
343 | 346 | return FsspecStore.from_url( |
344 | 347 | store_like, storage_options=storage_options, read_only=_read_only |
345 | 348 | ) |
346 | | - else: |
347 | | - # Assume a filesystem path |
348 | | - return await make_store(Path(store_like), mode=mode, storage_options=storage_options) |
349 | 349 |
|
350 | 350 | elif _has_fsspec and isinstance(store_like, FSMap): |
351 | 351 | return FsspecStore.from_mapper(store_like, read_only=_read_only) |
@@ -415,42 +415,25 @@ async def make_store_path( |
415 | 415 | "'path' was provided but is not used for FSMap store_like objects. Specify the path when creating the FSMap instance instead." |
416 | 416 | ) |
417 | 417 |
|
418 | | - elif isinstance(store_like, str) and store_like.startswith("memory://"): |
419 | | - # Handle memory:// URLs specially |
420 | | - # Parse the URL to extract name and path |
421 | | - _read_only = mode == "r" |
422 | | - url_without_scheme = store_like[len("memory://") :] |
423 | | - parts = url_without_scheme.split("/", 1) |
424 | | - name = parts[0] if parts[0] else None |
425 | | - url_path = parts[1] if len(parts) > 1 else "" |
426 | | - # Create or get the store - ManagedMemoryStore handles both cases |
427 | | - memory_store = ManagedMemoryStore(name=name, path=url_path, read_only=_read_only) |
428 | | - return await StorePath.open(memory_store, path=path_normalized, mode=mode) |
| 418 | + elif isinstance(store_like, str): |
| 419 | + parsed = parse_store_url(store_like) |
| 420 | + if parsed.scheme == "memory": |
| 421 | + # Handle memory:// URLs specially - the path in the URL becomes part of the store |
| 422 | + _read_only = mode == "r" |
| 423 | + memory_store = ManagedMemoryStore( |
| 424 | + name=parsed.name, path=parsed.path, read_only=_read_only |
| 425 | + ) |
| 426 | + return await StorePath.open(memory_store, path=path_normalized, mode=mode) |
| 427 | + else: |
| 428 | + # Fall through to make_store for other URL types |
| 429 | + store = await make_store(store_like, mode=mode, storage_options=storage_options) |
| 430 | + return await StorePath.open(store, path=path_normalized, mode=mode) |
429 | 431 |
|
430 | 432 | else: |
431 | 433 | store = await make_store(store_like, mode=mode, storage_options=storage_options) |
432 | 434 | return await StorePath.open(store, path=path_normalized, mode=mode) |
433 | 435 |
|
434 | 436 |
|
435 | | -def _is_fsspec_uri(uri: str) -> bool: |
436 | | - """ |
437 | | - Check if a URI looks like a non-local fsspec URI. |
438 | | -
|
439 | | - Examples |
440 | | - -------- |
441 | | - ```python |
442 | | - from zarr.storage._common import _is_fsspec_uri |
443 | | - _is_fsspec_uri("s3://bucket") |
444 | | - # True |
445 | | - _is_fsspec_uri("my-directory") |
446 | | - # False |
447 | | - _is_fsspec_uri("local://my-directory") |
448 | | - # False |
449 | | - ``` |
450 | | - """ |
451 | | - return "://" in uri or ("::" in uri and "local://" not in uri) |
452 | | - |
453 | | - |
454 | 437 | async def ensure_no_existing_node( |
455 | 438 | store_path: StorePath, |
456 | 439 | zarr_format: ZarrFormat, |
|
0 commit comments