A security fix, a new set of APIs providing background tasks for plugins, an endpoint for counting matching rows, and a collection of bug fixes.
Security fix
- Fixed a security issue where a trailing newline in a requested table name could bypass table permissions and expose private rows. Thanks for the report, dpfkdlemtp. GHSA-h547-rmjf-5m2m
Background tasks
Datasette plugins can now use background tasks to run code independent of the Datasette request/response cycle.
- New .add_background_task(func, name=None) API: plugins register supervised, long-lived background work - typically from a
startuphook - and these will be launched after everystartuphook has run. Tasks are cancelled (with a five-second grace period) on shutdown. - New
/-/tasksJSON debug endpoint lists every supervised background task and its state, in the style of/-/threads. See /-/tasks. It requires thepermissions-debugpermission. - New shutdown(datasette) plugin hook, called during graceful shutdown (Ctrl-C,
SIGTERM) before background tasks are cancelled and before database connections are closed. It is not called on a hard kill (SIGKILL). - Plugin
asgi_wrappermiddleware now always runs after startup has completed. - If your plugin uses
asgi_wrapperto start background tasks on the first incoming request, you should migrate todatasette.add_background_task()instead. datasette-cron and datasette-enrichments are being migrated to this pattern.
Other features
- New POST count endpoint for counting filtered table rows, now used by the count all button. (#2914)
- Datasette now uses httpx2, the Pydantic-maintained continuation of httpx, in place of
httpx. The public API is the same, but responses returned by datasette.client are nowhttpx2.Responseobjects rather thanhttpx.Response. Plugins that useisinstance()checks againsthttpx.Responseshould be updated to usehttpx2. Plugins that use httpx without explicitly depending on it will need to add an explicit dependency or switch to httpx2.
Bug fixes
- Column facets now show the remove-filter link for filters using
column__exact=value, as well ascolumn=value. (#1695) - The alter-table API now rolls back schema changes when a write_wrapper raises after the write. (#2924, #2925)
- The extra_template_vars() plugin hook can now return a function or awaitable that resolves to
Nonewhen no extra variables are needed. (#2005) - request.headers now supports case-insensitive header lookups, so
request.headers.get("Content-Type")works as well asrequest.headers.get("content-type"). (#1861) - CSV endpoints now return plain-text error messages for SQL errors. (#2129)
- The render_cell() plugin hook now receives an empty
pkslist when rendering SQL views in HTML, matching the JSON?_extra=render_cellbehavior. (#2639) - Numeric comparison filters now correctly handle decimal values, negative numbers and scientific notation when filtering computed columns and SQL views. Thanks, Rami Abdelrazzaq. (#1681, #2876)
- Fixed CSV streaming with
?_stream=onon SQL views repeating the second page of results until the CSV size limit was reached. Thanks, Ankita Advitot. (#2902, #2903)