mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-10-27 21:37:24 +01:00
Compare commits
6 commits
main
...
sqlite-uti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcf8b564ee | ||
|
|
829aa1438c | ||
|
|
d7b39f7393 | ||
|
|
8541829c2d | ||
|
|
cf6a12568b | ||
|
|
c9d8a12a7e |
7 changed files with 8 additions and 92 deletions
2
.github/workflows/test-coverage.yml
vendored
2
.github/workflows/test-coverage.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
|
|
@ -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: |-
|
||||
|
|
|
|||
5
Justfile
5
Justfile
|
|
@ -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}}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -380,13 +380,9 @@ 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)
|
||||
else:
|
||||
dialect = csv.Sniffer().sniff(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue