Skip to content

Commit 052b7bd

Browse files
Bug: Strings in Excel number fomat do not preserve case(fixes pandas-dev#63117)
1 parent 704d990 commit 052b7bd

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

pandas/io/formats/css.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,10 @@ def _error() -> str:
391391
def atomize(self, declarations: Iterable) -> Generator[tuple[str, str]]:
392392
for prop, value in declarations:
393393
prop = prop.lower()
394-
value = value.lower()
394+
if prop == "number-format":
395+
value = value.strip()
396+
else:
397+
value = value.lower()
395398
if prop in self.CSS_EXPANSIONS:
396399
expand = self.CSS_EXPANSIONS[prop]
397400
yield from expand(self, prop, value)
@@ -414,7 +417,10 @@ def parse(self, declarations_str: str) -> Iterator[tuple[str, str]]:
414417
prop, sep, val = decl.partition(":")
415418
prop = prop.strip().lower()
416419
# TODO: don't lowercase case sensitive parts of values (strings)
417-
val = val.strip().lower()
420+
if prop == "number-format":
421+
val = val.strip()
422+
else:
423+
val = val.strip().lower()
418424
if sep:
419425
yield prop, val
420426
else:

pandas/tests/io/excel/test_style.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def test_styler_to_excel_unstyled(engine, tmp_excel):
9696
("font-style: italic;", ["font", "i"], True),
9797
("text-decoration: underline;", ["font", "u"], "single"),
9898
("number-format: $??,???.00;", ["number_format"], "$??,???.00"),
99+
("number-format: #,,"'"M";', ["number_format"], '#,,"M"'),
99100
("text-align: left;", ["alignment", "horizontal"], "left"),
100101
(
101102
"vertical-align: bottom;",

pandas/tests/io/formats/test_css.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,28 @@ def test_css_relative_font_size(size, relative_to, resolved):
286286
else:
287287
inherited = {"font-size": relative_to}
288288
assert_resolves(f"font-size: {size}", {"font-size": resolved}, inherited=inherited)
289+
290+
def test_css_atomize_preserves_number_format_case():
291+
# GHxxxx: number-format strings must keep case (e.g. "M" not "m")
292+
resolver = CSSResolver()
293+
declarations = [("NUMBER-FORMAT", '#,,"M"')]
294+
295+
props = dict(resolver.atomize(declarations))
296+
297+
# property name is normalized to lowercase
298+
assert "number-format" in props
299+
# value is preserved exactly (no lowercasing)
300+
assert props["number-format"] == '#,,"M"'
301+
302+
def test_css_parse_preserves_number_format_case():
303+
# GHxxxx: number-format should not be lowercased by parse()
304+
resolver = CSSResolver()
305+
306+
css = 'NUMBER-FORMAT: #,,"M"; COLOR: Red'
307+
result = dict(resolver.parse(css))
308+
309+
# "number-format" key is lowercase, but its value must be preserved
310+
assert result["number-format"] == '#,,"M"'
311+
312+
# other properties still get lowercased in the value
313+
assert result["color"] == "red"

0 commit comments

Comments
 (0)