Home | History | Annotate | Download | only in test
      1 # Copyright (C) 2001,2002 Python Software Foundation
      2 # csv package unit tests
      3 
      4 import copy
      5 import sys
      6 import unittest
      7 from io import StringIO
      8 from tempfile import TemporaryFile
      9 import csv
     10 import gc
     11 import pickle
     12 from test import support
     13 from itertools import permutations
     14 from textwrap import dedent
     15 from collections import OrderedDict
     16 
     17 class 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 
    334 class 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 
    441 class 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 
    457 class 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 = '''\
    523 1,2,3,"""I see,""
    524 said the blind man","as he picked up his
    525 hammer and saw"
    526 9,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 
    554 class EscapedExcel(csv.excel):
    555     quoting = csv.QUOTE_NONE
    556     escapechar = '\\'
    557 
    558 class 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 
    567 class 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 
    576 class QuotedEscapedExcel(csv.excel):
    577     quoting = csv.QUOTE_NONNUMERIC
    578     escapechar = '\\'
    579 
    580 class 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 
    589 class 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 
    756 class 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 
    802 class 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 
    908 class TestSniffer(unittest.TestCase):
    909     sample1 = """\
    910 Harry's, Arlington Heights, IL, 2/1/03, Kimi Hayes
    911 Shark City, Glendale Heights, IL, 12/28/02, Prezence
    912 Tommy's Place, Blue Island, IL, 12/28/02, Blue Sunday/White Crow
    913 Stonecutters 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 = '''\
    925 05/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03
    926 05/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03
    927 05/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03
    928 '''
    929 
    930     sample4 = '''\
    931 2147483648;43.0e12;17;abc;def
    932 147483648;43.0e2;17;abc;def
    933 47483648;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 = """\
    946 Harry's+ Arlington Heights+ IL+ 2/1/03+ Kimi Hayes
    947 Shark City+ Glendale Heights+ IL+ 12/28/02+ Prezence
    948 Tommy's Place+ Blue Island+ IL+ 12/28/02+ Blue Sunday/White Crow
    949 Stonecutters 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 
   1021 class NUL:
   1022     def write(s, *args):
   1023         pass
   1024     writelines = write
   1025 
   1026 @unittest.skipUnless(hasattr(sys, "gettotalrefcount"),
   1027                      'requires sys.gettotalrefcount()')
   1028 class 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 
   1093 class TestUnicode(unittest.TestCase):
   1094 
   1095     names = ["Martin von Lwis",
   1096              "Marc Andr Lemburg",
   1097              "Guido van Rossum",
   1098              "Franois 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 
   1116 class 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 
   1168 class MiscTestCase(unittest.TestCase):
   1169     def test__all__(self):
   1170         extra = {'__doc__', '__version__'}
   1171         support.check__all__(self, csv, ('csv', '_csv'), extra=extra)
   1172 
   1173 
   1174 if __name__ == '__main__':
   1175     unittest.main()
   1176