From 323748ad7446c76972c80dbbf510534dc5c22ae8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 26 Jul 2018 13:21:09 +0300 Subject: [PATCH] bpo-34197: Make _csv.Dialect attributes booleans. (GH-8440) Attributes skipinitialspace, doublequote and strict are now booleans instead of integers 0 or 1. --- Lib/test/test_csv.py | 40 +++++++++---------- .../2018-07-23-14-12-28.bpo-34197.7yFSP5.rst | 3 ++ Modules/_csv.c | 31 +++++++------- 3 files changed, 38 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-07-23-14-12-28.bpo-34197.7yFSP5.rst diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index b65cbf6a50e..7a333139b5e 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -48,13 +48,13 @@ class Test_Csv(unittest.TestCase): obj = ctor(*args) # Check defaults self.assertEqual(obj.dialect.delimiter, ',') - self.assertEqual(obj.dialect.doublequote, True) + self.assertIs(obj.dialect.doublequote, True) self.assertEqual(obj.dialect.escapechar, None) self.assertEqual(obj.dialect.lineterminator, "\r\n") self.assertEqual(obj.dialect.quotechar, '"') self.assertEqual(obj.dialect.quoting, csv.QUOTE_MINIMAL) - self.assertEqual(obj.dialect.skipinitialspace, False) - self.assertEqual(obj.dialect.strict, False) + self.assertIs(obj.dialect.skipinitialspace, False) + self.assertIs(obj.dialect.strict, False) # Try deleting or changing attributes (they are read-only) self.assertRaises(AttributeError, delattr, obj.dialect, 'delimiter') self.assertRaises(AttributeError, setattr, obj.dialect, 'delimiter', ':') @@ -76,13 +76,13 @@ class Test_Csv(unittest.TestCase): strict=True) obj = ctor(*args, **kwargs) self.assertEqual(obj.dialect.delimiter, ':') - self.assertEqual(obj.dialect.doublequote, False) + self.assertIs(obj.dialect.doublequote, False) self.assertEqual(obj.dialect.escapechar, '\\') self.assertEqual(obj.dialect.lineterminator, "\r") self.assertEqual(obj.dialect.quotechar, '*') self.assertEqual(obj.dialect.quoting, csv.QUOTE_NONE) - self.assertEqual(obj.dialect.skipinitialspace, True) - self.assertEqual(obj.dialect.strict, True) + self.assertIs(obj.dialect.skipinitialspace, True) + self.assertIs(obj.dialect.strict, True) def test_reader_kw_attrs(self): self._test_kw_attrs(csv.reader, []) @@ -104,13 +104,13 @@ class Test_Csv(unittest.TestCase): args = args + (dialect,) obj = ctor(*args) self.assertEqual(obj.dialect.delimiter, '-') - self.assertEqual(obj.dialect.doublequote, False) + self.assertIs(obj.dialect.doublequote, False) self.assertEqual(obj.dialect.escapechar, '^') self.assertEqual(obj.dialect.lineterminator, "$") self.assertEqual(obj.dialect.quotechar, '#') self.assertEqual(obj.dialect.quoting, csv.QUOTE_ALL) - self.assertEqual(obj.dialect.skipinitialspace, True) - self.assertEqual(obj.dialect.strict, False) + self.assertIs(obj.dialect.skipinitialspace, True) + self.assertIs(obj.dialect.strict, False) def test_reader_dialect_attrs(self): self._test_dialect_attrs(csv.reader, []) @@ -976,15 +976,13 @@ Stonecutters Seafood and Chop House+ Lemont+ IL+ 12/19/02+ Week Back def test_has_header(self): sniffer = csv.Sniffer() - self.assertEqual(sniffer.has_header(self.sample1), False) - self.assertEqual(sniffer.has_header(self.header1 + self.sample1), - True) + self.assertIs(sniffer.has_header(self.sample1), False) + self.assertIs(sniffer.has_header(self.header1 + self.sample1), True) def test_has_header_regex_special_delimiter(self): sniffer = csv.Sniffer() - self.assertEqual(sniffer.has_header(self.sample8), False) - self.assertEqual(sniffer.has_header(self.header2 + self.sample8), - True) + self.assertIs(sniffer.has_header(self.sample8), False) + self.assertIs(sniffer.has_header(self.header2 + self.sample8), True) def test_guess_quote_and_delimiter(self): sniffer = csv.Sniffer() @@ -1001,12 +999,12 @@ Stonecutters Seafood and Chop House+ Lemont+ IL+ 12/19/02+ Week Back dialect = sniffer.sniff(self.sample1) self.assertEqual(dialect.delimiter, ",") self.assertEqual(dialect.quotechar, '"') - self.assertEqual(dialect.skipinitialspace, True) + self.assertIs(dialect.skipinitialspace, True) dialect = sniffer.sniff(self.sample2) self.assertEqual(dialect.delimiter, ":") self.assertEqual(dialect.quotechar, "'") - self.assertEqual(dialect.skipinitialspace, False) + self.assertIs(dialect.skipinitialspace, False) def test_delimiters(self): sniffer = csv.Sniffer() @@ -1068,7 +1066,7 @@ class TestLeaks(unittest.TestCase): delta = rc-lastrc lastrc = rc # if csv.reader() leaks, last delta should be 3 or more - self.assertEqual(delta < 3, True) + self.assertLess(delta, 3) def test_create_write(self): delta = 0 @@ -1084,7 +1082,7 @@ class TestLeaks(unittest.TestCase): delta = rc-lastrc lastrc = rc # if csv.writer() leaks, last delta should be 3 or more - self.assertEqual(delta < 3, True) + self.assertLess(delta, 3) def test_read(self): delta = 0 @@ -1100,7 +1098,7 @@ class TestLeaks(unittest.TestCase): delta = rc-lastrc lastrc = rc # if reader leaks during read, delta should be 5 or more - self.assertEqual(delta < 5, True) + self.assertLess(delta, 5) def test_write(self): delta = 0 @@ -1117,7 +1115,7 @@ class TestLeaks(unittest.TestCase): delta = rc-lastrc lastrc = rc # if writer leaks during write, last delta should be 5 or more - self.assertEqual(delta < 5, True) + self.assertLess(delta, 5) class TestUnicode(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2018-07-23-14-12-28.bpo-34197.7yFSP5.rst b/Misc/NEWS.d/next/Library/2018-07-23-14-12-28.bpo-34197.7yFSP5.rst new file mode 100644 index 00000000000..344d7280f20 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-23-14-12-28.bpo-34197.7yFSP5.rst @@ -0,0 +1,3 @@ +Attributes *skipinitialspace*, *doublequote* and *strict* of the *dialect* +attribute of the :mod:`csv` reader are now :class:`bool` instances instead +of integers 0 or 1. diff --git a/Modules/_csv.c b/Modules/_csv.c index 610f7bb0632..f58538c4272 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -12,6 +12,7 @@ module instead. #include "Python.h" #include "structmember.h" +#include typedef struct { @@ -74,15 +75,15 @@ static const StyleDesc quote_styles[] = { typedef struct { PyObject_HEAD - int doublequote; /* is " represented by ""? */ - Py_UCS4 delimiter; /* field separator */ - Py_UCS4 quotechar; /* quote character */ - Py_UCS4 escapechar; /* escape character */ - int skipinitialspace; /* ignore spaces following delimiter? */ - PyObject *lineterminator; /* string to write between records */ + char doublequote; /* is " represented by ""? */ + char skipinitialspace; /* ignore spaces following delimiter? */ + char strict; /* raise exception on bad CSV */ int quoting; /* style of quoting to write */ + Py_UCS4 delimiter; /* field separator */ + Py_UCS4 quotechar; /* quote character */ + Py_UCS4 escapechar; /* escape character */ + PyObject *lineterminator; /* string to write between records */ - int strict; /* raise exception on bad CSV */ } DialectObj; static PyTypeObject Dialect_Type; @@ -189,7 +190,7 @@ Dialect_get_quoting(DialectObj *self) } static int -_set_bool(const char *name, int *target, PyObject *src, int dflt) +_set_bool(const char *name, char *target, PyObject *src, bool dflt) { if (src == NULL) *target = dflt; @@ -197,7 +198,7 @@ _set_bool(const char *name, int *target, PyObject *src, int dflt) int b = PyObject_IsTrue(src); if (b < 0) return -1; - *target = b; + *target = (char)b; } return 0; } @@ -292,9 +293,9 @@ dialect_check_quoting(int quoting) #define D_OFF(x) offsetof(DialectObj, x) static struct PyMemberDef Dialect_memberlist[] = { - { "skipinitialspace", T_INT, D_OFF(skipinitialspace), READONLY }, - { "doublequote", T_INT, D_OFF(doublequote), READONLY }, - { "strict", T_INT, D_OFF(strict), READONLY }, + { "skipinitialspace", T_BOOL, D_OFF(skipinitialspace), READONLY }, + { "doublequote", T_BOOL, D_OFF(doublequote), READONLY }, + { "strict", T_BOOL, D_OFF(strict), READONLY }, { NULL } }; @@ -411,13 +412,13 @@ dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) if (meth(name, target, src, dflt)) \ goto err DIASET(_set_char, "delimiter", &self->delimiter, delimiter, ','); - DIASET(_set_bool, "doublequote", &self->doublequote, doublequote, 1); + DIASET(_set_bool, "doublequote", &self->doublequote, doublequote, true); DIASET(_set_char, "escapechar", &self->escapechar, escapechar, 0); DIASET(_set_str, "lineterminator", &self->lineterminator, lineterminator, "\r\n"); DIASET(_set_char, "quotechar", &self->quotechar, quotechar, '"'); DIASET(_set_int, "quoting", &self->quoting, quoting, QUOTE_MINIMAL); - DIASET(_set_bool, "skipinitialspace", &self->skipinitialspace, skipinitialspace, 0); - DIASET(_set_bool, "strict", &self->strict, strict, 0); + DIASET(_set_bool, "skipinitialspace", &self->skipinitialspace, skipinitialspace, false); + DIASET(_set_bool, "strict", &self->strict, strict, false); /* validate options */ if (dialect_check_quoting(self->quoting))