1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
Write a custom Hypothesis database
==================================
To define your own |ExampleDatabase| class, implement the |ExampleDatabase.save|, |ExampleDatabase.fetch|, and |ExampleDatabase.delete| methods.
For example, here's a simple database class that uses :mod:`sqlite <sqlite3>` as the backing data store:
.. code-block:: python
import sqlite3
from collections.abc import Iterable
from hypothesis.database import ExampleDatabase
class SQLiteExampleDatabase(ExampleDatabase):
def __init__(self, db_path: str):
self.conn = sqlite3.connect(db_path)
self.conn.execute(
"""
CREATE TABLE examples (
key BLOB,
value BLOB,
UNIQUE (key, value)
)
"""
)
def save(self, key: bytes, value: bytes) -> None:
self.conn.execute(
"INSERT OR IGNORE INTO examples VALUES (?, ?)",
(key, value),
)
def fetch(self, key: bytes) -> Iterable[bytes]:
cursor = self.conn.execute("SELECT value FROM examples WHERE key = ?", (key,))
yield from [value[0] for value in cursor.fetchall()]
def delete(self, key: bytes, value: bytes) -> None:
self.conn.execute(
"DELETE FROM examples WHERE key = ? AND value = ?",
(key, value),
)
Database classes are not required to implement |ExampleDatabase.move|. The default implementation of a move is a |ExampleDatabase.delete| of the value in the old key, followed by a |ExampleDatabase.save| of the value in the new key. You can override |ExampleDatabase.move| to override this behavior, if for instance the backing store offers a more efficient move implementation.
Change listening
----------------
To support change listening in a database class, you should call |ExampleDatabase._broadcast_change| whenever a value is saved, deleted, or moved in the backing database store. How you track this depends on the details of the database class. For instance, in |DirectoryBasedExampleDatabase|, Hypothesis installs a filesystem monitor via :pypi:`watchdog` in order to broadcast change events.
Two useful related methods are |ExampleDatabase._start_listening| and |ExampleDatabase._stop_listening|, which a database class can override to know when to start or stop expensive listening operations. See documentation for details.
|