Add add-database and remove-database lifecycle events

Fire plugin-visible events from Datasette.add_database() and
.remove_database() so plugins can react to databases being attached or
detached at runtime. Dispatch is best-effort fire-and-forget: events only
fire when startup has completed and an event loop is running, and
remove-database fires after close() so queued writes are flushed before
listeners run.

Motivating consumer is datasette-litestream, which needs to register newly
attached databases with its replication daemon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Alex Garcia 2026-08-04 14:43:01 -07:00
commit c6bbcc3298
6 changed files with 205 additions and 2 deletions

View file

@ -9,6 +9,15 @@ Note that these events will *not* fire for changes made to a SQLite database by
Plugins can listen for events using the {ref}`plugin_hook_track_event` plugin hook, which will be called with instances of the following classes - or additional classes {ref}`registered by other plugins <plugin_hook_register_events>`.
## Delivery guarantees for database lifecycle events
The ``add-database`` and ``remove-database`` events have some specific delivery characteristics:
- Delivery is asynchronous. Listeners run shortly after the change, not before the triggering ``add_database()`` or ``remove_database()`` call returns.
- These events fire only for changes made at runtime - while an event loop is running, after Datasette's startup has completed. Databases attached while the instance is starting up do not produce events: plugins that need to see those should iterate over ``datasette.databases`` in their own {ref}`plugin_hook_startup` hook.
- Rapid successive changes involving the same database name may reach listeners interleaved. Listeners should tolerate events arriving out of order.
- ``event.actor`` is ``None`` for programmatic calls made by Datasette itself or by plugins.
```{eval-rst}
.. automodule:: datasette.events
:members:

View file

@ -1357,6 +1357,8 @@ Use ``is_mutable=False`` to add an immutable database.
"CREATE TABLE foo(id integer primary key)"
)
Calling this method while the instance is running - after ``invoke_startup()`` has completed, with an event loop running - emits an ``add-database`` :ref:`event <events>`. Databases attached during startup do not emit events: plugins that need to see those should iterate over ``datasette.databases`` in their own :ref:`plugin_hook_startup` hook.
.. _datasette_add_memory_database:
.add_memory_database(memory_name, name=None, route=None)
@ -1392,6 +1394,8 @@ The ``name`` and ``route`` parameters are optional and work the same way as they
This removes a database that has been previously added. ``name=`` is the unique name of that database.
The database is closed but its file is not deleted. When called while the instance is running this emits a ``remove-database`` :ref:`event <events>` after the database has been closed - since closing flushes any queued writes first, an event listener can safely perform a final read of the database file. The exception is temporary on-disk databases, which remove their backing file when closed.
.. _datasette_close:
.close()