# artless-template
![PyPI Version](https://img.shields.io/pypi/v/artless-template)
![Development Status](https://img.shields.io/badge/status-3%20--%20Alpha-orange)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/artless-template)
[![Downloads](https://static.pepy.tech/badge/artless-template)](https://pepy.tech/project/artless-template)
![PyPI - License](https://img.shields.io/pypi/l/artless-template)
The artless and small template library for server-side rendering.
Artless-template allows to generate HTML, using template files or/and natively Python objects. The library encourages approaches like HTMX and No-JS.
**Main principles**:
1. Artless, fast and small (less than 200 LOC) single-file module.
2. No third party dependencies (standart library only).
3. Support only modern versions of Python (>=3.10).
4. Mostly pure functions without side effects.
5. Interfaces with type annotations.
6. Comprehensive documentation with examples of use.
7. Full test coverage.
**Table of Contents**:
* [Install](#install)
* [Usage](#usage)
* [Template and tags usage](#usage-tags)
* [Template and components usage](#usage-components)
* [Asynchronous functions](#async)
* [Performance](#performance)
* [Rodmap](#roadmap)
* [Related projects](#related)
<a id="install"></a>
## Install
``` shellsession
$ pip install artless-template
```
<a id="usage"></a>
## Usage
Basically, you can create any tag with any name, attributes, text and child tags:
``` python
from artless_template import Tag as t
div = t("div")
print(div)
<div></div>
div = t("div", {"class": "some-class"}, "Some text")
print(div)
<div class="some-class">Some text</div>
div = t("div", {"class": "some-class"}, "Div text", [t("span", "Span 1 text"), t("span", "Span 2 text")])
print(div)
<div class="some-class"><span>Span 1 text</span><span>Span 2 text</span>Div text</div>
button = t("button", {"onclick": "function() {alert('hello');}"}, "Say Hello")
print(button)
<button onclick="function() {alert('hello');}">Say Hello</button>
```
<a id="usage-tags"></a>
### Template and tags usage
Create `templates/index.html` with contents:
``` html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>@title</title>
</head>
<body>
<main>
<section>
<h1>@header</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Admin</th>
</tr>
</thead>
@users
</table>
</section>
</main>
</body>
</html>
```
``` python
from typing import final
from pathlib import Path
from random import randint
from dataclasses import dataclass
from artless_template import read_template, Tag as t
TEMPLATES_DIR: Path = Path(__file__).resolve().parent / "templates"
@final
@dataclass(frozen=True, slots=True, kw_only=True)
class UserModel:
name: str
email: str
is_admin: bool
users = [
UserModel(
name=f"User_{_}", email=f"user_{_}@gmail.com", is_admin=bool(randint(0, 1))
)
for _ in range(10_000)
]
users_markup = t(
"tbody",
children=[
t(
"tr",
children=[
t("td", user.name),
t("td", user.email),
t("td", "+" if user.is_admin else "-"),
],
)
for user in users
],
)
context = {
"title": "Artless-template example",
"header": "Users list",
"users": users_markup,
}
template = read_template(TEMPLATES_DIR / "index.html").render(**context)
```
<a id="usage-components"></a>
### Template and components usage
``` html
<!DOCTYPE html>
<html lang="en">
...
<body>
<main>
@main
</main>
</body>
</html>
```
``` python
from artless_template import read_template, Component, Tag as t
...
class UsersTableComponent:
def __init__(self, count: int):
self.users = [
UserModel(
name=f"User_{_}", email=f"user_{_}@gmail.com", is_admin=bool(randint(0, 1))
)
for _ in range(count)
]
def view(self):
return t(
"table",
children=[
t(
"thead",
[
t(
"tr",
[
t("th", "Name"),
t("th", "Email"),
t("th", "Admin"),
]
)
]
),
t(
"tbody",
[
t(
"tr",
children=[
t("td", user.name),
t("td", user.email),
t("td", "+" if user.is_admin else "-"),
],
)
for user in self.users
]
)
]
)
template = read_template(TEMPLATES_DIR / "index.html").render(main=UsersTableComponent(100500))
```
<a id="async"></a>
### Asynchronous functions
The library provides async version of io-bound function - `read_template`. An asynchronous function has `a` prefix and called `aread_template`.
``` python
from artless_template import aread_template
template = await aread_template("some_template.html")
...
```
Read detailed reference **[documentation](https://pages.peterbro.su/py3-artless-template/reference.html)**.
<a id="performance"></a>
## Performance
Performance comparison of the most popular template engines and artless-template library.
The benchmark render a HTML document with table of 10 thousand user models.
Run benchmark:
``` shellsession
$ python -m bemchmarks
```
Sorted results on i5 laptop (smaller is better):
``` python
{
'mako': 0.0541565099847503,
'jinja': 0.26099945200257935,
'artless': 0.99770416400861,
'dtl': 1.07213215198135,
'fasthtml': 11.734292993991403
}
```
1. [Mako](https://www.makotemplates.org/) (0.05415 sec.)
2. [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/) (0.26099 sec.)
3. **Artless-template (0.99770 sec.)**
4. [Django templates](https://docs.djangoproject.com/en/5.0/ref/templates/) (1.07213 sec.)
5. [FastHTML](https://github.com/AnswerDotAI/fasthtml/) (11.73429 sec.)
The performance of `artless-template` is better than the `Django template engine`, and much better than FastHTML, but worse than `Jinja2` and `Mako`.
<a id="roadmap"></a>
## Roadmap
- [x] Simplify the Tag constructor.
- [x] Write detailed documentation with Sphinx.
- [x] Create async version of `read_template()` - `aread_template()`.
- [ ] Cythonize CPU/RAM-bound of code.
<a id="related"></a>
## Related projects
* [artless-core](https://pypi.org/project/artless-core/) - the artless and minimalistic web library for creating small applications or APIs.
Raw data
{
"_id": null,
"home_page": null,
"name": "artless-template",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "artless-template, template engine, text processing, utility",
"author": null,
"author_email": "Peter Bro <p3t3rbr0@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/a6/1d/c8ce0b56b41e133985bf518fd61678b38588e46ccea0fbd9e46bd274f968/artless_template-0.5.0.tar.gz",
"platform": null,
"description": "# artless-template\n\n![PyPI Version](https://img.shields.io/pypi/v/artless-template)\n![Development Status](https://img.shields.io/badge/status-3%20--%20Alpha-orange)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/artless-template)\n[![Downloads](https://static.pepy.tech/badge/artless-template)](https://pepy.tech/project/artless-template)\n![PyPI - License](https://img.shields.io/pypi/l/artless-template)\n\nThe artless and small template library for server-side rendering.\n\nArtless-template allows to generate HTML, using template files or/and natively Python objects. The library encourages approaches like HTMX and No-JS.\n\n**Main principles**:\n1. Artless, fast and small (less than 200 LOC) single-file module.\n2. No third party dependencies (standart library only).\n3. Support only modern versions of Python (>=3.10).\n4. Mostly pure functions without side effects.\n5. Interfaces with type annotations.\n6. Comprehensive documentation with examples of use.\n7. Full test coverage.\n\n**Table of Contents**:\n* [Install](#install)\n* [Usage](#usage)\n * [Template and tags usage](#usage-tags)\n * [Template and components usage](#usage-components)\n * [Asynchronous functions](#async)\n* [Performance](#performance)\n* [Rodmap](#roadmap)\n* [Related projects](#related)\n\n<a id=\"install\"></a>\n## Install\n\n``` shellsession\n$ pip install artless-template\n```\n\n<a id=\"usage\"></a>\n## Usage\n\nBasically, you can create any tag with any name, attributes, text and child tags:\n\n``` python\nfrom artless_template import Tag as t\n\ndiv = t(\"div\")\nprint(div)\n<div></div>\n\ndiv = t(\"div\", {\"class\": \"some-class\"}, \"Some text\")\nprint(div)\n<div class=\"some-class\">Some text</div>\n\ndiv = t(\"div\", {\"class\": \"some-class\"}, \"Div text\", [t(\"span\", \"Span 1 text\"), t(\"span\", \"Span 2 text\")])\nprint(div)\n<div class=\"some-class\"><span>Span 1 text</span><span>Span 2 text</span>Div text</div>\n\nbutton = t(\"button\", {\"onclick\": \"function() {alert('hello');}\"}, \"Say Hello\")\nprint(button)\n<button onclick=\"function() {alert('hello');}\">Say Hello</button>\n```\n\n<a id=\"usage-tags\"></a>\n### Template and tags usage\n\nCreate `templates/index.html` with contents:\n\n``` html\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>@title</title>\n </head>\n <body>\n <main>\n <section>\n <h1>@header</h1>\n <table>\n <thead>\n <tr>\n <th>Name</th>\n <th>Email</th>\n <th>Admin</th>\n </tr>\n </thead>\n @users\n </table>\n </section>\n </main>\n </body>\n</html>\n```\n\n``` python\nfrom typing import final\nfrom pathlib import Path\nfrom random import randint\nfrom dataclasses import dataclass\nfrom artless_template import read_template, Tag as t\n\nTEMPLATES_DIR: Path = Path(__file__).resolve().parent / \"templates\"\n\n@final\n@dataclass(frozen=True, slots=True, kw_only=True)\nclass UserModel:\n name: str\n email: str\n is_admin: bool\n\n\nusers = [\n UserModel(\n name=f\"User_{_}\", email=f\"user_{_}@gmail.com\", is_admin=bool(randint(0, 1))\n )\n for _ in range(10_000)\n]\n\n\nusers_markup = t(\n \"tbody\",\n children=[\n t(\n \"tr\",\n children=[\n t(\"td\", user.name),\n t(\"td\", user.email),\n t(\"td\", \"+\" if user.is_admin else \"-\"),\n ],\n )\n for user in users\n ],\n)\n\ncontext = {\n \"title\": \"Artless-template example\",\n \"header\": \"Users list\",\n \"users\": users_markup,\n}\n\ntemplate = read_template(TEMPLATES_DIR / \"index.html\").render(**context)\n```\n\n<a id=\"usage-components\"></a>\n### Template and components usage\n\n``` html\n<!DOCTYPE html>\n<html lang=\"en\">\n ...\n <body>\n <main>\n @main\n </main>\n </body>\n</html>\n```\n\n``` python\nfrom artless_template import read_template, Component, Tag as t\n\n...\n\nclass UsersTableComponent:\n def __init__(self, count: int):\n self.users = [\n UserModel(\n name=f\"User_{_}\", email=f\"user_{_}@gmail.com\", is_admin=bool(randint(0, 1))\n )\n for _ in range(count)\n ]\n\n def view(self):\n return t(\n \"table\",\n children=[\n t(\n \"thead\",\n [\n t(\n \"tr\",\n [\n t(\"th\", \"Name\"),\n t(\"th\", \"Email\"),\n t(\"th\", \"Admin\"),\n ]\n )\n ]\n ),\n t(\n \"tbody\",\n [\n t(\n \"tr\",\n children=[\n t(\"td\", user.name),\n t(\"td\", user.email),\n t(\"td\", \"+\" if user.is_admin else \"-\"),\n ],\n )\n for user in self.users\n ]\n )\n ]\n )\n\ntemplate = read_template(TEMPLATES_DIR / \"index.html\").render(main=UsersTableComponent(100500))\n```\n\n<a id=\"async\"></a>\n### Asynchronous functions\n\nThe library provides async version of io-bound function - `read_template`. An asynchronous function has `a` prefix and called `aread_template`.\n\n``` python\nfrom artless_template import aread_template\n\ntemplate = await aread_template(\"some_template.html\")\n...\n```\n\nRead detailed reference **[documentation](https://pages.peterbro.su/py3-artless-template/reference.html)**.\n\n<a id=\"performance\"></a>\n## Performance\n\nPerformance comparison of the most popular template engines and artless-template library.\nThe benchmark render a HTML document with table of 10 thousand user models.\n\nRun benchmark:\n\n``` shellsession\n$ python -m bemchmarks\n```\n\nSorted results on i5 laptop (smaller is better):\n\n``` python\n{\n 'mako': 0.0541565099847503,\n 'jinja': 0.26099945200257935,\n 'artless': 0.99770416400861,\n 'dtl': 1.07213215198135,\n 'fasthtml': 11.734292993991403\n}\n```\n\n1. [Mako](https://www.makotemplates.org/) (0.05415 sec.)\n2. [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/) (0.26099 sec.)\n3. **Artless-template (0.99770 sec.)**\n4. [Django templates](https://docs.djangoproject.com/en/5.0/ref/templates/) (1.07213 sec.)\n5. [FastHTML](https://github.com/AnswerDotAI/fasthtml/) (11.73429 sec.)\n\nThe performance of `artless-template` is better than the `Django template engine`, and much better than FastHTML, but worse than `Jinja2` and `Mako`.\n\n<a id=\"roadmap\"></a>\n## Roadmap\n\n- [x] Simplify the Tag constructor.\n- [x] Write detailed documentation with Sphinx.\n- [x] Create async version of `read_template()` - `aread_template()`.\n- [ ] Cythonize CPU/RAM-bound of code.\n\n<a id=\"related\"></a>\n## Related projects\n\n* [artless-core](https://pypi.org/project/artless-core/) - the artless and minimalistic web library for creating small applications or APIs.\n",
"bugtrack_url": null,
"license": null,
"summary": "Artless and small template library for server-side rendering.",
"version": "0.5.0",
"project_urls": {
"Changelog": "https://git.peterbro.su/peter/py3-artless-template/src/branch/master/CHANGELOG.md",
"Documentation": "https://git.peterbro.su/peter/py3-artless-template/src/branch/master/README.md",
"Homepage": "https://git.peterbro.su/peter/py3-artless-template",
"Issues": "https://git.peterbro.su/peter/py3-artless-template/issues",
"Repository": "https://git.peterbro.su/peter/py3-artless-template.git"
},
"split_keywords": [
"artless-template",
" template engine",
" text processing",
" utility"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "c281309c79fbb36bdb5862b69797337e713118561eb99c5b872e5030b5ee16f2",
"md5": "30f32dc369465c4fef417d550677c46e",
"sha256": "33e44a3458cc466471ea904dbe952dca58612e46016e4abe5a368c9f5db7afa8"
},
"downloads": -1,
"filename": "artless_template-0.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "30f32dc369465c4fef417d550677c46e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 6662,
"upload_time": "2024-10-27T16:07:24",
"upload_time_iso_8601": "2024-10-27T16:07:24.862074Z",
"url": "https://files.pythonhosted.org/packages/c2/81/309c79fbb36bdb5862b69797337e713118561eb99c5b872e5030b5ee16f2/artless_template-0.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a61dc8ce0b56b41e133985bf518fd61678b38588e46ccea0fbd9e46bd274f968",
"md5": "97459053445f2569e6f51457fe479e4f",
"sha256": "1a79d44dd30d71948548bab317345787d5b9a248a1578b895de37adaf8496979"
},
"downloads": -1,
"filename": "artless_template-0.5.0.tar.gz",
"has_sig": false,
"md5_digest": "97459053445f2569e6f51457fe479e4f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 8186,
"upload_time": "2024-10-27T16:07:26",
"upload_time_iso_8601": "2024-10-27T16:07:26.476266Z",
"url": "https://files.pythonhosted.org/packages/a6/1d/c8ce0b56b41e133985bf518fd61678b38588e46ccea0fbd9e46bd274f968/artless_template-0.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-27 16:07:26",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "artless-template"
}