@@ -1189,6 +1189,50 @@ def test_doublequote(self):
11891189 dialect = sniffer .sniff (self .sample9 )
11901190 self .assertTrue (dialect .doublequote )
11911191
1192+ def test_sniff_regex_backtracking (self ):
1193+ # gh-109638: this artificial sample used to take minutes.
1194+ sniffer = csv .Sniffer ()
1195+ sample = '"",' * 100 + '"' * 100 + '0' + '"' * 100 + '0'
1196+ self .assertEqual (sniffer .sniff (sample ).delimiter , ',' )
1197+
1198+ def test_sniff_doublequote_across_fields (self ):
1199+ # A quoted field which contains the delimiter, followed by
1200+ # an empty quoted field, is not a doubled quote.
1201+ sniffer = csv .Sniffer ()
1202+ sample = '",","",","\n ' * 4
1203+ dialect = sniffer .sniff (sample )
1204+ self .assertEqual (dialect .delimiter , ',' )
1205+ self .assertEqual (dialect .quotechar , '"' )
1206+ self .assertIs (dialect .doublequote , False )
1207+ self .assertEqual (next (csv .reader (StringIO (sample ), dialect )),
1208+ [',' , '' , ',' ])
1209+
1210+ def test_sniff_doublequote_record_separators (self ):
1211+ # The record separator ends a field as a delimiter does.
1212+ sniffer = csv .Sniffer ()
1213+ for sep in '\n ' , '\r \n ' , '\r ' :
1214+ with self .subTest (sep = sep ):
1215+ sample = ('x,"a""b"' + sep + 'y,"c"' + sep ) * 2
1216+ self .assertIs (sniffer .sniff (sample ).doublequote , True )
1217+ sample = ('"",","' + sep ) * 4
1218+ self .assertIs (sniffer .sniff (sample ).doublequote , False )
1219+
1220+ def test_sniff_single_column (self ):
1221+ # This sample used to be quadratic.
1222+ sniffer = csv .Sniffer ()
1223+ sample = '"a"\n ' + ' ' * 100000
1224+ with self .assertRaisesRegex (csv .Error , "Could not determine delimiter" ):
1225+ sniffer .sniff (sample , delimiters = ',;' )
1226+
1227+ def test_sniff_space_delimiter (self ):
1228+ # This sample used to be quadratic.
1229+ sniffer = csv .Sniffer ()
1230+ sample = '"a" "b"\n ' + ' ' * 100000
1231+ dialect = sniffer .sniff (sample )
1232+ self .assertEqual (dialect .delimiter , ' ' )
1233+ self .assertIs (dialect .doublequote , False )
1234+
1235+
11921236class NUL :
11931237 def write (s , * args ):
11941238 pass
0 commit comments