Skip to content

Commit 34bbc87

Browse files
authored
bpo-20028: Improve error message of csv.Dialect when initializing (pythonGH-28705)
1 parent 5e173f5 commit 34bbc87

File tree

3 files changed

+70
-9
lines changed

3 files changed

+70
-9
lines changed

Lib/test/test_csv.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,7 @@ class mydialect(csv.Dialect):
897897
with self.assertRaises(csv.Error) as cm:
898898
mydialect()
899899
self.assertEqual(str(cm.exception),
900-
'"quotechar" must be string, not int')
900+
'"quotechar" must be string or None, not int')
901901

902902
def test_delimiter(self):
903903
class mydialect(csv.Dialect):
@@ -934,6 +934,35 @@ class mydialect(csv.Dialect):
934934
self.assertEqual(str(cm.exception),
935935
'"delimiter" must be string, not int')
936936

937+
mydialect.delimiter = None
938+
with self.assertRaises(csv.Error) as cm:
939+
mydialect()
940+
self.assertEqual(str(cm.exception),
941+
'"delimiter" must be string, not NoneType')
942+
943+
def test_escapechar(self):
944+
class mydialect(csv.Dialect):
945+
delimiter = ";"
946+
escapechar = '\\'
947+
doublequote = False
948+
skipinitialspace = True
949+
lineterminator = '\r\n'
950+
quoting = csv.QUOTE_NONE
951+
d = mydialect()
952+
self.assertEqual(d.escapechar, "\\")
953+
954+
mydialect.escapechar = "**"
955+
with self.assertRaisesRegex(csv.Error, '"escapechar" must be a 1-character string'):
956+
mydialect()
957+
958+
mydialect.escapechar = b"*"
959+
with self.assertRaisesRegex(csv.Error, '"escapechar" must be string or None, not bytes'):
960+
mydialect()
961+
962+
mydialect.escapechar = 4
963+
with self.assertRaisesRegex(csv.Error, '"escapechar" must be string or None, not int'):
964+
mydialect()
965+
937966
def test_lineterminator(self):
938967
class mydialect(csv.Dialect):
939968
delimiter = ";"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve error message of :class:`csv.Dialect` when initializing.
2+
Patch by Vajrasky Kok and Dong-hee Na.

Modules/_csv.c

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,30 +229,60 @@ _set_int(const char *name, int *target, PyObject *src, int dflt)
229229
}
230230

231231
static int
232-
_set_char(const char *name, Py_UCS4 *target, PyObject *src, Py_UCS4 dflt)
232+
_set_char_or_none(const char *name, Py_UCS4 *target, PyObject *src, Py_UCS4 dflt)
233233
{
234-
if (src == NULL)
234+
if (src == NULL) {
235235
*target = dflt;
236+
}
236237
else {
237238
*target = '\0';
238239
if (src != Py_None) {
239-
Py_ssize_t len;
240240
if (!PyUnicode_Check(src)) {
241241
PyErr_Format(PyExc_TypeError,
242-
"\"%s\" must be string, not %.200s", name,
242+
"\"%s\" must be string or None, not %.200s", name,
243243
Py_TYPE(src)->tp_name);
244244
return -1;
245245
}
246-
len = PyUnicode_GetLength(src);
246+
Py_ssize_t len = PyUnicode_GetLength(src);
247247
if (len > 1) {
248248
PyErr_Format(PyExc_TypeError,
249249
"\"%s\" must be a 1-character string",
250250
name);
251251
return -1;
252252
}
253253
/* PyUnicode_READY() is called in PyUnicode_GetLength() */
254-
if (len > 0)
254+
else {
255255
*target = PyUnicode_READ_CHAR(src, 0);
256+
}
257+
}
258+
}
259+
return 0;
260+
}
261+
262+
static int
263+
_set_char(const char *name, Py_UCS4 *target, PyObject *src, Py_UCS4 dflt)
264+
{
265+
if (src == NULL) {
266+
*target = dflt;
267+
}
268+
else {
269+
*target = '\0';
270+
if (!PyUnicode_Check(src)) {
271+
PyErr_Format(PyExc_TypeError,
272+
"\"%s\" must be string, not %.200s", name,
273+
Py_TYPE(src)->tp_name);
274+
return -1;
275+
}
276+
Py_ssize_t len = PyUnicode_GetLength(src);
277+
if (len > 1) {
278+
PyErr_Format(PyExc_TypeError,
279+
"\"%s\" must be a 1-character string",
280+
name);
281+
return -1;
282+
}
283+
/* PyUnicode_READY() is called in PyUnicode_GetLength() */
284+
else {
285+
*target = PyUnicode_READ_CHAR(src, 0);
256286
}
257287
}
258288
return 0;
@@ -445,9 +475,9 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
445475
goto err
446476
DIASET(_set_char, "delimiter", &self->delimiter, delimiter, ',');
447477
DIASET(_set_bool, "doublequote", &self->doublequote, doublequote, true);
448-
DIASET(_set_char, "escapechar", &self->escapechar, escapechar, 0);
478+
DIASET(_set_char_or_none, "escapechar", &self->escapechar, escapechar, 0);
449479
DIASET(_set_str, "lineterminator", &self->lineterminator, lineterminator, "\r\n");
450-
DIASET(_set_char, "quotechar", &self->quotechar, quotechar, '"');
480+
DIASET(_set_char_or_none, "quotechar", &self->quotechar, quotechar, '"');
451481
DIASET(_set_int, "quoting", &self->quoting, quoting, QUOTE_MINIMAL);
452482
DIASET(_set_bool, "skipinitialspace", &self->skipinitialspace, skipinitialspace, false);
453483
DIASET(_set_bool, "strict", &self->strict, strict, false);

0 commit comments

Comments
 (0)