Skip to content

Commit 4f30fdc

Browse files
committed
Raise exception if 'fn' not in cached metadata
1 parent 36c35b2 commit 4f30fdc

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

fsspec/implementations/cached.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,11 @@ def clear_expired_cache(self, expiry_time=None):
253253

254254
for path, detail in self.cached_files[-1].copy().items():
255255
if time.time() - detail["time"] > expiry_time:
256-
fn = getattr(detail, "fn", "")
256+
fn = detail.get("fn", "")
257257
if not fn:
258-
# fn should always be set, but be defensive here.
259-
fn = self._mapper(detail["original"])
258+
raise RuntimeError(
259+
f"Cache metadata does not contain 'fn' for {path}"
260+
)
260261
fn = os.path.join(self.storage[-1], fn)
261262
if os.path.exists(fn):
262263
os.remove(fn)

fsspec/implementations/tests/test_cached.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,35 @@ def test_mapper():
5757
assert hash(create_cache_mapper(False)) == hash(mapper1)
5858

5959

60+
@pytest.mark.parametrize("same_names", [False, True])
61+
def test_metadata(tmpdir, same_names):
62+
source = os.path.join(tmpdir, "source")
63+
afile = os.path.join(source, "afile")
64+
os.mkdir(source)
65+
open(afile, "w").write("test")
66+
67+
fs = fsspec.filesystem(
68+
"filecache",
69+
target_protocol="file",
70+
cache_storage=os.path.join(tmpdir, "cache"),
71+
same_names=same_names,
72+
)
73+
with fs.open(afile, "rb") as f:
74+
assert f.read(5) == b"test"
75+
76+
detail = fs.cached_files[0][afile]
77+
assert sorted(detail.keys()) == ["blocks", "fn", "original", "time", "uid"]
78+
assert isinstance(detail["blocks"], bool)
79+
assert isinstance(detail["fn"], str)
80+
assert isinstance(detail["time"], float)
81+
assert isinstance(detail["uid"], str)
82+
83+
assert detail["original"] == afile
84+
assert detail["fn"] == fs._mapper(afile)
85+
if same_names:
86+
assert detail["fn"] == "afile"
87+
88+
6089
def test_idempotent():
6190
fs = CachingFileSystem("file")
6291
fs2 = CachingFileSystem("file")
@@ -179,7 +208,7 @@ def test_clear():
179208

180209

181210
def test_clear_expired(tmp_path):
182-
def __ager(cache_fn, fn):
211+
def __ager(cache_fn, fn, del_fn=False):
183212
"""
184213
Modify the cache file to virtually add time lag to selected files.
185214
@@ -189,6 +218,8 @@ def __ager(cache_fn, fn):
189218
cache path
190219
fn: str
191220
file name to be modified
221+
del_fn: bool
222+
whether or not to delete 'fn' from cache details
192223
"""
193224
import pathlib
194225
import time
@@ -199,6 +230,8 @@ def __ager(cache_fn, fn):
199230
fn_posix = pathlib.Path(fn).as_posix()
200231
cached_files[fn_posix]["time"] = cached_files[fn_posix]["time"] - 691200
201232
assert os.access(cache_fn, os.W_OK), "Cache is not writable"
233+
if del_fn:
234+
del cached_files[fn_posix]["fn"]
202235
with open(cache_fn, "wb") as f:
203236
pickle.dump(cached_files, f)
204237
time.sleep(1)
@@ -280,6 +313,22 @@ def __ager(cache_fn, fn):
280313
fs.clear_expired_cache()
281314
assert not fs._check_file(str(f4))
282315

316+
# check cache metadata lacking 'fn' raises RuntimeError.
317+
fs = fsspec.filesystem(
318+
"filecache",
319+
target_protocol="file",
320+
cache_storage=str(cache1),
321+
same_names=True,
322+
cache_check=1,
323+
)
324+
assert fs.cat(str(f1)) == data
325+
326+
cache_fn = os.path.join(fs.storage[-1], "cache")
327+
__ager(cache_fn, f1, del_fn=True)
328+
329+
with pytest.raises(RuntimeError, match="Cache metadata does not contain 'fn' for"):
330+
fs.clear_expired_cache()
331+
283332

284333
def test_pop():
285334
import tempfile

0 commit comments

Comments
 (0)