Skip to content

Commit 45309f4

Browse files
attack68Kevin D Smith
authored and
Kevin D Smith
committed
PERF: styler uuid control and security (pandas-dev#36345)
1 parent ae37133 commit 45309f4

File tree

3 files changed

+34
-3
lines changed

3 files changed

+34
-3
lines changed

doc/source/whatsnew/v1.2.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ Performance improvements
226226
- Performance improvement in :meth:`GroupBy.agg` with the ``numba`` engine (:issue:`35759`)
227227
- Performance improvements when creating :meth:`pd.Series.map` from a huge dictionary (:issue:`34717`)
228228
- Performance improvement in :meth:`GroupBy.transform` with the ``numba`` engine (:issue:`36240`)
229+
- ``Styler`` uuid method altered to compress data transmission over web whilst maintaining reasonably low table collision probability (:issue:`36345`)
229230

230231
.. ---------------------------------------------------------------------------
231232

pandas/io/formats/style.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
Tuple,
1919
Union,
2020
)
21-
from uuid import uuid1
21+
from uuid import uuid4
2222

2323
import numpy as np
2424

@@ -89,6 +89,12 @@ class Styler:
8989
9090
.. versionadded:: 1.0.0
9191
92+
uuid_len : int, default 5
93+
If ``uuid`` is not specified, the length of the ``uuid`` to randomly generate
94+
expressed in hex characters, in range [0, 32].
95+
96+
.. versionadded:: 1.2.0
97+
9298
Attributes
9399
----------
94100
env : Jinja2 jinja2.Environment
@@ -144,6 +150,7 @@ def __init__(
144150
table_attributes: Optional[str] = None,
145151
cell_ids: bool = True,
146152
na_rep: Optional[str] = None,
153+
uuid_len: int = 5,
147154
):
148155
self.ctx: DefaultDict[Tuple[int, int], List[str]] = defaultdict(list)
149156
self._todo: List[Tuple[Callable, Tuple, Dict]] = []
@@ -159,7 +166,10 @@ def __init__(
159166
self.index = data.index
160167
self.columns = data.columns
161168

162-
self.uuid = uuid
169+
if not isinstance(uuid_len, int) or not uuid_len >= 0:
170+
raise TypeError("``uuid_len`` must be an integer in range [0, 32].")
171+
self.uuid_len = min(32, uuid_len)
172+
self.uuid = (uuid or uuid4().hex[: self.uuid_len]) + "_"
163173
self.table_styles = table_styles
164174
self.caption = caption
165175
if precision is None:
@@ -248,7 +258,7 @@ def _translate(self):
248258
precision = self.precision
249259
hidden_index = self.hidden_index
250260
hidden_columns = self.hidden_columns
251-
uuid = self.uuid or str(uuid1()).replace("-", "_")
261+
uuid = self.uuid
252262
ROW_HEADING_CLASS = "row_heading"
253263
COL_HEADING_CLASS = "col_heading"
254264
INDEX_NAME_CLASS = "index_name"

pandas/tests/io/formats/test_style.py

+20
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,26 @@ def test_colspan_w3(self):
17181718
s = Styler(df, uuid="_", cell_ids=False)
17191719
assert '<th class="col_heading level0 col0" colspan="2">l0</th>' in s.render()
17201720

1721+
@pytest.mark.parametrize("len_", [1, 5, 32, 33, 100])
1722+
def test_uuid_len(self, len_):
1723+
# GH 36345
1724+
df = pd.DataFrame(data=[["A"]])
1725+
s = Styler(df, uuid_len=len_, cell_ids=False).render()
1726+
strt = s.find('id="T_')
1727+
end = s[strt + 6 :].find('"')
1728+
if len_ > 32:
1729+
assert end == 32 + 1
1730+
else:
1731+
assert end == len_ + 1
1732+
1733+
@pytest.mark.parametrize("len_", [-2, "bad", None])
1734+
def test_uuid_len_raises(self, len_):
1735+
# GH 36345
1736+
df = pd.DataFrame(data=[["A"]])
1737+
msg = "``uuid_len`` must be an integer in range \\[0, 32\\]."
1738+
with pytest.raises(TypeError, match=msg):
1739+
Styler(df, uuid_len=len_, cell_ids=False).render()
1740+
17211741

17221742
@td.skip_if_no_mpl
17231743
class TestStylerMatplotlibDep:

0 commit comments

Comments
 (0)