From a610a4dab98b8883959bc44bbaa81c1e9b84a5b2 Mon Sep 17 00:00:00 2001
From: danja100 <56681540+danja100@users.noreply.github.com>
Date: Tue, 7 Apr 2020 08:02:07 +0100
Subject: [PATCH] add in unsafe html format (PR#42)
adding in unsafe html format, in case unescaped html is required in table
---
README.md | 4 +++-
tabulate.py | 34 +++++++++++++++++++++++++--------
test/test_output.py | 46 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 75 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index a4f9881..f7c15a6 100644
--- a/README.md
+++ b/README.md
@@ -149,6 +149,7 @@ Supported table formats are:
- "moinmoin"
- "youtrack"
- "html"
+- "unsafehtml"
- "latex"
- "latex\_raw"
- "latex\_booktabs"
@@ -334,7 +335,8 @@ MediaWiki-based sites:
`html` produces standard HTML markup as an html.escape'd str
with a ._repr_html_ method so that Jupyter Lab and Notebook display the HTML
-and a .str property so that the raw HTML remains accessible:
+and a .str property so that the raw HTML remains accessible
+the unsafehtml table format can be used if an unescaped HTML format is required:
>>> print(tabulate(table, headers, tablefmt="html"))
diff --git a/tabulate.py b/tabulate.py
index b73bbe0..5d57167 100755
--- a/tabulate.py
+++ b/tabulate.py
@@ -182,17 +182,23 @@ def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore):
return "\n"
-def _html_row_with_attrs(celltag, cell_values, colwidths, colaligns):
+def _html_row_with_attrs(celltag, unsafe, cell_values, colwidths, colaligns):
alignment = {
"left": "",
"right": ' style="text-align: right;"',
"center": ' style="text-align: center;"',
"decimal": ' style="text-align: right;"',
}
- values_with_attrs = [
- "<{0}{1}>{2}{0}>".format(celltag, alignment.get(a, ""), htmlescape(c))
- for c, a in zip(cell_values, colaligns)
- ]
+ if unsafe:
+ values_with_attrs = [
+ "<{0}{1}>{2}{0}>".format(celltag, alignment.get(a, ""), c)
+ for c, a in zip(cell_values, colaligns)
+ ]
+ else:
+ values_with_attrs = [
+ "<{0}{1}>{2}{0}>".format(celltag, alignment.get(a, ""), htmlescape(c))
+ for c, a in zip(cell_values, colaligns)
+ ]
rowhtml = "{}
".format("".join(values_with_attrs).rstrip())
if celltag == "th": # it's a header row, create a new table header
rowhtml = "\n\n{}\n\n".format(rowhtml)
@@ -429,8 +435,18 @@ def escape_empty(val):
linebelowheader="",
linebetweenrows=None,
linebelow=Line("\n
", "", "", ""),
- headerrow=partial(_html_row_with_attrs, "th"),
- datarow=partial(_html_row_with_attrs, "td"),
+ headerrow=partial(_html_row_with_attrs, "th", False),
+ datarow=partial(_html_row_with_attrs, "td", False),
+ padding=0,
+ with_header_hide=["lineabove"],
+ ),
+ "unsafehtml": TableFormat(
+ lineabove=_html_begin_table_without_header,
+ linebelowheader="",
+ linebetweenrows=None,
+ linebelow=Line("\n
", "", "", ""),
+ headerrow=partial(_html_row_with_attrs, "th", True),
+ datarow=partial(_html_row_with_attrs, "td", True),
padding=0,
with_header_hide=["lineabove"],
),
@@ -1360,7 +1376,8 @@ def tabulate(
"html" produces HTML markup as an html.escape'd str
with a ._repr_html_ method so that Jupyter Lab and Notebook display the HTML
- and a .str property so that the raw HTML remains accessible:
+ and a .str property so that the raw HTML remains accessible
+ the unsafehtml table format can be used if an unescaped HTML format is required:
>>> print(tabulate([["strings", "numbers"], ["spam", 41.9999], ["eggs", "451.0"]],
... headers="firstrow", tablefmt="html"))
@@ -1421,6 +1438,7 @@ def tabulate(
e.g. `disable_numparse=[0, 2]` would disable number parsing only on the
first and third columns.
"""
+
if tabular_data is None:
tabular_data = []
list_of_lists, headers = _normalize_tabular_data(
diff --git a/test/test_output.py b/test/test_output.py
index 3951354..0e72c71 100644
--- a/test/test_output.py
+++ b/test/test_output.py
@@ -1004,6 +1004,11 @@ def test_moinmoin_headerless():
_test_table_html_headers = ["", "<&numbers&>"]
_test_table_html = [["spam >", 41.9999], ["eggs &", 451.0]]
+_test_table_unsafehtml_headers = ["strings", "numbers"]
+_test_table_unsafehtml = [
+ ["spam", '41.9999'],
+ ["eggs", '451.0'],
+]
def test_html():
@@ -1027,6 +1032,29 @@ def test_html():
assert result._repr_html_() == result.str
+def test_unsafehtml():
+ "Output: unsafe html with headers"
+ expected = "\n".join(
+ [
+ "",
+ "",
+ "strings | numbers |
", # noqa
+ "",
+ "",
+ 'spam | 41.9999 |
',
+ 'eggs | 451.0 |
',
+ "",
+ "
",
+ ]
+ )
+ result = tabulate(
+ _test_table_unsafehtml, _test_table_unsafehtml_headers, tablefmt="unsafehtml"
+ )
+ assert_equal(expected, result)
+ assert hasattr(result, "_repr_html_")
+ assert result._repr_html_() == result.str
+
+
def test_html_headerless():
"Output: html without headers"
expected = "\n".join(
@@ -1045,6 +1073,24 @@ def test_html_headerless():
assert result._repr_html_() == result.str
+def test_unsafehtml_headerless():
+ "Output: unsafe html without headers"
+ expected = "\n".join(
+ [
+ "",
+ "",
+ 'spam | 41.9999 |
',
+ 'eggs | 451.0 |
',
+ "",
+ "
",
+ ]
+ )
+ result = tabulate(_test_table_unsafehtml, tablefmt="unsafehtml")
+ assert_equal(expected, result)
+ assert hasattr(result, "_repr_html_")
+ assert result._repr_html_() == result.str
+
+
def test_latex():
"Output: latex with headers and replaced characters"
raw_test_table_headers = list(_test_table_headers)