Skip to content

Commit 60e6bf6

Browse files
authored
Merge pull request #4430 from tybug/db-delete-empty
Remove empty key dirs
2 parents aa735a3 + bee0287 commit 60e6bf6

File tree

5 files changed

+56
-5
lines changed

5 files changed

+56
-5
lines changed

hypothesis-python/RELEASE.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
RELEASE_TYPE: patch
2+
3+
|DirectoryBasedExampleDatabase| now removes empty directories after |ExampleDatabase.delete| is called.

hypothesis-python/docs/conf.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,25 @@ def setup(app):
113113
assert "xps" not in sys.modules
114114
sys.modules["xps"] = mod
115115

116+
def process_signature(app, what, name, obj, options, signature, return_annotation):
117+
# manually override an ugly signature from .. autofunction. Alternative we
118+
# could manually document with `.. function:: run_conformance_test(...)`,
119+
# but that's less likely to stay up to date.
120+
if (
121+
name
122+
== "hypothesis.internal.conjecture.provider_conformance.run_conformance_test"
123+
):
124+
# so we know if this ever becomes obsolete
125+
assert "_realize_objects" in signature
126+
signature = re.sub(
127+
r"_realize_objects=.*",
128+
"_realize_objects=st.from_type(object) | st.from_type(type).flatmap(st.from_type))",
129+
signature,
130+
)
131+
return signature, return_annotation
132+
133+
app.connect("autodoc-process-signature", process_signature)
134+
116135

117136
language = "en"
118137
exclude_patterns = ["_build", "prolog.rst"]

hypothesis-python/src/hypothesis/database.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,20 @@ def move(self, src: bytes, dest: bytes, value: bytes) -> None:
511511
def delete(self, key: bytes, value: bytes) -> None:
512512
try:
513513
self._value_path(key, value).unlink()
514+
except OSError:
515+
return
516+
517+
# try deleting the key dir, which will only succeed if the dir is empty
518+
# (i.e. ``value`` was the last value in this key).
519+
try:
520+
self._key_path(key).rmdir()
514521
except OSError:
515522
pass
523+
else:
524+
# if the deletion succeeded, also delete this key entry from metakeys.
525+
# (if this key happens to be the metakey itself, this deletion will
526+
# fail; that's ok and faster than checking for this rare case.)
527+
self.delete(self._metakeys_name, key)
516528

517529
def _start_listening(self) -> None:
518530
try:

hypothesis-python/src/hypothesis/internal/conjecture/provider_conformance.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,9 @@ def run_conformance_test(
338338
*,
339339
context_manager_exceptions: Collection[type[BaseException]] = (),
340340
settings: Optional[Settings] = None,
341-
_realize_objects: SearchStrategy[Any] = st.from_type(object)
342-
| st.from_type(type).flatmap(st.from_type),
341+
_realize_objects: SearchStrategy[Any] = (
342+
st.from_type(object) | st.from_type(type).flatmap(st.from_type)
343+
),
343344
) -> None:
344345
"""
345346
Test that the given ``Provider`` class conforms to the |PrimitiveProvider|

hypothesis-python/tests/cover/test_database_backend.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -738,13 +738,13 @@ def test_metakeys(tmp_path):
738738
db.save(b"k1", b"v2")
739739
assert set(db.fetch(db._metakeys_name)) == {b"k1"}
740740

741-
# deleting all the values from a key doesn't (currently?) clean up that key
741+
# deleting all the values from a key removes that metakey
742742
db.delete(b"k1", b"v1")
743743
db.delete(b"k1", b"v2")
744-
assert set(db.fetch(db._metakeys_name)) == {b"k1"}
744+
assert set(db.fetch(db._metakeys_name)) == set()
745745

746746
db.save(b"k2", b"v1")
747-
assert set(db.fetch(db._metakeys_name)) == {b"k1", b"k2"}
747+
assert set(db.fetch(db._metakeys_name)) == {b"k2"}
748748

749749

750750
class TracksListens(ExampleDatabase):
@@ -849,3 +849,19 @@ def test_database_equal(db1, db2):
849849
)
850850
def test_database_not_equal(db1, db2):
851851
assert db1 != db2
852+
853+
854+
def test_directory_db_removes_empty_dirs(tmp_path):
855+
db = DirectoryBasedExampleDatabase(tmp_path)
856+
db.save(b"k1", b"v1")
857+
db.save(b"k1", b"v2")
858+
assert db._key_path(b"k1").exists()
859+
assert set(db.fetch(db._metakeys_name)) == {b"k1"}
860+
861+
db.delete(b"k1", b"v1")
862+
assert db._key_path(b"k1").exists()
863+
assert set(db.fetch(db._metakeys_name)) == {b"k1"}
864+
865+
db.delete(b"k1", b"v2")
866+
assert not db._key_path(b"k1").exists()
867+
assert set(db.fetch(db._metakeys_name)) == set()

0 commit comments

Comments
 (0)