Skip to content

Commit 5dcaf49

Browse files
authored
BUG: Incomplete Styler copy methods fix (#39708) (#39975)
1 parent 5b8952d commit 5dcaf49

File tree

3 files changed

+108
-22
lines changed

3 files changed

+108
-22
lines changed

doc/source/whatsnew/v1.3.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ Other enhancements
131131
- Disallow :class:`DataFrame` indexer for ``iloc`` for :meth:`Series.__getitem__` and :meth:`DataFrame.__getitem__`, (:issue:`39004`)
132132
- :meth:`Series.apply` can now accept list-like or dictionary-like arguments that aren't lists or dictionaries, e.g. ``ser.apply(np.array(["sum", "mean"]))``, which was already the case for :meth:`DataFrame.apply` (:issue:`39140`)
133133
- :meth:`DataFrame.plot.scatter` can now accept a categorical column as the argument to ``c`` (:issue:`12380`, :issue:`31357`)
134-
- :meth:`.Styler.set_tooltips` allows on hover tooltips to be added to styled HTML dataframes (:issue:`35643`, :issue:`21266`, :issue:`39317`)
134+
- :meth:`.Styler.set_tooltips` allows on hover tooltips to be added to styled HTML dataframes (:issue:`35643`, :issue:`21266`, :issue:`39317`, :issue:`39708`)
135135
- :meth:`.Styler.set_tooltips_class` and :meth:`.Styler.set_table_styles` amended to optionally allow certain css-string input arguments (:issue:`39564`)
136136
- :meth:`.Styler.apply` now more consistently accepts ndarray function returns, i.e. in all cases for ``axis`` is ``0, 1 or None`` (:issue:`39359`)
137137
- :meth:`.Styler.apply` and :meth:`.Styler.applymap` now raise errors if wrong format CSS is passed on render (:issue:`39660`)

pandas/io/formats/style.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -785,16 +785,29 @@ def _copy(self, deepcopy: bool = False) -> Styler:
785785
self.data,
786786
precision=self.precision,
787787
caption=self.caption,
788-
uuid=self.uuid,
789-
table_styles=self.table_styles,
788+
table_attributes=self.table_attributes,
789+
cell_ids=self.cell_ids,
790790
na_rep=self.na_rep,
791791
)
792+
793+
styler.uuid = self.uuid
794+
styler.hidden_index = self.hidden_index
795+
792796
if deepcopy:
793797
styler.ctx = copy.deepcopy(self.ctx)
794798
styler._todo = copy.deepcopy(self._todo)
799+
styler.table_styles = copy.deepcopy(self.table_styles)
800+
styler.hidden_columns = copy.copy(self.hidden_columns)
801+
styler.cell_context = copy.deepcopy(self.cell_context)
802+
styler.tooltips = copy.deepcopy(self.tooltips)
795803
else:
796804
styler.ctx = self.ctx
797805
styler._todo = self._todo
806+
styler.table_styles = self.table_styles
807+
styler.hidden_columns = self.hidden_columns
808+
styler.cell_context = self.cell_context
809+
styler.tooltips = self.tooltips
810+
798811
return styler
799812

800813
def __copy__(self) -> Styler:

pandas/tests/io/formats/style/test_style.py

+92-19
Original file line numberDiff line numberDiff line change
@@ -72,28 +72,101 @@ def test_update_ctx_flatten_multi_and_trailing_semi(self):
7272
}
7373
assert self.styler.ctx == expected
7474

75-
def test_copy(self):
76-
s2 = copy.copy(self.styler)
77-
assert self.styler is not s2
78-
assert self.styler.ctx is s2.ctx # shallow
79-
assert self.styler._todo is s2._todo
80-
81-
self.styler._update_ctx(self.attrs)
82-
self.styler.highlight_max()
83-
assert self.styler.ctx == s2.ctx
84-
assert self.styler._todo == s2._todo
85-
86-
def test_deepcopy(self):
87-
s2 = copy.deepcopy(self.styler)
88-
assert self.styler is not s2
89-
assert self.styler.ctx is not s2.ctx
90-
assert self.styler._todo is not s2._todo
75+
@pytest.mark.parametrize("do_changes", [True, False])
76+
@pytest.mark.parametrize("do_render", [True, False])
77+
def test_copy(self, do_changes, do_render):
78+
# Updated in GH39708
79+
# Change some defaults (to check later if the new values are copied)
80+
if do_changes:
81+
self.styler.set_table_styles(
82+
[{"selector": "th", "props": [("foo", "bar")]}]
83+
)
84+
self.styler.set_table_attributes('class="foo" data-bar')
85+
self.styler.hidden_index = not self.styler.hidden_index
86+
self.styler.hide_columns("A")
87+
classes = pd.DataFrame(
88+
[["favorite-val red", ""], [None, "blue my-val"]],
89+
index=self.df.index,
90+
columns=self.df.columns,
91+
)
92+
self.styler.set_td_classes(classes)
93+
ttips = pd.DataFrame(
94+
data=[["Favorite", ""], [np.nan, "my"]],
95+
columns=self.df.columns,
96+
index=self.df.index,
97+
)
98+
self.styler.set_tooltips(ttips)
99+
self.styler.cell_ids = not self.styler.cell_ids
100+
101+
if do_render:
102+
self.styler.render()
103+
104+
s_copy = copy.copy(self.styler)
105+
s_deepcopy = copy.deepcopy(self.styler)
106+
107+
assert self.styler is not s_copy
108+
assert self.styler is not s_deepcopy
109+
110+
# Check for identity
111+
assert self.styler.ctx is s_copy.ctx
112+
assert self.styler._todo is s_copy._todo
113+
assert self.styler.table_styles is s_copy.table_styles
114+
assert self.styler.hidden_columns is s_copy.hidden_columns
115+
assert self.styler.cell_context is s_copy.cell_context
116+
assert self.styler.tooltips is s_copy.tooltips
117+
if do_changes: # self.styler.tooltips is not None
118+
assert self.styler.tooltips.tt_data is s_copy.tooltips.tt_data
119+
assert (
120+
self.styler.tooltips.class_properties
121+
is s_copy.tooltips.class_properties
122+
)
123+
assert self.styler.tooltips.table_styles is s_copy.tooltips.table_styles
124+
125+
# Check for non-identity
126+
assert self.styler.ctx is not s_deepcopy.ctx
127+
assert self.styler._todo is not s_deepcopy._todo
128+
assert self.styler.hidden_columns is not s_deepcopy.hidden_columns
129+
assert self.styler.cell_context is not s_deepcopy.cell_context
130+
if do_changes: # self.styler.table_style is not None
131+
assert self.styler.table_styles is not s_deepcopy.table_styles
132+
if do_changes: # self.styler.tooltips is not None
133+
assert self.styler.tooltips is not s_deepcopy.tooltips
134+
assert self.styler.tooltips.tt_data is not s_deepcopy.tooltips.tt_data
135+
assert (
136+
self.styler.tooltips.class_properties
137+
is not s_deepcopy.tooltips.class_properties
138+
)
139+
assert (
140+
self.styler.tooltips.table_styles
141+
is not s_deepcopy.tooltips.table_styles
142+
)
91143

92144
self.styler._update_ctx(self.attrs)
93145
self.styler.highlight_max()
94-
assert self.styler.ctx != s2.ctx
95-
assert s2._todo == []
96-
assert self.styler._todo != s2._todo
146+
assert self.styler.ctx == s_copy.ctx
147+
assert self.styler.ctx != s_deepcopy.ctx
148+
assert self.styler._todo == s_copy._todo
149+
assert self.styler._todo != s_deepcopy._todo
150+
assert s_deepcopy._todo == []
151+
152+
equal_attributes = [
153+
"table_styles",
154+
"table_attributes",
155+
"cell_ids",
156+
"hidden_index",
157+
"hidden_columns",
158+
"cell_context",
159+
]
160+
for s2 in [s_copy, s_deepcopy]:
161+
for att in equal_attributes:
162+
assert self.styler.__dict__[att] == s2.__dict__[att]
163+
if do_changes: # self.styler.tooltips is not None
164+
tm.assert_frame_equal(self.styler.tooltips.tt_data, s2.tooltips.tt_data)
165+
assert (
166+
self.styler.tooltips.class_properties
167+
== s2.tooltips.class_properties
168+
)
169+
assert self.styler.tooltips.table_styles == s2.tooltips.table_styles
97170

98171
def test_clear(self):
99172
# updated in GH 39396

0 commit comments

Comments
 (0)