@@ -1564,6 +1564,50 @@ def test_zero_mode_tie_order_colon_first(self):
15641564 sniffer .sniff (sample )
15651565
15661566
1567+ def test_sniff_regex_backtracking (self ):
1568+ # gh-109638: this artificial sample used to take minutes.
1569+ sniffer = csv .Sniffer ()
1570+ sample = '"",' * 100 + '"' * 100 + '0' + '"' * 100 + '0'
1571+ self .assertEqual (sniffer .sniff (sample ).delimiter , ',' )
1572+
1573+ def test_sniff_doublequote_across_fields (self ):
1574+ # A quoted field which contains the delimiter, followed by
1575+ # an empty quoted field, is not a doubled quote.
1576+ sniffer = csv .Sniffer ()
1577+ sample = '",","",","\n ' * 4
1578+ dialect = sniffer .sniff (sample )
1579+ self .assertEqual (dialect .delimiter , ',' )
1580+ self .assertEqual (dialect .quotechar , '"' )
1581+ self .assertIs (dialect .doublequote , False )
1582+ self .assertEqual (next (csv .reader (StringIO (sample ), dialect )),
1583+ [',' , '' , ',' ])
1584+
1585+ def test_sniff_doublequote_record_separators (self ):
1586+ # The record separator ends a field as a delimiter does.
1587+ sniffer = csv .Sniffer ()
1588+ for sep in '\n ' , '\r \n ' , '\r ' :
1589+ with self .subTest (sep = sep ):
1590+ sample = ('x,"a""b"' + sep + 'y,"c"' + sep ) * 2
1591+ self .assertIs (sniffer .sniff (sample ).doublequote , True )
1592+ sample = ('"",","' + sep ) * 4
1593+ self .assertIs (sniffer .sniff (sample ).doublequote , False )
1594+
1595+ def test_sniff_single_column (self ):
1596+ # This sample used to be quadratic.
1597+ sniffer = csv .Sniffer ()
1598+ sample = '"a"\n ' + ' ' * 100000
1599+ with self .assertRaisesRegex (csv .Error , "Could not determine delimiter" ):
1600+ sniffer .sniff (sample , delimiters = ',;' )
1601+
1602+ def test_sniff_space_delimiter (self ):
1603+ # This sample used to be quadratic.
1604+ sniffer = csv .Sniffer ()
1605+ sample = '"a" "b"\n ' + ' ' * 100000
1606+ dialect = sniffer .sniff (sample )
1607+ self .assertEqual (dialect .delimiter , ' ' )
1608+ self .assertIs (dialect .doublequote , False )
1609+
1610+
15671611class NUL :
15681612 def write (s , * args ):
15691613 pass
0 commit comments