diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index ef904c1d7021a..1dcde2000fc89 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -385,6 +385,7 @@ Other ^^^^^ - Bug in :class:`Index` constructor sometimes silently ignorning a specified ``dtype`` (:issue:`38879`) - Bug in constructing a :class:`Series` from a list and a :class:`PandasDtype` (:issue:`39357`) +- Bug in :class:`Styler` which caused CSS to duplicate on multiple renders. (:issue:`39395`) - .. --------------------------------------------------------------------------- diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 73b62a99f4a7e..0cb9aa3bea6ab 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -830,6 +830,7 @@ def _compute(self): r = self for func, args, kwargs in self._todo: r = func(self)(*args, **kwargs) + self._todo = [] return r def _apply( @@ -1454,9 +1455,8 @@ def set_properties(self, subset=None, **kwargs) -> Styler: >>> df.style.set_properties(color="white", align="right") >>> df.style.set_properties(**{'background-color': 'yellow'}) """ - values = ";".join(f"{p}: {v}" for p, v in kwargs.items()) - f = lambda x: values - return self.applymap(f, subset=subset) + values = "".join(f"{p}: {v};" for p, v in kwargs.items()) + return self.applymap(lambda x: values, subset=subset) @staticmethod def _bar( diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index 51810786395b5..6556075272308 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -102,12 +102,27 @@ def test_deepcopy(self): assert self.styler._todo != s2._todo def test_clear(self): - s = self.df.style.highlight_max()._compute() - assert len(s.ctx) > 0 + # updated in GH 39396 + tt = DataFrame({"A": [None, "tt"]}) + css = DataFrame({"A": [None, "cls-a"]}) + s = self.df.style.highlight_max().set_tooltips(tt).set_td_classes(css) + # _todo, tooltips and cell_context items added to.. assert len(s._todo) > 0 + assert s.tooltips + assert len(s.cell_context) > 0 + + s = s._compute() + # ctx and _todo items affected when a render takes place + assert len(s.ctx) > 0 + assert len(s._todo) == 0 # _todo is emptied after compute. + + s._todo = [1] s.clear() + # ctx, _todo, tooltips and cell_context items all revert to null state. assert len(s.ctx) == 0 assert len(s._todo) == 0 + assert not s.tooltips + assert len(s.cell_context) == 0 def test_render(self): df = DataFrame({"A": [0, 1]}) @@ -116,6 +131,15 @@ def test_render(self): s.render() # it worked? + def test_multiple_render(self): + # GH 39396 + s = Styler(self.df, uuid_len=0).applymap(lambda x: "color: red;", subset=["A"]) + s.render() # do 2 renders to ensure css styles not duplicated + assert ( + '" in s.render() + ) + def test_render_empty_dfs(self): empty_df = DataFrame() es = Styler(empty_df)