artless-template


Nameartless-template JSON
Version 0.6.3 PyPI version JSON
download
home_pageNone
SummaryThe artless and minimalist templating for Python server-side rendering.
upload_time2025-08-02 06:56:55
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseNone
keywords artless-template template engine text processing utility
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # artless-template

![PyPI Version](https://img.shields.io/pypi/v/artless-template)
![Development Status](https://img.shields.io/badge/status-3%20--%20Beta-blue)
![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 minimalist templating for Python server-side rendering.

**artless-template** is a tiny (under 200 lines), dependency-free template engine, designed for generating HTML using either template files or native Python objects.

Perfect for modern, clean server-side rendering with a focus on simplicity, performance, and patterns like HTMX and No-JS.

## Why artless-template?

* ๐Ÿชถ Tiny: Single module, no dependencies, no magic, no bloat
* โšก Fast & Small: Under 200 LOC, built for speed (see benchmarks)
* ๐Ÿงน Functional style: Mostly pure functions, no side effects, fully type-annotated
* ๐Ÿ Modern: Python 3.11+ only
* โœ… Tested: 100% test coverage.
* ๐Ÿ“š Well-documented: With usage examples.

## Quickstart

### Installation

From PyPI:

``` console
$ pip install artless-template
```

From source:

``` console
$ git clone https://git.peterbro.su/peter/py3-artless-template
$ cd py3-artless-template
$ pip install .
```

### 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",
    [
        t(
            "tr",
            [
                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)
```

### 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",
            [
                t(
                    "thead",
                    [
                        t(
                            "tr",
                            [
                                t("th", "Name"),
                                t("th", "Email"),
                                t("th", "Admin"),
                            ]
                        )
                    ]
                ),
                t(
                    "tbody",
                    [
                        t(
                            "tr",
                            [
                                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))
```

### 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)**.

## 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.05319607999990694,
    'jinja2': 0.27525966498069465,
    'artless': 0.5908581139810849,
    'dtl': 1.034598412021296,
    'fasthtml': 12.420113595988369
}
```

1. [Mako](https://www.makotemplates.org/) (0.05319 sec.)
2. [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/) (0.27525 sec.)
3. **Artless-template (0.59085 sec.)**
4. [Django templates](https://docs.djangoproject.com/en/5.0/ref/templates/) (1.03459 sec.)
5. [FastHTML](https://github.com/AnswerDotAI/fasthtml/) (12.42011 sec.)

The performance of `artless-template` is better than the `Django template engine`, and much better than FastHTML, but worse than `Jinja2` and `Mako`.

## 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.

## Related projects

* [artless-core](https://pypi.org/project/artless-core/) - the artless and ultralight web framework for building minimal APIs and apps.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "artless-template",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "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/1c/3b/d26676f18e3929fe086214f8ad7bf15ee7bf8e227995cadbc2dddd3899ab/artless_template-0.6.3.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--%20Beta-blue)\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 minimalist templating for Python server-side rendering.\n\n**artless-template** is a tiny (under 200 lines), dependency-free template engine, designed for generating HTML using either template files or native Python objects.\n\nPerfect for modern, clean server-side rendering with a focus on simplicity, performance, and patterns like HTMX and No-JS.\n\n## Why artless-template?\n\n* \ud83e\udeb6 Tiny: Single module, no dependencies, no magic, no bloat\n* \u26a1 Fast & Small: Under 200 LOC, built for speed (see benchmarks)\n* \ud83e\uddf9 Functional style: Mostly pure functions, no side effects, fully type-annotated\n* \ud83d\udc0d Modern: Python 3.11+ only\n* \u2705 Tested: 100% test coverage.\n* \ud83d\udcda Well-documented: With usage examples.\n\n## Quickstart\n\n### Installation\n\nFrom PyPI:\n\n``` console\n$ pip install artless-template\n```\n\nFrom source:\n\n``` console\n$ git clone https://git.peterbro.su/peter/py3-artless-template\n$ cd py3-artless-template\n$ pip install .\n```\n\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    [\n        t(\n            \"tr\",\n            [\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### 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            [\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                            [\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### 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## 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.05319607999990694,\n    'jinja2': 0.27525966498069465,\n    'artless': 0.5908581139810849,\n    'dtl': 1.034598412021296,\n    'fasthtml': 12.420113595988369\n}\n```\n\n1. [Mako](https://www.makotemplates.org/) (0.05319 sec.)\n2. [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/) (0.27525 sec.)\n3. **Artless-template (0.59085 sec.)**\n4. [Django templates](https://docs.djangoproject.com/en/5.0/ref/templates/) (1.03459 sec.)\n5. [FastHTML](https://github.com/AnswerDotAI/fasthtml/) (12.42011 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## 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## Related projects\n\n* [artless-core](https://pypi.org/project/artless-core/) - the artless and ultralight web framework for building minimal APIs and apps.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "The artless and minimalist templating for Python server-side rendering.",
    "version": "0.6.3",
    "project_urls": {
        "Changelog": "https://pages.peterbro.su/py3-pure-template/changelog.html",
        "Documentation": "https://pages.peterbro.su/py3-artless-template/",
        "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": null,
            "digests": {
                "blake2b_256": "c963f44e4e69ac120b6e39dc2000150666bf2f4c4d5d826d72df2c35b6d72d28",
                "md5": "f02eb4e5e7d70bee02c92e0ce97fc561",
                "sha256": "1096cc07b6656178c4d3f40d22970c67edf5dde4417123e84ba10d4143406a50"
            },
            "downloads": -1,
            "filename": "artless_template-0.6.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f02eb4e5e7d70bee02c92e0ce97fc561",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 6472,
            "upload_time": "2025-08-02T06:56:54",
            "upload_time_iso_8601": "2025-08-02T06:56:54.748418Z",
            "url": "https://files.pythonhosted.org/packages/c9/63/f44e4e69ac120b6e39dc2000150666bf2f4c4d5d826d72df2c35b6d72d28/artless_template-0.6.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1c3bd26676f18e3929fe086214f8ad7bf15ee7bf8e227995cadbc2dddd3899ab",
                "md5": "662d20e613517bd4870df45025805a8e",
                "sha256": "e155c1eac877b5d27fff22a7b3a2000fc9430dd4b79371261679a12d98445d8f"
            },
            "downloads": -1,
            "filename": "artless_template-0.6.3.tar.gz",
            "has_sig": false,
            "md5_digest": "662d20e613517bd4870df45025805a8e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 8474,
            "upload_time": "2025-08-02T06:56:55",
            "upload_time_iso_8601": "2025-08-02T06:56:55.701979Z",
            "url": "https://files.pythonhosted.org/packages/1c/3b/d26676f18e3929fe086214f8ad7bf15ee7bf8e227995cadbc2dddd3899ab/artless_template-0.6.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-02 06:56:55",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "artless-template"
}
        
Elapsed time: 1.22998s