Skip to main content

A templating language that is both a superset and subset of HTML.

Project description

HTMTL

HTMTL (HyperText Markup Templating Language) is a templating language that uses HTML attributes for its rendering logic. It is both a subset and superset of HTML, meaning that valid HTML is also valid HTMTL and valid HTMTL is also valid HTML, allowing you to use any editor without needing any additional editor extensions.

Features

  • Interpolation: You can interpolate data from a data dictionary into your templates.
  • Modifiers: You can modify the interpolated values using modifiers.
  • Conditionals: You can show or hide blocks using expressions.
  • Partials: You can include other templates inside your templates.
  • Loops: You can loop over iterable data.
  • Extendable: You can implement custom parsers and modifiers.

Example syntax

<!DOCTYPE html>
<html>
<head>
    <title inner-text="{title}"></title>
</head>
<body>
    <h1 inner-text="{title}"></h1>
    
    <div class="posts" when="posts">
        <div iterate="posts as post">
            <h2 class="post-title">
                <a :href="/blog/{post.url}" inner-text="{post.title | Capitalize}"></a>
            </h2>
            <div class="post-date" inner-text="{post.date | Date('yyyy-MM-dd')}"></div>
            <div class="post-content" inner-html="{post.body}"></div>
        </div>
    </div>
</body>
</html>

Installation

pip install htmtl

Usage

A simple example of how to use HTMTL with default configuration looks like this:

from htmtl import Htmtl

template = Htmtl('<p inner-text="Hello {who}"></p>', {'who': 'World'})
html = template.to_html() # returns: <p>Hello World</p>

Attributes

HTMTL works by parsing attributes in the template.

inner-text

Sets the inner text of the element to the value of the attribute.

HTMTL template where title key is Hello, World!:

<h1 inner-text="{title}"></h1>

Results in:

<h1>Hello, World!</h1>

inner-html

Sets the inner HTML of the element to the value of the attribute.

HTMTL template where content key is <p>Hello, World!</p>:

<div inner-html="{content}"></div>

Results in:

<div>
    <p>Hello, World!</p>
</div>

inner-partial

Sets the inner HTML of the element to the value of the parsed HTMTL template. Inherits all the same data as the parent template.

HTMTL template with data such as:

data = {
    'title': 'My Web Portal Thing',
    'header': '<div class="header"><h1 inner-text="title"></h1></div>'
}

And where the template is:

<div inner-partial="header"></div>

Results in:

<div>
    <div class="header">
        <h1>My Web Portal Thing</h1>
    </div>
</div>

outer-text

Sets the outer text of the element to the value of the attribute.

HTMTL template where title key is Hello, World!:

<h1 outer-text="{title}"></h1>

Results in:

Hello, World!

outer-html

Sets the outer HTML of the element to the value of the attribute.

HTMTL template where content key is <p>Hello, World!</p>:

<div outer-html="{content}"></div>

Results in:

<p>Hello, World!</p>

outer-partial

Sets the outer HTML of the element to the value of the parsed Toretto template. Inherits all the same data as the parent template.

HTMTL template with data such as:

data = {
    'title': 'My Web Portal Thing',
    'header': '<div class="header"><h1 inner-text="title"></h1></div>'
}

And where the template is:

<div outer-partial="header"></div>

Results in:

<div class="header">
    <h1>My Web Portal Thing</h1>
</div>

when

Removes the element if the attribute is false-y.

HTMTL template where show key is False:

<div when="show">Hello, World!</div>

Results in:

<!-- Empty -->

when-not

Removes the element if the attribute is truthy.

HTMTL template where hide key is True:

<div when-not="hide">Hello, World!</div>

Results in:

<!-- Empty -->

iterate

Loops anything iterable.

For example, to loop over a collection of posts and then use post as the variable of each iteration, you can do something like this:

<div iterate="posts as post">
    <h2 inner-text="post.title"></h2>
</div>

If you do not care about using any of the iteration data, you can also entirely omit as ... from the expression, like so:

<div iterate="posts">
    ...
</div>

And, you can also assign the key of the iteration to a variable, like so:

<div iterate="posts as index:post">
    <h2 :class="post-{post.index}" inner-text="post.title"></h2>
</div>

This would add the key of the iteration to as post.index variable, but you can name it whatever you want.

:* (Generic Value Attributes)

You can use the :* attribute to set any attribute on an element to the interpolated value of the generic value attribute.

For example, to set the href attribute of an element, you can use the :href attribute:

<a :href="/blog/{slug}">Hello, World!</a>

Results in:

<a href="/blog/hello-world">Hello, World!</a>

If the slug key is hello-world.

Modifiers

All interpolated expressions can be modified using modifiers. Modifiers are applied to the value of the attribute, and they can be chained, like so:

<h1 inner-text="{title | Uppercase | Reverse}"></h1>

Note that if you have nothing other than the interpolated variable in the attribute, then you can omit the curly brackets, and so this would also work:

<h1 inner-text="title | Uppercase | Reverse"></h1>

Modifiers can also take arguments which are passed within parentheses ( and ), and can be either int, float, str or bool. For example:

<h1 inner-text="some_var | SomeModifier(123, 'asd', true)"></h1>

Date

Parses the value into a formatted date string.

<p inner-text="published_at | Date('YYYY-mm-dd')"></p>

Truncate

Truncates the value to the specified length.

<p inner-text="{title | Truncate(10)}"></p>

This also works on collections, so you can use truncate to limit items in an array as well.

Extending

Parsers

You can add (or replace) parsers in HTMTL when creating a new instance of the Htmtl class, like so:

from htmtl import Htmtl
from htmtl.parsers import InnerText

template = Htmtl('<p inner-text="Hello {who}"></p>', {'who': 'World'})
template.set_parsers([
    InnerText,
])

html = template.to_html() # returns: <p>Hello World</p>

Prsers must extend the Parser class, like so:

from typing import Optional
from dompa.nodes import Node, TextNode
from htmtl import Parser


class InnerText(Parser):
    def traverse(self, node: Node) -> Optional[Node]:
        if "inner-text" in node.attributes:
            node.children = [TextNode(value=self.expression(node.attributes["inner-text"]))]
            node.attributes.pop("inner-text")

        return node

All parsers traverse the entire DOM tree to do whatever DOM manipulation they want. It's important to know that a parser must have the traverse method, and it must return a Node, or None if you want to remove the Node.

HTMTL is built upon the Dompa HTML parser, so check that out for more granular info on things.

List of built-in parsers

  • htmtl.parsers.GenericValue - Parser the :* attributes.
  • htmtl.parsers.When - Parser the when attributes.
  • htmtl.parsers.WhenNot - Parser the when-not attributes.
  • htmtl.parsers.InnerPartial - Parser the inner-partial attributes.
  • htmtl.parsers.InnerHtml - Parser the inner-html attributes.
  • htmtl.parsers.InnerText - Parser the inner-text attributes.
  • htmtl.parsers.OuterPartial - Parser the outer-partial attributes.
  • htmtl.parsers.OuterHtml - Parser the outer-html attributes.
  • htmtl.parsers.OuterText - Parser the outer-text attributes.
  • htmtl.parsers.Iterate - Parses the iterate attributes.

Modifiers

You can add (or replace) modifiers in HTMTL when creating a new instance of the Htmtl class, like so:

from htmtl import Htmtl
from htmtl.modifiers import Truncate

template = Htmtl('<p inner-text="Hello {who}"></p>', {'who': 'World'})
template.set_modifiers([
    Truncate,
])

html = template.to_html() # returns: <p>Hello World</p>

Mdifiers must extend the Modifier class, like so:

from typing import Any
from htmtl import Modifier


class Truncate(Modifier):
    def modify(self, value: Any, opts: list[Any]) -> Any:
        if isinstance(value, str) and len(opts) > 0:
            if all([x in "1234567890" for x in opts[0]]):
                char_limit = int(opts[0])

                if len(value) > char_limit:
                    return f"{value[:char_limit - 3]}..."

        return value

List of built-in modifiers

  • htmtl.modifiers.Truncate - Truncates the value (both strings and collections).

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

htmtl-0.2.0.tar.gz (10.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

htmtl-0.2.0-py3-none-any.whl (12.4 kB view details)

Uploaded Python 3

File details

Details for the file htmtl-0.2.0.tar.gz.

File metadata

  • Download URL: htmtl-0.2.0.tar.gz
  • Upload date:
  • Size: 10.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.13.1

File hashes

Hashes for htmtl-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c919f7e38274134b9b3cd751c61ca3c2e5f6d9dc48c580d828a37ea38af1180f
MD5 a98cb76720edf94297e8a1d4cd10db03
BLAKE2b-256 563dee7ac85bc358f50eb0e6cccceb5bdd67e58d6f824feb0dfd89bb7f82d7be

See more details on using hashes here.

File details

Details for the file htmtl-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: htmtl-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 12.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.13.1

File hashes

Hashes for htmtl-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b3c16f5a68be546b67ff9a2d65528b53603a5dd6e47fba4b7ddc7aceaba78ee4
MD5 0acd8f0e66dececa537768a19e9de93f
BLAKE2b-256 8fc9d47453fc40a1a0b08a249717d7246e433640f847bf931d6cf7fa691117e6

See more details on using hashes here.

Supported by

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