diff -r 3bec2d0fb668 Lib/test/test_csv.py --- a/Lib/test/test_csv.py Thu Dec 19 22:31:23 2013 +0200 +++ b/Lib/test/test_csv.py Fri Dec 20 15:42:53 2013 +0800 @@ -893,7 +893,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): @@ -930,6 +936,42 @@ 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') + + if test_support.have_unicode: + mydialect.escapechar = unicode("*") + with self.assertRaises(csv.Error) as cm: + mydialect() + self.assertEqual(str(cm.exception), + '"escapechar" must be string or None, not unicode') + + 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 3bec2d0fb668 Modules/_csv.c --- a/Modules/_csv.c Thu Dec 19 22:31:23 2013 +0200 +++ b/Modules/_csv.c Fri Dec 20 15:42:53 2013 +0800 @@ -243,19 +243,24 @@ if (src != Py_None) { Py_ssize_t len; if (!PyString_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 = PyString_GET_SIZE(src); - if (len > 1) { + if (len != 1) { PyErr_Format(PyExc_TypeError, "\"%s\" must be an 1-character string", name); return -1; } - if (len > 0) + else *target = *PyString_AS_STRING(src); } } @@ -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)