Scheduled tasks and background jobs for Datasette
Project description
datasette-cron
Database-backed scheduled tasks for Datasette.
Plugins can register handler functions, then create tasks that run on a schedule. Tasks persist across restarts, support cron expressions and intervals, and record execution history.
Installation
pip install datasette-cron
Quick Start
A plugin registers a handler function and creates a task that runs on a schedule:
from datasette import hookimpl
@hookimpl
def cron_register_handlers(datasette):
async def my_handler(datasette, config):
db = datasette.get_database(config["database"])
await db.execute_write("INSERT INTO log (message) VALUES ('tick')")
return {"my-handler": my_handler}
@hookimpl
def startup(datasette):
async def inner():
scheduler = datasette._cron_scheduler
await scheduler.add_task(
name="log-every-minute",
handler="myplugin:my-handler",
schedule={"interval": 60},
config={"database": "mydb"},
)
return inner
How It Works
- Startup: datasette-cron creates a
Scheduleratdatasette._cron_schedulerand collects handlers from all plugins via thecron_register_handlershook - First request: The scheduler loop starts (via
asgi_wrapper), ticking every ~1 second - Each tick: Queries
datasette_cron_tasksfor tasks wherenext_run_at <= nowandenabled = 1 - Execution: Looks up the handler function, calls it with
(datasette, config), records the result indatasette_cron_runs - Next run: Advances
next_run_atbased on the schedule
Plugin Hook
cron_register_handlers(datasette)
Return a dict mapping handler names to callable functions:
@hookimpl
def cron_register_handlers(datasette):
return {
"check-feeds": check_feeds_handler,
"cleanup": cleanup_handler,
}
Handlers are registered with a plugin-derived prefix. If your plugin module is
datasette_myplugin, handlers are accessible as myplugin:check-feeds and
myplugin:cleanup.
Handler Signature
async def my_handler(datasette, config):
"""
datasette: the Datasette instance
config: dict from the task's config field
"""
pass
Handlers can be sync or async.
Scheduler API
Access the scheduler via datasette._cron_scheduler after startup.
add_task()
Create or update a task (idempotent upsert). If the task already exists,
next_run_at is preserved.
await scheduler.add_task(
name="my-task",
handler="myplugin:my-handler",
schedule={"interval": 300}, # every 5 minutes
config={"key": "value"}, # passed to handler
timezone="America/New_York", # optional
overlap="skip", # "skip" prevents overlapping runs
retry={"max_retries": 3, "backoff": "exponential"},
)
Schedule Types
Interval (seconds):
schedule={"interval": 60} # every 60 seconds
schedule={"interval": 1} # every second
Cron expression:
schedule="0 8 * * *" # daily at 8am
schedule="*/5 * * * *" # every 5 minutes
RFC 5545 RRULE:
schedule={"rrule": "FREQ=WEEKLY;BYDAY=MO"}
Other Methods
await scheduler.remove_task("my-task")
await scheduler.trigger_task("my-task") # run immediately
await scheduler.enable_task("my-task")
await scheduler.disable_task("my-task")
await scheduler.update_task("my-task", schedule={"interval": 10})
Data Models
Query results from InternalDB return typed dataclasses:
from datasette_cron.models import CronTask, CronRun
task: CronTask = await scheduler.internal_db.get_task("my-task")
print(task.name, task.handler, task.next_run_at, task.last_status)
runs: list[CronRun] = await scheduler.internal_db.get_runs("my-task")
for run in runs:
print(run.started_at, run.status, run.duration_ms)
CronTask
| Field | Type | Description |
|---|---|---|
name |
str |
Unique task identifier |
handler |
str |
Handler reference (e.g., "myplugin:my-handler") |
config |
dict |
JSON config passed to handler |
schedule_type |
str |
"interval", "cron", or "rrule" |
schedule_config |
str |
JSON schedule parameters |
timezone |
str | None |
IANA timezone |
overlap_policy |
str |
"skip" or "allow" |
retry_max |
int |
Max retry attempts |
retry_backoff |
str |
"exponential" or "linear" |
enabled |
bool |
Whether task is active |
next_run_at |
str | None |
ISO timestamp of next scheduled run |
last_run_at |
str | None |
ISO timestamp of last run |
last_status |
str | None |
"success" or "error" |
CronRun
| Field | Type | Description |
|---|---|---|
id |
int |
Auto-increment ID |
task_name |
str |
Which task this run belongs to |
started_at |
str |
ISO timestamp |
finished_at |
str | None |
ISO timestamp |
status |
str |
"running", "success", or "error" |
error_message |
str | None |
Error details on failure |
attempt |
int |
Retry attempt number |
duration_ms |
int | None |
Execution time in milliseconds |
REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /-/api/cron/tasks |
List all tasks |
| GET | /-/api/cron/tasks/{name} |
Task detail |
| GET | /-/api/cron/tasks/{name}/runs |
Run history |
| POST | /-/api/cron/tasks/{name}/trigger |
Trigger immediate run |
| POST | /-/api/cron/tasks/{name}/enable |
Enable/disable task |
All endpoints require the datasette-cron-access permission.
Database Tables
Stored in Datasette's internal database:
datasette_cron_tasks — task definitions and scheduling state
datasette_cron_runs — execution history with timing, status, and errors
Development
just dev # start dev server
just test # run tests
just format # format code (backend + frontend)
just check # lint + type check (backend + frontend)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file datasette_cron-0.0.1a2.tar.gz.
File metadata
- Download URL: datasette_cron-0.0.1a2.tar.gz
- Upload date:
- Size: 52.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9d3c4ee4f2a287ce374590a1e1084c502efb89642c77e4ae85cda37e64d52e4
|
|
| MD5 |
8d681b0600eea6a277522e75d201dd43
|
|
| BLAKE2b-256 |
bd784f13e053664a5d33b5becb00ec75ac729d583686d4db9dddbacd971c8507
|
Provenance
The following attestation bundles were made for datasette_cron-0.0.1a2.tar.gz:
Publisher:
publish.yml on datasette/datasette-cron
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
datasette_cron-0.0.1a2.tar.gz -
Subject digest:
b9d3c4ee4f2a287ce374590a1e1084c502efb89642c77e4ae85cda37e64d52e4 - Sigstore transparency entry: 1521367043
- Sigstore integration time:
-
Permalink:
datasette/datasette-cron@3bdb077db96ce62298a286d4e383fc52a028ea72 -
Branch / Tag:
refs/tags/0.0.1a2 - Owner: https://github.com/datasette
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3bdb077db96ce62298a286d4e383fc52a028ea72 -
Trigger Event:
release
-
Statement type:
File details
Details for the file datasette_cron-0.0.1a2-py3-none-any.whl.
File metadata
- Download URL: datasette_cron-0.0.1a2-py3-none-any.whl
- Upload date:
- Size: 62.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb41ae1aefcf424304097ef9445e0c6039595ca7c4964379ecc359def2e627d7
|
|
| MD5 |
55c5d793d5a1d07bd318d78a97caeedd
|
|
| BLAKE2b-256 |
1d329eac4e2eaa38a3410e7a8b903ae8f8c63cae186ef3052aa3c859e243e510
|
Provenance
The following attestation bundles were made for datasette_cron-0.0.1a2-py3-none-any.whl:
Publisher:
publish.yml on datasette/datasette-cron
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
datasette_cron-0.0.1a2-py3-none-any.whl -
Subject digest:
fb41ae1aefcf424304097ef9445e0c6039595ca7c4964379ecc359def2e627d7 - Sigstore transparency entry: 1521367080
- Sigstore integration time:
-
Permalink:
datasette/datasette-cron@3bdb077db96ce62298a286d4e383fc52a028ea72 -
Branch / Tag:
refs/tags/0.0.1a2 - Owner: https://github.com/datasette
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3bdb077db96ce62298a286d4e383fc52a028ea72 -
Trigger Event:
release
-
Statement type: