tinyhtml
========
A tiny library to safely render compact HTML5 from Python expressions.
.. image:: https://github.com/niklasf/python-tinyhtml/workflows/Test/badge.svg
:target: https://github.com/niklasf/python-tinyhtml/actions
:alt: Test status
.. image:: https://badge.fury.io/py/tinyhtml.svg
:target: https://pypi.python.org/pypi/tinyhtml
:alt: PyPI package
Introduction
------------
This is the entire API. The following documentation is longer than the
implementation.
.. code:: python
>>> from tinyhtml import html, h, frag, raw
The most important function is ``h()``. Below you see how to render attributes,
normal elements, and void/self-closing elements.
.. code:: python
>>> html(lang="en")(
... h("head")(
... h("meta", charset="utf-8"),
... ),
... ).render()
'<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head></html>'
Use ``frag()`` to pass around groups of elements.
.. code:: python
>>> frag(
... h("h1")("Lorem ipsum ..."),
... h("p")("... dolor sit amet."),
... )
raw('<h1>Lorem ipsum ...</h1><p>... dolor sit amet.</p>')
Of course all content and attributes are properly escaped. Use ``raw()`` as an
escape hatch to render unescaped HTML.
.. code:: python
>>> print(h("a", title="&<>\"'")("&<>\"'").render())
<a title="&<>"'">&<>"'</a>
>>> print(raw("<!-- 💥"))
<!-- 💥
Installing
----------
::
pip install tinyhtml
Features and patterns
---------------------
* Output is compact: Naturally produces no superfluous whitespace between
elements.
* Fragments provide ``_repr_html_()`` for Jupyter notebook integration and
``__html__`` for integration with other ecosystems (see
`MarkupSafe <https://markupsafe.palletsprojects.com/en/stable/html/>`_).
* Fragments can include and render objects providing ``_repr_html_()`` and
``__html__()``. This means objects that already render as HTML in a
Jupyter notebook will be rendered by tinyhtml.
* Includes mypy typings.
.. code:: python
>>> from tinyhtml import Frag
* Write **templates** as functions.
.. code:: python
>>> def layout(title: str, body: Frag) -> Frag:
... return html()(
... h("head")(
... h("title")(title),
... ),
... h("body")(body)
... )
>>> layout("Hello world", frag(
... h("h1")("Hello world"),
... h("p")("Lorem ipsum dolor sit amet."),
... ))
raw('<!DOCTYPE html><html><head><title>Hello world</title></head><body><h1>Hello world</h1><p>Lorem ipsum dolor sit amet.</p></body></html>')
* Use ``str``, ``int``, other fragments, ``None``, or iterables of these as
**child elements**. (Note that rendering consumes the iterables, so fragments
using generators can be rendered only once.)
.. code:: python
>>> h("ul")(
... h("li")(n) for n in range(3)
... )
raw('<ul><li>0</li><li>1</li><li>2</li></ul>')
>>> h("ul")(
... h("li")("Foo") if False else None,
... h("li")("Bar"),
... )
raw('<ul><li>Bar</li></ul>')
* Use ``str``, ``int``, ``None``, iterables of these, ``bool``, or dictionaries
with boolean values as **attributes**.
.. code:: python
>>> h("input", type="checkbox", checked=True, disabled=False)
raw('<input type="checkbox" checked>')
>>> h("body", klass=["a", "b"])()
raw('<body class="a b"></body>')
>>> h("body", klass={
... "a": True,
... "b": False,
... })()
raw('<body class="a"></body>')
* Use ``klass`` instead of ``class``, append a trailing underscore (``for_``),
or use underscores instead of dashes (``http_equiv``) for attribute names
that cannot be Python identifiers.
.. code:: python
>>> h("div", klass="container")()
raw('<div class="container"></div>')
>>> h("label", for_="name")("Name")
raw('<label for="name">Name</label>')
>>> h("meta", http_equiv="refresh", content=10)
raw('<meta http-equiv="refresh" content="10">')
* Render fragments as ``str``, or into a list of ``str`` for efficient string
building.
.. code:: python
>>> frag("Hello world", "!").render()
'Hello world!'
>>> builder = []
>>> frag("Hello world", "!").render_into(builder)
>>> builder
['Hello world', '!']
>>> "".join(builder)
'Hello world!'
* Does not support comment nodes, unescapable raw text elements
(like inline styles and scripts), or foreign elements (like inline SVG).
Instead, reference external files, or use ``raw()`` with appropriate caution.
Interoperability
----------------
Fragments implement ``_repr_html_`` and can be displayed in Jupyter notebooks
as HTML, but they can also render object that implement ``_repr_html_``.
Similarly fragments can include and be included in other template systems that
use the ``__html__`` convention, such as Jinja2 via
`MarkupSafe`_.
* Render fragments into a Jinja2 template.
.. code:: python
>>> import jinja2
>>>
>>> template = jinja2.Template('<div>{{ fragment }}</div>')
>>> frag = h('ul')(h('li')(i) for i in range(2))
>>> template.render(fragment=frag)
'<div><ul><li>0</li><li>1</li></ul></div>'
* Render an object the supports display in a Jupyter notebook, such as a pandas
dataframe.
.. code:: python
>>> import pandas as pd
>>>
>>> table = pd.DataFrame({'Fruit': ['apple', 'pear'], 'Count': [3, 4]})
>>> h('div')(h('h1')('A table'), table) # doctest: +ELLIPSIS
raw('<div><h1>A table</h1><div>...<table ...>...<td>apple</td>...</table>...</div>')
License
-------
Licensed under the
`Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>`_,
or the `MIT license <https://opensource.org/licenses/MIT>`_, at your option.
Raw data
{
"_id": null,
"home_page": "https://github.com/niklasf/python-tinyhtml",
"name": "tinyhtml",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "html html5 vdom functional-programming",
"author": "Niklas Fiekas",
"author_email": "niklas.fiekas@backscattering.de",
"download_url": "https://files.pythonhosted.org/packages/c1/9d/de470c06fd3c02f9a14e0ac1429ccbd6320ef17bed264a225a2c85dca38a/tinyhtml-1.3.0.tar.gz",
"platform": null,
"description": "tinyhtml\n========\n\nA tiny library to safely render compact HTML5 from Python expressions.\n\n.. image:: https://github.com/niklasf/python-tinyhtml/workflows/Test/badge.svg\n :target: https://github.com/niklasf/python-tinyhtml/actions\n :alt: Test status\n\n.. image:: https://badge.fury.io/py/tinyhtml.svg\n :target: https://pypi.python.org/pypi/tinyhtml\n :alt: PyPI package\n\nIntroduction\n------------\n\nThis is the entire API. The following documentation is longer than the\nimplementation.\n\n.. code:: python\n\n >>> from tinyhtml import html, h, frag, raw\n\nThe most important function is ``h()``. Below you see how to render attributes,\nnormal elements, and void/self-closing elements.\n\n.. code:: python\n\n >>> html(lang=\"en\")(\n ... h(\"head\")(\n ... h(\"meta\", charset=\"utf-8\"),\n ... ),\n ... ).render()\n '<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"></head></html>'\n\nUse ``frag()`` to pass around groups of elements.\n\n.. code:: python\n\n >>> frag(\n ... h(\"h1\")(\"Lorem ipsum ...\"),\n ... h(\"p\")(\"... dolor sit amet.\"),\n ... )\n raw('<h1>Lorem ipsum ...</h1><p>... dolor sit amet.</p>')\n\nOf course all content and attributes are properly escaped. Use ``raw()`` as an\nescape hatch to render unescaped HTML.\n\n.. code:: python\n\n >>> print(h(\"a\", title=\"&<>\\\"'\")(\"&<>\\\"'\").render())\n <a title=\"&<>"'\">&<>\"'</a>\n\n >>> print(raw(\"<!-- \ud83d\udca5\"))\n <!-- \ud83d\udca5\n\n\nInstalling\n----------\n\n::\n\n pip install tinyhtml\n\n\nFeatures and patterns\n---------------------\n\n* Output is compact: Naturally produces no superfluous whitespace between\n elements.\n\n* Fragments provide ``_repr_html_()`` for Jupyter notebook integration and\n ``__html__`` for integration with other ecosystems (see\n `MarkupSafe <https://markupsafe.palletsprojects.com/en/stable/html/>`_).\n\n* Fragments can include and render objects providing ``_repr_html_()`` and\n ``__html__()``. This means objects that already render as HTML in a\n Jupyter notebook will be rendered by tinyhtml.\n\n* Includes mypy typings.\n\n .. code:: python\n\n >>> from tinyhtml import Frag\n\n* Write **templates** as functions.\n\n .. code:: python\n\n >>> def layout(title: str, body: Frag) -> Frag:\n ... return html()(\n ... h(\"head\")(\n ... h(\"title\")(title),\n ... ),\n ... h(\"body\")(body)\n ... )\n\n >>> layout(\"Hello world\", frag(\n ... h(\"h1\")(\"Hello world\"),\n ... h(\"p\")(\"Lorem ipsum dolor sit amet.\"),\n ... ))\n raw('<!DOCTYPE html><html><head><title>Hello world</title></head><body><h1>Hello world</h1><p>Lorem ipsum dolor sit amet.</p></body></html>')\n\n* Use ``str``, ``int``, other fragments, ``None``, or iterables of these as\n **child elements**. (Note that rendering consumes the iterables, so fragments\n using generators can be rendered only once.)\n\n .. code:: python\n\n >>> h(\"ul\")(\n ... h(\"li\")(n) for n in range(3)\n ... )\n raw('<ul><li>0</li><li>1</li><li>2</li></ul>')\n\n >>> h(\"ul\")(\n ... h(\"li\")(\"Foo\") if False else None,\n ... h(\"li\")(\"Bar\"),\n ... )\n raw('<ul><li>Bar</li></ul>')\n\n* Use ``str``, ``int``, ``None``, iterables of these, ``bool``, or dictionaries\n with boolean values as **attributes**.\n\n .. code:: python\n\n >>> h(\"input\", type=\"checkbox\", checked=True, disabled=False)\n raw('<input type=\"checkbox\" checked>')\n\n >>> h(\"body\", klass=[\"a\", \"b\"])()\n raw('<body class=\"a b\"></body>')\n\n >>> h(\"body\", klass={\n ... \"a\": True,\n ... \"b\": False,\n ... })()\n raw('<body class=\"a\"></body>')\n\n\n* Use ``klass`` instead of ``class``, append a trailing underscore (``for_``),\n or use underscores instead of dashes (``http_equiv``) for attribute names\n that cannot be Python identifiers.\n\n .. code:: python\n\n >>> h(\"div\", klass=\"container\")()\n raw('<div class=\"container\"></div>')\n\n >>> h(\"label\", for_=\"name\")(\"Name\")\n raw('<label for=\"name\">Name</label>')\n\n >>> h(\"meta\", http_equiv=\"refresh\", content=10)\n raw('<meta http-equiv=\"refresh\" content=\"10\">')\n\n* Render fragments as ``str``, or into a list of ``str`` for efficient string\n building.\n\n .. code:: python\n\n >>> frag(\"Hello world\", \"!\").render()\n 'Hello world!'\n\n >>> builder = []\n >>> frag(\"Hello world\", \"!\").render_into(builder)\n >>> builder\n ['Hello world', '!']\n >>> \"\".join(builder)\n 'Hello world!'\n\n* Does not support comment nodes, unescapable raw text elements\n (like inline styles and scripts), or foreign elements (like inline SVG).\n Instead, reference external files, or use ``raw()`` with appropriate caution.\n\n\nInteroperability\n----------------\n\nFragments implement ``_repr_html_`` and can be displayed in Jupyter notebooks\nas HTML, but they can also render object that implement ``_repr_html_``.\nSimilarly fragments can include and be included in other template systems that\nuse the ``__html__`` convention, such as Jinja2 via\n`MarkupSafe`_.\n\n* Render fragments into a Jinja2 template.\n\n .. code:: python\n\n >>> import jinja2\n >>>\n >>> template = jinja2.Template('<div>{{ fragment }}</div>')\n >>> frag = h('ul')(h('li')(i) for i in range(2))\n >>> template.render(fragment=frag)\n '<div><ul><li>0</li><li>1</li></ul></div>'\n\n\n* Render an object the supports display in a Jupyter notebook, such as a pandas\n dataframe.\n\n .. code:: python\n\n >>> import pandas as pd\n >>>\n >>> table = pd.DataFrame({'Fruit': ['apple', 'pear'], 'Count': [3, 4]})\n >>> h('div')(h('h1')('A table'), table) # doctest: +ELLIPSIS\n raw('<div><h1>A table</h1><div>...<table ...>...<td>apple</td>...</table>...</div>')\n\n\nLicense\n-------\n\nLicensed under the\n`Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>`_,\nor the `MIT license <https://opensource.org/licenses/MIT>`_, at your option.\n",
"bugtrack_url": null,
"license": "MIT/Apache-2.0",
"summary": "A tiny library to safely render compact HTML5 from Python expressions.",
"version": "1.3.0",
"project_urls": {
"Homepage": "https://github.com/niklasf/python-tinyhtml"
},
"split_keywords": [
"html",
"html5",
"vdom",
"functional-programming"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "bdb10f1243a6adf8363b3a46d0d98fd34850bcc3ae9d568fd7efaf6da82b653a",
"md5": "5507a8ab3807a34256f085d43e69081f",
"sha256": "b9a726fd3332f0a6c6ef8ef501cb1aabb6bad23ac98a61ae6ac321c605439c4b"
},
"downloads": -1,
"filename": "tinyhtml-1.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5507a8ab3807a34256f085d43e69081f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 29993,
"upload_time": "2025-01-11T12:13:49",
"upload_time_iso_8601": "2025-01-11T12:13:49.747095Z",
"url": "https://files.pythonhosted.org/packages/bd/b1/0f1243a6adf8363b3a46d0d98fd34850bcc3ae9d568fd7efaf6da82b653a/tinyhtml-1.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c19dde470c06fd3c02f9a14e0ac1429ccbd6320ef17bed264a225a2c85dca38a",
"md5": "783e8f14c919e4ec25d42730ffb297a9",
"sha256": "457f2683a22051ac48674e00d978f39098344546da7276a0aaef467380d3bb53"
},
"downloads": -1,
"filename": "tinyhtml-1.3.0.tar.gz",
"has_sig": false,
"md5_digest": "783e8f14c919e4ec25d42730ffb297a9",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 30529,
"upload_time": "2025-01-11T12:13:52",
"upload_time_iso_8601": "2025-01-11T12:13:52.762306Z",
"url": "https://files.pythonhosted.org/packages/c1/9d/de470c06fd3c02f9a14e0ac1429ccbd6320ef17bed264a225a2c85dca38a/tinyhtml-1.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-11 12:13:52",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "niklasf",
"github_project": "python-tinyhtml",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "tinyhtml"
}