@@ -1447,6 +1447,50 @@ def test_doublequote(self):
14471447 dialect = sniffer .sniff (self .sample9 )
14481448 self .assertTrue (dialect .doublequote )
14491449
1450+ def test_sniff_regex_backtracking (self ):
1451+ # gh-109638: this artificial sample used to take minutes.
1452+ sniffer = csv .Sniffer ()
1453+ sample = '"",' * 100 + '"' * 100 + '0' + '"' * 100 + '0'
1454+ self .assertEqual (sniffer .sniff (sample ).delimiter , ',' )
1455+
1456+ def test_sniff_doublequote_across_fields (self ):
1457+ # A quoted field which contains the delimiter, followed by
1458+ # an empty quoted field, is not a doubled quote.
1459+ sniffer = csv .Sniffer ()
1460+ sample = '",","",","\n ' * 4
1461+ dialect = sniffer .sniff (sample )
1462+ self .assertEqual (dialect .delimiter , ',' )
1463+ self .assertEqual (dialect .quotechar , '"' )
1464+ self .assertIs (dialect .doublequote , False )
1465+ self .assertEqual (next (csv .reader (StringIO (sample ), dialect )),
1466+ [',' , '' , ',' ])
1467+
1468+ def test_sniff_doublequote_record_separators (self ):
1469+ # The record separator ends a field as a delimiter does.
1470+ sniffer = csv .Sniffer ()
1471+ for sep in '\n ' , '\r \n ' , '\r ' :
1472+ with self .subTest (sep = sep ):
1473+ sample = ('x,"a""b"' + sep + 'y,"c"' + sep ) * 2
1474+ self .assertIs (sniffer .sniff (sample ).doublequote , True )
1475+ sample = ('"",","' + sep ) * 4
1476+ self .assertIs (sniffer .sniff (sample ).doublequote , False )
1477+
1478+ def test_sniff_single_column (self ):
1479+ # This sample used to be quadratic.
1480+ sniffer = csv .Sniffer ()
1481+ sample = '"a"\n ' + ' ' * 100000
1482+ with self .assertRaisesRegex (csv .Error , "Could not determine delimiter" ):
1483+ sniffer .sniff (sample , delimiters = ',;' )
1484+
1485+ def test_sniff_space_delimiter (self ):
1486+ # This sample used to be quadratic.
1487+ sniffer = csv .Sniffer ()
1488+ sample = '"a" "b"\n ' + ' ' * 100000
1489+ dialect = sniffer .sniff (sample )
1490+ self .assertEqual (dialect .delimiter , ' ' )
1491+ self .assertIs (dialect .doublequote , False )
1492+
1493+
14501494class NUL :
14511495 def write (s , * args ):
14521496 pass
0 commit comments