Skip to content

Commit 7f32dae

Browse files
committed
feat: use staged writing for backup downloads
1 parent 1bcecae commit 7f32dae

File tree

1 file changed

+14
-3
lines changed

1 file changed

+14
-3
lines changed

src/seadex/_backup.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
from os import PathLike
55
from os.path import basename
66
from pathlib import Path
7+
from shutil import move
8+
from tempfile import TemporaryDirectory
9+
from uuid import uuid4
710
from zipfile import BadZipFile, ZipFile
811

912
from httpx import Client
@@ -141,13 +144,21 @@ def download(self, file: StrPath | BackupFile | None = None, *, destination: Str
141144

142145
outfile = destination / key
143146

144-
with outfile.open("wb") as f:
145-
data = self.client.backups.download(key, self.client.get_file_token())
146-
f.write(data)
147+
with TemporaryDirectory(ignore_cleanup_errors=True) as tmpdir:
148+
tmpfile = Path(tmpdir).resolve() / str(uuid4())
149+
with tmpfile.open("wb", errors="strict") as fp:
150+
data = self.client.backups.download(key, self.client.get_file_token())
151+
fp.write(data)
152+
try:
153+
tmpfile.replace(outfile) # Attempt atomic replace
154+
except OSError:
155+
# Failed, do a normal move
156+
move(tmpfile, outfile)
147157

148158
with ZipFile(outfile) as archive:
149159
check = archive.testzip()
150160
if check is not None: # pragma: no cover
161+
outfile.unlink(missing_ok=True)
151162
raise BadZipFile(f"{outfile} failed integrity check!")
152163

153164
return outfile

0 commit comments

Comments
 (0)