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}".format(celltag, alignment.get(a, ""), htmlescape(c)) - for c, a in zip(cell_values, colaligns) - ] + if unsafe: + values_with_attrs = [ + "<{0}{1}>{2}".format(celltag, alignment.get(a, ""), c) + for c, a in zip(cell_values, colaligns) + ] + else: + values_with_attrs = [ + "<{0}{1}>{2}".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( + [ + "", + "", + "", # noqa + "", + "", + '', + '', + "", + "
strings numbers
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( + [ + "", + "", + '', + '', + "", + "
spam41.9999
eggs451.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)