Export DuckDB UI notebooks to standalone static HTML, re-executing cells against the target database.
Project description
duckdb-nb-export
duckdb-nb-export is a CLI for exporting DuckDB UI (duckdb --ui) notebooks
to a single static HTML file by re-executing notebook cells against a target
database. It fills the current gap where DuckDB UI notebooks do not have a
Jupyter nbconvert equivalent.
Status: alpha, Phase 1, CLI only. The package is not published on PyPI yet.
It depends on DuckDB UI's unofficial internal ui.db schema, so a DuckDB UI
update can break notebook discovery or parsing. This is an unofficial
third-party tool, not affiliated with DuckDB, DuckDB Labs, or MotherDuck.
For users
Requirements
- Python >=3.11
duckdb>=1.5.4- Linux or macOS. Windows is not verified and is outside the Phase 1 support target.
Installation
Until PyPI publication, install from a repository URL:
uv tool install git+<repository-url>
or:
pip install git+<repository-url>
The intended future install command is:
pip install duckdb-nb-export
Quick start
List available notebooks:
duckdb-nb-export --list
Export a notebook:
duckdb-nb-export "My Notebook" -o report.html
By default, the exporter reads DuckDB UI notebooks from
~/.duckdb/extension_data/ui/ui.db. If your ui.db is stored in a custom
location, pass it explicitly:
duckdb-nb-export --ui-db ~/duckdb-ui-profiles/work/ui.db --list
duckdb-nb-export "My Notebook" --ui-db ~/duckdb-ui-profiles/work/ui.db -o report.html
The default reader path uses a snapshot copy of ui.db and its WAL file, so
the command can read notebooks while DuckDB UI is still running. Use
--require-ui-closed only when you want to open ui.db directly.
How it works
The exporter copies ui.db together with its WAL, reads the notebook
definition, re-executes each SQL cell against the target DuckDB database, and
renders one standalone HTML file. The HTML has inline CSS, supports light and
dark color schemes, and does not reference external resources.
Safety model
Exporting a notebook executes its SQL with your privileges. Do not export notebooks from sources you would not trust enough to run yourself.
By default, notebook cells run inside one transaction and the exporter finishes
with ROLLBACK, so changes inside the target database file are not retained.
Use --allow-writes only when you want the exporter to commit those changes.
ROLLBACK cannot undo external side effects such as COPY ... TO file writes,
writes to an attached database, remote writes, INSTALL, or LOAD. The CLI
therefore asks for confirmation before execution; in non-interactive contexts,
use --yes to make that confirmation explicit. --no-external-access runs
with DuckDB external access disabled, which also disables external file reads
such as CSV or Parquet scans.
CREATE SECRET parameter values are masked as *** in rendered SQL. Secrets
written in any other SQL form are not detected and will remain in the HTML.
If no target database is resolved, execution falls back to :memory: and emits
a warning. DuckDB UI notebook JSON stores database names, not reliable file
paths, so pass --db <path> for exports that depend on existing tables.
CLI reference
The command is registered by [project.scripts] as duckdb-nb-export.
| Argument or option | Meaning | Default |
|---|---|---|
notebook_name |
Notebook name to export. Optional when --list is used. |
None |
-h, --help |
Show help and exit. | Off |
-o, --output |
Output HTML path. | <notebook-name>.html under the allowed base |
--output-dir |
Allowed base directory and default output directory. | Current directory |
--notebook-id |
Export the notebook with this exact ID (from --list); use when names are ambiguous. |
None |
--db |
Target DuckDB database path for notebook re-execution. | Resolved from notebook metadata, then :memory: |
--ui-db |
Path to DuckDB UI ui.db. |
~/.duckdb/extension_data/ui/ui.db |
--nb-version |
Notebook version identifier to export. | Latest version |
--list |
List notebooks and exit. | Off |
--list-versions |
List versions for the selected notebook and exit. | Off |
--max-rows |
Maximum rows to render per cell. | 1000 |
--cell-timeout |
Per-cell execution timeout in seconds. | 300.0 |
--stop-on-error |
Stop processing after the first cell error. | Off |
--allow-writes |
Commit notebook changes instead of rolling them back. | Off |
--no-external-access |
Disable DuckDB external access during execution. | Off |
--require-ui-closed |
Open ui.db directly and require DuckDB UI to be closed. |
Off |
--yes |
Skip the execution confirmation prompt. | Off |
Existing output files are not overwritten; a numeric suffix is added.
If -o/--output points outside the allowed base directory (the current
directory by default, or the directory passed to --output-dir), the export
is rejected with exit code 3. To write outside the current directory, pass
that location to --output-dir so it becomes the allowed base itself, for
example:
duckdb-nb-export "My Notebook" --output-dir /tmp -o /tmp/report.html
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success. |
| 1 | Notebook not found, or notebook name is ambiguous (use --notebook-id). |
| 2 | Cell execution stopped because --stop-on-error was set, or a timeout interrupt failed and the export ended partially. |
| 3 | Output path rejected because it escapes the allowed base directory. |
| 4 | ui.db access or export setup failed, including lock, corruption, or storage-version mismatch. |
| 5 | Execution confirmation was declined, including non-interactive execution without --yes. |
Without --stop-on-error, a run that completes exits 0 even if individual
cells failed; per-cell failures are reported in the rendered HTML and on
stderr, not through the exit code. Use --stop-on-error if you need failure
detection through the exit code, for example in CI.
Limitations
- Chart rendering is not supported in Phase 1. Chart cells are re-executed and displayed as tables with a note; stored format v3 has no chart representation.
- Result display is limited to the first 1,000 rows by default. The total row count is not computed.
- Long scalar values are truncated after 500 characters in HTML output.
- The reader depends on DuckDB UI's unofficial schema and stored notebook format v3.
- Notebook JSON's
currentDatabase/useDatabase(database name) is not applied on re-execution yet; environment replay is not yet implemented, so pass--db <path>to point at the right database.
License
MIT. See LICENSE.
For developers
Documentation
- Design document (Japanese)
- Architecture decision records (Japanese)
- Test design document (Japanese)
- Changelog
Setup
uv sync --group dev
uv run pre-commit install
Common tasks
uv run pytest
uv run poe check
Pytest markers: assumptions guards DuckDB behavior and the unofficial UI
schema; integration covers multi-layer real-file/process tests; e2e drives
the real CLI and compares golden HTML.
Test suite
The project is test-driven: tests describe Phase 1 behavior before the
implementation is treated as complete. Failures in assumption tests (AT-*)
are not ordinary implementation bugs; they signal that a documented design
assumption about DuckDB behavior or the unofficial DuckDB UI schema changed.
Golden HTML fixtures are updated with:
UPDATE_GOLDEN_HTML=1 uv run pytest tests/test_e2e.py
Fixtures
tests/fixtures/ui_db is regenerated with:
uv run python scripts/regenerate_ui_db_fixtures.py
The current checked-in fixtures were generated by the fallback SQL generator because the fixture environment did not have an available browser.
Architecture
Reader copies and reads ui.db; Executor re-executes cells in one transaction;
Renderer builds a single Jinja2/Pygments HTML document. The CLI wires those
layers together. models/ separates internal notebook models from stored
DuckDB UI format v3 models (Stored*).
DuckDB UI ui.db -> Reader -> Executor -> Renderer -> standalone HTML
CLI wiring and validation connects the full pipeline.
CI
GitHub Actions runs lint once on Ubuntu across Python 3.11 and latest Python
3, then runs tests across ubuntu-latest and macos-latest, Python 3.11 and
latest Python 3, and DuckDB 1.5.4 plus latest DuckDB. Assumption-test
failures on latest DuckDB are non-blocking signals for design review; failures
on DuckDB 1.5.4 remain blocking.
Roadmap
See design document section 2.1. Phase 1.5 adds a notebook-cell callable
export_notebook_html() path after the target-database lock problem is
resolved. Phase 2 targets client-side chart embedding and a C++ core.
Conventions
Application logging uses structlog; do not use print in package code.
Docstrings are English and follow NumPy-style sections. User-facing messages
are English.
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 duckdb_nb_export-0.0.1.tar.gz.
File metadata
- Download URL: duckdb_nb_export-0.0.1.tar.gz
- Upload date:
- Size: 27.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d112dff40e839cc08fbba0517b064ebe1809a894859dced91140a404b1eed9d1
|
|
| MD5 |
07cd0232c6cfe7a25919547d8f282b3e
|
|
| BLAKE2b-256 |
e8505f21b997e5550fe20a38fc591611df69110fe65122209bab0f7cf496e017
|
Provenance
The following attestation bundles were made for duckdb_nb_export-0.0.1.tar.gz:
Publisher:
publish.yml on b-trout/duckdb-nb-export
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duckdb_nb_export-0.0.1.tar.gz -
Subject digest:
d112dff40e839cc08fbba0517b064ebe1809a894859dced91140a404b1eed9d1 - Sigstore transparency entry: 2081506066
- Sigstore integration time:
-
Permalink:
b-trout/duckdb-nb-export@4d53a4031913e9617a953e4f0c4595c2e240d5b0 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/b-trout
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4d53a4031913e9617a953e4f0c4595c2e240d5b0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file duckdb_nb_export-0.0.1-py3-none-any.whl.
File metadata
- Download URL: duckdb_nb_export-0.0.1-py3-none-any.whl
- Upload date:
- Size: 31.4 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 |
62c5e9d604ba0b1a7032afa985b2c48ab113f0816b168ba79feb1ceeb0a51199
|
|
| MD5 |
415a00d0740e3fc838596b37ec115684
|
|
| BLAKE2b-256 |
e744f445f55dfee631c41780b444502f802bb01ad2eade01e47dbf90dc9544c0
|
Provenance
The following attestation bundles were made for duckdb_nb_export-0.0.1-py3-none-any.whl:
Publisher:
publish.yml on b-trout/duckdb-nb-export
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
duckdb_nb_export-0.0.1-py3-none-any.whl -
Subject digest:
62c5e9d604ba0b1a7032afa985b2c48ab113f0816b168ba79feb1ceeb0a51199 - Sigstore transparency entry: 2081506084
- Sigstore integration time:
-
Permalink:
b-trout/duckdb-nb-export@4d53a4031913e9617a953e4f0c4595c2e240d5b0 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/b-trout
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4d53a4031913e9617a953e4f0c4595c2e240d5b0 -
Trigger Event:
push
-
Statement type: