From 7e92373cbba32aea398196b5287ce77193463707 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Mon, 8 Nov 2021 07:53:21 -0600 Subject: [PATCH 1/8] bpo-23041: update proposed changes to csv module. --- Doc/library/csv.rst | 12 ++++++++++++ Lib/csv.py | 2 ++ Lib/test/test_csv.py | 4 ++++ .../2021-11-07-15-31-25.bpo-23041.564i32.rst | 2 ++ Modules/_csv.c | 16 +++++++++++++++- 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 3a7817cfdfad87..a7cd08c38bf6ac 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -335,6 +335,18 @@ The :mod:`csv` module defines the following constants: Instructs :class:`reader` to perform no special processing of quote characters. +.. data:: QUOTE_NOTNULL + + Instructs :class:`writer` objects to quote all fields which are not + ``None``. If a field value is ``None`` an empty (unquoted) string + is written. + +.. data:: QUOTE_STRINGS + + Instructs :class:`writer` quotes are always placed around fields + which are strings. Note that ``None`` will be written as a + bar (unquoted) empty string. + The :mod:`csv` module defines the following exception: diff --git a/Lib/csv.py b/Lib/csv.py index bb3ee269ae7931..58eff2139f81af 100644 --- a/Lib/csv.py +++ b/Lib/csv.py @@ -8,12 +8,14 @@ unregister_dialect, get_dialect, list_dialects, \ field_size_limit, \ QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE, \ + QUOTE_STRINGS, QUOTE_NOTNULL, \ __doc__ from _csv import Dialect as _Dialect from io import StringIO __all__ = ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE", + "QUOTE_STRINGS", "QUOTE_NOTNULL", "Error", "Dialect", "__doc__", "excel", "excel_tab", "field_size_limit", "reader", "writer", "register_dialect", "get_dialect", "list_dialects", "Sniffer", diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 95a19dd46cb4ff..699a88488e5aa0 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -187,6 +187,10 @@ def test_write_quoting(self): quoting = csv.QUOTE_ALL) self._write_test(['a\nb',1], '"a\nb","1"', quoting = csv.QUOTE_ALL) + self._write_test(['a',None,1], '"a",,1', + quoting = csv.QUOTE_STRINGS) + self._write_test(['a',None,1], '"a",,"1"', + quoting = csv.QUOTE_NOTNULL) def test_write_escape(self): self._write_test(['a',1,'p,q'], 'a,1,"p,q"', diff --git a/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst b/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst new file mode 100644 index 00000000000000..1249bee4df3c99 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst @@ -0,0 +1,2 @@ +Add QUOTE_STRINGS and QUOTE_NOTNULL to the suite of csv module quoting +styles. diff --git a/Modules/_csv.c b/Modules/_csv.c index 1c2f504ea5c097..49300212214a78 100644 --- a/Modules/_csv.c +++ b/Modules/_csv.c @@ -74,7 +74,8 @@ typedef enum { } ParserState; typedef enum { - QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE + QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE, + QUOTE_STRINGS, QUOTE_NOTNULL } QuoteStyle; typedef struct { @@ -87,6 +88,8 @@ static const StyleDesc quote_styles[] = { { QUOTE_ALL, "QUOTE_ALL" }, { QUOTE_NONNUMERIC, "QUOTE_NONNUMERIC" }, { QUOTE_NONE, "QUOTE_NONE" }, + { QUOTE_STRINGS, "QUOTE_STRINGS" }, + { QUOTE_NOTNULL, "QUOTE_NOTNULL" }, { 0 } }; @@ -1259,6 +1262,12 @@ csv_writerow(WriterObj *self, PyObject *seq) case QUOTE_ALL: quoted = 1; break; + case QUOTE_STRINGS: + quoted = PyUnicode_Check(field); + break; + case QUOTE_NOTNULL: + quoted = field != Py_None; + break; default: quoted = 0; break; @@ -1610,6 +1619,11 @@ PyDoc_STRVAR(csv_module_doc, " csv.QUOTE_NONNUMERIC means that quotes are always placed around\n" " fields which do not parse as integers or floating point\n" " numbers.\n" +" csv.QUOTE_STRINGS means that quotes are always placed around\n" +" fields which are strings. Note that the Python value None\n" +" is not a string.\n" +" csv.QUOTE_NOTNULL means that quotes are only placed around fields\n" +" that are not the Python value None.\n" " csv.QUOTE_NONE means that quotes are never placed around fields.\n" " * escapechar - specifies a one-character string used to escape\n" " the delimiter when quoting is set to QUOTE_NONE.\n" From 54df6b8f798ae7dbbdb8f882ab78615000df866d Mon Sep 17 00:00:00 2001 From: Sam Denton Date: Tue, 11 Apr 2023 10:02:10 -0500 Subject: [PATCH 2/8] Update test_csv.py Improve tests of QUOTE_STRINGS and QUOTE_NOTNULL --- Lib/test/test_csv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index db5b902631bdfb..8fb97bc0c1a1a7 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -187,9 +187,9 @@ def test_write_quoting(self): quoting = csv.QUOTE_ALL) self._write_test(['a\nb',1], '"a\nb","1"', quoting = csv.QUOTE_ALL) - self._write_test(['a',None,1], '"a",,1', + self._write_test(['a','',None,1], '"a","",,1', quoting = csv.QUOTE_STRINGS) - self._write_test(['a',None,1], '"a",,"1"', + self._write_test(['a','',None,1], '"a","",,"1"', quoting = csv.QUOTE_NOTNULL) def test_write_escape(self): From d2337816ce9251056a242cf4ddb971a656940791 Mon Sep 17 00:00:00 2001 From: Sam Denton Date: Tue, 11 Apr 2023 10:24:40 -0500 Subject: [PATCH 3/8] Update csv.rst Improve descriptions of QUOTE_NOTNULL and QUOTE_STRINGS with writer objects. Add descriptions for reader objects. --- Doc/library/csv.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 54770a63c7d71f..dcc986d30470e5 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -327,7 +327,7 @@ The :mod:`csv` module defines the following constants: Instructs :class:`writer` objects to quote all non-numeric fields. - Instructs the reader to convert all non-quoted fields to type *float*. + Instructs :class:`reader` to convert all non-quoted fields to type *float*. .. data:: QUOTE_NONE @@ -342,14 +342,20 @@ The :mod:`csv` module defines the following constants: .. data:: QUOTE_NOTNULL Instructs :class:`writer` objects to quote all fields which are not - ``None``. If a field value is ``None`` an empty (unquoted) string - is written. + ``None``. This is similar to QUOTE_ALL, except that if a + field value is ``None`` an empty (unquoted) string is written. + + Instructs :class:`reader` to interpret an empty (unquoted) field as None, and + otherwise behave as QUOTE_ALL. .. data:: QUOTE_STRINGS - Instructs :class:`writer` quotes are always placed around fields - which are strings. Note that ``None`` will be written as a - bar (unquoted) empty string. + Instructs :class:`writer` objects to always place quotes around fields + which are strings. This is similar to QUOTE_NONNUMERIC, except that if a + field value is ``None`` an empty (unquoted) string is written. + + Instructs :class:`reader` to interpret an empty (unquoted) string as None, and + otherwise behave as QUOTE_NONNUMERIC. The :mod:`csv` module defines the following exception: From 1f573045390aa0885251a7baf0f757fd7742b96f Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Wed, 12 Apr 2023 05:45:13 -0500 Subject: [PATCH 4/8] remove trailing whitespace --- Doc/library/csv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index dcc986d30470e5..4b78d7495958dc 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -344,7 +344,7 @@ The :mod:`csv` module defines the following constants: Instructs :class:`writer` objects to quote all fields which are not ``None``. This is similar to QUOTE_ALL, except that if a field value is ``None`` an empty (unquoted) string is written. - + Instructs :class:`reader` to interpret an empty (unquoted) field as None, and otherwise behave as QUOTE_ALL. From a32ef44a69b778770ddc7a3038750f9f97586df1 Mon Sep 17 00:00:00 2001 From: Sam Denton Date: Wed, 12 Apr 2023 09:55:43 -0500 Subject: [PATCH 5/8] Update csv.rst Harmonize references to reader and writer objects. Change to "instructs reader to interpret ... and to otherwise behave" --- Doc/library/csv.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 4b78d7495958dc..97ed4313e198f4 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -327,7 +327,7 @@ The :mod:`csv` module defines the following constants: Instructs :class:`writer` objects to quote all non-numeric fields. - Instructs :class:`reader` to convert all non-quoted fields to type *float*. + Instructs :class:`reader` objects to convert all non-quoted fields to type *float*. .. data:: QUOTE_NONE @@ -337,25 +337,25 @@ The :mod:`csv` module defines the following constants: character. If *escapechar* is not set, the writer will raise :exc:`Error` if any characters that require escaping are encountered. - Instructs :class:`reader` to perform no special processing of quote characters. + Instructs :class:`reader` objects to perform no special processing of quote characters. .. data:: QUOTE_NOTNULL Instructs :class:`writer` objects to quote all fields which are not - ``None``. This is similar to QUOTE_ALL, except that if a + ``None``. This is similar to :data:`QUOTE_ALL`, except that if a field value is ``None`` an empty (unquoted) string is written. - Instructs :class:`reader` to interpret an empty (unquoted) field as None, and - otherwise behave as QUOTE_ALL. + Instructs :class:`reader` objects to interpret an empty (unquoted) field as None and + to otherwise behave as :data:`QUOTE_ALL`. .. data:: QUOTE_STRINGS Instructs :class:`writer` objects to always place quotes around fields - which are strings. This is similar to QUOTE_NONNUMERIC, except that if a + which are strings. This is similar to :data:`QUOTE_NONNUMERIC`, except that if a field value is ``None`` an empty (unquoted) string is written. - Instructs :class:`reader` to interpret an empty (unquoted) string as None, and - otherwise behave as QUOTE_NONNUMERIC. + Instructs :class:`reader`objects to interpret an empty (unquoted) string as None and + to otherwise behave as :data:`QUOTE_NONNUMERIC`. The :mod:`csv` module defines the following exception: From f1bc87b3548381f9f5a239f218fdc4b8176f8b21 Mon Sep 17 00:00:00 2001 From: Sam Denton Date: Wed, 12 Apr 2023 09:58:38 -0500 Subject: [PATCH 6/8] Update 2021-11-07-15-31-25.bpo-23041.564i32.rst Add styling --- .../next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst b/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst index 1249bee4df3c99..035637b7c6b30c 100644 --- a/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst +++ b/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst @@ -1,2 +1,2 @@ -Add QUOTE_STRINGS and QUOTE_NOTNULL to the suite of csv module quoting +Add :data:`~csv.QUOTE_STRINGS` and :data:`~csv.QUOTE_NOTNULL` to the suite of :mod:`csv` module quoting styles. From 4d03980b6454ec19c6bbb4c81dbd6ba00550004b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric?= Date: Wed, 12 Apr 2023 13:27:33 -0400 Subject: [PATCH 7/8] wrap line --- .../next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst b/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst index 035637b7c6b30c..53c32d397b206b 100644 --- a/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst +++ b/Misc/NEWS.d/next/Library/2021-11-07-15-31-25.bpo-23041.564i32.rst @@ -1,2 +1,2 @@ -Add :data:`~csv.QUOTE_STRINGS` and :data:`~csv.QUOTE_NOTNULL` to the suite of :mod:`csv` module quoting -styles. +Add :data:`~csv.QUOTE_STRINGS` and :data:`~csv.QUOTE_NOTNULL` to the suite +of :mod:`csv` module quoting styles. From ba74857783bf2e50b5ee3c678b55772a1bbf06eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric?= Date: Wed, 12 Apr 2023 13:28:12 -0400 Subject: [PATCH 8/8] fix markup --- Doc/library/csv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst index 97ed4313e198f4..64baa69be4af31 100644 --- a/Doc/library/csv.rst +++ b/Doc/library/csv.rst @@ -354,7 +354,7 @@ The :mod:`csv` module defines the following constants: which are strings. This is similar to :data:`QUOTE_NONNUMERIC`, except that if a field value is ``None`` an empty (unquoted) string is written. - Instructs :class:`reader`objects to interpret an empty (unquoted) string as None and + Instructs :class:`reader` objects to interpret an empty (unquoted) string as ``None`` and to otherwise behave as :data:`QUOTE_NONNUMERIC`. The :mod:`csv` module defines the following exception: