mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-10-28 06:07:09 +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: pip
|
||||||
cache-dependency-path: pyproject.toml
|
cache-dependency-path: pyproject.toml
|
||||||
- name: Install SpatiaLite
|
- 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
|
- name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
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 }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
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]
|
numpy: [0, 1]
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest, macos-14]
|
os: [ubuntu-latest, macos-latest, windows-latest, macos-14]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v7
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v7
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
allow-prereleases: true
|
||||||
cache: pip
|
cache: pip
|
||||||
cache-dependency-path: pyproject.toml
|
cache-dependency-path: pyproject.toml
|
||||||
check-latest: true
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
pip install . --group dev
|
pip install . --group dev
|
||||||
|
|
@ -30,7 +30,7 @@ jobs:
|
||||||
run: pip install numpy
|
run: pip install numpy
|
||||||
- name: Install SpatiaLite
|
- name: Install SpatiaLite
|
||||||
if: matrix.os == 'ubuntu-latest'
|
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
|
- name: Build extension for --load-extension test
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: |-
|
run: |-
|
||||||
|
|
|
||||||
5
Justfile
5
Justfile
|
|
@ -2,12 +2,9 @@
|
||||||
@default: test lint
|
@default: test lint
|
||||||
|
|
||||||
# Run pytest with supplied options
|
# Run pytest with supplied options
|
||||||
@test *options: test-no-dev-dependencies
|
@test *options:
|
||||||
uv run pytest {{options}}
|
uv run pytest {{options}}
|
||||||
|
|
||||||
@test-no-dev-dependencies:
|
|
||||||
uv run --isolated --no-default-groups sqlite-utils --help > /dev/null
|
|
||||||
|
|
||||||
@run *options:
|
@run *options:
|
||||||
uv run -- {{options}}
|
uv run -- {{options}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,6 @@
|
||||||
Changelog
|
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:
|
.. _v4_2:
|
||||||
|
|
||||||
4.2 (2026-08-13)
|
4.2 (2026-08-13)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "sqlite-utils"
|
name = "sqlite-utils"
|
||||||
version = "4.2.1"
|
version = "4.2"
|
||||||
description = "CLI tool and Python library for manipulating SQLite databases"
|
description = "CLI tool and Python library for manipulating SQLite databases"
|
||||||
readme = { file = "README.md", content-type = "text/markdown" }
|
readme = { file = "README.md", content-type = "text/markdown" }
|
||||||
authors = [
|
authors = [
|
||||||
|
|
|
||||||
|
|
@ -380,13 +380,9 @@ def rows_from_file(
|
||||||
"rows_from_file() requires a file-like object that supports peek(), such as io.BytesIO"
|
"rows_from_file() requires a file-like object that supports peek(), such as io.BytesIO"
|
||||||
)
|
)
|
||||||
if not first_bytes:
|
if not first_bytes:
|
||||||
buffered.close()
|
|
||||||
return (), Format.CSV
|
return (), Format.CSV
|
||||||
if first_bytes.startswith((b"[", b"{")):
|
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
|
# TODO: Detect newline-JSON
|
||||||
with buffered:
|
|
||||||
return rows_from_file(buffered, format=Format.JSON)
|
return rows_from_file(buffered, format=Format.JSON)
|
||||||
else:
|
else:
|
||||||
dialect = csv.Sniffer().sniff(
|
dialect = csv.Sniffer().sniff(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import io
|
|
||||||
import json
|
|
||||||
from io import BytesIO, StringIO
|
from io import BytesIO, StringIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -63,71 +61,3 @@ def test_rows_from_file_error_on_string_io():
|
||||||
assert ex.value.args == (
|
assert ex.value.args == (
|
||||||
"rows_from_file() requires a file-like object that supports peek(), such as io.BytesIO",
|
"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