From 7c192d8211ab515fae4f96103f3af908616fe67c Mon Sep 17 00:00:00 2001 From: Martin Durant Date: Fri, 3 Mar 2023 15:38:16 -0500 Subject: [PATCH] make dirFS first class (#1201) --- fsspec/implementations/dirfs.py | 36 ++++++++++++++++++---- fsspec/implementations/tests/test_dirfs.py | 10 ++++++ fsspec/registry.py | 1 + 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/fsspec/implementations/dirfs.py b/fsspec/implementations/dirfs.py index b43256f7d..c614e22f3 100644 --- a/fsspec/implementations/dirfs.py +++ b/fsspec/implementations/dirfs.py @@ -1,8 +1,17 @@ +from .. import filesystem from ..asyn import AsyncFileSystem class DirFileSystem(AsyncFileSystem): - def __init__(self, path, fs, *args, **storage_options): + def __init__( + self, + path=None, + fs=None, + fo=None, + target_protocol=None, + target_options=None, + **storage_options, + ): """ Parameters ---------- @@ -10,8 +19,17 @@ def __init__(self, path, fs, *args, **storage_options): Path to the directory. fs: AbstractFileSystem An instantiated filesystem to wrap. + target_protocol, target_options: + if fs is none, construct it from these + fo: str + Alternate for path; do not provide both """ - super().__init__(*args, **storage_options) + super().__init__(**storage_options) + if fs is None: + fs = filesystem(protocol=target_protocol, **(target_options or {})) + if (path is not None) ^ (fo is not None) is False: + raise ValueError("Provide path or fo, not both") + path = path or fo if self.asynchronous and not fs.async_impl: raise ValueError("can't use asynchronous with non-async fs") @@ -198,20 +216,26 @@ def info(self, path, **kwargs): return self.fs.info(self._join(path), **kwargs) async def _ls(self, path, detail=True, **kwargs): - ret = await self.fs._ls(self._join(path), detail=detail, **kwargs) + ret = (await self.fs._ls(self._join(path), detail=detail, **kwargs)).copy() if detail: + out = [] for entry in ret: + entry = entry.copy() entry["name"] = self._relpath(entry["name"]) - return ret + out.append(entry) + return out return self._relpath(ret) def ls(self, path, detail=True, **kwargs): - ret = self.fs.ls(self._join(path), detail=detail, **kwargs) + ret = self.fs.ls(self._join(path), detail=detail, **kwargs).copy() if detail: + out = [] for entry in ret: + entry = entry.copy() entry["name"] = self._relpath(entry["name"]) - return ret + out.append(entry) + return out return self._relpath(ret) diff --git a/fsspec/implementations/tests/test_dirfs.py b/fsspec/implementations/tests/test_dirfs.py index e4b6a9164..402f8f67d 100644 --- a/fsspec/implementations/tests/test_dirfs.py +++ b/fsspec/implementations/tests/test_dirfs.py @@ -559,3 +559,13 @@ def test_open(mocker, dirfs): dirfs.fs.open = mocker.Mock() assert dirfs.open("file", *ARGS, **KWARGS) == dirfs.fs.open.return_value dirfs.fs.open.assert_called_once_with(f"{PATH}/file", *ARGS, **KWARGS) + + +def test_from_url(m): + from fsspec.core import url_to_fs + + m.pipe("inner/file", b"data") + fs, _ = url_to_fs("dir::memory://inner") + assert fs.ls("", False) == ["file"] + assert fs.ls("", True)[0]["name"] == "file" + assert fs.cat("file") == b"data" diff --git a/fsspec/registry.py b/fsspec/registry.py index 6f5c64f02..e488effb7 100644 --- a/fsspec/registry.py +++ b/fsspec/registry.py @@ -185,6 +185,7 @@ def register_implementation(name, cls, clobber=True, errtxt=None): + " Note: 'root' is the protocol name for xrootd storage systems," + " not referring to root directories", }, + "dir": {"class": "fsspec.implementations.dirfs.DirFileSystem"}, }