Compare commits

..

6 commits

Author SHA1 Message Date
Simon Willison
bcf8b564ee Only run uv smoke test on Ubuntu
Refs https://github.com/simonw/sqlite-utils/pull/843#issuecomment-5287666426
2026-08-13 16:46:31 -07:00
Simon Willison
829aa1438c Remove from typing_extensions import Self, refs #842 2026-08-13 16:39:47 -07:00
Simon Willison
d7b39f7393 install uv first 2026-08-13 16:39:38 -07:00
Simon Willison
8541829c2d Revert "Remove from typing_extensions import Self, refs #842"
This reverts commit cf6a12568b.
2026-08-13 16:39:19 -07:00
Simon Willison
cf6a12568b Remove from typing_extensions import Self, refs #842 2026-08-13 16:38:58 -07:00
Simon Willison
c9d8a12a7e Test that should fail
Refs https://github.com/simonw/sqlite-utils/issues/842#issuecomment-5287583061
2026-08-13 16:37:06 -07:00
7 changed files with 8 additions and 92 deletions

View file

@ -20,7 +20,7 @@ jobs:
cache: pip
cache-dependency-path: pyproject.toml
- name: Install SpatiaLite
run: sudo apt-get update && sudo apt-get install -y libsqlite3-mod-spatialite
run: sudo apt-get install libsqlite3-mod-spatialite
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip

View file

@ -10,18 +10,18 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15-dev"]
numpy: [0, 1]
os: [ubuntu-latest, macos-latest, windows-latest, macos-14]
steps:
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v7
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
cache: pip
cache-dependency-path: pyproject.toml
check-latest: true
- name: Install dependencies
run: |
pip install . --group dev
@ -30,7 +30,7 @@ jobs:
run: pip install numpy
- name: Install SpatiaLite
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y libsqlite3-mod-spatialite
run: sudo apt-get install libsqlite3-mod-spatialite
- name: Build extension for --load-extension test
if: matrix.os == 'ubuntu-latest'
run: |-

View file

@ -2,12 +2,9 @@
@default: test lint
# Run pytest with supplied options
@test *options: test-no-dev-dependencies
@test *options:
uv run pytest {{options}}
@test-no-dev-dependencies:
uv run --isolated --no-default-groups sqlite-utils --help > /dev/null
@run *options:
uv run -- {{options}}

View file

@ -4,13 +4,6 @@
Changelog
===========
.. _v4_2_1:
4.2.1 (2026-08-13)
------------------
- Fix for ``No module named 'typing_extensions'`` crashing bug accidentally shipped in version 4.2. (:issue:`842`)
.. _v4_2:
4.2 (2026-08-13)

View file

@ -1,6 +1,6 @@
[project]
name = "sqlite-utils"
version = "4.2.1"
version = "4.2"
description = "CLI tool and Python library for manipulating SQLite databases"
readme = { file = "README.md", content-type = "text/markdown" }
authors = [

View file

@ -380,14 +380,10 @@ def rows_from_file(
"rows_from_file() requires a file-like object that supports peek(), such as io.BytesIO"
)
if not first_bytes:
buffered.close()
return (), Format.CSV
if first_bytes.startswith((b"[", b"{")):
# JSON is read eagerly, so the detection wrapper can close now,
# including when parsing raises an error.
# TODO: Detect newline-JSON
with buffered:
return rows_from_file(buffered, format=Format.JSON)
return rows_from_file(buffered, format=Format.JSON)
else:
dialect = csv.Sniffer().sniff(
first_bytes.decode(encoding or "utf-8-sig", "ignore")

View file

@ -1,5 +1,3 @@
import io
import json
from io import BytesIO, StringIO
import pytest
@ -63,71 +61,3 @@ def test_rows_from_file_error_on_string_io():
assert ex.value.args == (
"rows_from_file() requires a file-like object that supports peek(), such as io.BytesIO",
)
@pytest.fixture
def buffered_readers(monkeypatch):
# Keep wrappers alive so these checks cannot pass due to garbage collection.
readers = []
original = io.BufferedReader
def buffered_reader(*args, **kwargs):
reader = original(*args, **kwargs)
readers.append(reader)
return reader
monkeypatch.setattr(io, "BufferedReader", buffered_reader)
yield readers
for reader in readers:
reader.close()
@pytest.mark.parametrize(
"content, expected_format, expected_rows",
[
(b'[{"id": 1}]', Format.JSON, [{"id": 1}]),
(b'{"id": 1}', Format.JSON, [{"id": 1}]),
(b"[]", Format.JSON, []),
(b"", Format.CSV, []),
(b" \n\t", Format.CSV, []),
],
)
def test_detect_format_closes_eager_reader(
tmp_path, buffered_readers, content, expected_format, expected_rows
):
path = tmp_path / "input"
path.write_bytes(content)
with path.open("rb") as fp:
rows, detected = rows_from_file(fp)
assert detected == expected_format
assert list(rows) == expected_rows
assert len(buffered_readers) == 1
assert buffered_readers[0].closed
@pytest.mark.parametrize("content", [b"[", b'{"id":'])
def test_detect_format_closes_reader_on_invalid_json(
tmp_path, buffered_readers, content
):
path = tmp_path / "input.json"
path.write_bytes(content)
with path.open("rb") as fp:
with pytest.raises(json.JSONDecodeError):
rows_from_file(fp)
assert len(buffered_readers) == 1
assert buffered_readers[0].closed
@pytest.mark.parametrize(
"delimiter, expected_format", [(b",", Format.CSV), (b"\t", Format.TSV)]
)
def test_detect_format_keeps_streaming_reader_open(
buffered_readers, delimiter, expected_format
):
content = b"id" + delimiter + b"name\n" + (b"1" + delimiter + b"Cleo\n") * 2000
rows, detected = rows_from_file(BytesIO(content))
assert detected == expected_format
assert len(buffered_readers) == 1
assert not buffered_readers[0].closed
assert len(list(rows)) == 2000
assert buffered_readers[0].closed