diff --git a/.github/actions/setup-sqlite-version/action.yml b/.github/actions/setup-sqlite-version/action.yml
deleted file mode 100644
index fdbc71c9..00000000
--- a/.github/actions/setup-sqlite-version/action.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: "Setup SQLite version"
-description: "Build and activate a specific SQLite version from its amalgamation archive"
-inputs:
- version:
- description: "The SQLite version to install"
- required: true
- cflags:
- description: "CFLAGS to use when compiling SQLite"
- required: false
- default: ""
- skip-activate:
- description: "Set to true to skip modifying the library path"
- required: false
- default: "false"
- fallback-urls:
- description: "Whitespace-separated fallback download URLs to try after sqlite.org"
- required: false
- default: ""
-outputs:
- sqlite-location:
- description: "Directory containing the compiled SQLite library"
- value: ${{ steps.build.outputs.sqlite-location }}
-runs:
- using: "composite"
- steps:
- - shell: bash
- run: mkdir -p "$RUNNER_TEMP/sqlite-versions/downloads"
- - uses: actions/cache@v6
- with:
- path: ${{ runner.temp }}/sqlite-versions/downloads
- key: setup-sqlite-version-${{ inputs.version }}-amalgamation-v1
- - id: build
- shell: bash
- run: bash "$GITHUB_ACTION_PATH/setup-sqlite-version.sh"
- env:
- SQLITE_VERSION: ${{ inputs.version }}
- SQLITE_CFLAGS: ${{ inputs.cflags }}
- SQLITE_SKIP_ACTIVATE: ${{ inputs.skip-activate }}
- SQLITE_EXTRA_FALLBACK_URLS: ${{ inputs.fallback-urls }}
diff --git a/.github/actions/setup-sqlite-version/setup-sqlite-version.sh b/.github/actions/setup-sqlite-version/setup-sqlite-version.sh
deleted file mode 100644
index 03d6a68f..00000000
--- a/.github/actions/setup-sqlite-version/setup-sqlite-version.sh
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-version_spec="${SQLITE_VERSION:?SQLITE_VERSION is required}"
-cflags="${SQLITE_CFLAGS:-}"
-skip_activate="${SQLITE_SKIP_ACTIVATE:-false}"
-extra_fallback_urls="${SQLITE_EXTRA_FALLBACK_URLS:-}"
-
-case "$version_spec" in
- 3.46 | 3.46.0)
- sqlite_version="3.46.0"
- sqlite_year="2024"
- amalgamation_id="3460000"
- builtin_fallback_urls="https://static.simonwillison.net/static/2026/sqlite-amalgamation-3460000.zip"
- ;;
- 3.25 | 3.25.0)
- sqlite_version="3.25.0"
- sqlite_year="2018"
- amalgamation_id="3250000"
- builtin_fallback_urls="https://static.simonwillison.net/static/2026/sqlite-amalgamation-3250000.zip?v=1"
- ;;
- *)
- echo "::error::Unsupported SQLite version '$version_spec'. Add its release year and amalgamation id to $GITHUB_ACTION_PATH/setup-sqlite-version.sh."
- exit 1
- ;;
-esac
-
-case "$(uname -s)" in
- Linux)
- library_name="libsqlite3.so.0"
- library_path_var="LD_LIBRARY_PATH"
- ;;
- Darwin)
- library_name="libsqlite3.dylib"
- library_path_var="DYLD_LIBRARY_PATH"
- ;;
- *)
- echo "::error::Unsupported platform $(uname -s)"
- exit 1
- ;;
-esac
-
-runner_temp="${RUNNER_TEMP:-}"
-if [ -z "$runner_temp" ]; then
- runner_temp="$(mktemp -d)"
-fi
-
-filename="sqlite-amalgamation-${amalgamation_id}"
-official_url="https://www.sqlite.org/${sqlite_year}/${filename}.zip"
-download_dir="${runner_temp}/sqlite-versions/downloads"
-source_root="${runner_temp}/sqlite-versions/source"
-source_dir="${source_root}/${filename}"
-build_dir="${runner_temp}/sqlite-versions/build/${sqlite_version}"
-archive_path="${download_dir}/${filename}.zip"
-
-mkdir -p "$download_dir" "$source_root" "$build_dir"
-
-download_archive() {
- local url
- local candidate_path="${archive_path}.tmp"
- local urls=("$official_url")
-
- for url in $builtin_fallback_urls $extra_fallback_urls; do
- urls+=("$url")
- done
-
- rm -f "$candidate_path"
- for url in "${urls[@]}"; do
- echo "Downloading SQLite ${sqlite_version} amalgamation from ${url}"
- if curl \
- --fail \
- --location \
- --show-error \
- --retry 5 \
- --retry-delay 2 \
- --retry-max-time 180 \
- --retry-all-errors \
- --connect-timeout 20 \
- --max-time 240 \
- --output "$candidate_path" \
- "$url"; then
- mv "$candidate_path" "$archive_path"
- return 0
- fi
-
- echo "::warning::Download failed from ${url}"
- rm -f "$candidate_path"
- done
-
- echo "::error::Could not download SQLite ${sqlite_version} amalgamation"
- return 1
-}
-
-if [ ! -f "${source_dir}/sqlite3.c" ]; then
- if [ ! -f "$archive_path" ]; then
- download_archive
- fi
-
- rm -rf "$source_dir"
- unzip -q "$archive_path" -d "$source_root"
-fi
-
-if [ ! -f "${source_dir}/sqlite3.c" ]; then
- echo "::error::Expected ${source_dir}/sqlite3.c after extracting ${archive_path}"
- exit 1
-fi
-
-read -r -a cflag_args <<< "$cflags"
-
-echo "Compiling SQLite ${sqlite_version} to ${build_dir}/${library_name}"
-gcc \
- -fPIC \
- -shared \
- "${cflag_args[@]}" \
- "${source_dir}/sqlite3.c" \
- "-I${source_dir}" \
- -o "${build_dir}/${library_name}"
-
-if [ "$library_name" = "libsqlite3.so.0" ]; then
- ln -sf "$library_name" "${build_dir}/libsqlite3.so"
-fi
-
-if [ -n "${GITHUB_OUTPUT:-}" ]; then
- echo "sqlite-location=${build_dir}" >> "$GITHUB_OUTPUT"
-else
- echo "sqlite-location=${build_dir}"
-fi
-
-case "$(printf '%s' "$skip_activate" | tr '[:upper:]' '[:lower:]')" in
- true | 1 | yes)
- echo "Skipping ${library_path_var} activation"
- ;;
- *)
- existing_value="${!library_path_var:-}"
- if [ -n "${GITHUB_ENV:-}" ]; then
- if [ -n "$existing_value" ]; then
- echo "${library_path_var}=${build_dir}:${existing_value}" >> "$GITHUB_ENV"
- else
- echo "${library_path_var}=${build_dir}" >> "$GITHUB_ENV"
- fi
- fi
- echo "Added ${build_dir} to ${library_path_var}"
- ;;
-esac
diff --git a/.github/workflows/deploy-branch-preview.yml b/.github/workflows/deploy-branch-preview.yml
new file mode 100644
index 00000000..e56d9c27
--- /dev/null
+++ b/.github/workflows/deploy-branch-preview.yml
@@ -0,0 +1,35 @@
+name: Deploy a Datasette branch preview to Vercel
+
+on:
+ workflow_dispatch:
+ inputs:
+ branch:
+ description: "Branch to deploy"
+ required: true
+ type: string
+
+jobs:
+ deploy-branch-preview:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v6
+ with:
+ python-version: "3.11"
+ - name: Install dependencies
+ run: |
+ pip install datasette-publish-vercel
+ - name: Deploy the preview
+ env:
+ VERCEL_TOKEN: ${{ secrets.BRANCH_PREVIEW_VERCEL_TOKEN }}
+ run: |
+ export BRANCH="${{ github.event.inputs.branch }}"
+ wget https://latest.datasette.io/fixtures.db
+ datasette publish vercel fixtures.db \
+ --branch $BRANCH \
+ --project "datasette-preview-$BRANCH" \
+ --token $VERCEL_TOKEN \
+ --scope datasette \
+ --about "Preview of $BRANCH" \
+ --about_url "https://github.com/simonw/datasette/tree/$BRANCH"
diff --git a/.github/workflows/deploy-latest.yml b/.github/workflows/deploy-latest.yml
index 3fc83438..7349a1ab 100644
--- a/.github/workflows/deploy-latest.yml
+++ b/.github/workflows/deploy-latest.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out datasette
- uses: actions/checkout@v7
+ uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -57,7 +57,7 @@ jobs:
db.route = "alternative-route"
' > plugins/alternative_route.py
cp fixtures.db fixtures2.db
- - name: And the counters writable stored query demo
+ - name: And the counters writable canned query demo
run: |
cat > plugins/counters.py <=0.2.2' \
--service "datasette-latest$SUFFIX" \
--secret $LATEST_DATASETTE_SECRET
diff --git a/.github/workflows/documentation-links.yml b/.github/workflows/documentation-links.yml
new file mode 100644
index 00000000..a54bd83a
--- /dev/null
+++ b/.github/workflows/documentation-links.yml
@@ -0,0 +1,16 @@
+name: Read the Docs Pull Request Preview
+on:
+ pull_request_target:
+ types:
+ - opened
+
+permissions:
+ pull-requests: write
+
+jobs:
+ documentation-links:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: readthedocs/actions/preview@v1
+ with:
+ project-slug: "datasette"
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
deleted file mode 100644
index f5b8dbf6..00000000
--- a/.github/workflows/playwright.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-name: Playwright
-
-on:
- push:
- pull_request:
- workflow_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- test:
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- browser: [chromium, firefox, webkit]
- steps:
- - uses: actions/checkout@v7
- - name: Set up Python 3.14
- uses: actions/setup-python@v6
- with:
- python-version: "3.14"
- allow-prereleases: true
- cache: pip
- cache-dependency-path: pyproject.toml
- - name: Cache uv
- uses: actions/cache@v6
- with:
- path: ~/.cache/uv
- key: ${{ runner.os }}-py3.14-uv-${{ hashFiles('pyproject.toml') }}
- restore-keys: |
- ${{ runner.os }}-py3.14-uv-
- - name: Cache Playwright browsers
- uses: actions/cache@v6
- with:
- path: ~/.cache/ms-playwright/
- key: ${{ runner.os }}-playwright-${{ matrix.browser }}-${{ hashFiles('pyproject.toml') }}
- restore-keys: |
- ${{ runner.os }}-playwright-${{ matrix.browser }}-
- - name: Install uv
- run: python -m pip install uv
- - name: Install dependencies
- run: uv sync --group dev --group playwright
- - name: Install ${{ matrix.browser }}
- run: uv run --group dev --group playwright playwright install --with-deps ${{ matrix.browser }}
- - name: Run Playwright tests
- run: uv run --group dev --group playwright pytest tests/test_playwright.py --playwright --browser ${{ matrix.browser }}
diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml
index d92ab82b..77cce7d1 100644
--- a/.github/workflows/prettier.yml
+++ b/.github/workflows/prettier.yml
@@ -10,8 +10,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repo
- uses: actions/checkout@v7
- - uses: actions/cache@v6
+ uses: actions/checkout@v4
+ - uses: actions/cache@v4
name: Configure npm caching
with:
path: ~/.npm
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 21ed4c12..2e8cea9c 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -14,7 +14,7 @@ jobs:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
@@ -35,7 +35,7 @@ jobs:
permissions:
id-token: write
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -56,7 +56,7 @@ jobs:
needs: [deploy]
if: "!github.event.release.prerelease"
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
@@ -92,7 +92,7 @@ jobs:
needs: [deploy]
if: "!github.event.release.prerelease"
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Build and push to Docker Hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
diff --git a/.github/workflows/push_docker_tag.yml b/.github/workflows/push_docker_tag.yml
index c5a4f0db..afe8d6b2 100644
--- a/.github/workflows/push_docker_tag.yml
+++ b/.github/workflows/push_docker_tag.yml
@@ -13,7 +13,7 @@ jobs:
deploy_docker:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v2
- name: Build and push to Docker Hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml
index 58635025..d42ae96b 100644
--- a/.github/workflows/spellcheck.yml
+++ b/.github/workflows/spellcheck.yml
@@ -9,7 +9,7 @@ jobs:
spellcheck:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
diff --git a/.github/workflows/stable-docs.yml b/.github/workflows/stable-docs.yml
index ecde5940..3119d617 100644
--- a/.github/workflows/stable-docs.yml
+++ b/.github/workflows/stable-docs.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@v7
+ uses: actions/checkout@v5
with:
fetch-depth: 0 # We need all commits to find docs/ changes
- name: Set up Git user
diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml
index e9bd4bab..1b3d2f2c 100644
--- a/.github/workflows/test-coverage.yml
+++ b/.github/workflows/test-coverage.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out datasette
- uses: actions/checkout@v7
+ uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v6
with:
diff --git a/.github/workflows/test-pyodide.yml b/.github/workflows/test-pyodide.yml
index 5e81ed82..b490a9bf 100644
--- a/.github/workflows/test-pyodide.yml
+++ b/.github/workflows/test-pyodide.yml
@@ -12,7 +12,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v6
with:
@@ -20,7 +20,7 @@ jobs:
cache: 'pip'
cache-dependency-path: '**/pyproject.toml'
- name: Cache Playwright browsers
- uses: actions/cache@v6
+ uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright/
key: ${{ runner.os }}-browsers
diff --git a/.github/workflows/test-sqlite-support.yml b/.github/workflows/test-sqlite-support.yml
index 2fdb3a40..c81a3c0b 100644
--- a/.github/workflows/test-sqlite-support.yml
+++ b/.github/workflows/test-sqlite-support.yml
@@ -25,7 +25,7 @@ jobs:
#"3.23.1" # 2018-04-10, before UPSERT
]
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
@@ -34,7 +34,7 @@ jobs:
cache: pip
cache-dependency-path: pyproject.toml
- name: Set up SQLite ${{ matrix.sqlite-version }}
- uses: ./.github/actions/setup-sqlite-version
+ uses: asg017/sqlite-versions@71ea0de37ae739c33e447af91ba71dda8fcf22e6
with:
version: ${{ matrix.sqlite-version }}
cflags: "-DSQLITE_ENABLE_DESERIALIZE -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_JSON1"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 751eedfd..a0f5477b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,11 +9,10 @@ jobs:
test:
runs-on: ubuntu-latest
strategy:
- fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
diff --git a/.github/workflows/tmate-mac.yml b/.github/workflows/tmate-mac.yml
index f2c074a6..fcee0f21 100644
--- a/.github/workflows/tmate-mac.yml
+++ b/.github/workflows/tmate-mac.yml
@@ -10,6 +10,6 @@ jobs:
build:
runs-on: macos-latest
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v2
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
diff --git a/.github/workflows/tmate.yml b/.github/workflows/tmate.yml
index 5b8818c3..123f6c71 100644
--- a/.github/workflows/tmate.yml
+++ b/.github/workflows/tmate.yml
@@ -11,7 +11,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v2
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
env:
diff --git a/.gitignore b/.gitignore
index 2a7f6620..12acd87e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,8 @@
build-metadata.json
datasets.json
-.playwright-mcp
-
scratchpad
-ignored/
-
.vscode
uv.lock
@@ -135,4 +131,4 @@ tests/*.dylib
tests/*.so
tests/*.dll
-.idea
+.idea
\ No newline at end of file
diff --git a/Justfile b/Justfile
index 6ffff870..657881be 100644
--- a/Justfile
+++ b/Justfile
@@ -11,33 +11,16 @@ export DATASETTE_SECRET := "not_a_secret"
@test *options: init
uv run pytest -n auto {{options}}
-# Install Playwright browser support, Chromium by default
-@playwright-install browser="chromium":
- uv run --group playwright playwright install {{browser}}
-
-# Install all Playwright browsers used by the test suite
-@playwright-install-all:
- uv run --group playwright playwright install chromium firefox webkit
-
-# Run Playwright tests, Chromium by default
-@playwright browser="chromium" *options:
- uv run --group playwright pytest tests/test_playwright.py --playwright --browser {{browser}} {{options}}
-
-# Run Playwright tests against all supported browsers
-@playwright-all *options:
- uv run --group playwright pytest tests/test_playwright.py --playwright --browser chromium --browser firefox --browser webkit {{options}}
-
@codespell:
uv run codespell README.md --ignore-words docs/codespell-ignore-words.txt
uv run codespell docs/*.rst --ignore-words docs/codespell-ignore-words.txt
uv run codespell datasette -S datasette/static --ignore-words docs/codespell-ignore-words.txt
uv run codespell tests --ignore-words docs/codespell-ignore-words.txt
-# Run linters: black, ruff, prettier, cog
+# Run linters: black, ruff, cog
@lint: codespell
uv run black datasette tests --check
uv run ruff check datasette tests
- npm run prettier -- --check
uv run cog --check README.md docs/*.rst
# Apply ruff fixes
diff --git a/datasette/__init__.py b/datasette/__init__.py
index e0022178..eb18e59e 100644
--- a/datasette/__init__.py
+++ b/datasette/__init__.py
@@ -1,14 +1,8 @@
from datasette.permissions import Permission # noqa
from datasette.version import __version_info__, __version__ # noqa
from datasette.events import Event # noqa
-from datasette.tokens import TokenHandler, TokenInvalid, TokenRestrictions # noqa
-from datasette.utils.asgi import ( # noqa
- Forbidden,
- NotFound,
- PayloadTooLarge,
- Request,
- Response,
-)
+from datasette.tokens import TokenHandler, TokenRestrictions # noqa
+from datasette.utils.asgi import Forbidden, NotFound, Request, Response # noqa
from datasette.utils import actor_matches_allow # noqa
from datasette.views import Context # noqa
from .hookspecs import hookimpl # noqa
diff --git a/datasette/_pytest_plugin.py b/datasette/_pytest_plugin.py
index 103c616d..5fb6b473 100644
--- a/datasette/_pytest_plugin.py
+++ b/datasette/_pytest_plugin.py
@@ -19,38 +19,23 @@ import weakref
import pytest
+from datasette.app import Datasette
+
_active_instances: contextvars.ContextVar[list | None] = contextvars.ContextVar(
"datasette_active_instances", default=None
)
-_original_init = None
+_original_init = Datasette.__init__
-def _install_tracking():
- # datasette.app is imported lazily here rather than at module level:
- # as a pytest11 entry point this module is imported during pytest
- # startup, before pytest-cov starts measuring, so a module-level
- # import would drag in all of datasette and make every import-time
- # line in the package invisible to coverage
- global _original_init
- if _original_init is not None:
- return
- from datasette.app import Datasette
-
- _original_init = Datasette.__init__
-
- def _tracking_init(self, *args, **kwargs):
- _original_init(self, *args, **kwargs)
- instances = _active_instances.get()
- if instances is not None:
- instances.append(weakref.ref(self))
-
- Datasette.__init__ = _tracking_init
+def _tracking_init(self, *args, **kwargs):
+ _original_init(self, *args, **kwargs)
+ instances = _active_instances.get()
+ if instances is not None:
+ instances.append(weakref.ref(self))
-def pytest_configure(config):
- if _enabled(config):
- _install_tracking()
+Datasette.__init__ = _tracking_init
def pytest_addoption(parser):
diff --git a/datasette/app.py b/datasette/app.py
index 4ba5d20f..358081ef 100644
--- a/datasette/app.py
+++ b/datasette/app.py
@@ -2,7 +2,7 @@ from __future__ import annotations
import asyncio
import contextvars
-from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Sequence
+from typing import TYPE_CHECKING, Any, Dict, Iterable, List
if TYPE_CHECKING:
from datasette.permissions import Resource
@@ -12,6 +12,7 @@ import dataclasses
import datetime
import functools
import glob
+import hashlib
import httpx
import importlib.metadata
import inspect
@@ -34,7 +35,6 @@ from jinja2 import (
ChoiceLoader,
Environment,
FileSystemLoader,
- pass_context,
PrefixLoader,
)
from jinja2.environment import Template
@@ -42,36 +42,12 @@ from jinja2.exceptions import TemplateNotFound
from .events import Event
from .column_types import SQLiteType
-from . import stored_queries, write_sql
from .views import Context
-from .views.database import (
- database_download,
- DatabaseView,
- QueryView,
-)
-from .views.table_create_alter import (
- DatabaseForeignKeyTargetsView,
- TableAlterView,
- TableCreateView,
- TableForeignKeySuggestionsView,
-)
-from .views.execute_write import ExecuteWriteAnalyzeView, ExecuteWriteView
-from .views.stored_queries import (
- QueryCreateAnalyzeView,
- QueryDeleteView,
- QueryDefinitionView,
- QueryEditView,
- GlobalQueryListView,
- QueryListView,
- QueryParametersView,
- QueryStoreView,
- QueryUpdateView,
-)
+from .views.database import database_download, DatabaseView, TableCreateView, QueryView
from .views.index import IndexView
from .views.special import (
JsonDataView,
PatternPortfolioView,
- AutocompleteDebugView,
AuthTokenView,
ApiExplorerView,
CreateTokenView,
@@ -82,18 +58,16 @@ from .views.special import (
AllowedResourcesView,
PermissionRulesView,
PermissionCheckView,
- JumpView,
+ TablesView,
InstanceSchemaView,
DatabaseSchemaView,
TableSchemaView,
)
from .views.table import (
- TableAutocompleteView,
TableInsertView,
TableUpsertView,
TableSetColumnTypeView,
TableDropView,
- TableFragmentView,
table_view,
)
from .views.row import RowView, RowDeleteView, RowUpdateView
@@ -111,7 +85,6 @@ from .utils import (
baseconv,
call_with_supported_arguments,
detect_json1,
- add_cors_headers,
display_actor,
escape_css_string,
escape_sqlite,
@@ -123,7 +96,6 @@ from .utils import (
parse_metadata,
resolve_env_secrets,
resolve_routes,
- sha256_file,
tilde_decode,
tilde_encode,
to_css_class,
@@ -131,10 +103,8 @@ from .utils import (
redact_keys,
row_sql_params_pks,
)
-from .tokens import TokenInvalid
from .utils.asgi import (
AsgiLifespan,
- BadRequest,
Forbidden,
NotFound,
DatabaseNotFound,
@@ -209,11 +179,6 @@ SETTINGS = (
100,
"Maximum rows that can be inserted at a time using the bulk insert API",
),
- Setting(
- "max_post_body_bytes",
- 2 * 1024 * 1024,
- "Maximum size in bytes for a POST body read into memory, e.g. JSON API requests - set 0 to disable this limit",
- ),
Setting(
"num_sql_threads",
3,
@@ -308,21 +273,12 @@ DEFAULT_NOT_SET = object()
ResourcesSQL = collections.namedtuple("ResourcesSQL", ("sql", "params"))
-def _permission_cache_key(actor, action, parent, child):
- # Key on the full serialized actor so actors differing in any field
- # (e.g. token restrictions) never share cache entries
- actor_key = (
- json.dumps(actor, sort_keys=True, default=repr) if actor is not None else None
- )
- return (actor_key, action, parent, child)
-
-
async def favicon(request, send):
await asgi_send_file(
send,
str(FAVICON_PATH),
content_type="image/png",
- headers={"Cache-Control": "max-age=3600, public"},
+ headers={"Cache-Control": "max-age=3600, immutable, public"},
)
@@ -339,57 +295,6 @@ def _to_string(value):
return json.dumps(value, default=str)
-def _template_context_json_default(value):
- if dataclasses.is_dataclass(value) and not isinstance(value, type):
- return {
- field.name: getattr(value, field.name)
- for field in dataclasses.fields(value)
- }
- return repr(value)
-
-
-@pass_context
-def _legacy_template_csrftoken(context):
- request = context.get("request")
- if request and "csrftoken" in request.scope:
- return request.scope["csrftoken"]()
- return ""
-
-
-def _resolve_static_asset_path(root_path, path):
- root = Path(root_path).resolve()
- full_path = (root / path).resolve()
- try:
- full_path.relative_to(root)
- except ValueError:
- raise ValueError("Static asset path cannot escape static root") from None
- return full_path
-
-
-# Documentation for the variables Datasette.render_template() adds to the
-# context for every page. This is part of the documented template contract:
-# keys added in render_template() must be documented here - the contract
-# tests in tests/test_template_context.py enforce this, and the docs in
-# docs/template_context.rst are generated from it.
-TEMPLATE_BASE_CONTEXT = {
- "request": "The current :ref:`Request object `, or None. Common properties include ``request.path``, ``request.args``, ``request.actor``, ``request.url_vars`` and ``request.host``.",
- "crumb_items": 'Async function returning breadcrumb navigation items for the current page. Call it with ``request=request`` plus optional ``database=`` and ``table=`` arguments; it returns a list of ``{"href": url, "label": label}`` dictionaries.',
- "urls": "Object with methods for constructing URLs within Datasette. Common methods include ``urls.instance()``, ``urls.database(database)``, ``urls.table(database, table)``, ``urls.query(database, query)``, ``urls.row(database, table, row_path)`` and ``urls.static(path)`` - see :ref:`internals_datasette_urls`.",
- "actor": "The currently authenticated actor dictionary, or None. Actors usually include an ``id`` key and may include any other keys supplied by authentication plugins.",
- "menu_links": "Async function returning links for the Datasette application menu, including links added by plugins. Each item is a link dictionary with ``href`` and ``label`` keys. See :ref:`plugin_hook_menu_links`; for page action menus that can also include JavaScript-backed buttons, see :ref:`plugin_actions`.",
- "display_actor": "Function that accepts an actor dictionary and returns the display string used in the navigation menu.",
- "show_logout": "True if the logout link should be shown in the navigation menu",
- "zip": "Python's ``zip()`` builtin, made available to template logic",
- "body_scripts": 'List of JavaScript snippets contributed by plugins using :ref:`plugin_hook_extra_body_script`. Each item is a dictionary with ``script`` containing JavaScript source and ``module`` indicating whether Datasette will wrap it in ``
diff --git a/datasette/templates/_codemirror.html b/datasette/templates/_codemirror.html
index 75c16168..c4629aeb 100644
--- a/datasette/templates/_codemirror.html
+++ b/datasette/templates/_codemirror.html
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/datasette/templates/_facet_results.html b/datasette/templates/_facet_results.html
index 570bb37e..034e9678 100644
--- a/datasette/templates/_facet_results.html
+++ b/datasette/templates/_facet_results.html
@@ -12,9 +12,9 @@
{% for facet_value in facet_info.results %}
{% if not facet_value.selected %}
- - {{ (facet_value.label | string()) or "-" }} {{ "{:,}".format(facet_value.count) }}
+ - {{ (facet_value.label | string()) or "-" }} {{ "{:,}".format(facet_value.count) }}
{% else %}
- - {{ facet_value.label or "-" }} · {{ "{:,}".format(facet_value.count) }} ✖
+ - {{ facet_value.label or "-" }} · {{ "{:,}".format(facet_value.count) }} ✖
{% endif %}
{% endfor %}
{% if facet_info.truncated %}
diff --git a/datasette/templates/_query_form_styles.html b/datasette/templates/_query_form_styles.html
deleted file mode 100644
index cf2dd42c..00000000
--- a/datasette/templates/_query_form_styles.html
+++ /dev/null
@@ -1,138 +0,0 @@
-
diff --git a/datasette/templates/_query_results.html b/datasette/templates/_query_results.html
deleted file mode 100644
index 5e1e2f72..00000000
--- a/datasette/templates/_query_results.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% if display_rows %}
-
-
-
- {% for column in columns %}| {{ column }} | {% endfor %}
-
-
-
- {% for row in display_rows %}
-
- {% for column, td in zip(columns, row) %}
- | {{ td }} |
- {% endfor %}
-
- {% endfor %}
-
-
-{% elif show_zero_results %}
- 0 results
-{% endif %}
diff --git a/datasette/templates/_sql_parameter_scripts.html b/datasette/templates/_sql_parameter_scripts.html
deleted file mode 100644
index 9b83889e..00000000
--- a/datasette/templates/_sql_parameter_scripts.html
+++ /dev/null
@@ -1,307 +0,0 @@
-
diff --git a/datasette/templates/_sql_parameter_styles.html b/datasette/templates/_sql_parameter_styles.html
deleted file mode 100644
index bc6838f5..00000000
--- a/datasette/templates/_sql_parameter_styles.html
+++ /dev/null
@@ -1,58 +0,0 @@
-
diff --git a/datasette/templates/_sql_parameters.html b/datasette/templates/_sql_parameters.html
deleted file mode 100644
index b5c1bde8..00000000
--- a/datasette/templates/_sql_parameters.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% set sql_parameter_name_prefix = sql_parameter_name_prefix|default("") %}
-
- {% if parameter_names %}
-
Parameters
- {% for parameter in parameter_names %}
- {% set parameter_id = (sql_parameter_id_prefix|default("qp")) ~ loop.index %}
-
{% if sql_parameters_allow_expand|default(false) %} {% endif %}
- {% endfor %}
- {% endif %}
-
diff --git a/datasette/templates/_table.html b/datasette/templates/_table.html
index 171b6442..ba34b60f 100644
--- a/datasette/templates/_table.html
+++ b/datasette/templates/_table.html
@@ -1,6 +1,6 @@
-{% if display_columns %}
+{% if display_rows %}
@@ -22,7 +22,7 @@
{% for row in display_rows %}
-
+
{% for cell in row %}
| {{ cell.value }} |
{% endfor %}
@@ -31,7 +31,6 @@
-{% endif %}
-{% if not display_rows %}
+{% else %}
0 records
{% endif %}
diff --git a/datasette/templates/api_explorer.html b/datasette/templates/api_explorer.html
index 4927cb8d..dc393c20 100644
--- a/datasette/templates/api_explorer.html
+++ b/datasette/templates/api_explorer.html
@@ -3,7 +3,7 @@
{% block title %}API Explorer{% endblock %}
{% block extra_head %}
-
+
{% endblock %}
{% block content %}
@@ -19,7 +19,7 @@
GET
-