Skip to content

Commit a4fc97e

Browse files
BUG: escape single quotes in index names when printing (#60251)
1 parent 1d809c3 commit a4fc97e

File tree

4 files changed

+27
-3
lines changed

4 files changed

+27
-3
lines changed

doc/source/whatsnew/v3.0.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ Indexing
667667
^^^^^^^^
668668
- Bug in :meth:`DataFrame.__getitem__` returning modified columns when called with ``slice`` in Python 3.12 (:issue:`57500`)
669669
- Bug in :meth:`DataFrame.from_records` throwing a ``ValueError`` when passed an empty list in ``index`` (:issue:`58594`)
670-
-
670+
- Bug in printing :attr:`Index.names` and :attr:`MultiIndex.levels` would not escape single quotes (:issue:`60190`)
671671

672672
Missing
673673
^^^^^^^

pandas/core/indexes/frozen.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ def _disabled(self, *args, **kwargs) -> NoReturn:
110110
raise TypeError(f"'{type(self).__name__}' does not support mutable operations.")
111111

112112
def __str__(self) -> str:
113-
return pprint_thing(self, quote_strings=True, escape_chars=("\t", "\r", "\n"))
113+
return pprint_thing(
114+
self, quote_strings=True, escape_chars=("\t", "\r", "\n", "'")
115+
)
114116

115117
def __repr__(self) -> str:
116118
return f"{type(self).__name__}({self!s})"

pandas/io/formats/printing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def pprint_thing(
203203
def as_escaped_string(
204204
thing: Any, escape_chars: EscapeChars | None = escape_chars
205205
) -> str:
206-
translate = {"\t": r"\t", "\n": r"\n", "\r": r"\r"}
206+
translate = {"\t": r"\t", "\n": r"\n", "\r": r"\r", "'": r"\'"}
207207
if isinstance(escape_chars, Mapping):
208208
if default_escapes:
209209
translate.update(escape_chars)

pandas/tests/io/formats/test_printing.py

+22
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,33 @@
33
from collections.abc import Mapping
44
import string
55

6+
import pytest
7+
68
import pandas._config.config as cf
79

10+
import pandas as pd
11+
812
from pandas.io.formats import printing
913

1014

15+
@pytest.mark.parametrize(
16+
"input_names, expected_names",
17+
[
18+
(["'a b"], "['\\'a b']"), # Escape leading quote
19+
(["test's b"], "['test\\'s b']"), # Escape apostrophe
20+
(["'test' b"], "['\\'test\\' b']"), # Escape surrounding quotes
21+
(["test b'"], "['test b\\'']"), # Escape single quote
22+
(["test\n' b"], "['test\\n\\' b']"), # Escape quotes, preserve newline
23+
],
24+
)
25+
def test_formatted_index_names(input_names, expected_names):
26+
# GH#60190
27+
df = pd.DataFrame({name: [1, 2, 3] for name in input_names}).set_index(input_names)
28+
formatted_names = str(df.index.names)
29+
30+
assert formatted_names == expected_names
31+
32+
1133
def test_adjoin():
1234
data = [["a", "b", "c"], ["dd", "ee", "ff"], ["ggg", "hhh", "iii"]]
1335
expected = "a dd ggg\nb ee hhh\nc ff iii"

0 commit comments

Comments
 (0)