Skip to content

Commit 1196cf1

Browse files
committed
Improved the implementation of the internal "dialect" type. The new
implementation features better error reporting, and better compliance with the PEP.
1 parent 72b83c8 commit 1196cf1

3 files changed

Lines changed: 339 additions & 212 deletions

File tree

Lib/csv.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def _validate(self):
6868
elif not isinstance(self.lineterminator, str):
6969
errors.append("lineterminator must be a string")
7070

71-
if self.doublequote not in (True, False):
71+
if self.doublequote not in (True, False) and self.quoting != QUOTE_NONE:
7272
errors.append("doublequote parameter must be True or False")
7373

7474
if self.skipinitialspace not in (True, False):

Lib/test/test_csv.py

Lines changed: 99 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,44 +17,112 @@ class Test_Csv(unittest.TestCase):
1717
from the high level interface. Further tests of this nature are done
1818
in TestDialectRegistry.
1919
"""
20-
def test_reader_arg_valid(self):
21-
self.assertRaises(TypeError, csv.reader)
22-
self.assertRaises(TypeError, csv.reader, None)
23-
self.assertRaises(AttributeError, csv.reader, [], bad_attr = 0)
24-
self.assertRaises(csv.Error, csv.reader, [], 'foo')
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, None)
28+
self.assertRaises(TypeError, ctor, arg, delimiter=None)
29+
self.assertRaises(TypeError, ctor, arg, delimiter=1)
30+
self.assertRaises(TypeError, ctor, arg, quotechar=1)
31+
self.assertRaises(TypeError, ctor, arg, lineterminator=None)
32+
self.assertRaises(TypeError, ctor, arg, lineterminator=1)
33+
self.assertRaises(TypeError, ctor, arg, quoting=None)
34+
# We now allow this, only raising an exception if quoting is needed.
35+
# self.assertRaises(TypeError, ctor, arg, quotechar=None)
36+
# self.assertRaises(TypeError, ctor, arg,
37+
# quoting=csv.QUOTE_NONE, escapechar=None)
38+
# No longer complains about dialects with invalid attributes [AM]
39+
# class BadDialect:
40+
# bad_attr = 0
41+
# self.assertRaises(AttributeError, csv.reader, [], BadDialect)
2542
class BadClass:
2643
def __init__(self):
2744
raise IOError
2845
self.assertRaises(IOError, csv.reader, [], BadClass)
29-
self.assertRaises(TypeError, csv.reader, [], None)
30-
class BadDialect:
31-
bad_attr = 0
32-
self.assertRaises(AttributeError, csv.reader, [], BadDialect)
46+
47+
def test_reader_arg_valid(self):
48+
self._test_arg_valid(csv.reader, [])
3349

3450
def test_writer_arg_valid(self):
35-
self.assertRaises(TypeError, csv.writer)
36-
self.assertRaises(TypeError, csv.writer, None)
37-
self.assertRaises(AttributeError, csv.writer, StringIO(), bad_attr = 0)
51+
self._test_arg_valid(csv.writer, StringIO())
3852

39-
def _test_attrs(self, obj):
53+
def _test_default_attrs(self, ctor, *args):
54+
obj = ctor(*args)
55+
# Check defaults
4056
self.assertEqual(obj.dialect.delimiter, ',')
41-
obj.dialect.delimiter = '\t'
42-
self.assertEqual(obj.dialect.delimiter, '\t')
43-
self.assertRaises(TypeError, delattr, obj.dialect, 'delimiter')
44-
self.assertRaises(TypeError, setattr, obj.dialect,
45-
'lineterminator', None)
46-
obj.dialect.escapechar = None
57+
self.assertEqual(obj.dialect.doublequote, True)
4758
self.assertEqual(obj.dialect.escapechar, None)
59+
self.assertEqual(obj.dialect.lineterminator, "\r\n")
60+
self.assertEqual(obj.dialect.quotechar, '"')
61+
self.assertEqual(obj.dialect.quoting, csv.QUOTE_MINIMAL)
62+
self.assertEqual(obj.dialect.skipinitialspace, False)
63+
self.assertEqual(obj.dialect.strict, False)
64+
# Try deleting or changing attributes (they are read-only)
65+
self.assertRaises(TypeError, delattr, obj.dialect, 'delimiter')
66+
self.assertRaises(TypeError, setattr, obj.dialect, 'delimiter', ':')
4867
self.assertRaises(TypeError, delattr, obj.dialect, 'quoting')
4968
self.assertRaises(TypeError, setattr, obj.dialect, 'quoting', None)
50-
obj.dialect.quoting = csv.QUOTE_MINIMAL
51-
self.assertEqual(obj.dialect.quoting, csv.QUOTE_MINIMAL)
5269

5370
def test_reader_attrs(self):
54-
self._test_attrs(csv.reader([]))
71+
self._test_default_attrs(csv.reader, [])
5572

5673
def test_writer_attrs(self):
57-
self._test_attrs(csv.writer(StringIO()))
74+
self._test_default_attrs(csv.writer, StringIO())
75+
76+
def _test_kw_attrs(self, ctor, *args):
77+
# Now try with alternate options
78+
kwargs = dict(delimiter=':', doublequote=False, escapechar='\\',
79+
lineterminator='\r', quotechar='*',
80+
quoting=csv.QUOTE_NONE, skipinitialspace=True,
81+
strict=True)
82+
obj = ctor(*args, **kwargs)
83+
self.assertEqual(obj.dialect.delimiter, ':')
84+
self.assertEqual(obj.dialect.doublequote, False)
85+
self.assertEqual(obj.dialect.escapechar, '\\')
86+
self.assertEqual(obj.dialect.lineterminator, "\r")
87+
self.assertEqual(obj.dialect.quotechar, '*')
88+
self.assertEqual(obj.dialect.quoting, csv.QUOTE_NONE)
89+
self.assertEqual(obj.dialect.skipinitialspace, True)
90+
self.assertEqual(obj.dialect.strict, True)
91+
92+
def test_reader_kw_attrs(self):
93+
self._test_kw_attrs(csv.reader, [])
94+
95+
def test_writer_kw_attrs(self):
96+
self._test_kw_attrs(csv.writer, StringIO())
97+
98+
def _test_dialect_attrs(self, ctor, *args):
99+
# Now try with dialect-derived options
100+
class dialect:
101+
delimiter='-'
102+
doublequote=False
103+
escapechar='^'
104+
lineterminator='$'
105+
quotechar='#'
106+
quoting=csv.QUOTE_ALL
107+
skipinitialspace=True
108+
strict=False
109+
args = args + (dialect,)
110+
obj = ctor(*args)
111+
self.assertEqual(obj.dialect.delimiter, '-')
112+
self.assertEqual(obj.dialect.doublequote, False)
113+
self.assertEqual(obj.dialect.escapechar, '^')
114+
self.assertEqual(obj.dialect.lineterminator, "$")
115+
self.assertEqual(obj.dialect.quotechar, '#')
116+
self.assertEqual(obj.dialect.quoting, csv.QUOTE_ALL)
117+
self.assertEqual(obj.dialect.skipinitialspace, True)
118+
self.assertEqual(obj.dialect.strict, False)
119+
120+
def test_reader_dialect_attrs(self):
121+
self._test_dialect_attrs(csv.reader, [])
122+
123+
def test_writer_dialect_attrs(self):
124+
self._test_dialect_attrs(csv.writer, StringIO())
125+
58126

59127
def _write_test(self, fields, expect, **kwargs):
60128
fd, name = tempfile.mkstemp()
@@ -166,6 +234,13 @@ def test_read_escape(self):
166234
self._read_test(['a,"b,c\\""'], [['a', 'b,c"']], escapechar='\\')
167235
self._read_test(['a,"b,c"\\'], [['a', 'b,c\\']], escapechar='\\')
168236

237+
def test_read_quoting(self):
238+
self._read_test(['1,",3,",5'], [['1', ',3,', '5']])
239+
self._read_test(['1,",3,",5'], [['1', '"', '3', '"', '5']],
240+
quotechar=None, escapechar='\\')
241+
self._read_test(['1,",3,",5'], [['1', '"', '3', '"', '5']],
242+
quoting=csv.QUOTE_NONE, escapechar='\\')
243+
169244
def test_read_bigfield(self):
170245
# This exercises the buffer realloc functionality
171246
bigstring = 'X' * 50000
@@ -297,7 +372,7 @@ class testC(csv.excel):
297372

298373
def test_bad_dialect(self):
299374
# Unknown parameter
300-
self.assertRaises(AttributeError, csv.reader, [], bad_attr = 0)
375+
self.assertRaises(TypeError, csv.reader, [], bad_attr = 0)
301376
# Bad values
302377
self.assertRaises(TypeError, csv.reader, [], delimiter = None)
303378
self.assertRaises(TypeError, csv.reader, [], quoting = -1)

0 commit comments

Comments
 (0)