[Python-checkins] bpo-20028: Empty escapechar/quotechar is not allowed for csv.Dialect (GH-28833)

corona10 webhook-mailer at python.org
Mon Oct 11 07:08:20 EDT 2021


https://github.com/python/cpython/commit/ab62051152cb24470056ffaeb9107c8b4311375e
commit: ab62051152cb24470056ffaeb9107c8b4311375e
branch: main
author: Dong-hee Na <donghee.na at python.org>
committer: corona10 <donghee.na92 at gmail.com>
date: 2021-10-11T20:08:15+09:00
summary:

bpo-20028: Empty escapechar/quotechar is not allowed for csv.Dialect (GH-28833)

files:
A Misc/NEWS.d/next/Library/2021-10-10-00-25-36.bpo-20028.bPx4Z8.rst
M Doc/library/csv.rst
M Lib/test/test_csv.py
M Modules/_csv.c

diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst
index 899ce0225ce7f..3a7817cfdfad8 100644
--- a/Doc/library/csv.rst
+++ b/Doc/library/csv.rst
@@ -383,6 +383,8 @@ Dialects support the following attributes:
    :const:`False`. On reading, the *escapechar* removes any special meaning from
    the following character. It defaults to :const:`None`, which disables escaping.
 
+   .. versionchanged:: 3.11
+      An empty *escapechar* is not allowed.
 
 .. attribute:: Dialect.lineterminator
 
@@ -402,6 +404,8 @@ Dialects support the following attributes:
    as the *delimiter* or *quotechar*, or which contain new-line characters.  It
    defaults to ``'"'``.
 
+   .. versionchanged:: 3.11
+      An empty *quotechar* is not allowed.
 
 .. attribute:: Dialect.quoting
 
diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py
index fb27ea396e04d..95a19dd46cb4f 100644
--- a/Lib/test/test_csv.py
+++ b/Lib/test/test_csv.py
@@ -44,6 +44,8 @@ def _test_arg_valid(self, ctor, arg):
                           quoting=csv.QUOTE_ALL, quotechar='')
         self.assertRaises(TypeError, ctor, arg,
                           quoting=csv.QUOTE_ALL, quotechar=None)
+        self.assertRaises(TypeError, ctor, arg,
+                          quoting=csv.QUOTE_NONE, quotechar='')
 
     def test_reader_arg_valid(self):
         self._test_arg_valid(csv.reader, [])
@@ -342,7 +344,6 @@ def test_read_escape(self):
         self._read_test(['a,^b,c'], [['a', 'b', 'c']], escapechar='^')
         self._read_test(['a,\0b,c'], [['a', 'b', 'c']], escapechar='\0')
         self._read_test(['a,\\b,c'], [['a', '\\b', 'c']], escapechar=None)
-        self._read_test(['a,\\b,c'], [['a', '\\b', 'c']], escapechar='')
         self._read_test(['a,\\b,c'], [['a', '\\b', 'c']])
 
     def test_read_quoting(self):
@@ -913,6 +914,12 @@ class mydialect(csv.Dialect):
         self.assertEqual(d.quotechar, '"')
         self.assertTrue(d.doublequote)
 
+        mydialect.quotechar = ""
+        with self.assertRaises(csv.Error) as cm:
+            mydialect()
+        self.assertEqual(str(cm.exception),
+                         '"quotechar" must be a 1-character string')
+
         mydialect.quotechar = "''"
         with self.assertRaises(csv.Error) as cm:
             mydialect()
@@ -977,6 +984,10 @@ class mydialect(csv.Dialect):
         d = mydialect()
         self.assertEqual(d.escapechar, "\\")
 
+        mydialect.escapechar = ""
+        with self.assertRaisesRegex(csv.Error, '"escapechar" must be a 1-character string'):
+            mydialect()
+
         mydialect.escapechar = "**"
         with self.assertRaisesRegex(csv.Error, '"escapechar" must be a 1-character string'):
             mydialect()
diff --git a/Misc/NEWS.d/next/Library/2021-10-10-00-25-36.bpo-20028.bPx4Z8.rst b/Misc/NEWS.d/next/Library/2021-10-10-00-25-36.bpo-20028.bPx4Z8.rst
new file mode 100644
index 0000000000000..9db15bc39e7ca
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-10-10-00-25-36.bpo-20028.bPx4Z8.rst
@@ -0,0 +1,2 @@
+Empty escapechar/quotechar is not allowed when initializing
+:class:`csv.Dialect`. Patch by Vajrasky Kok and Dong-hee Na.
diff --git a/Modules/_csv.c b/Modules/_csv.c
index 469c1a15c340c..1c2f504ea5c09 100644
--- a/Modules/_csv.c
+++ b/Modules/_csv.c
@@ -250,16 +250,14 @@ _set_char_or_none(const char *name, Py_UCS4 *target, PyObject *src, Py_UCS4 dflt
             if (len < 0) {
                 return -1;
             }
-            if (len > 1) {
+            if (len != 1) {
                 PyErr_Format(PyExc_TypeError,
                     "\"%s\" must be a 1-character string",
                     name);
                 return -1;
             }
             /* PyUnicode_READY() is called in PyUnicode_GetLength() */
-            else if (len > 0) {
-                *target = PyUnicode_READ_CHAR(src, 0);
-            }
+            *target = PyUnicode_READ_CHAR(src, 0);
         }
     }
     return 0;
@@ -272,7 +270,6 @@ _set_char(const char *name, Py_UCS4 *target, PyObject *src, Py_UCS4 dflt)
         *target = dflt;
     }
     else {
-        *target = NOT_SET;
         if (!PyUnicode_Check(src)) {
             PyErr_Format(PyExc_TypeError,
                          "\"%s\" must be string, not %.200s", name,
@@ -283,16 +280,14 @@ _set_char(const char *name, Py_UCS4 *target, PyObject *src, Py_UCS4 dflt)
         if (len < 0) {
             return -1;
         }
-        if (len > 1) {
+        if (len != 1) {
             PyErr_Format(PyExc_TypeError,
                          "\"%s\" must be a 1-character string",
                          name);
             return -1;
         }
         /* PyUnicode_READY() is called in PyUnicode_GetLength() */
-        else if (len > 0) {
-            *target = PyUnicode_READ_CHAR(src, 0);
-        }
+        *target = PyUnicode_READ_CHAR(src, 0);
     }
     return 0;
 }



More information about the Python-checkins mailing list