diff --git a/.github/actions/setup-sqlite-version/action.yml b/.github/actions/setup-sqlite-version/action.yml
deleted file mode 100644
index fdbc71c..0000000
--- 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 0df7290..0000000
--- 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.23.1)
- sqlite_version="3.23.1"
- sqlite_year="2018"
- amalgamation_id="3230100"
- builtin_fallback_urls="https://static.simonwillison.net/static/2026/sqlite-amalgamation-3230100.zip"
- ;;
- *)
- 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/publish.yml b/.github/workflows/publish.yml
index 23b23bd..d5a0b69 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -9,19 +9,24 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- cache: pip
- cache-dependency-path: pyproject.toml
+ - uses: actions/cache@v3
+ name: Configure pip caching
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-
- name: Install dependencies
run: |
- pip install . --group dev
+ pip install -e '.[test,tui]'
- name: Run tests
run: |
pytest
@@ -29,20 +34,25 @@ jobs:
runs-on: ubuntu-latest
needs: [test]
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v4
with:
- python-version: '3.14'
- cache: pip
- cache-dependency-path: pyproject.toml
+ python-version: '3.11'
+ - uses: actions/cache@v3
+ name: Configure pip caching
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-publish-pip-${{ hashFiles('**/setup.py') }}
+ restore-keys: |
+ ${{ runner.os }}-publish-pip-
- name: Install dependencies
run: |
- pip install build twine
+ pip install setuptools wheel twine
- name: Publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: |
- python -m build
+ python setup.py sdist bdist_wheel
twine upload dist/*
diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml
index 2afa7b7..8a86cd2 100644
--- a/.github/workflows/spellcheck.yml
+++ b/.github/workflows/spellcheck.yml
@@ -6,16 +6,21 @@ jobs:
spellcheck:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - name: Set up Python
- uses: actions/setup-python@v5
+ - uses: actions/checkout@v2
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
with:
- python-version: "3.12"
- cache: pip
- cache-dependency-path: pyproject.toml
+ python-version: 3.9
+ - uses: actions/cache@v2
+ name: Configure pip caching
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-
- name: Install dependencies
run: |
- pip install . --group docs
+ pip install -e '.[docs]'
- name: Check spelling
run: |
codespell docs/*.rst --ignore-words docs/codespell-ignore-words.txt
diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml
index 7668f1b..0681f66 100644
--- a/.github/workflows/test-coverage.yml
+++ b/.github/workflows/test-coverage.yml
@@ -12,19 +12,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repo
- uses: actions/checkout@v7
+ uses: actions/checkout@v2
- name: Set up Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v2
with:
- python-version: "3.11"
- cache: pip
- cache-dependency-path: pyproject.toml
+ python-version: 3.9
+ - uses: actions/cache@v2
+ name: Configure pip caching
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-
- name: Install SpatiaLite
run: sudo apt-get install libsqlite3-mod-spatialite
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
- python -m pip install . --group dev
+ python -m pip install -e .[test]
python -m pip install pytest-cov
- name: Run tests
run: |-
diff --git a/.github/workflows/test-sqlite-support.yml b/.github/workflows/test-sqlite-support.yml
deleted file mode 100644
index f195cba..0000000
--- a/.github/workflows/test-sqlite-support.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-name: Test SQLite versions
-
-on: [push, pull_request]
-
-permissions:
- contents: read
-
-jobs:
- test:
- runs-on: ${{ matrix.platform }}
- continue-on-error: true
- strategy:
- matrix:
- platform: [ubuntu-latest]
- python-version: ["3.10"]
- sqlite-version: [
- "3.46",
- "3.23.1", # 2018-04-10, before UPSERT
- ]
- steps:
- - uses: actions/checkout@v7
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v6
- with:
- python-version: ${{ matrix.python-version }}
- allow-prereleases: true
- cache: pip
- cache-dependency-path: pyproject.toml
- - name: Set up SQLite ${{ matrix.sqlite-version }}
- uses: ./.github/actions/setup-sqlite-version
- 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"
- - run: python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
- - name: Install dependencies
- run: |
- pip install . --group dev
- pip freeze
- - name: Run tests
- run: |
- python -m pytest
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 923de2e..ab30b2d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,21 +10,25 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15-dev"]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
numpy: [0, 1]
- os: [ubuntu-latest, macos-latest, windows-latest, macos-14]
+ os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- allow-prereleases: true
- cache: pip
- cache-dependency-path: pyproject.toml
+ - uses: actions/cache@v3
+ name: Configure pip caching
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-
- name: Install dependencies
run: |
- pip install . --group dev
+ pip install -e '.[test,mypy,flake8,tui]'
- name: Optionally install numpy
if: matrix.numpy == 1
run: pip install numpy
@@ -38,20 +42,13 @@ jobs:
- name: Run tests
run: |
pytest -v
- - name: Run autocommit tests just on 3.14/Ubuntu
- if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.14'
- run: pytest --sqlite-autocommit
- name: run mypy
run: mypy sqlite_utils tests
- - name: run flake8
+ - name: run flake8 if Python 3.8 or higher
+ if: matrix.python-version >= 3.8
run: flake8
- - name: run ty
- if: matrix.os != 'windows-latest' && matrix.python-version == '3.14'
- run: |
- pip install uv
- uv run ty check sqlite_utils
- name: Check formatting
run: black . --check
- name: Check if cog needs to be run
run: |
- cog --check --diff README.md docs/*.rst
+ cog --check README.md docs/*.rst
diff --git a/.gitignore b/.gitignore
index 5b5d2c6..7947f33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
.venv
-dist
build
*.db
__pycache__/
@@ -15,10 +14,9 @@ venv
.schema
.vscode
.hypothesis
-.claude/
Pipfile
Pipfile.lock
-uv.lock
+pyproject.toml
tests/*.dylib
tests/*.so
tests/*.dll
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 0000000..fe8ba1e
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,2 @@
+tasks:
+- init: pip install '.[test]'
\ No newline at end of file
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 08c2f81..aa15c7c 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -4,14 +4,13 @@ sphinx:
configuration: docs/conf.py
build:
- os: ubuntu-24.04
+ os: ubuntu-22.04
tools:
- python: "3.13"
- jobs:
- install:
- - pip install --upgrade pip
- - pip install . --group docs
+ python: "3.11"
-formats:
-- pdf
-- epub
+python:
+ install:
+ - method: pip
+ path: .
+ extra_requirements:
+ - docs
diff --git a/Justfile b/Justfile
index be41523..66863f6 100644
--- a/Justfile
+++ b/Justfile
@@ -1,34 +1,29 @@
# Run tests and linters
@default: test lint
+# Setup project
+@init:
+ pipenv run pip install -e '.[test,docs,mypy,flake8]'
+
# Run pytest with supplied options
@test *options:
- uv run pytest {{options}}
+ pipenv run pytest {{options}}
-@run *options:
- uv run -- {{options}}
-
-# Run linters: black, flake8, mypy, ty, cog
+# Run linters: black, flake8, mypy, cog
@lint:
- just run black . --check
- uv run flake8
- uv run mypy sqlite_utils tests
- uv run ty check sqlite_utils
- uv run cog --check README.md docs/*.rst
- uv run --group docs codespell docs/*.rst --ignore-words docs/codespell-ignore-words.txt
- uv run --group docs codespell sqlite_utils --ignore-words docs/codespell-ignore-words.txt
+ pipenv run black . --check
+ pipenv run flake8
+ pipenv run mypy sqlite_utils tests
+ pipenv run cog --check README.md docs/*.rst
# Rebuild docs with cog
@cog:
- uv run --group docs cog -r README.md docs/*.rst
+ pipenv run cog -r README.md docs/*.rst
# Serve live docs on localhost:8000
@docs: cog
- #!/usr/bin/env bash
- cd docs
- uv run --group docs make livehtml
-
+ cd docs && pipenv run make livehtml
# Apply Black
@black:
- uv run black .
+ pipenv run black .
diff --git a/README.md b/README.md
index c444c64..49a26ed 100644
--- a/README.md
+++ b/README.md
@@ -18,12 +18,8 @@ Python CLI utility and library for manipulating SQLite databases.
- [Configure SQLite full-text search](https://sqlite-utils.datasette.io/en/stable/cli.html#configuring-full-text-search) against your database tables and run search queries against them, ordered by relevance
- Run [transformations against your tables](https://sqlite-utils.datasette.io/en/stable/cli.html#transforming-tables) to make schema changes that SQLite `ALTER TABLE` does not directly support, such as changing the type of a column
- [Extract columns](https://sqlite-utils.datasette.io/en/stable/cli.html#extracting-columns-into-a-separate-table) into separate tables to better normalize your existing data
-- [Manage database migrations](https://sqlite-utils.datasette.io/en/stable/migrations.html) using Python migration files and the `sqlite-utils migrate` command
-- [Install plugins](https://sqlite-utils.datasette.io/en/stable/plugins.html) to add custom SQL functions and additional features
-Upgrading from sqlite-utils 3.x? See the [4.0 upgrade guide](https://sqlite-utils.datasette.io/en/stable/upgrading.html#upgrading-from-3-x-to-4-0).
-
-Read more on my blog, in this series of posts on [New features in sqlite-utils](https://simonwillison.net/series/sqlite-utils-features/) and other [entries tagged sqlite-utils](https://simonwillison.net/tags/sqlite-utils/).
+Read more on my blog, in this series of posts on [New features in sqlite-utils](https://simonwillison.net/series/sqlite-utils-features/) and other [entries tagged sqliteutils](https://simonwillison.net/tags/sqliteutils/).
## Installation
diff --git a/docs/_static/img/tui.png b/docs/_static/img/tui.png
new file mode 100644
index 0000000..04cbeb1
Binary files /dev/null and b/docs/_static/img/tui.png differ
diff --git a/docs/_templates/base.html b/docs/_templates/base.html
index a253a46..43c2003 100644
--- a/docs/_templates/base.html
+++ b/docs/_templates/base.html
@@ -7,11 +7,6 @@
{% block scripts %}
{{ super() }}
-