Repository: fsspec/filesystem_spec

Issue:
bug: FSMap on_error option does not work with simplecache
Recently, I noticed that when I create a FSMap to a `simplecache` FS, the `on_error="omit"` does not work as intended. Here is an example, which is a modified version of `test_getitems_errors` in `/fsspec/tests/test_mapping.py` that I ran with FSSpec version 2023.1.0:

```py
def test_getitems_errors(tmpdir):
    tmpdir = str(tmpdir)
    os.makedirs(os.path.join(tmpdir, "afolder"))
    open(os.path.join(tmpdir, "afile"), "w").write("test")
    open(os.path.join(tmpdir, "afolder", "anotherfile"), "w").write("test2")
    m = fsspec.get_mapper("file://" + tmpdir)
    assert m.getitems(["afile", "bfile"], on_error="omit") == {"afile": b"test"}

    # my code
    m2 = fsspec.get_mapper("simplecache::file://" + tmpdir)
    assert m2.getitems(["afile"], on_error="omit") == {"afile": b"test"} # works
    assert m2.getitems(["afile", "bfile"], on_error="omit") == {"afile": b"test"} # throws KeyError

    with pytest.raises(KeyError):
        m.getitems(["afile", "bfile"])
    out = m.getitems(["afile", "bfile"], on_error="return")
    assert isinstance(out["bfile"], KeyError)
    m = fsspec.get_mapper("file://" + tmpdir, missing_exceptions=())
    assert m.getitems(["afile", "bfile"], on_error="omit") == {"afile": b"test"}
    with pytest.raises(FileNotFoundError):
        m.getitems(["afile", "bfile"])
```

In the example, a `KeyError` is still thrown even when `on_error="omit"` is specified. I was able to reproduce this bug both on the local filesystem (as shown in the example) as well as on GCS.

Discussion:

Patch:
diff --git a/fsspec/implementations/cached.py b/fsspec/implementations/cached.py
index 4a13834..31fb3ff 100644
--- a/fsspec/implementations/cached.py
+++ b/fsspec/implementations/cached.py
@@ -614,20 +614,28 @@ class WholeFileCacheFileSystem(CachingFileSystem):
         getpaths = []
         storepaths = []
         fns = []
-        for p in paths:
-            detail = self._check_file(p)
-            if not detail:
-                fn = self._make_local_details(p)
-                getpaths.append(p)
-                storepaths.append(fn)
-            else:
-                detail, fn = detail if isinstance(detail, tuple) else (None, detail)
-            fns.append(fn)
+        out = {}
+        for p in paths.copy():
+            try:
+                detail = self._check_file(p)
+                if not detail:
+                    fn = self._make_local_details(p)
+                    getpaths.append(p)
+                    storepaths.append(fn)
+                else:
+                    detail, fn = detail if isinstance(detail, tuple) else (None, detail)
+                fns.append(fn)
+            except Exception as e:
+                if on_error == "raise":
+                    raise
+                if on_error == "return":
+                    out[p] = e
+                paths.remove(p)
+
         if getpaths:
             self.fs.get(getpaths, storepaths)
             self.save_cache()
 
-        out = {}
         callback.set_size(len(paths))
         for p, fn in zip(paths, fns):
             with open(fn, "rb") as f:


Test patch:
diff --git a/fsspec/implementations/tests/test_cached.py b/fsspec/implementations/tests/test_cached.py
index 2b6d538..53ec289 100644
--- a/fsspec/implementations/tests/test_cached.py
+++ b/fsspec/implementations/tests/test_cached.py
@@ -924,3 +924,28 @@ def test_str():
     lfs = LocalFileSystem()
     cfs = CachingFileSystem(fs=lfs)
     assert "CachingFileSystem" in str(cfs)
+
+
+def test_getitems_errors(tmpdir):
+    tmpdir = str(tmpdir)
+    os.makedirs(os.path.join(tmpdir, "afolder"))
+    open(os.path.join(tmpdir, "afile"), "w").write("test")
+    open(os.path.join(tmpdir, "afolder", "anotherfile"), "w").write("test2")
+    m = fsspec.get_mapper("file://" + tmpdir)
+    assert m.getitems(["afile", "bfile"], on_error="omit") == {"afile": b"test"}
+
+    # my code
+    m2 = fsspec.get_mapper("simplecache::file://" + tmpdir)
+    assert m2.getitems(["afile"], on_error="omit") == {"afile": b"test"}  # works
+    assert m2.getitems(["afile", "bfile"], on_error="omit") == {
+        "afile": b"test"
+    }  # throws KeyError
+
+    with pytest.raises(KeyError):
+        m.getitems(["afile", "bfile"])
+    out = m.getitems(["afile", "bfile"], on_error="return")
+    assert isinstance(out["bfile"], KeyError)
+    m = fsspec.get_mapper("file://" + tmpdir, missing_exceptions=())
+    assert m.getitems(["afile", "bfile"], on_error="omit") == {"afile": b"test"}
+    with pytest.raises(FileNotFoundError):
+        m.getitems(["afile", "bfile"])