diff -r 13a505260f17 Lib/test/test_csv.py --- a/Lib/test/test_csv.py Thu Dec 19 13:49:32 2013 -0800 +++ b/Lib/test/test_csv.py Fri Dec 20 15:41:36 2013 +0800 @@ -779,7 +779,13 @@ with self.assertRaises(csv.Error) as cm: mydialect() self.assertEqual(str(cm.exception), - '"quotechar" must be string, not int') + '"quotechar" must be string or None, not int') + + mydialect.quotechar = "" + with self.assertRaises(csv.Error) as cm: + mydialect() + self.assertEqual(str(cm.exception), + '"quotechar" must be an 1-character string') def test_delimiter(self): class mydialect(csv.Dialect): @@ -816,6 +822,41 @@ self.assertEqual(str(cm.exception), '"delimiter" must be string, not int') + def test_escapechar(self): + class mydialect(csv.Dialect): + delimiter = ";" + escapechar = '\\' + doublequote = False + skipinitialspace = True + lineterminator = '\r\n' + quoting = csv.QUOTE_NONE + d = mydialect() + self.assertEqual(d.escapechar, "\\") + + mydialect.escapechar = "" + with self.assertRaises(csv.Error) as cm: + mydialect() + self.assertEqual(str(cm.exception), + '"escapechar" must be an 1-character string') + + mydialect.escapechar = "**" + with self.assertRaises(csv.Error) as cm: + mydialect() + self.assertEqual(str(cm.exception), + '"escapechar" must be an 1-character string') + + mydialect.escapechar = b"*" + with self.assertRaises(csv.Error) as cm: + mydialect() + self.assertEqual(str(cm.exception), + '"escapechar" must be string or None, not bytes') + + mydialect.escapechar = 4 + with self.assertRaises(csv.Error) as cm: + mydialect() + self.assertEqual(str(cm.exception), + '"escapechar" must be string or None, not int') + def test_lineterminator(self): class mydialect(csv.Dialect): delimiter = ";" diff -r 13a505260f17 Modules/_csv.c --- a/Modules/_csv.c Thu Dec 19 13:49:32 2013 -0800 +++ b/Modules/_csv.c Fri Dec 20 15:41:36 2013 +0800 @@ -240,20 +240,25 @@ if (src != Py_None) { Py_ssize_t len; if (!PyUnicode_Check(src)) { - PyErr_Format(PyExc_TypeError, - "\"%s\" must be string, not %.200s", name, - src->ob_type->tp_name); + if (strcmp(name, "delimiter") == 0) + PyErr_Format(PyExc_TypeError, + "\"%s\" must be string, not %.200s", name, + src->ob_type->tp_name); + else + PyErr_Format(PyExc_TypeError, + "\"%s\" must be string or None, not %.200s", name, + src->ob_type->tp_name); return -1; } len = PyUnicode_GetLength(src); - if (len > 1) { + if (len != 1) { PyErr_Format(PyExc_TypeError, "\"%s\" must be an 1-character string", name); return -1; } /* PyUnicode_READY() is called in PyUnicode_GetLength() */ - if (len > 0) + else *target = PyUnicode_READ_CHAR(src, 0); } } @@ -432,7 +437,7 @@ goto err; if (self->delimiter == 0) { PyErr_SetString(PyExc_TypeError, - "\"delimiter\" must be an 1-character string"); + "delimiter must be set"); goto err; } if (quotechar == Py_None && quoting == NULL)