Skip to content

Commit 057f533

Browse files
Write cache atomically using a tempfile in the same directory (#1111)
* Write cache atomically using a tempfile in the same directory * Docstring * update flake8 precommit Co-authored-by: Martin Durant <[email protected]>
1 parent 4ec63f0 commit 057f533

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ repos:
1616
rev: 22.3.0
1717
hooks:
1818
- id: black
19-
- repo: https://gitlab.com/pycqa/flake8
20-
rev: 3.8.4
19+
- repo: https://github.com/PyCQA/flake8
20+
rev: 5.0.4
2121
hooks:
2222
- id: flake8
2323
- repo: https://github.com/asottile/seed-isort-config

fsspec/implementations/cached.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import contextlib
12
import hashlib
23
import inspect
34
import logging
45
import os
56
import pickle
67
import tempfile
78
import time
8-
from shutil import move, rmtree
9+
from shutil import rmtree
910

1011
from fsspec import AbstractFileSystem, filesystem
1112
from fsspec.callbacks import _DEFAULT_CALLBACK
@@ -184,11 +185,9 @@ def save_cache(self):
184185
for c in cache.values():
185186
if isinstance(c["blocks"], set):
186187
c["blocks"] = list(c["blocks"])
187-
fd2, fn2 = tempfile.mkstemp()
188-
with open(fd2, "wb") as f:
189-
pickle.dump(cache, f)
190188
self._mkcache()
191-
move(fn2, fn)
189+
with atomic_write(fn) as f:
190+
pickle.dump(cache, f)
192191
self.cached_files[-1] = cached_files
193192
self.last_cache = time.time()
194193

@@ -264,7 +263,7 @@ def clear_expired_cache(self, expiry_time=None):
264263

265264
if self.cached_files[-1]:
266265
cache_path = os.path.join(self.storage[-1], "cache")
267-
with open(cache_path, "wb") as fc:
266+
with atomic_write(cache_path) as fc:
268267
pickle.dump(self.cached_files[-1], fc)
269268
else:
270269
rmtree(self.storage[-1])
@@ -834,3 +833,24 @@ def hash_name(path, same_name):
834833
else:
835834
hash = hashlib.sha256(path.encode()).hexdigest()
836835
return hash
836+
837+
838+
@contextlib.contextmanager
839+
def atomic_write(path, mode="wb"):
840+
"""
841+
A context manager that opens a temporary file next to `path` and, on exit,
842+
replaces `path` with the temporary file, thereby updating `path`
843+
atomically.
844+
"""
845+
fd, fn = tempfile.mkstemp(
846+
dir=os.path.dirname(path), prefix=os.path.basename(path) + "-"
847+
)
848+
try:
849+
with open(fd, mode) as fp:
850+
yield fp
851+
except BaseException:
852+
with contextlib.suppress(FileNotFoundError):
853+
os.unlink(fn)
854+
raise
855+
else:
856+
os.replace(fn, path)

0 commit comments

Comments
 (0)