Repository: fsspec/filesystem_spec
Pull request #1794 (2025-02-19 14:23:42)

Make asynchronous optional in async_wrapper

martindurant: @observingClouds , this will make your case slightly simpler.
martindurant: Test showing integration with dir prefix: [here](https://github.com/fsspec/filesystem_spec/pull/1794/files#diff-e4abe919ed35fd23bd5a20bfe545ff6af5fdac3de69af2994d11b10bb8f59740R152-R163)

Patch:
diff --git a/fsspec/implementations/asyn_wrapper.py b/fsspec/implementations/asyn_wrapper.py
index f58b0b6..ee009f3 100644
--- a/fsspec/implementations/asyn_wrapper.py
+++ b/fsspec/implementations/asyn_wrapper.py
@@ -2,7 +2,7 @@ import asyncio
 import functools
 import inspect
 
-from fsspec.asyn import AsyncFileSystem
+from fsspec.asyn import AsyncFileSystem, running_async
 
 
 def async_wrapper(func, obj=None):
@@ -42,10 +42,14 @@ class AsyncFileSystemWrapper(AsyncFileSystem):
         The synchronous filesystem instance to wrap.
     """
 
-    def __init__(self, sync_fs, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.asynchronous = True
-        self.sync_fs = sync_fs
+    protocol = "async_wrapper"
+    cachable = False
+
+    def __init__(self, fs, *args, asynchronous=None, **kwargs):
+        if asynchronous is None:
+            asynchronous = running_async()
+        super().__init__(*args, asynchronous=asynchronous, **kwargs)
+        self.sync_fs = fs
         self.protocol = self.sync_fs.protocol
         self._wrap_all_sync_methods()
 
diff --git a/fsspec/implementations/dirfs.py b/fsspec/implementations/dirfs.py
index 0bee864..a944452 100644
--- a/fsspec/implementations/dirfs.py
+++ b/fsspec/implementations/dirfs.py
@@ -36,8 +36,6 @@ class DirFileSystem(AsyncFileSystem):
         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:
diff --git a/fsspec/registry.py b/fsspec/registry.py
index 5d104f2..59be4db 100644
--- a/fsspec/registry.py
+++ b/fsspec/registry.py
@@ -72,6 +72,9 @@ known_implementations = {
         "class": "fsspec.implementations.arrow.HadoopFileSystem",
         "err": "pyarrow and local java libraries required for HDFS",
     },
+    "async_wrapper": {
+        "class": "morefs.asyn_wrapper.AsyncWrapperFileSystem",
+    },
     "asynclocal": {
         "class": "morefs.asyn_local.AsyncLocalFileSystem",
         "err": "Install 'morefs[asynclocalfs]' to use AsyncLocalFileSystem",


Test patch:
diff --git a/fsspec/implementations/tests/test_asyn_wrapper.py b/fsspec/implementations/tests/test_asyn_wrapper.py
index 82e9f3d..13b766f 100644
--- a/fsspec/implementations/tests/test_asyn_wrapper.py
+++ b/fsspec/implementations/tests/test_asyn_wrapper.py
@@ -10,10 +10,14 @@ from fsspec.implementations.local import LocalFileSystem
 from .test_local import csv_files, filetexts
 
 
-def test_is_async():
+@pytest.mark.asyncio
+async def test_is_async_default():
     fs = fsspec.filesystem("file")
     async_fs = AsyncFileSystemWrapper(fs)
     assert async_fs.async_impl
+    assert async_fs.asynchronous
+    async_fs = AsyncFileSystemWrapper(fs, asynchronous=False)
+    assert not async_fs.asynchronous
 
 
 def test_class_wrapper():
@@ -53,6 +57,7 @@ async def test_cats():
         assert result == b"a,b\n1,2\n"[1:-2]
 
         # test synchronous API is available as expected
+        async_fs = AsyncFileSystemWrapper(fs, asynchronous=False)
         result = async_fs.cat(".test.fakedata.1.csv", start=1, end=-2)
         assert result == b"a,b\n1,2\n"[1:-2]
 
@@ -142,3 +147,17 @@ async def test_batch_operations():
         await async_fs._rm([".test.fakedata.1.csv", ".test.fakedata.2.csv"])
         assert not await async_fs._exists(".test.fakedata.1.csv")
         assert not await async_fs._exists(".test.fakedata.2.csv")
+
+
+def test_open(tmpdir):
+    fn = f"{tmpdir}/afile"
+    with open(fn, "wb") as f:
+        f.write(b"hello")
+    of = fsspec.open(
+        "dir://afile::async_wrapper::file",
+        mode="rb",
+        async_wrapper={"asynchronous": False},
+        dir={"path": str(tmpdir)},
+    )
+    with of as f:
+        assert f.read() == b"hello"
diff --git a/fsspec/implementations/tests/test_reference.py b/fsspec/implementations/tests/test_reference.py
index 3d3a614..52d39fb 100644
--- a/fsspec/implementations/tests/test_reference.py
+++ b/fsspec/implementations/tests/test_reference.py
@@ -515,10 +515,10 @@ def test_fss_has_defaults(m):
     assert fs.fss[None] is m
 
     fs = fsspec.filesystem("reference", fo={"key": ["memory://a"]})
-    assert fs.fss[None] is fs.fss["memory"]
+    assert fs.fss[None] == fs.fss["memory"]
 
     fs = fsspec.filesystem("reference", fo={"key": ["memory://a"], "blah": ["path"]})
-    assert fs.fss[None] is fs.fss["memory"]
+    assert fs.fss[None] == fs.fss["memory"]
 
 
 def test_merging(m):