@@ -1314,6 +1314,50 @@ def test_doublequote(self):
13141314 dialect = sniffer .sniff (self .sample9 )
13151315 self .assertTrue (dialect .doublequote )
13161316
1317+ def test_sniff_regex_backtracking (self ):
1318+ # gh-109638: this artificial sample used to take minutes.
1319+ sniffer = csv .Sniffer ()
1320+ sample = '"",' * 100 + '"' * 100 + '0' + '"' * 100 + '0'
1321+ self .assertEqual (sniffer .sniff (sample ).delimiter , ',' )
1322+
1323+ def test_sniff_doublequote_across_fields (self ):
1324+ # A quoted field which contains the delimiter, followed by
1325+ # an empty quoted field, is not a doubled quote.
1326+ sniffer = csv .Sniffer ()
1327+ sample = '",","",","\n ' * 4
1328+ dialect = sniffer .sniff (sample )
1329+ self .assertEqual (dialect .delimiter , ',' )
1330+ self .assertEqual (dialect .quotechar , '"' )
1331+ self .assertIs (dialect .doublequote , False )
1332+ self .assertEqual (next (csv .reader (StringIO (sample ), dialect )),
1333+ [',' , '' , ',' ])
1334+
1335+ def test_sniff_doublequote_record_separators (self ):
1336+ # The record separator ends a field as a delimiter does.
1337+ sniffer = csv .Sniffer ()
1338+ for sep in '\n ' , '\r \n ' , '\r ' :
1339+ with self .subTest (sep = sep ):
1340+ sample = ('x,"a""b"' + sep + 'y,"c"' + sep ) * 2
1341+ self .assertIs (sniffer .sniff (sample ).doublequote , True )
1342+ sample = ('"",","' + sep ) * 4
1343+ self .assertIs (sniffer .sniff (sample ).doublequote , False )
1344+
1345+ def test_sniff_single_column (self ):
1346+ # This sample used to be quadratic.
1347+ sniffer = csv .Sniffer ()
1348+ sample = '"a"\n ' + ' ' * 100000
1349+ with self .assertRaisesRegex (csv .Error , "Could not determine delimiter" ):
1350+ sniffer .sniff (sample , delimiters = ',;' )
1351+
1352+ def test_sniff_space_delimiter (self ):
1353+ # This sample used to be quadratic.
1354+ sniffer = csv .Sniffer ()
1355+ sample = '"a" "b"\n ' + ' ' * 100000
1356+ dialect = sniffer .sniff (sample )
1357+ self .assertEqual (dialect .delimiter , ' ' )
1358+ self .assertIs (dialect .doublequote , False )
1359+
1360+
13171361class NUL :
13181362 def write (s , * args ):
13191363 pass
0 commit comments