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