From 1ddc3f0400e0995497ad8eb9828cb9b6267a5299 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Jul 2026 14:31:28 +0000 Subject: [PATCH] fix rows_from_file() crash on empty or whitespace-only input When no format is specified, the auto-detect path calls csv.Sniffer().sniff() on the first bytes of the file. For empty or whitespace-only files, strip() produces an empty byte string, causing sniff("") to raise csv.Error. Return an empty iterator early when first_bytes is empty. --- sqlite_utils/utils.py | 2 ++ tests/test_rows_from_file.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/sqlite_utils/utils.py b/sqlite_utils/utils.py index b39b11764..685105df2 100644 --- a/sqlite_utils/utils.py +++ b/sqlite_utils/utils.py @@ -379,6 +379,8 @@ class Format(enum.Enum): raise TypeError( "rows_from_file() requires a file-like object that supports peek(), such as io.BytesIO" ) + if not first_bytes: + return iter([]), Format.CSV if first_bytes.startswith(b"[") or first_bytes.startswith(b"{"): # TODO: Detect newline-JSON return rows_from_file(buffered, format=Format.JSON) diff --git a/tests/test_rows_from_file.py b/tests/test_rows_from_file.py index a19fed6ee..12a55131d 100644 --- a/tests/test_rows_from_file.py +++ b/tests/test_rows_from_file.py @@ -52,3 +52,10 @@ 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.mark.parametrize("content", [b"", b" \n\n "]) +def test_rows_from_file_empty_file(content): + rows, fmt = rows_from_file(BytesIO(content)) + assert list(rows) == [] + assert fmt == Format.CSV