diff --git a/doc/source/reference/style.rst b/doc/source/reference/style.rst index c94795aac732c..7b790daea37ff 100644 --- a/doc/source/reference/style.rst +++ b/doc/source/reference/style.rst @@ -24,6 +24,8 @@ Styler properties Styler.env Styler.template_html + Styler.template_html_style + Styler.template_html_table Styler.template_latex Styler.loader diff --git a/doc/source/user_guide/style.ipynb b/doc/source/user_guide/style.ipynb index 6e10e6ec74b48..cc499204318c1 100644 --- a/doc/source/user_guide/style.ipynb +++ b/doc/source/user_guide/style.ipynb @@ -1780,7 +1780,7 @@ " Styler.loader, # the default\n", " ])\n", " )\n", - " template_html = env.get_template(\"myhtml.tpl\")" + " template_html_table = env.get_template(\"myhtml.tpl\")" ] }, { @@ -1833,14 +1833,35 @@ "outputs": [], "source": [ "EasyStyler = Styler.from_custom_template(\"templates\", \"myhtml.tpl\")\n", - "EasyStyler(df3)" + "HTML(EasyStyler(df3).render(table_title=\"Another Title\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Here's the template structure:" + "#### Template Structure\n", + "\n", + "Here's the template structure for the both the style generation template and the table generation template:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Style template:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "nbsphinx": "hidden" + }, + "outputs": [], + "source": [ + "with open(\"templates/html_style_structure.html\") as f:\n", + " style_structure = f.read()" ] }, { @@ -1849,10 +1870,35 @@ "metadata": {}, "outputs": [], "source": [ - "with open(\"templates/template_structure.html\") as f:\n", - " structure = f.read()\n", - " \n", - "HTML(structure)" + "HTML(style_structure)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Table template:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "nbsphinx": "hidden" + }, + "outputs": [], + "source": [ + "with open(\"templates/html_table_structure.html\") as f:\n", + " table_structure = f.read()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "HTML(table_structure)" ] }, { diff --git a/doc/source/user_guide/templates/html_style_structure.html b/doc/source/user_guide/templates/html_style_structure.html new file mode 100644 index 0000000000000..dc0c03ac363a9 --- /dev/null +++ b/doc/source/user_guide/templates/html_style_structure.html @@ -0,0 +1,35 @@ + + + +
before_style
+
style +
<style type="text/css">
+
table_styles
+
before_cellstyle
+
cellstyle
+
</style>
+
diff --git a/doc/source/user_guide/templates/template_structure.html b/doc/source/user_guide/templates/html_table_structure.html similarity index 80% rename from doc/source/user_guide/templates/template_structure.html rename to doc/source/user_guide/templates/html_table_structure.html index 0778d8e2e6f18..e03f9591d2a35 100644 --- a/doc/source/user_guide/templates/template_structure.html +++ b/doc/source/user_guide/templates/html_table_structure.html @@ -25,15 +25,6 @@ } -
before_style
-
style -
<style type="text/css">
-
table_styles
-
before_cellstyle
-
cellstyle
-
</style>
-
-
before_table
table diff --git a/doc/source/user_guide/templates/myhtml.tpl b/doc/source/user_guide/templates/myhtml.tpl index 1170fd3def653..1e204d0bd4568 100644 --- a/doc/source/user_guide/templates/myhtml.tpl +++ b/doc/source/user_guide/templates/myhtml.tpl @@ -1,4 +1,4 @@ -{% extends "html.tpl" %} +{% extends "html_table.tpl" %} {% block table %}

{{ table_title|default("My Table") }}

{{ super() }} diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 49c168cd5eb84..0641b32383125 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -707,6 +707,7 @@ Other API changes - Added new ``engine`` and ``**engine_kwargs`` parameters to :meth:`DataFrame.to_sql` to support other future "SQL engines". Currently we still only use ``SQLAlchemy`` under the hood, but more engines are planned to be supported such as `turbodbc `_ (:issue:`36893`) - Removed redundant ``freq`` from :class:`PeriodIndex` string representation (:issue:`41653`) - :meth:`ExtensionDtype.construct_array_type` is now a required method instead of an optional one for :class:`ExtensionDtype` subclasses (:issue:`24860`) +- :meth:`.Styler.from_custom_template` now has two new arguments for template names, and removed the old ``name``, due to template inheritance having been introducing for better parsing (:issue:`42053`). Subclassing modifications to Styler attributes are also needed. .. _whatsnew_130.api_breaking.build: diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index c03275b565fd4..279549a6379d1 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -2529,23 +2529,35 @@ def highlight_quantile( ) @classmethod - def from_custom_template(cls, searchpath, name): + def from_custom_template( + cls, searchpath, html_table: str | None = None, html_style: str | None = None + ): """ Factory function for creating a subclass of ``Styler``. - Uses a custom template and Jinja environment. + Uses custom templates and Jinja environment. + + .. versionchanged:: 1.3.0 Parameters ---------- searchpath : str or list Path or paths of directories containing the templates. - name : str - Name of your custom template to use for rendering. + html_table : str + Name of your custom template to replace the html_table template. + + .. versionadded:: 1.3.0 + + html_style : str + Name of your custom template to replace the html_style template. + + .. versionadded:: 1.3.0 Returns ------- MyStyler : subclass of Styler - Has the correct ``env`` and ``template`` class attributes set. + Has the correct ``env``,``template_html``, ``template_html_table`` and + ``template_html_style`` class attributes set. """ loader = jinja2.ChoiceLoader([jinja2.FileSystemLoader(searchpath), cls.loader]) @@ -2554,7 +2566,10 @@ def from_custom_template(cls, searchpath, name): # error: Invalid base class "cls" class MyStyler(cls): # type:ignore[valid-type,misc] env = jinja2.Environment(loader=loader) - template_html = env.get_template(name) + if html_table: + template_html_table = env.get_template(html_table) + if html_style: + template_html_style = env.get_template(html_style) return MyStyler diff --git a/pandas/io/formats/style_render.py b/pandas/io/formats/style_render.py index 054dd443bd657..616b89f9e519f 100644 --- a/pandas/io/formats/style_render.py +++ b/pandas/io/formats/style_render.py @@ -67,6 +67,8 @@ class StylerRenderer: loader = jinja2.PackageLoader("pandas", "io/formats/templates") env = jinja2.Environment(loader=loader, trim_blocks=True) template_html = env.get_template("html.tpl") + template_html_table = env.get_template("html_table.tpl") + template_html_style = env.get_template("html_style.tpl") template_latex = env.get_template("latex.tpl") def __init__( @@ -120,7 +122,11 @@ def _render_html(self, sparse_index: bool, sparse_columns: bool, **kwargs) -> st # TODO: namespace all the pandas keys d = self._translate(sparse_index, sparse_columns) d.update(kwargs) - return self.template_html.render(**d) + return self.template_html.render( + **d, + html_table_tpl=self.template_html_table, + html_style_tpl=self.template_html_style, + ) def _render_latex(self, sparse_index: bool, sparse_columns: bool, **kwargs) -> str: """ diff --git a/pandas/io/formats/templates/html.tpl b/pandas/io/formats/templates/html.tpl index 880c78c8d6b05..8c63be3ad788a 100644 --- a/pandas/io/formats/templates/html.tpl +++ b/pandas/io/formats/templates/html.tpl @@ -1,16 +1,16 @@ -{# Update the template_structure.html documentation too #} +{# Update the html_style/table_structure.html documentation too #} {% if doctype_html %} -{% if not exclude_styles %}{% include "html_style.tpl" %}{% endif %} +{% if not exclude_styles %}{% include html_style_tpl %}{% endif %} -{% include "html_table.tpl" %} +{% include html_table_tpl %} {% elif not doctype_html %} -{% if not exclude_styles %}{% include "html_style.tpl" %}{% endif %} -{% include "html_table.tpl" %} +{% if not exclude_styles %}{% include html_style_tpl %}{% endif %} +{% include html_table_tpl %} {% endif %} diff --git a/pandas/tests/io/formats/style/test_html.py b/pandas/tests/io/formats/style/test_html.py index 1ef5fc3adc50e..495dc82f0e7bd 100644 --- a/pandas/tests/io/formats/style/test_html.py +++ b/pandas/tests/io/formats/style/test_html.py @@ -41,8 +41,8 @@ def test_html_template_extends_options(): # to understand the dependency with open("pandas/io/formats/templates/html.tpl") as file: result = file.read() - assert '{% include "html_style.tpl" %}' in result - assert '{% include "html_table.tpl" %}' in result + assert "{% include html_style_tpl %}" in result + assert "{% include html_table_tpl %}" in result def test_exclude_styles(styler): @@ -223,24 +223,46 @@ def test_block_names(tpl_style, tpl_table): assert result2 == expected_table -def test_from_custom_template(tmpdir): - p = tmpdir.mkdir("templates").join("myhtml.tpl") +def test_from_custom_template_table(tmpdir): + p = tmpdir.mkdir("tpl").join("myhtml_table.tpl") p.write( dedent( """\ - {% extends "html.tpl" %} - {% block table %} -

{{ table_title|default("My Table") }}

- {{ super() }} - {% endblock table %}""" + {% extends "html_table.tpl" %} + {% block table %} +

{{custom_title}}

+ {{ super() }} + {% endblock table %}""" ) ) - result = Styler.from_custom_template(str(tmpdir.join("templates")), "myhtml.tpl") + result = Styler.from_custom_template(str(tmpdir.join("tpl")), "myhtml_table.tpl") assert issubclass(result, Styler) assert result.env is not Styler.env - assert result.template_html is not Styler.template_html + assert result.template_html_table is not Styler.template_html_table styler = result(DataFrame({"A": [1, 2]})) - assert styler.render() + assert "

My Title

\n\n\n + {{ super() }} + {% endblock style %}""" + ) + ) + result = Styler.from_custom_template( + str(tmpdir.join("tpl")), html_style="myhtml_style.tpl" + ) + assert issubclass(result, Styler) + assert result.env is not Styler.env + assert result.template_html_style is not Styler.template_html_style + styler = result(DataFrame({"A": [1, 2]})) + assert '\n\n