Skip to content

[3.15] gh-98820: Fix quadratic time in csv.Sniffer for quoted fields - #154867

Open
serhiy-storchaka wants to merge 1 commit into
python:3.15from
serhiy-storchaka:sniff-315-gh98820
Open

[3.15] gh-98820: Fix quadratic time in csv.Sniffer for quoted fields#154867
serhiy-storchaka wants to merge 1 commit into
python:3.15from
serhiy-storchaka:sniff-315-gh98820

Conversation

@serhiy-storchaka

@serhiy-storchaka serhiy-storchaka commented Jul 29, 2026

Copy link
Copy Markdown
Member

The four regular expressions which look for a quoted field match its body lazily. A closing quote which is not followed by a delimiter is therefore retried with every following quote, to the very end of the sample, and findall() repeats that from every start position -- so the search is quadratic. On a single column of quoted fields none of them ever matches, so the whole cost is a failing scan.

>>> sample = '"abcdefghijklmnopqrstuvwxyz"\n' * 30000
>>> csv.Sniffer().sniff(sample, delimiters=',:|\t')   # minutes

The body now ends at the first quote which is not doubled, which is the only closing quote a reader would accept, and is matched possessively.

rows      before      after
 4000    3.1264 s    0.0427 s
30000        --      0.3190 s

A minimal reproducer needs no quoted fields at all -- ',"x' * n is quadratic too, because two of the four patterns are anchored on a delimiter rather than a line.

Being unambiguous is what makes it linear, so a few matches necessarily change: 16 of 560 files in the CSVsniffer corpora. Against their ground truth the net is positive -- delimiter 73.3% -> 74.5%, quotechar 81.3% -> 81.1%, doublequote unchanged.

main is not affected: the sniffer was rewritten there in gh-83273.

The regular expressions which look for a quoted field matched its body
lazily, so a closing quote which was not followed by a delimiter was
retried with every following quote, to the end of the sample.  Match
the body possessively instead: it ends at the first quote which is not
doubled, as it does for a reader.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant