You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
rows_from_file() reads CSV/TSV through a text wrapper opened in universal-newline mode, so a carriage return inside a quoted field is silently rewritten to a line feed before csv ever sees it.
The same happens through the CLI, so it is not just a library-level issue:
$ sqlite-utils rows t.db t -c id -c v --csv | od -c
0000000 i d , v \r \n 9 , " a \r \n b " \r \n
sqlite-utils insert new.db t out.csv --csv --pk id round-trips "a\r\nb" as "a\nb", so a rows --csv piped into insert --csv loses the byte with no warning.
The --nl format is unaffected, because JSON escapes control characters — which is why the formats disagree about the same data:
io.TextIOWrapper defaults to newline=None, which enables universal newlines: \r\n and lone \r are translated to \n as the stream is read. That happens underneath csv, so by the time csv.DictReader gets the text the carriage return has already been destroyed. The csv module documents the opposite requirement — "If csvfile is a file object, it should be opened with newline=''" — precisely so that newlines inside quoted fields survive.
The writer side is fine: csv.writer(sys.stdout, ...) emits the field intact. It is only the read path that is affected, so the loss is easy to miss — the file on disk looks correct.
This also makes rows_from_file() agree with the documented csv behaviour for TSV and for the --delimiter/--quotechar variants that funnel through the same helper.
Happy to send a PR with a regression test if that is useful — I did not want to open one unasked, but I can say the fix is a single keyword argument.
Activity
Preserve carriage returns in quoted CSV/TSV fields (simonw#895)