1# Copyright (C) 2001,2002 Python Software Foundation 2# csv package unit tests 3 4import copy 5import sys 6import unittest 7from io import StringIO 8from tempfile import TemporaryFile 9import csv 10import gc 11import pickle 12from test import support 13from itertools import permutations 14from textwrap import dedent 15from collections import OrderedDict 16 17class Test_Csv(unittest.TestCase): 18 """ 19 Test the underlying C csv parser in ways that are not appropriate 20 from the high level interface. Further tests of this nature are done 21 in TestDialectRegistry. 22 """ 23 def _test_arg_valid(self, ctor, arg): 24 self.assertRaises(TypeError, ctor) 25 self.assertRaises(TypeError, ctor, None) 26 self.assertRaises(TypeError, ctor, arg, bad_attr = 0) 27 self.assertRaises(TypeError, ctor, arg, delimiter = 0) 28 self.assertRaises(TypeError, ctor, arg, delimiter = 'XX') 29 self.assertRaises(csv.Error, ctor, arg, 'foo') 30 self.assertRaises(TypeError, ctor, arg, delimiter=None) 31 self.assertRaises(TypeError, ctor, arg, delimiter=1) 32 self.assertRaises(TypeError, ctor, arg, quotechar=1) 33 self.assertRaises(TypeError, ctor, arg, lineterminator=None) 34 self.assertRaises(TypeError, ctor, arg, lineterminator=1) 35 self.assertRaises(TypeError, ctor, arg, quoting=None) 36 self.assertRaises(TypeError, ctor, arg, 37 quoting=csv.QUOTE_ALL, quotechar='') 38 self.assertRaises(TypeError, ctor, arg, 39 quoting=csv.QUOTE_ALL, quotechar=None) 40 41 def test_reader_arg_valid(self): 42 self._test_arg_valid(csv.reader, []) 43 44 def test_writer_arg_valid(self): 45 self._test_arg_valid(csv.writer, StringIO()) 46 47 def _test_default_attrs(self, ctor, *args): 48 obj = ctor(*args) 49 # Check defaults 50 self.assertEqual(obj.dialect.delimiter, ',') 51 self.assertEqual(obj.dialect.doublequote, True) 52 self.assertEqual(obj.dialect.escapechar, None) 53 self.assertEqual(obj.dialect.lineterminator, "\r\n") 54 self.assertEqual(obj.dialect.quotechar, '"') 55 self.assertEqual(obj.dialect.quoting, csv.QUOTE_MINIMAL) 56 self.assertEqual(obj.dialect.skipinitialspace, False) 57 self.assertEqual(obj.dialect.strict, False) 58 # Try deleting or changing attributes (they are read-only) 59 self.assertRaises(AttributeError, delattr, obj.dialect, 'delimiter') 60 self.assertRaises(AttributeError, setattr, obj.dialect, 'delimiter', ':') 61 self.assertRaises(AttributeError, delattr, obj.dialect, 'quoting') 62 self.assertRaises(AttributeError, setattr, obj.dialect, 63 'quoting', None) 64 65 def test_reader_attrs(self): 66 self._test_default_attrs(csv.reader, []) 67 68 def test_writer_attrs(self): 69 self._test_default_attrs(csv.writer, StringIO()) 70 71 def _test_kw_attrs(self, ctor, *args): 72 # Now try with alternate options 73 kwargs = dict(delimiter=':', doublequote=False, escapechar='\\', 74 lineterminator='\r', quotechar='*', 75 quoting=csv.QUOTE_NONE, skipinitialspace=True, 76 strict=True) 77 obj = ctor(*args, **kwargs) 78 self.assertEqual(obj.dialect.delimiter, ':') 79 self.assertEqual(obj.dialect.doublequote, False) 80 self.assertEqual(obj.dialect.escapechar, '\\') 81 self.assertEqual(obj.dialect.lineterminator, "\r") 82 self.assertEqual(obj.dialect.quotechar, '*') 83 self.assertEqual(obj.dialect.quoting, csv.QUOTE_NONE) 84 self.assertEqual(obj.dialect.skipinitialspace, True) 85 self.assertEqual(obj.dialect.strict, True) 86 87 def test_reader_kw_attrs(self): 88 self._test_kw_attrs(csv.reader, []) 89 90 def test_writer_kw_attrs(self): 91 self._test_kw_attrs(csv.writer, StringIO()) 92 93 def _test_dialect_attrs(self, ctor, *args): 94 # Now try with dialect-derived options 95 class dialect: 96 delimiter='-' 97 doublequote=False 98 escapechar='^' 99 lineterminator='$' 100 quotechar='#' 101 quoting=csv.QUOTE_ALL 102 skipinitialspace=True 103 strict=False 104 args = args + (dialect,) 105 obj = ctor(*args) 106 self.assertEqual(obj.dialect.delimiter, '-') 107 self.assertEqual(obj.dialect.doublequote, False) 108 self.assertEqual(obj.dialect.escapechar, '^') 109 self.assertEqual(obj.dialect.lineterminator, "$") 110 self.assertEqual(obj.dialect.quotechar, '#') 111 self.assertEqual(obj.dialect.quoting, csv.QUOTE_ALL) 112 self.assertEqual(obj.dialect.skipinitialspace, True) 113 self.assertEqual(obj.dialect.strict, False) 114 115 def test_reader_dialect_attrs(self): 116 self._test_dialect_attrs(csv.reader, []) 117 118 def test_writer_dialect_attrs(self): 119 self._test_dialect_attrs(csv.writer, StringIO()) 120 121 122 def _write_test(self, fields, expect, **kwargs): 123 with TemporaryFile("w+", newline='') as fileobj: 124 writer = csv.writer(fileobj, **kwargs) 125 writer.writerow(fields) 126 fileobj.seek(0) 127 self.assertEqual(fileobj.read(), 128 expect + writer.dialect.lineterminator) 129 130 def _write_error_test(self, exc, fields, **kwargs): 131 with TemporaryFile("w+", newline='') as fileobj: 132 writer = csv.writer(fileobj, **kwargs) 133 with self.assertRaises(exc): 134 writer.writerow(fields) 135 fileobj.seek(0) 136 self.assertEqual(fileobj.read(), '') 137 138 def test_write_arg_valid(self): 139 self._write_error_test(csv.Error, None) 140 self._write_test((), '') 141 self._write_test([None], '""') 142 self._write_error_test(csv.Error, [None], quoting = csv.QUOTE_NONE) 143 # Check that exceptions are passed up the chain 144 class BadList: 145 def __len__(self): 146 return 10; 147 def __getitem__(self, i): 148 if i > 2: 149 raise OSError 150 self._write_error_test(OSError, BadList()) 151 class BadItem: 152 def __str__(self): 153 raise OSError 154 self._write_error_test(OSError, [BadItem()]) 155 156 def test_write_bigfield(self): 157 # This exercises the buffer realloc functionality 158 bigstring = 'X' * 50000 159 self._write_test([bigstring,bigstring], '%s,%s' % \ 160 (bigstring, bigstring)) 161 162 def test_write_quoting(self): 163 self._write_test(['a',1,'p,q'], 'a,1,"p,q"') 164 self._write_error_test(csv.Error, ['a',1,'p,q'], 165 quoting = csv.QUOTE_NONE) 166 self._write_test(['a',1,'p,q'], 'a,1,"p,q"', 167 quoting = csv.QUOTE_MINIMAL) 168 self._write_test(['a',1,'p,q'], '"a",1,"p,q"', 169 quoting = csv.QUOTE_NONNUMERIC) 170 self._write_test(['a',1,'p,q'], '"a","1","p,q"', 171 quoting = csv.QUOTE_ALL) 172 self._write_test(['a\nb',1], '"a\nb","1"', 173 quoting = csv.QUOTE_ALL) 174 175 def test_write_escape(self): 176 self._write_test(['a',1,'p,q'], 'a,1,"p,q"', 177 escapechar='\\') 178 self._write_error_test(csv.Error, ['a',1,'p,"q"'], 179 escapechar=None, doublequote=False) 180 self._write_test(['a',1,'p,"q"'], 'a,1,"p,\\"q\\""', 181 escapechar='\\', doublequote = False) 182 self._write_test(['"'], '""""', 183 escapechar='\\', quoting = csv.QUOTE_MINIMAL) 184 self._write_test(['"'], '\\"', 185 escapechar='\\', quoting = csv.QUOTE_MINIMAL, 186 doublequote = False) 187 self._write_test(['"'], '\\"', 188 escapechar='\\', quoting = csv.QUOTE_NONE) 189 self._write_test(['a',1,'p,q'], 'a,1,p\\,q', 190 escapechar='\\', quoting = csv.QUOTE_NONE) 191 192 def test_write_iterable(self): 193 self._write_test(iter(['a', 1, 'p,q']), 'a,1,"p,q"') 194 self._write_test(iter(['a', 1, None]), 'a,1,') 195 self._write_test(iter([]), '') 196 self._write_test(iter([None]), '""') 197 self._write_error_test(csv.Error, iter([None]), quoting=csv.QUOTE_NONE) 198 self._write_test(iter([None, None]), ',') 199 200 def test_writerows(self): 201 class BrokenFile: 202 def write(self, buf): 203 raise OSError 204 writer = csv.writer(BrokenFile()) 205 self.assertRaises(OSError, writer.writerows, [['a']]) 206 207 with TemporaryFile("w+", newline='') as fileobj: 208 writer = csv.writer(fileobj) 209 self.assertRaises(TypeError, writer.writerows, None) 210 writer.writerows([['a','b'],['c','d']]) 211 fileobj.seek(0) 212 self.assertEqual(fileobj.read(), "a,b\r\nc,d\r\n") 213 214 @support.cpython_only 215 def test_writerows_legacy_strings(self): 216 import _testcapi 217 218 c = _testcapi.unicode_legacy_string('a') 219 with TemporaryFile("w+", newline='') as fileobj: 220 writer = csv.writer(fileobj) 221 writer.writerows([[c]]) 222 fileobj.seek(0) 223 self.assertEqual(fileobj.read(), "a\r\n") 224 225 def _read_test(self, input, expect, **kwargs): 226 reader = csv.reader(input, **kwargs) 227 result = list(reader) 228 self.assertEqual(result, expect) 229 230 def test_read_oddinputs(self): 231 self._read_test([], []) 232 self._read_test([''], [[]]) 233 self.assertRaises(csv.Error, self._read_test, 234 ['"ab"c'], None, strict = 1) 235 # cannot handle null bytes for the moment 236 self.assertRaises(csv.Error, self._read_test, 237 ['ab\0c'], None, strict = 1) 238 self._read_test(['"ab"c'], [['abc']], doublequote = 0) 239 240 self.assertRaises(csv.Error, self._read_test, 241 [b'ab\0c'], None) 242 243 244 def test_read_eol(self): 245 self._read_test(['a,b'], [['a','b']]) 246 self._read_test(['a,b\n'], [['a','b']]) 247 self._read_test(['a,b\r\n'], [['a','b']]) 248 self._read_test(['a,b\r'], [['a','b']]) 249 self.assertRaises(csv.Error, self._read_test, ['a,b\rc,d'], []) 250 self.assertRaises(csv.Error, self._read_test, ['a,b\nc,d'], []) 251 self.assertRaises(csv.Error, self._read_test, ['a,b\r\nc,d'], []) 252 253 def test_read_eof(self): 254 self._read_test(['a,"'], [['a', '']]) 255 self._read_test(['"a'], [['a']]) 256 self._read_test(['^'], [['\n']], escapechar='^') 257 self.assertRaises(csv.Error, self._read_test, ['a,"'], [], strict=True) 258 self.assertRaises(csv.Error, self._read_test, ['"a'], [], strict=True) 259 self.assertRaises(csv.Error, self._read_test, 260 ['^'], [], escapechar='^', strict=True) 261 262 def test_read_escape(self): 263 self._read_test(['a,\\b,c'], [['a', 'b', 'c']], escapechar='\\') 264 self._read_test(['a,b\\,c'], [['a', 'b,c']], escapechar='\\') 265 self._read_test(['a,"b\\,c"'], [['a', 'b,c']], escapechar='\\') 266 self._read_test(['a,"b,\\c"'], [['a', 'b,c']], escapechar='\\') 267 self._read_test(['a,"b,c\\""'], [['a', 'b,c"']], escapechar='\\') 268 self._read_test(['a,"b,c"\\'], [['a', 'b,c\\']], escapechar='\\') 269 270 def test_read_quoting(self): 271 self._read_test(['1,",3,",5'], [['1', ',3,', '5']]) 272 self._read_test(['1,",3,",5'], [['1', '"', '3', '"', '5']], 273 quotechar=None, escapechar='\\') 274 self._read_test(['1,",3,",5'], [['1', '"', '3', '"', '5']], 275 quoting=csv.QUOTE_NONE, escapechar='\\') 276 # will this fail where locale uses comma for decimals? 277 self._read_test([',3,"5",7.3, 9'], [['', 3, '5', 7.3, 9]], 278 quoting=csv.QUOTE_NONNUMERIC) 279 self._read_test(['"a\nb", 7'], [['a\nb', ' 7']]) 280 self.assertRaises(ValueError, self._read_test, 281 ['abc,3'], [[]], 282 quoting=csv.QUOTE_NONNUMERIC) 283 284 def test_read_bigfield(self): 285 # This exercises the buffer realloc functionality and field size 286 # limits. 287 limit = csv.field_size_limit() 288 try: 289 size = 50000 290 bigstring = 'X' * size 291 bigline = '%s,%s' % (bigstring, bigstring) 292 self._read_test([bigline], [[bigstring, bigstring]]) 293 csv.field_size_limit(size) 294 self._read_test([bigline], [[bigstring, bigstring]]) 295 self.assertEqual(csv.field_size_limit(), size) 296 csv.field_size_limit(size-1) 297 self.assertRaises(csv.Error, self._read_test, [bigline], []) 298 self.assertRaises(TypeError, csv.field_size_limit, None) 299 self.assertRaises(TypeError, csv.field_size_limit, 1, None) 300 finally: 301 csv.field_size_limit(limit) 302 303 def test_read_linenum(self): 304 r = csv.reader(['line,1', 'line,2', 'line,3']) 305 self.assertEqual(r.line_num, 0) 306 next(r) 307 self.assertEqual(r.line_num, 1) 308 next(r) 309 self.assertEqual(r.line_num, 2) 310 next(r) 311 self.assertEqual(r.line_num, 3) 312 self.assertRaises(StopIteration, next, r) 313 self.assertEqual(r.line_num, 3) 314 315 def test_roundtrip_quoteed_newlines(self): 316 with TemporaryFile("w+", newline='') as fileobj: 317 writer = csv.writer(fileobj) 318 self.assertRaises(TypeError, writer.writerows, None) 319 rows = [['a\nb','b'],['c','x\r\nd']] 320 writer.writerows(rows) 321 fileobj.seek(0) 322 for i, row in enumerate(csv.reader(fileobj)): 323 self.assertEqual(row, rows[i]) 324 325 def test_roundtrip_escaped_unquoted_newlines(self): 326 with TemporaryFile("w+", newline='') as fileobj: 327 writer = csv.writer(fileobj,quoting=csv.QUOTE_NONE,escapechar="\\") 328 rows = [['a\nb','b'],['c','x\r\nd']] 329 writer.writerows(rows) 330 fileobj.seek(0) 331 for i, row in enumerate(csv.reader(fileobj,quoting=csv.QUOTE_NONE,escapechar="\\")): 332 self.assertEqual(row,rows[i]) 333 334class TestDialectRegistry(unittest.TestCase): 335 def test_registry_badargs(self): 336 self.assertRaises(TypeError, csv.list_dialects, None) 337 self.assertRaises(TypeError, csv.get_dialect) 338 self.assertRaises(csv.Error, csv.get_dialect, None) 339 self.assertRaises(csv.Error, csv.get_dialect, "nonesuch") 340 self.assertRaises(TypeError, csv.unregister_dialect) 341 self.assertRaises(csv.Error, csv.unregister_dialect, None) 342 self.assertRaises(csv.Error, csv.unregister_dialect, "nonesuch") 343 self.assertRaises(TypeError, csv.register_dialect, None) 344 self.assertRaises(TypeError, csv.register_dialect, None, None) 345 self.assertRaises(TypeError, csv.register_dialect, "nonesuch", 0, 0) 346 self.assertRaises(TypeError, csv.register_dialect, "nonesuch", 347 badargument=None) 348 self.assertRaises(TypeError, csv.register_dialect, "nonesuch", 349 quoting=None) 350 self.assertRaises(TypeError, csv.register_dialect, []) 351 352 def test_registry(self): 353 class myexceltsv(csv.excel): 354 delimiter = "\t" 355 name = "myexceltsv" 356 expected_dialects = csv.list_dialects() + [name] 357 expected_dialects.sort() 358 csv.register_dialect(name, myexceltsv) 359 self.addCleanup(csv.unregister_dialect, name) 360 self.assertEqual(csv.get_dialect(name).delimiter, '\t') 361 got_dialects = sorted(csv.list_dialects()) 362 self.assertEqual(expected_dialects, got_dialects) 363 364 def test_register_kwargs(self): 365 name = 'fedcba' 366 csv.register_dialect(name, delimiter=';') 367 self.addCleanup(csv.unregister_dialect, name) 368 self.assertEqual(csv.get_dialect(name).delimiter, ';') 369 self.assertEqual([['X', 'Y', 'Z']], list(csv.reader(['X;Y;Z'], name))) 370 371 def test_incomplete_dialect(self): 372 class myexceltsv(csv.Dialect): 373 delimiter = "\t" 374 self.assertRaises(csv.Error, myexceltsv) 375 376 def test_space_dialect(self): 377 class space(csv.excel): 378 delimiter = " " 379 quoting = csv.QUOTE_NONE 380 escapechar = "\\" 381 382 with TemporaryFile("w+") as fileobj: 383 fileobj.write("abc def\nc1ccccc1 benzene\n") 384 fileobj.seek(0) 385 reader = csv.reader(fileobj, dialect=space()) 386 self.assertEqual(next(reader), ["abc", "def"]) 387 self.assertEqual(next(reader), ["c1ccccc1", "benzene"]) 388 389 def compare_dialect_123(self, expected, *writeargs, **kwwriteargs): 390 391 with TemporaryFile("w+", newline='', encoding="utf-8") as fileobj: 392 393 writer = csv.writer(fileobj, *writeargs, **kwwriteargs) 394 writer.writerow([1,2,3]) 395 fileobj.seek(0) 396 self.assertEqual(fileobj.read(), expected) 397 398 def test_dialect_apply(self): 399 class testA(csv.excel): 400 delimiter = "\t" 401 class testB(csv.excel): 402 delimiter = ":" 403 class testC(csv.excel): 404 delimiter = "|" 405 class testUni(csv.excel): 406 delimiter = "\u039B" 407 408 csv.register_dialect('testC', testC) 409 try: 410 self.compare_dialect_123("1,2,3\r\n") 411 self.compare_dialect_123("1\t2\t3\r\n", testA) 412 self.compare_dialect_123("1:2:3\r\n", dialect=testB()) 413 self.compare_dialect_123("1|2|3\r\n", dialect='testC') 414 self.compare_dialect_123("1;2;3\r\n", dialect=testA, 415 delimiter=';') 416 self.compare_dialect_123("1\u039B2\u039B3\r\n", 417 dialect=testUni) 418 419 finally: 420 csv.unregister_dialect('testC') 421 422 def test_bad_dialect(self): 423 # Unknown parameter 424 self.assertRaises(TypeError, csv.reader, [], bad_attr = 0) 425 # Bad values 426 self.assertRaises(TypeError, csv.reader, [], delimiter = None) 427 self.assertRaises(TypeError, csv.reader, [], quoting = -1) 428 self.assertRaises(TypeError, csv.reader, [], quoting = 100) 429 430 def test_copy(self): 431 for name in csv.list_dialects(): 432 dialect = csv.get_dialect(name) 433 self.assertRaises(TypeError, copy.copy, dialect) 434 435 def test_pickle(self): 436 for name in csv.list_dialects(): 437 dialect = csv.get_dialect(name) 438 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 439 self.assertRaises(TypeError, pickle.dumps, dialect, proto) 440 441class TestCsvBase(unittest.TestCase): 442 def readerAssertEqual(self, input, expected_result): 443 with TemporaryFile("w+", newline='') as fileobj: 444 fileobj.write(input) 445 fileobj.seek(0) 446 reader = csv.reader(fileobj, dialect = self.dialect) 447 fields = list(reader) 448 self.assertEqual(fields, expected_result) 449 450 def writerAssertEqual(self, input, expected_result): 451 with TemporaryFile("w+", newline='') as fileobj: 452 writer = csv.writer(fileobj, dialect = self.dialect) 453 writer.writerows(input) 454 fileobj.seek(0) 455 self.assertEqual(fileobj.read(), expected_result) 456 457class TestDialectExcel(TestCsvBase): 458 dialect = 'excel' 459 460 def test_single(self): 461 self.readerAssertEqual('abc', [['abc']]) 462 463 def test_simple(self): 464 self.readerAssertEqual('1,2,3,4,5', [['1','2','3','4','5']]) 465 466 def test_blankline(self): 467 self.readerAssertEqual('', []) 468 469 def test_empty_fields(self): 470 self.readerAssertEqual(',', [['', '']]) 471 472 def test_singlequoted(self): 473 self.readerAssertEqual('""', [['']]) 474 475 def test_singlequoted_left_empty(self): 476 self.readerAssertEqual('"",', [['','']]) 477 478 def test_singlequoted_right_empty(self): 479 self.readerAssertEqual(',""', [['','']]) 480 481 def test_single_quoted_quote(self): 482 self.readerAssertEqual('""""', [['"']]) 483 484 def test_quoted_quotes(self): 485 self.readerAssertEqual('""""""', [['""']]) 486 487 def test_inline_quote(self): 488 self.readerAssertEqual('a""b', [['a""b']]) 489 490 def test_inline_quotes(self): 491 self.readerAssertEqual('a"b"c', [['a"b"c']]) 492 493 def test_quotes_and_more(self): 494 # Excel would never write a field containing '"a"b', but when 495 # reading one, it will return 'ab'. 496 self.readerAssertEqual('"a"b', [['ab']]) 497 498 def test_lone_quote(self): 499 self.readerAssertEqual('a"b', [['a"b']]) 500 501 def test_quote_and_quote(self): 502 # Excel would never write a field containing '"a" "b"', but when 503 # reading one, it will return 'a "b"'. 504 self.readerAssertEqual('"a" "b"', [['a "b"']]) 505 506 def test_space_and_quote(self): 507 self.readerAssertEqual(' "a"', [[' "a"']]) 508 509 def test_quoted(self): 510 self.readerAssertEqual('1,2,3,"I think, therefore I am",5,6', 511 [['1', '2', '3', 512 'I think, therefore I am', 513 '5', '6']]) 514 515 def test_quoted_quote(self): 516 self.readerAssertEqual('1,2,3,"""I see,"" said the blind man","as he picked up his hammer and saw"', 517 [['1', '2', '3', 518 '"I see," said the blind man', 519 'as he picked up his hammer and saw']]) 520 521 def test_quoted_nl(self): 522 input = '''\ 5231,2,3,"""I see,"" 524said the blind man","as he picked up his 525hammer and saw" 5269,8,7,6''' 527 self.readerAssertEqual(input, 528 [['1', '2', '3', 529 '"I see,"\nsaid the blind man', 530 'as he picked up his\nhammer and saw'], 531 ['9','8','7','6']]) 532 533 def test_dubious_quote(self): 534 self.readerAssertEqual('12,12,1",', [['12', '12', '1"', '']]) 535 536 def test_null(self): 537 self.writerAssertEqual([], '') 538 539 def test_single_writer(self): 540 self.writerAssertEqual([['abc']], 'abc\r\n') 541 542 def test_simple_writer(self): 543 self.writerAssertEqual([[1, 2, 'abc', 3, 4]], '1,2,abc,3,4\r\n') 544 545 def test_quotes(self): 546 self.writerAssertEqual([[1, 2, 'a"bc"', 3, 4]], '1,2,"a""bc""",3,4\r\n') 547 548 def test_quote_fieldsep(self): 549 self.writerAssertEqual([['abc,def']], '"abc,def"\r\n') 550 551 def test_newlines(self): 552 self.writerAssertEqual([[1, 2, 'a\nbc', 3, 4]], '1,2,"a\nbc",3,4\r\n') 553 554class EscapedExcel(csv.excel): 555 quoting = csv.QUOTE_NONE 556 escapechar = '\\' 557 558class TestEscapedExcel(TestCsvBase): 559 dialect = EscapedExcel() 560 561 def test_escape_fieldsep(self): 562 self.writerAssertEqual([['abc,def']], 'abc\\,def\r\n') 563 564 def test_read_escape_fieldsep(self): 565 self.readerAssertEqual('abc\\,def\r\n', [['abc,def']]) 566 567class TestDialectUnix(TestCsvBase): 568 dialect = 'unix' 569 570 def test_simple_writer(self): 571 self.writerAssertEqual([[1, 'abc def', 'abc']], '"1","abc def","abc"\n') 572 573 def test_simple_reader(self): 574 self.readerAssertEqual('"1","abc def","abc"\n', [['1', 'abc def', 'abc']]) 575 576class QuotedEscapedExcel(csv.excel): 577 quoting = csv.QUOTE_NONNUMERIC 578 escapechar = '\\' 579 580class TestQuotedEscapedExcel(TestCsvBase): 581 dialect = QuotedEscapedExcel() 582 583 def test_write_escape_fieldsep(self): 584 self.writerAssertEqual([['abc,def']], '"abc,def"\r\n') 585 586 def test_read_escape_fieldsep(self): 587 self.readerAssertEqual('"abc\\,def"\r\n', [['abc,def']]) 588 589class TestDictFields(unittest.TestCase): 590 ### "long" means the row is longer than the number of fieldnames 591 ### "short" means there are fewer elements in the row than fieldnames 592 def test_write_simple_dict(self): 593 with TemporaryFile("w+", newline='') as fileobj: 594 writer = csv.DictWriter(fileobj, fieldnames = ["f1", "f2", "f3"]) 595 writer.writeheader() 596 fileobj.seek(0) 597 self.assertEqual(fileobj.readline(), "f1,f2,f3\r\n") 598 writer.writerow({"f1": 10, "f3": "abc"}) 599 fileobj.seek(0) 600 fileobj.readline() # header 601 self.assertEqual(fileobj.read(), "10,,abc\r\n") 602 603 def test_write_multiple_dict_rows(self): 604 fileobj = StringIO() 605 writer = csv.DictWriter(fileobj, fieldnames=["f1", "f2", "f3"]) 606 writer.writeheader() 607 self.assertEqual(fileobj.getvalue(), "f1,f2,f3\r\n") 608 writer.writerows([{"f1": 1, "f2": "abc", "f3": "f"}, 609 {"f1": 2, "f2": 5, "f3": "xyz"}]) 610 self.assertEqual(fileobj.getvalue(), 611 "f1,f2,f3\r\n1,abc,f\r\n2,5,xyz\r\n") 612 613 def test_write_no_fields(self): 614 fileobj = StringIO() 615 self.assertRaises(TypeError, csv.DictWriter, fileobj) 616 617 def test_write_fields_not_in_fieldnames(self): 618 with TemporaryFile("w+", newline='') as fileobj: 619 writer = csv.DictWriter(fileobj, fieldnames = ["f1", "f2", "f3"]) 620 # Of special note is the non-string key (issue 19449) 621 with self.assertRaises(ValueError) as cx: 622 writer.writerow({"f4": 10, "f2": "spam", 1: "abc"}) 623 exception = str(cx.exception) 624 self.assertIn("fieldnames", exception) 625 self.assertIn("'f4'", exception) 626 self.assertNotIn("'f2'", exception) 627 self.assertIn("1", exception) 628 629 def test_typo_in_extrasaction_raises_error(self): 630 fileobj = StringIO() 631 self.assertRaises(ValueError, csv.DictWriter, fileobj, ['f1', 'f2'], 632 extrasaction="raised") 633 634 def test_write_field_not_in_field_names_raise(self): 635 fileobj = StringIO() 636 writer = csv.DictWriter(fileobj, ['f1', 'f2'], extrasaction="raise") 637 dictrow = {'f0': 0, 'f1': 1, 'f2': 2, 'f3': 3} 638 self.assertRaises(ValueError, csv.DictWriter.writerow, writer, dictrow) 639 640 def test_write_field_not_in_field_names_ignore(self): 641 fileobj = StringIO() 642 writer = csv.DictWriter(fileobj, ['f1', 'f2'], extrasaction="ignore") 643 dictrow = {'f0': 0, 'f1': 1, 'f2': 2, 'f3': 3} 644 csv.DictWriter.writerow(writer, dictrow) 645 self.assertEqual(fileobj.getvalue(), "1,2\r\n") 646 647 def test_read_dict_fields(self): 648 with TemporaryFile("w+") as fileobj: 649 fileobj.write("1,2,abc\r\n") 650 fileobj.seek(0) 651 reader = csv.DictReader(fileobj, 652 fieldnames=["f1", "f2", "f3"]) 653 self.assertEqual(next(reader), {"f1": '1', "f2": '2', "f3": 'abc'}) 654 655 def test_read_dict_no_fieldnames(self): 656 with TemporaryFile("w+") as fileobj: 657 fileobj.write("f1,f2,f3\r\n1,2,abc\r\n") 658 fileobj.seek(0) 659 reader = csv.DictReader(fileobj) 660 self.assertEqual(next(reader), {"f1": '1', "f2": '2', "f3": 'abc'}) 661 self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"]) 662 663 # Two test cases to make sure existing ways of implicitly setting 664 # fieldnames continue to work. Both arise from discussion in issue3436. 665 def test_read_dict_fieldnames_from_file(self): 666 with TemporaryFile("w+") as fileobj: 667 fileobj.write("f1,f2,f3\r\n1,2,abc\r\n") 668 fileobj.seek(0) 669 reader = csv.DictReader(fileobj, 670 fieldnames=next(csv.reader(fileobj))) 671 self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"]) 672 self.assertEqual(next(reader), {"f1": '1', "f2": '2', "f3": 'abc'}) 673 674 def test_read_dict_fieldnames_chain(self): 675 import itertools 676 with TemporaryFile("w+") as fileobj: 677 fileobj.write("f1,f2,f3\r\n1,2,abc\r\n") 678 fileobj.seek(0) 679 reader = csv.DictReader(fileobj) 680 first = next(reader) 681 for row in itertools.chain([first], reader): 682 self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"]) 683 self.assertEqual(row, {"f1": '1', "f2": '2', "f3": 'abc'}) 684 685 def test_read_long(self): 686 with TemporaryFile("w+") as fileobj: 687 fileobj.write("1,2,abc,4,5,6\r\n") 688 fileobj.seek(0) 689 reader = csv.DictReader(fileobj, 690 fieldnames=["f1", "f2"]) 691 self.assertEqual(next(reader), {"f1": '1', "f2": '2', 692 None: ["abc", "4", "5", "6"]}) 693 694 def test_read_long_with_rest(self): 695 with TemporaryFile("w+") as fileobj: 696 fileobj.write("1,2,abc,4,5,6\r\n") 697 fileobj.seek(0) 698 reader = csv.DictReader(fileobj, 699 fieldnames=["f1", "f2"], restkey="_rest") 700 self.assertEqual(next(reader), {"f1": '1', "f2": '2', 701 "_rest": ["abc", "4", "5", "6"]}) 702 703 def test_read_long_with_rest_no_fieldnames(self): 704 with TemporaryFile("w+") as fileobj: 705 fileobj.write("f1,f2\r\n1,2,abc,4,5,6\r\n") 706 fileobj.seek(0) 707 reader = csv.DictReader(fileobj, restkey="_rest") 708 self.assertEqual(reader.fieldnames, ["f1", "f2"]) 709 self.assertEqual(next(reader), {"f1": '1', "f2": '2', 710 "_rest": ["abc", "4", "5", "6"]}) 711 712 def test_read_short(self): 713 with TemporaryFile("w+") as fileobj: 714 fileobj.write("1,2,abc,4,5,6\r\n1,2,abc\r\n") 715 fileobj.seek(0) 716 reader = csv.DictReader(fileobj, 717 fieldnames="1 2 3 4 5 6".split(), 718 restval="DEFAULT") 719 self.assertEqual(next(reader), {"1": '1', "2": '2', "3": 'abc', 720 "4": '4', "5": '5', "6": '6'}) 721 self.assertEqual(next(reader), {"1": '1', "2": '2', "3": 'abc', 722 "4": 'DEFAULT', "5": 'DEFAULT', 723 "6": 'DEFAULT'}) 724 725 def test_read_multi(self): 726 sample = [ 727 '2147483648,43.0e12,17,abc,def\r\n', 728 '147483648,43.0e2,17,abc,def\r\n', 729 '47483648,43.0,170,abc,def\r\n' 730 ] 731 732 reader = csv.DictReader(sample, 733 fieldnames="i1 float i2 s1 s2".split()) 734 self.assertEqual(next(reader), {"i1": '2147483648', 735 "float": '43.0e12', 736 "i2": '17', 737 "s1": 'abc', 738 "s2": 'def'}) 739 740 def test_read_with_blanks(self): 741 reader = csv.DictReader(["1,2,abc,4,5,6\r\n","\r\n", 742 "1,2,abc,4,5,6\r\n"], 743 fieldnames="1 2 3 4 5 6".split()) 744 self.assertEqual(next(reader), {"1": '1', "2": '2', "3": 'abc', 745 "4": '4', "5": '5', "6": '6'}) 746 self.assertEqual(next(reader), {"1": '1', "2": '2', "3": 'abc', 747 "4": '4', "5": '5', "6": '6'}) 748 749 def test_read_semi_sep(self): 750 reader = csv.DictReader(["1;2;abc;4;5;6\r\n"], 751 fieldnames="1 2 3 4 5 6".split(), 752 delimiter=';') 753 self.assertEqual(next(reader), {"1": '1', "2": '2', "3": 'abc', 754 "4": '4', "5": '5', "6": '6'}) 755 756class TestArrayWrites(unittest.TestCase): 757 def test_int_write(self): 758 import array 759 contents = [(20-i) for i in range(20)] 760 a = array.array('i', contents) 761 762 with TemporaryFile("w+", newline='') as fileobj: 763 writer = csv.writer(fileobj, dialect="excel") 764 writer.writerow(a) 765 expected = ",".join([str(i) for i in a])+"\r\n" 766 fileobj.seek(0) 767 self.assertEqual(fileobj.read(), expected) 768 769 def test_double_write(self): 770 import array 771 contents = [(20-i)*0.1 for i in range(20)] 772 a = array.array('d', contents) 773 with TemporaryFile("w+", newline='') as fileobj: 774 writer = csv.writer(fileobj, dialect="excel") 775 writer.writerow(a) 776 expected = ",".join([str(i) for i in a])+"\r\n" 777 fileobj.seek(0) 778 self.assertEqual(fileobj.read(), expected) 779 780 def test_float_write(self): 781 import array 782 contents = [(20-i)*0.1 for i in range(20)] 783 a = array.array('f', contents) 784 with TemporaryFile("w+", newline='') as fileobj: 785 writer = csv.writer(fileobj, dialect="excel") 786 writer.writerow(a) 787 expected = ",".join([str(i) for i in a])+"\r\n" 788 fileobj.seek(0) 789 self.assertEqual(fileobj.read(), expected) 790 791 def test_char_write(self): 792 import array, string 793 a = array.array('u', string.ascii_letters) 794 795 with TemporaryFile("w+", newline='') as fileobj: 796 writer = csv.writer(fileobj, dialect="excel") 797 writer.writerow(a) 798 expected = ",".join(a)+"\r\n" 799 fileobj.seek(0) 800 self.assertEqual(fileobj.read(), expected) 801 802class TestDialectValidity(unittest.TestCase): 803 def test_quoting(self): 804 class mydialect(csv.Dialect): 805 delimiter = ";" 806 escapechar = '\\' 807 doublequote = False 808 skipinitialspace = True 809 lineterminator = '\r\n' 810 quoting = csv.QUOTE_NONE 811 d = mydialect() 812 self.assertEqual(d.quoting, csv.QUOTE_NONE) 813 814 mydialect.quoting = None 815 self.assertRaises(csv.Error, mydialect) 816 817 mydialect.doublequote = True 818 mydialect.quoting = csv.QUOTE_ALL 819 mydialect.quotechar = '"' 820 d = mydialect() 821 self.assertEqual(d.quoting, csv.QUOTE_ALL) 822 self.assertEqual(d.quotechar, '"') 823 self.assertTrue(d.doublequote) 824 825 mydialect.quotechar = "''" 826 with self.assertRaises(csv.Error) as cm: 827 mydialect() 828 self.assertEqual(str(cm.exception), 829 '"quotechar" must be a 1-character string') 830 831 mydialect.quotechar = 4 832 with self.assertRaises(csv.Error) as cm: 833 mydialect() 834 self.assertEqual(str(cm.exception), 835 '"quotechar" must be string, not int') 836 837 def test_delimiter(self): 838 class mydialect(csv.Dialect): 839 delimiter = ";" 840 escapechar = '\\' 841 doublequote = False 842 skipinitialspace = True 843 lineterminator = '\r\n' 844 quoting = csv.QUOTE_NONE 845 d = mydialect() 846 self.assertEqual(d.delimiter, ";") 847 848 mydialect.delimiter = ":::" 849 with self.assertRaises(csv.Error) as cm: 850 mydialect() 851 self.assertEqual(str(cm.exception), 852 '"delimiter" must be a 1-character string') 853 854 mydialect.delimiter = "" 855 with self.assertRaises(csv.Error) as cm: 856 mydialect() 857 self.assertEqual(str(cm.exception), 858 '"delimiter" must be a 1-character string') 859 860 mydialect.delimiter = b"," 861 with self.assertRaises(csv.Error) as cm: 862 mydialect() 863 self.assertEqual(str(cm.exception), 864 '"delimiter" must be string, not bytes') 865 866 mydialect.delimiter = 4 867 with self.assertRaises(csv.Error) as cm: 868 mydialect() 869 self.assertEqual(str(cm.exception), 870 '"delimiter" must be string, not int') 871 872 def test_lineterminator(self): 873 class mydialect(csv.Dialect): 874 delimiter = ";" 875 escapechar = '\\' 876 doublequote = False 877 skipinitialspace = True 878 lineterminator = '\r\n' 879 quoting = csv.QUOTE_NONE 880 d = mydialect() 881 self.assertEqual(d.lineterminator, '\r\n') 882 883 mydialect.lineterminator = ":::" 884 d = mydialect() 885 self.assertEqual(d.lineterminator, ":::") 886 887 mydialect.lineterminator = 4 888 with self.assertRaises(csv.Error) as cm: 889 mydialect() 890 self.assertEqual(str(cm.exception), 891 '"lineterminator" must be a string') 892 893 def test_invalid_chars(self): 894 def create_invalid(field_name, value): 895 class mydialect(csv.Dialect): 896 pass 897 setattr(mydialect, field_name, value) 898 d = mydialect() 899 900 for field_name in ("delimiter", "escapechar", "quotechar"): 901 with self.subTest(field_name=field_name): 902 self.assertRaises(csv.Error, create_invalid, field_name, "") 903 self.assertRaises(csv.Error, create_invalid, field_name, "abc") 904 self.assertRaises(csv.Error, create_invalid, field_name, b'x') 905 self.assertRaises(csv.Error, create_invalid, field_name, 5) 906 907 908class TestSniffer(unittest.TestCase): 909 sample1 = """\ 910Harry's, Arlington Heights, IL, 2/1/03, Kimi Hayes 911Shark City, Glendale Heights, IL, 12/28/02, Prezence 912Tommy's Place, Blue Island, IL, 12/28/02, Blue Sunday/White Crow 913Stonecutters Seafood and Chop House, Lemont, IL, 12/19/02, Week Back 914""" 915 sample2 = """\ 916'Harry''s':'Arlington Heights':'IL':'2/1/03':'Kimi Hayes' 917'Shark City':'Glendale Heights':'IL':'12/28/02':'Prezence' 918'Tommy''s Place':'Blue Island':'IL':'12/28/02':'Blue Sunday/White Crow' 919'Stonecutters ''Seafood'' and Chop House':'Lemont':'IL':'12/19/02':'Week Back' 920""" 921 header1 = '''\ 922"venue","city","state","date","performers" 923''' 924 sample3 = '''\ 92505/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03 92605/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03 92705/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03 928''' 929 930 sample4 = '''\ 9312147483648;43.0e12;17;abc;def 932147483648;43.0e2;17;abc;def 93347483648;43.0;170;abc;def 934''' 935 936 sample5 = "aaa\tbbb\r\nAAA\t\r\nBBB\t\r\n" 937 sample6 = "a|b|c\r\nd|e|f\r\n" 938 sample7 = "'a'|'b'|'c'\r\n'd'|e|f\r\n" 939 940# Issue 18155: Use a delimiter that is a special char to regex: 941 942 header2 = '''\ 943"venue"+"city"+"state"+"date"+"performers" 944''' 945 sample8 = """\ 946Harry's+ Arlington Heights+ IL+ 2/1/03+ Kimi Hayes 947Shark City+ Glendale Heights+ IL+ 12/28/02+ Prezence 948Tommy's Place+ Blue Island+ IL+ 12/28/02+ Blue Sunday/White Crow 949Stonecutters Seafood and Chop House+ Lemont+ IL+ 12/19/02+ Week Back 950""" 951 sample9 = """\ 952'Harry''s'+ Arlington Heights'+ 'IL'+ '2/1/03'+ 'Kimi Hayes' 953'Shark City'+ Glendale Heights'+' IL'+ '12/28/02'+ 'Prezence' 954'Tommy''s Place'+ Blue Island'+ 'IL'+ '12/28/02'+ 'Blue Sunday/White Crow' 955'Stonecutters ''Seafood'' and Chop House'+ 'Lemont'+ 'IL'+ '12/19/02'+ 'Week Back' 956""" 957 958 def test_has_header(self): 959 sniffer = csv.Sniffer() 960 self.assertEqual(sniffer.has_header(self.sample1), False) 961 self.assertEqual(sniffer.has_header(self.header1 + self.sample1), 962 True) 963 964 def test_has_header_regex_special_delimiter(self): 965 sniffer = csv.Sniffer() 966 self.assertEqual(sniffer.has_header(self.sample8), False) 967 self.assertEqual(sniffer.has_header(self.header2 + self.sample8), 968 True) 969 970 def test_sniff(self): 971 sniffer = csv.Sniffer() 972 dialect = sniffer.sniff(self.sample1) 973 self.assertEqual(dialect.delimiter, ",") 974 self.assertEqual(dialect.quotechar, '"') 975 self.assertEqual(dialect.skipinitialspace, True) 976 977 dialect = sniffer.sniff(self.sample2) 978 self.assertEqual(dialect.delimiter, ":") 979 self.assertEqual(dialect.quotechar, "'") 980 self.assertEqual(dialect.skipinitialspace, False) 981 982 def test_delimiters(self): 983 sniffer = csv.Sniffer() 984 dialect = sniffer.sniff(self.sample3) 985 # given that all three lines in sample3 are equal, 986 # I think that any character could have been 'guessed' as the 987 # delimiter, depending on dictionary order 988 self.assertIn(dialect.delimiter, self.sample3) 989 dialect = sniffer.sniff(self.sample3, delimiters="?,") 990 self.assertEqual(dialect.delimiter, "?") 991 dialect = sniffer.sniff(self.sample3, delimiters="/,") 992 self.assertEqual(dialect.delimiter, "/") 993 dialect = sniffer.sniff(self.sample4) 994 self.assertEqual(dialect.delimiter, ";") 995 dialect = sniffer.sniff(self.sample5) 996 self.assertEqual(dialect.delimiter, "\t") 997 dialect = sniffer.sniff(self.sample6) 998 self.assertEqual(dialect.delimiter, "|") 999 dialect = sniffer.sniff(self.sample7) 1000 self.assertEqual(dialect.delimiter, "|") 1001 self.assertEqual(dialect.quotechar, "'") 1002 dialect = sniffer.sniff(self.sample8) 1003 self.assertEqual(dialect.delimiter, '+') 1004 dialect = sniffer.sniff(self.sample9) 1005 self.assertEqual(dialect.delimiter, '+') 1006 self.assertEqual(dialect.quotechar, "'") 1007 1008 def test_doublequote(self): 1009 sniffer = csv.Sniffer() 1010 dialect = sniffer.sniff(self.header1) 1011 self.assertFalse(dialect.doublequote) 1012 dialect = sniffer.sniff(self.header2) 1013 self.assertFalse(dialect.doublequote) 1014 dialect = sniffer.sniff(self.sample2) 1015 self.assertTrue(dialect.doublequote) 1016 dialect = sniffer.sniff(self.sample8) 1017 self.assertFalse(dialect.doublequote) 1018 dialect = sniffer.sniff(self.sample9) 1019 self.assertTrue(dialect.doublequote) 1020 1021class NUL: 1022 def write(s, *args): 1023 pass 1024 writelines = write 1025 1026@unittest.skipUnless(hasattr(sys, "gettotalrefcount"), 1027 'requires sys.gettotalrefcount()') 1028class TestLeaks(unittest.TestCase): 1029 def test_create_read(self): 1030 delta = 0 1031 lastrc = sys.gettotalrefcount() 1032 for i in range(20): 1033 gc.collect() 1034 self.assertEqual(gc.garbage, []) 1035 rc = sys.gettotalrefcount() 1036 csv.reader(["a,b,c\r\n"]) 1037 csv.reader(["a,b,c\r\n"]) 1038 csv.reader(["a,b,c\r\n"]) 1039 delta = rc-lastrc 1040 lastrc = rc 1041 # if csv.reader() leaks, last delta should be 3 or more 1042 self.assertEqual(delta < 3, True) 1043 1044 def test_create_write(self): 1045 delta = 0 1046 lastrc = sys.gettotalrefcount() 1047 s = NUL() 1048 for i in range(20): 1049 gc.collect() 1050 self.assertEqual(gc.garbage, []) 1051 rc = sys.gettotalrefcount() 1052 csv.writer(s) 1053 csv.writer(s) 1054 csv.writer(s) 1055 delta = rc-lastrc 1056 lastrc = rc 1057 # if csv.writer() leaks, last delta should be 3 or more 1058 self.assertEqual(delta < 3, True) 1059 1060 def test_read(self): 1061 delta = 0 1062 rows = ["a,b,c\r\n"]*5 1063 lastrc = sys.gettotalrefcount() 1064 for i in range(20): 1065 gc.collect() 1066 self.assertEqual(gc.garbage, []) 1067 rc = sys.gettotalrefcount() 1068 rdr = csv.reader(rows) 1069 for row in rdr: 1070 pass 1071 delta = rc-lastrc 1072 lastrc = rc 1073 # if reader leaks during read, delta should be 5 or more 1074 self.assertEqual(delta < 5, True) 1075 1076 def test_write(self): 1077 delta = 0 1078 rows = [[1,2,3]]*5 1079 s = NUL() 1080 lastrc = sys.gettotalrefcount() 1081 for i in range(20): 1082 gc.collect() 1083 self.assertEqual(gc.garbage, []) 1084 rc = sys.gettotalrefcount() 1085 writer = csv.writer(s) 1086 for row in rows: 1087 writer.writerow(row) 1088 delta = rc-lastrc 1089 lastrc = rc 1090 # if writer leaks during write, last delta should be 5 or more 1091 self.assertEqual(delta < 5, True) 1092 1093class TestUnicode(unittest.TestCase): 1094 1095 names = ["Martin von Löwis", 1096 "Marc André Lemburg", 1097 "Guido van Rossum", 1098 "François Pinard"] 1099 1100 def test_unicode_read(self): 1101 with TemporaryFile("w+", newline='', encoding="utf-8") as fileobj: 1102 fileobj.write(",".join(self.names) + "\r\n") 1103 fileobj.seek(0) 1104 reader = csv.reader(fileobj) 1105 self.assertEqual(list(reader), [self.names]) 1106 1107 1108 def test_unicode_write(self): 1109 with TemporaryFile("w+", newline='', encoding="utf-8") as fileobj: 1110 writer = csv.writer(fileobj) 1111 writer.writerow(self.names) 1112 expected = ",".join(self.names)+"\r\n" 1113 fileobj.seek(0) 1114 self.assertEqual(fileobj.read(), expected) 1115 1116class KeyOrderingTest(unittest.TestCase): 1117 1118 def test_ordering_for_the_dict_reader_and_writer(self): 1119 resultset = set() 1120 for keys in permutations("abcde"): 1121 with TemporaryFile('w+', newline='', encoding="utf-8") as fileobject: 1122 dw = csv.DictWriter(fileobject, keys) 1123 dw.writeheader() 1124 fileobject.seek(0) 1125 dr = csv.DictReader(fileobject) 1126 kt = tuple(dr.fieldnames) 1127 self.assertEqual(keys, kt) 1128 resultset.add(kt) 1129 # Final sanity check: were all permutations unique? 1130 self.assertEqual(len(resultset), 120, "Key ordering: some key permutations not collected (expected 120)") 1131 1132 def test_ordered_dict_reader(self): 1133 data = dedent('''\ 1134 FirstName,LastName 1135 Eric,Idle 1136 Graham,Chapman,Over1,Over2 1137 1138 Under1 1139 John,Cleese 1140 ''').splitlines() 1141 1142 self.assertEqual(list(csv.DictReader(data)), 1143 [OrderedDict([('FirstName', 'Eric'), ('LastName', 'Idle')]), 1144 OrderedDict([('FirstName', 'Graham'), ('LastName', 'Chapman'), 1145 (None, ['Over1', 'Over2'])]), 1146 OrderedDict([('FirstName', 'Under1'), ('LastName', None)]), 1147 OrderedDict([('FirstName', 'John'), ('LastName', 'Cleese')]), 1148 ]) 1149 1150 self.assertEqual(list(csv.DictReader(data, restkey='OtherInfo')), 1151 [OrderedDict([('FirstName', 'Eric'), ('LastName', 'Idle')]), 1152 OrderedDict([('FirstName', 'Graham'), ('LastName', 'Chapman'), 1153 ('OtherInfo', ['Over1', 'Over2'])]), 1154 OrderedDict([('FirstName', 'Under1'), ('LastName', None)]), 1155 OrderedDict([('FirstName', 'John'), ('LastName', 'Cleese')]), 1156 ]) 1157 1158 del data[0] # Remove the header row 1159 self.assertEqual(list(csv.DictReader(data, fieldnames=['fname', 'lname'])), 1160 [OrderedDict([('fname', 'Eric'), ('lname', 'Idle')]), 1161 OrderedDict([('fname', 'Graham'), ('lname', 'Chapman'), 1162 (None, ['Over1', 'Over2'])]), 1163 OrderedDict([('fname', 'Under1'), ('lname', None)]), 1164 OrderedDict([('fname', 'John'), ('lname', 'Cleese')]), 1165 ]) 1166 1167 1168class MiscTestCase(unittest.TestCase): 1169 def test__all__(self): 1170 extra = {'__doc__', '__version__'} 1171 support.check__all__(self, csv, ('csv', '_csv'), extra=extra) 1172 1173 1174if __name__ == '__main__': 1175 unittest.main() 1176