Skip to content

Commit a610a4d

Browse files
authored
add in unsafe html format (PR#42)
adding in unsafe html format, in case unescaped html is required in table
1 parent 5ec0a6b commit a610a4d

File tree

3 files changed

+75
-9
lines changed

3 files changed

+75
-9
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ Supported table formats are:
149149
- "moinmoin"
150150
- "youtrack"
151151
- "html"
152+
- "unsafehtml"
152153
- "latex"
153154
- "latex\_raw"
154155
- "latex\_booktabs"
@@ -334,7 +335,8 @@ MediaWiki-based sites:
334335

335336
`html` produces standard HTML markup as an html.escape'd str
336337
with a ._repr_html_ method so that Jupyter Lab and Notebook display the HTML
337-
and a .str property so that the raw HTML remains accessible:
338+
and a .str property so that the raw HTML remains accessible
339+
the unsafehtml table format can be used if an unescaped HTML format is required:
338340

339341
>>> print(tabulate(table, headers, tablefmt="html"))
340342
<table>

tabulate.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,17 +182,23 @@ def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore):
182182
return "<table>\n<tbody>"
183183

184184

185-
def _html_row_with_attrs(celltag, cell_values, colwidths, colaligns):
185+
def _html_row_with_attrs(celltag, unsafe, cell_values, colwidths, colaligns):
186186
alignment = {
187187
"left": "",
188188
"right": ' style="text-align: right;"',
189189
"center": ' style="text-align: center;"',
190190
"decimal": ' style="text-align: right;"',
191191
}
192-
values_with_attrs = [
193-
"<{0}{1}>{2}</{0}>".format(celltag, alignment.get(a, ""), htmlescape(c))
194-
for c, a in zip(cell_values, colaligns)
195-
]
192+
if unsafe:
193+
values_with_attrs = [
194+
"<{0}{1}>{2}</{0}>".format(celltag, alignment.get(a, ""), c)
195+
for c, a in zip(cell_values, colaligns)
196+
]
197+
else:
198+
values_with_attrs = [
199+
"<{0}{1}>{2}</{0}>".format(celltag, alignment.get(a, ""), htmlescape(c))
200+
for c, a in zip(cell_values, colaligns)
201+
]
196202
rowhtml = "<tr>{}</tr>".format("".join(values_with_attrs).rstrip())
197203
if celltag == "th": # it's a header row, create a new table header
198204
rowhtml = "<table>\n<thead>\n{}\n</thead>\n<tbody>".format(rowhtml)
@@ -429,8 +435,18 @@ def escape_empty(val):
429435
linebelowheader="",
430436
linebetweenrows=None,
431437
linebelow=Line("</tbody>\n</table>", "", "", ""),
432-
headerrow=partial(_html_row_with_attrs, "th"),
433-
datarow=partial(_html_row_with_attrs, "td"),
438+
headerrow=partial(_html_row_with_attrs, "th", False),
439+
datarow=partial(_html_row_with_attrs, "td", False),
440+
padding=0,
441+
with_header_hide=["lineabove"],
442+
),
443+
"unsafehtml": TableFormat(
444+
lineabove=_html_begin_table_without_header,
445+
linebelowheader="",
446+
linebetweenrows=None,
447+
linebelow=Line("</tbody>\n</table>", "", "", ""),
448+
headerrow=partial(_html_row_with_attrs, "th", True),
449+
datarow=partial(_html_row_with_attrs, "td", True),
434450
padding=0,
435451
with_header_hide=["lineabove"],
436452
),
@@ -1360,7 +1376,8 @@ def tabulate(
13601376
13611377
"html" produces HTML markup as an html.escape'd str
13621378
with a ._repr_html_ method so that Jupyter Lab and Notebook display the HTML
1363-
and a .str property so that the raw HTML remains accessible:
1379+
and a .str property so that the raw HTML remains accessible
1380+
the unsafehtml table format can be used if an unescaped HTML format is required:
13641381
13651382
>>> print(tabulate([["strings", "numbers"], ["spam", 41.9999], ["eggs", "451.0"]],
13661383
... headers="firstrow", tablefmt="html"))
@@ -1421,6 +1438,7 @@ def tabulate(
14211438
e.g. `disable_numparse=[0, 2]` would disable number parsing only on the
14221439
first and third columns.
14231440
"""
1441+
14241442
if tabular_data is None:
14251443
tabular_data = []
14261444
list_of_lists, headers = _normalize_tabular_data(

test/test_output.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,11 @@ def test_moinmoin_headerless():
10041004

10051005
_test_table_html_headers = ["<strings>", "<&numbers&>"]
10061006
_test_table_html = [["spam >", 41.9999], ["eggs &", 451.0]]
1007+
_test_table_unsafehtml_headers = ["strings", "numbers"]
1008+
_test_table_unsafehtml = [
1009+
["spam", '<font color="red">41.9999</font>'],
1010+
["eggs", '<font color="red">451.0</font>'],
1011+
]
10071012

10081013

10091014
def test_html():
@@ -1027,6 +1032,29 @@ def test_html():
10271032
assert result._repr_html_() == result.str
10281033

10291034

1035+
def test_unsafehtml():
1036+
"Output: unsafe html with headers"
1037+
expected = "\n".join(
1038+
[
1039+
"<table>",
1040+
"<thead>",
1041+
"<tr><th>strings </th><th>numbers </th></tr>", # noqa
1042+
"</thead>",
1043+
"<tbody>",
1044+
'<tr><td>spam </td><td><font color="red">41.9999</font></td></tr>',
1045+
'<tr><td>eggs </td><td><font color="red">451.0</font> </td></tr>',
1046+
"</tbody>",
1047+
"</table>",
1048+
]
1049+
)
1050+
result = tabulate(
1051+
_test_table_unsafehtml, _test_table_unsafehtml_headers, tablefmt="unsafehtml"
1052+
)
1053+
assert_equal(expected, result)
1054+
assert hasattr(result, "_repr_html_")
1055+
assert result._repr_html_() == result.str
1056+
1057+
10301058
def test_html_headerless():
10311059
"Output: html without headers"
10321060
expected = "\n".join(
@@ -1045,6 +1073,24 @@ def test_html_headerless():
10451073
assert result._repr_html_() == result.str
10461074

10471075

1076+
def test_unsafehtml_headerless():
1077+
"Output: unsafe html without headers"
1078+
expected = "\n".join(
1079+
[
1080+
"<table>",
1081+
"<tbody>",
1082+
'<tr><td>spam</td><td><font color="red">41.9999</font></td></tr>',
1083+
'<tr><td>eggs</td><td><font color="red">451.0</font> </td></tr>',
1084+
"</tbody>",
1085+
"</table>",
1086+
]
1087+
)
1088+
result = tabulate(_test_table_unsafehtml, tablefmt="unsafehtml")
1089+
assert_equal(expected, result)
1090+
assert hasattr(result, "_repr_html_")
1091+
assert result._repr_html_() == result.str
1092+
1093+
10481094
def test_latex():
10491095
"Output: latex with headers and replaced characters"
10501096
raw_test_table_headers = list(_test_table_headers)

0 commit comments

Comments
 (0)