Skip to content

Commit 5447362

Browse files
authored
Merge pull request #4346 from tybug/deprecate-abstract-db
Deprecate `ExampleDatabase()`
2 parents af89e9f + 2f33a17 commit 5447362

File tree

10 files changed

+62
-27
lines changed

10 files changed

+62
-27
lines changed

hypothesis-python/RELEASE.rst

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
RELEASE_TYPE: patch
2+
3+
This patch deprecates creating a database using the abstract ``ExampleDatabase()`` class. Use one of the following instead:
4+
5+
* Replace ``ExampleDatabase(":memory:")`` with |InMemoryExampleDatabase|.
6+
* Replace ``ExampleDatabase("/path/to/dir")`` with |DirectoryBasedExampleDatabase|.
7+
* Replace ``ExampleDatabase()`` with either |InMemoryExampleDatabase| or |DirectoryBasedExampleDatabase|, depending on your needs. Previously, Hypothesis interpreted ``ExampleDatabase()`` as a |DirectoryBasedExampleDatabase| in the default ``.hypothesis`` directory, with a fallback to |InMemoryExampleDatabase| if that location was not available.

hypothesis-python/docs/conf.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,14 @@ def setup(app):
250250
251251
.. |@reproduce_failure| replace:: :func:`@reproduce_failure <hypothesis.reproduce_failure>`
252252
253-
.. |RedisExampleDatabase| replace:: :class:`~hypothesis.extra.redis.RedisExampleDatabase`
253+
.. |ExampleDatabase| replace:: :class:`~hypothesis.database.ExampleDatabase`
254+
.. |DirectoryBasedExampleDatabase| replace:: :class:`~hypothesis.database.DirectoryBasedExampleDatabase`
254255
.. |InMemoryExampleDatabase| replace:: :class:`~hypothesis.database.InMemoryExampleDatabase`
255256
.. |ReadOnlyDatabase| replace:: :class:`~hypothesis.database.ReadOnlyDatabase`
256257
.. |MultiplexedDatabase| replace:: :class:`~hypothesis.database.MultiplexedDatabase`
257258
.. |GitHubArtifactDatabase| replace:: :class:`~hypothesis.database.GitHubArtifactDatabase`
258259
.. |BackgroundWriteDatabase| replace:: :class:`~hypothesis.database.BackgroundWriteDatabase`
260+
.. |RedisExampleDatabase| replace:: :class:`~hypothesis.extra.redis.RedisExampleDatabase`
259261
260262
.. |str| replace:: :obj:`python:str`
261263
.. |int| replace:: :obj:`python:int`

hypothesis-python/src/hypothesis/_settings.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ def __get__(self, obj, type=None):
7373
# you can change the storage directory and it will be reflected
7474
# in the default database.
7575
if self.name == "database" and result is not_set:
76-
from hypothesis.database import ExampleDatabase
76+
from hypothesis.database import _db_for_path
7777

78-
result = ExampleDatabase(not_set)
78+
result = _db_for_path(not_set)
7979
assert result is not not_set
8080
return result
8181
except KeyError:
@@ -440,8 +440,7 @@ def _validate_database(db: "ExampleDatabase") -> "ExampleDatabase":
440440
return db
441441
raise InvalidArgument(
442442
"Arguments to the database setting must be None or an instance of "
443-
f"ExampleDatabase. Try passing database=ExampleDatabase({db!r}), or "
444-
"construct and use one of the specific subclasses in "
443+
"ExampleDatabase. Try using one of the specific subclasses in "
445444
"hypothesis.database"
446445
)
447446

hypothesis-python/src/hypothesis/database.py

+10
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from urllib.request import Request, urlopen
3939
from zipfile import BadZipFile, ZipFile
4040

41+
from hypothesis._settings import note_deprecation
4142
from hypothesis.configuration import storage_directory
4243
from hypothesis.errors import HypothesisException, HypothesisWarning
4344
from hypothesis.internal.conjecture.choice import ChoiceT
@@ -112,6 +113,15 @@ def _db_for_path(
112113
class _EDMeta(abc.ABCMeta):
113114
def __call__(self, *args: Any, **kwargs: Any) -> "ExampleDatabase":
114115
if self is ExampleDatabase:
116+
note_deprecation(
117+
"Creating a database using the abstract ExampleDatabase() class "
118+
"is deprecated. Prefer using a concrete subclass, like "
119+
"InMemoryExampleDatabase() or DirectoryBasedExampleDatabase(path). "
120+
'In particular, the special string ExampleDatabase(":memory:") '
121+
"should be replaced by InMemoryExampleDatabase().",
122+
since="RELEASEDAY",
123+
has_codemod=False,
124+
)
115125
return _db_for_path(*args, **kwargs)
116126
return super().__call__(*args, **kwargs)
117127

hypothesis-python/tests/conjecture/test_engine.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
strategies as st,
2727
)
2828
from hypothesis.database import (
29-
ExampleDatabase,
3029
InMemoryExampleDatabase,
3130
choices_from_bytes,
3231
choices_to_bytes,
@@ -85,7 +84,7 @@ def nodes(data):
8584

8685
def test_can_load_data_from_a_corpus():
8786
key = b"hi there"
88-
db = ExampleDatabase()
87+
db = InMemoryExampleDatabase()
8988
value = b"=\xc3\xe4l\x81\xe1\xc2H\xc9\xfb\x1a\xb6bM\xa8\x7f"
9089
db.save(key, choices_to_bytes([value]))
9190

@@ -223,7 +222,7 @@ def f(data):
223222
def test_stops_after_max_examples_when_reading():
224223
key = b"key"
225224

226-
db = ExampleDatabase(":memory:")
225+
db = InMemoryExampleDatabase()
227226
for i in range(10):
228227
db.save(key, bytes([i]))
229228

hypothesis-python/tests/cover/test_database_backend.py

+26-8
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@
3939
InMemoryExampleDatabase,
4040
MultiplexedDatabase,
4141
ReadOnlyDatabase,
42+
_db_for_path,
4243
_pack_uleb128,
4344
_unpack_uleb128,
4445
choices_from_bytes,
4546
choices_to_bytes,
4647
)
47-
from hypothesis.errors import HypothesisWarning
48+
from hypothesis.errors import HypothesisDeprecationWarning, HypothesisWarning
4849
from hypothesis.internal.compat import WINDOWS
4950
from hypothesis.internal.conjecture.choice import choice_equal
5051
from hypothesis.stateful import (
@@ -58,7 +59,7 @@
5859
from hypothesis.strategies import binary, lists, tuples
5960
from hypothesis.utils.conventions import not_set
6061

61-
from tests.common.utils import skipif_emscripten
62+
from tests.common.utils import checks_deprecated_behaviour, skipif_emscripten
6263
from tests.conjecture.common import ir, nodes
6364

6465

@@ -86,13 +87,15 @@ def test_can_delete_keys():
8687

8788

8889
def test_default_database_is_in_memory():
89-
assert isinstance(ExampleDatabase(), InMemoryExampleDatabase)
90+
with pytest.warns(HypothesisDeprecationWarning):
91+
assert isinstance(ExampleDatabase(), InMemoryExampleDatabase)
9092

9193

9294
def test_default_on_disk_database_is_dir(tmp_path):
93-
assert isinstance(
94-
ExampleDatabase(tmp_path.joinpath("foo")), DirectoryBasedExampleDatabase
95-
)
95+
with pytest.warns(HypothesisDeprecationWarning):
96+
assert isinstance(
97+
ExampleDatabase(tmp_path.joinpath("foo")), DirectoryBasedExampleDatabase
98+
)
9699

97100

98101
def test_does_not_error_when_fetching_when_not_exist(tmp_path):
@@ -103,7 +106,7 @@ def test_does_not_error_when_fetching_when_not_exist(tmp_path):
103106
@pytest.fixture(scope="function", params=["memory", "directory"])
104107
def exampledatabase(request, tmp_path):
105108
if request.param == "memory":
106-
return ExampleDatabase()
109+
return InMemoryExampleDatabase()
107110
assert request.param == "directory"
108111
return DirectoryBasedExampleDatabase(tmp_path / "examples")
109112

@@ -468,7 +471,7 @@ def test_database_directory_inaccessible(dirs, tmp_path, monkeypatch):
468471
if WINDOWS
469472
else pytest.warns(HypothesisWarning, match=".*the default location is unusable")
470473
):
471-
database = ExampleDatabase(not_set)
474+
database = _db_for_path(not_set)
472475
database.save(b"fizz", b"buzz")
473476

474477

@@ -784,3 +787,18 @@ def listener2(event):
784787

785788
db.clear_listeners()
786789
assert db.ends == 1
790+
791+
792+
@checks_deprecated_behaviour
793+
def test_deprecated_example_database_path(tmp_path):
794+
ExampleDatabase(tmp_path)
795+
796+
797+
@checks_deprecated_behaviour
798+
def test_deprecated_example_database_memory():
799+
ExampleDatabase(":memory:")
800+
801+
802+
@checks_deprecated_behaviour
803+
def test_deprecated_example_database_no_args():
804+
ExampleDatabase()

hypothesis-python/tests/cover/test_settings.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
note_deprecation,
2727
settings,
2828
)
29-
from hypothesis.database import ExampleDatabase, InMemoryExampleDatabase
29+
from hypothesis.database import InMemoryExampleDatabase
3030
from hypothesis.errors import (
3131
HypothesisDeprecationWarning,
3232
InvalidArgument,
@@ -107,7 +107,7 @@ def test_can_not_set_verbosity_to_non_verbosity():
107107
settings(verbosity="kittens")
108108

109109

110-
@pytest.mark.parametrize("db", [None, ExampleDatabase()])
110+
@pytest.mark.parametrize("db", [None, InMemoryExampleDatabase()])
111111
def test_inherits_an_empty_database(db):
112112
with local_settings(settings(database=InMemoryExampleDatabase())):
113113
assert settings.default.database is not None
@@ -118,7 +118,7 @@ def test_inherits_an_empty_database(db):
118118
assert t.database is db
119119

120120

121-
@pytest.mark.parametrize("db", [None, ExampleDatabase()])
121+
@pytest.mark.parametrize("db", [None, InMemoryExampleDatabase()])
122122
def test_can_assign_database(db):
123123
x = settings(database=db)
124124
assert x.database is db
@@ -195,7 +195,7 @@ def test_can_have_none_database():
195195
assert settings(database=None).database is None
196196

197197

198-
@pytest.mark.parametrize("db", [None, ExampleDatabase(":memory:")])
198+
@pytest.mark.parametrize("db", [None, InMemoryExampleDatabase()])
199199
@pytest.mark.parametrize("bad_db", [":memory:", ".hypothesis/examples"])
200200
def test_database_type_must_be_ExampleDatabase(db, bad_db):
201201
with local_settings(settings(database=db)):
@@ -444,7 +444,7 @@ def a_rule(self, x):
444444

445445
def test_derandomise_with_explicit_database_is_invalid():
446446
with pytest.raises(InvalidArgument):
447-
settings(derandomize=True, database=ExampleDatabase(":memory:"))
447+
settings(derandomize=True, database=InMemoryExampleDatabase())
448448

449449

450450
@pytest.mark.parametrize(

hypothesis-python/tests/cover/test_stateful.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
)
2828
from hypothesis.control import current_build_context
2929
from hypothesis.core import encode_failure
30-
from hypothesis.database import ExampleDatabase
30+
from hypothesis.database import InMemoryExampleDatabase
3131
from hypothesis.errors import DidNotReproduce, Flaky, InvalidArgument, InvalidDefinition
3232
from hypothesis.internal.entropy import deterministic_PRNG
3333
from hypothesis.stateful import (
@@ -434,7 +434,7 @@ def test_settings_attribute_is_validated():
434434

435435

436436
def test_saves_failing_example_in_database():
437-
db = ExampleDatabase(":memory:")
437+
db = InMemoryExampleDatabase()
438438
ss = Settings(
439439
database=db, max_examples=1000, suppress_health_check=list(HealthCheck)
440440
)

hypothesis-python/tests/nocover/test_database_usage.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from hypothesis import assume, core, find, given, settings, strategies as st
1414
from hypothesis.database import (
15-
ExampleDatabase,
15+
DirectoryBasedExampleDatabase,
1616
GitHubArtifactDatabase,
1717
InMemoryExampleDatabase,
1818
ReadOnlyDatabase,
@@ -165,7 +165,7 @@ def test(i):
165165
def test_database_not_created_when_not_used(tmp_path_factory, key, value):
166166
path = tmp_path_factory.mktemp("hypothesis") / "examples"
167167
assert not path.exists()
168-
database = ExampleDatabase(path)
168+
database = DirectoryBasedExampleDatabase(path)
169169
assert not list(database.fetch(key))
170170
assert not path.exists()
171171
database.save(key, value)

hypothesis-python/tests/nocover/test_flatmap.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import pytest
1414

1515
from hypothesis import HealthCheck, assume, given, settings
16-
from hypothesis.database import ExampleDatabase
16+
from hypothesis.database import InMemoryExampleDatabase
1717
from hypothesis.strategies import (
1818
booleans,
1919
builds,
@@ -55,7 +55,7 @@ def test_flatmap_retrieve_from_db():
5555
track = []
5656

5757
@given(floats(0, 1).flatmap(lambda x: lists(just(x))))
58-
@settings(database=ExampleDatabase())
58+
@settings(database=InMemoryExampleDatabase())
5959
def record_and_test_size(xs):
6060
if sum(xs) >= 1:
6161
track.append(xs)

0 commit comments

Comments
 (0)