Skip to main content

Artless and small template library for server-side rendering.

Project description

artless-template

PyPI Version Development Status PyPI - Python Version Downloads PyPI - License

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

$ pip install artless-template

Usage

Basically, you can create any tag with any name, attributes, text and child tags:

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>

Template and tags usage

Create templates/index.html with contents:

<!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>
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)

Template and components usage

<!DOCTYPE html>
<html lang="en">
  ...
  <body>
    <main>
      @main
    </main>
  </body>
</html>
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))

Asynchronous functions

The library provides async version of io-bound function - read_template. An asynchronous function has a prefix and called aread_template.

from artless_template import aread_template

template = await aread_template("some_template.html")
...

Read detailed reference documentation.

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:

$ python -m bemchmarks

Sorted results on i5 laptop (smaller is better):

{
    'mako': 0.0541565099847503,
    'jinja': 0.26099945200257935,
    'artless': 0.99770416400861,
    'dtl': 1.07213215198135,
    'fasthtml': 11.734292993991403
}
  1. Mako (0.05415 sec.)
  2. Jinja2 (0.26099 sec.)
  3. Artless-template (0.99770 sec.)
  4. Django templates (1.07213 sec.)
  5. 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.

Roadmap

  • Simplify the Tag constructor.
  • Write detailed documentation with Sphinx.
  • Create async version of read_template() - aread_template().
  • Cythonize CPU/RAM-bound of code.

Related projects

  • artless-core - the artless and minimalistic web library for creating small applications or APIs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

artless_template-0.5.0.tar.gz (8.2 kB view details)

Uploaded Source

Built Distribution

artless_template-0.5.0-py3-none-any.whl (6.7 kB view details)

Uploaded Python 3

File details

Details for the file artless_template-0.5.0.tar.gz.

File metadata

  • Download URL: artless_template-0.5.0.tar.gz
  • Upload date:
  • Size: 8.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.12.3

File hashes

Hashes for artless_template-0.5.0.tar.gz
Algorithm Hash digest
SHA256 1a79d44dd30d71948548bab317345787d5b9a248a1578b895de37adaf8496979
MD5 97459053445f2569e6f51457fe479e4f
BLAKE2b-256 a61dc8ce0b56b41e133985bf518fd61678b38588e46ccea0fbd9e46bd274f968

See more details on using hashes here.

Provenance

File details

Details for the file artless_template-0.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for artless_template-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 33e44a3458cc466471ea904dbe952dca58612e46016e4abe5a368c9f5db7afa8
MD5 30f32dc369465c4fef417d550677c46e
BLAKE2b-256 c281309c79fbb36bdb5862b69797337e713118561eb99c5b872e5030b5ee16f2

See more details on using hashes here.

Provenance

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page