~ubuntu-branches/ubuntu/trusty/python3.4/trusty-proposed

« back to all changes in this revision

Viewing changes to Lib/test/test_csv.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-11-25 09:44:27 UTC
  • Revision ID: package-import@ubuntu.com-20131125094427-lzxj8ap5w01lmo7f
Tags: upstream-3.4~b1
Import upstream version 3.4~b1

Show diffs side-by-side

added added

removed removed

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