Skip to content

Templating redux - #13

Merged
Tronic merged 21 commits into
mainfrom
templating-redux
Jul 9, 2026
Merged

Tronic merged 21 commits into
mainfrom
templating-redux

Conversation

@Tronic

@Tronic Tronic commented Jul 5, 2026

Copy link
Copy Markdown
Member

A new templating implementation replacing the old one introduced in version 1.3. The templates are immutable objects instantiated at render time with dynamic content but not internally preserving such content anymore. We are running far faster than the old template system, and can now properly support for loops for nested items (sub templates) which the old one did not.

The templating is for the highest possible performance and also to ease the construction of complex documents.

This is intended for 2.0 release, as it breaks compatibility with 1.3 templates.

New Syntax

from html5tagger import Document, E, Template

# Define the reusable templates once
Page = Document(E.Title).h1.Title.ul.Items @ Template
Item = Template(E.li.span(class_="name").Name._(": ").span(class_="price").Price("N/A"))

# Super fast rendering just fills in the dynamic data
def render(products: list) -> str:
    return Page(
        Title="Product List",
        Items=[Item(**product) for product in products],
    )

html = render([
    {"Name": "Apple", "Price": "$1.20"},
    {"Name": "Banana"},
])
<!DOCTYPE html>
<meta charset="utf-8">
<title>Product List</title>
<h1>Product List</h1>
<ul>
  <li><span class=name>Apple</span>: <span class=price>$1.20</span>
  <li><span class=name>Banana</span>: <span class=price>N/A</span>
</ul>

Performance

Generated HTML length: 20573 bytes
Number of products:    100

Single page render time (averaged over 1000 renders):
  Template callable:     0.189 ms  (1.89 µs/item)
  Build from scratch:    0.812 ms  (8.12 µs/item)

Template is 4.3x faster

The benchmark script shows difference between no templating (built from scratch) and the new system. The old system was slower than building from scratch and not really comparable here. Python web frameworks take some 0.5ms to even Hello World. The build from scratch for a large document can reduce the maximal req/s, while if templated it hardly affects performance.

Tronic added 5 commits July 5, 2026 04:50
- Remove mutable template-tag leftovers:
  - Builder.render(), _render_piece(), _render_value()
  - Builder.__setattr__() slot setter
  - trailing-underscore handling for placeholders
  - debug _optimize() method
- Update tests to use immutable Template objects only
- Update README and Template docstrings for new API
- Fix benchmark unused imports and formatting
@Tronic
Tronic force-pushed the templating-redux branch from 64c3a4b to 5a04f52 Compare July 5, 2026 20:23
@Tronic
Tronic marked this pull request as ready for review July 5, 2026 20:25
Copilot AI review requested due to automatic review settings July 5, 2026 20:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new stateless, immutable Template implementation intended for a breaking 2.0 release, replacing the prior templating behavior and updating docs/tests accordingly.

Changes:

  • Add html5tagger.Template and builder @ Template support, with placeholder semantics based on uppercase attribute access.
  • Update templating tests and README examples to the new API/semantics, and remove legacy optimization test.
  • Add a benchmark script and adjust Ruff configuration for fluent side-effecting attribute access.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
html5tagger/template.py Adds the new Template/Slot implementation that flattens builders into static fragments plus dynamic slots.
html5tagger/builder.py Updates placeholder handling and adds @ Template operator support on builders.
html5tagger/__init__.py Exposes Template in the public package API.
tests/test_templating.py Replaces old template-variable tests with coverage for the new stateless template behavior.
tests/test_html5tagger.py Removes the _optimize test corresponding to removed functionality.
README.md Updates public documentation and examples for the new templating syntax and semantics.
scripts/benchmark.py Adds a benchmark comparing template rendering vs building from scratch.
pyproject.toml Ignores Ruff B018 to accommodate the fluent attribute-access API style.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread html5tagger/template.py Outdated
Comment thread html5tagger/builder.py
Comment thread README.md Outdated
Comment thread scripts/benchmark.py Outdated
Comment thread scripts/benchmark.py
@Tronic

Tronic commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

This work is essentially finished. Anyone wishing to comment on it should do so promptly, before we release v2 with this.

The benchmark script is extended to a heavier and more realistic page, which also widens the performance gap:

Generated HTML length: 45230 bytes
Number of products:    100

Single page render time (averaged over 1000 renders):
  Template callable:     0.204 ms  (2.04 µs/item)
  Build from scratch:    1.356 ms  (13.56 µs/item)
  With CSS selectors:    1.612 ms  (16.12 µs/item)

Template is 6.7x faster than building from scratch

Build from scratch is the old API. CSS selectors were added in v1.4 just released. Templates provide a massive speedup and perform identically well regardless of whether selectors or "from scratch" was used (typically used together).

Tronic added 3 commits July 8, 2026 22:27
Add ClassesAttributeSlot so that classes=E.ClassesTag can be supplied at
render time with string/list/dict/iterable class specifications, just like
static classes=. Combines correctly with static class_ and CSS selector
classes.
Only support the explicit Template(builder) form. Update tests, template
docstring, and README examples accordingly.
@Tronic
Tronic merged commit 6d2e88d into main Jul 9, 2026
7 checks passed
@Tronic
Tronic deleted the templating-redux branch July 9, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants