From ec79ab72db55267f8c45985cf4aee01f212138a0 Mon Sep 17 00:00:00 2001 From: KevsterAmp Date: Thu, 29 May 2025 19:51:52 +0800 Subject: [PATCH 1/4] initialize quotechar when self.escapechar is not None even if self.quoting is csv.QUOTENONE --- pandas/io/formats/csvs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/csvs.py b/pandas/io/formats/csvs.py index 75bcb51ef4be2..1b9eb6303fe74 100644 --- a/pandas/io/formats/csvs.py +++ b/pandas/io/formats/csvs.py @@ -90,9 +90,9 @@ def __init__( self.index_label = self._initialize_index_label(index_label) self.errors = errors self.quoting = quoting or csvlib.QUOTE_MINIMAL - self.quotechar = self._initialize_quotechar(quotechar) self.doublequote = doublequote self.escapechar = escapechar + self.quotechar = self._initialize_quotechar(quotechar) self.lineterminator = lineterminator or os.linesep self.date_format = date_format self.cols = self._initialize_columns(cols) @@ -141,7 +141,7 @@ def _get_index_label_flat(self) -> Sequence[Hashable]: return [""] if index_label is None else [index_label] def _initialize_quotechar(self, quotechar: str | None) -> str | None: - if self.quoting != csvlib.QUOTE_NONE: + if self.quoting != csvlib.QUOTE_NONE or self.escapechar is not None: # prevents crash in _csv return quotechar return None From 75f5761c6f030ec7e8c321c42a910bab59603557 Mon Sep 17 00:00:00 2001 From: KevsterAmp Date: Thu, 29 May 2025 22:03:22 +0800 Subject: [PATCH 2/4] add test --- pandas/tests/frame/methods/test_to_csv.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pandas/tests/frame/methods/test_to_csv.py b/pandas/tests/frame/methods/test_to_csv.py index 9eafc69013ffe..34d120145b381 100644 --- a/pandas/tests/frame/methods/test_to_csv.py +++ b/pandas/tests/frame/methods/test_to_csv.py @@ -1450,3 +1450,22 @@ def test_to_csv_warn_when_zip_tar_and_append_mode(self, tmp_path): RuntimeWarning, match=msg, raise_on_extra_warnings=False ): df.to_csv(tar_path, mode="a") + + def test_to_csv_escape_quotechar(self): + # GH61514 + df = DataFrame( + { + "col_a": ["a", "a2"], + "col_b": ['b"c', None], + "col_c": ['de,f"', '"c'], + } + ) + + result = df.to_csv(quotechar='"', escapechar="\\", quoting=csv.QUOTE_NONE) + expected_rows = [ + ",col_a,col_b,col_c", + '0,a,b\\"c,de\\,f\\"', + '1,a2,,\\"c', + ] + expected = tm.convert_rows_list_to_csv_str(expected_rows) + assert result == expected From 3737a65aff5f63bd81c227e01c236dab934568f4 Mon Sep 17 00:00:00 2001 From: KevsterAmp Date: Thu, 29 May 2025 22:07:08 +0800 Subject: [PATCH 3/4] add to whatsnew --- doc/source/whatsnew/v3.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 76f6eab97c4eb..bc1bfcbf459a2 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -790,8 +790,8 @@ I/O - Bug in :meth:`read_stata` where extreme value integers were incorrectly interpreted as missing for format versions 111 and prior (:issue:`58130`) - Bug in :meth:`read_stata` where the missing code for double was not recognised for format versions 105 and prior (:issue:`58149`) - Bug in :meth:`set_option` where setting the pandas option ``display.html.use_mathjax`` to ``False`` has no effect (:issue:`59884`) +- Bug in :meth:`to_csv` where ``quotechar``` is not escaped when ``escapechar`` is not None (:issue:`61407`) - Bug in :meth:`to_excel` where :class:`MultiIndex` columns would be merged to a single row when ``merge_cells=False`` is passed (:issue:`60274`) - Period ^^^^^^ - Fixed error message when passing invalid period alias to :meth:`PeriodIndex.to_timestamp` (:issue:`58974`) From bf563253a7ed5f4a6d7fad265bbf7e2534d5704d Mon Sep 17 00:00:00 2001 From: KevsterAmp Date: Fri, 30 May 2025 07:58:25 +0800 Subject: [PATCH 4/4] add space --- doc/source/whatsnew/v3.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index bc1bfcbf459a2..a1d98e99a3cf2 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -792,6 +792,7 @@ I/O - Bug in :meth:`set_option` where setting the pandas option ``display.html.use_mathjax`` to ``False`` has no effect (:issue:`59884`) - Bug in :meth:`to_csv` where ``quotechar``` is not escaped when ``escapechar`` is not None (:issue:`61407`) - Bug in :meth:`to_excel` where :class:`MultiIndex` columns would be merged to a single row when ``merge_cells=False`` is passed (:issue:`60274`) + Period ^^^^^^ - Fixed error message when passing invalid period alias to :meth:`PeriodIndex.to_timestamp` (:issue:`58974`)