Skip to content

Commit

Permalink
Merge pull request #4232 from tybug/db-race
Browse files Browse the repository at this point in the history
Write atomically in `DirectoryBasedExampleDatabase`
  • Loading branch information
tybug authored Jan 9, 2025
2 parents 737a141 + 88d19cc commit a1f6888
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 7 deletions.
3 changes: 3 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RELEASE_TYPE: patch

:class:`~hypothesis.database.DirectoryBasedExampleDatabase` now creates files representing database entries atomically, avoiding a very brief intermediary state where a file could be created but not yet written to.
19 changes: 12 additions & 7 deletions hypothesis-python/src/hypothesis/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
# obtain one at https://mozilla.org/MPL/2.0/.

import abc
import binascii
import json
import os
import struct
import sys
import tempfile
import warnings
import weakref
from collections.abc import Iterable
Expand Down Expand Up @@ -235,14 +235,19 @@ def save(self, key: bytes, value: bytes) -> None:
self._key_path(key).mkdir(exist_ok=True, parents=True)
path = self._value_path(key, value)
if not path.exists():
suffix = binascii.hexlify(os.urandom(16)).decode("ascii")
tmpname = path.with_suffix(f"{path.suffix}.{suffix}")
tmpname.write_bytes(value)
# to mimic an atomic write, create and write in a temporary
# directory, and only move to the final path after. This avoids
# any intermediate state where the file is created (and empty)
# but not yet written to.
fd, tmpname = tempfile.mkstemp()
tmppath = Path(tmpname)
os.write(fd, value)
os.close(fd)
try:
tmpname.rename(path)
tmppath.rename(path)
except OSError: # pragma: no cover
tmpname.unlink()
assert not tmpname.exists()
tmppath.unlink()
assert not tmppath.exists()
except OSError: # pragma: no cover
pass

Expand Down

0 comments on commit a1f6888

Please sign in to comment.