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