Skip to main content

Generate HTML with Python!

Project description

HTMGEM

Generate HTML with Python (Inspired by Mithriljs).

htmgem is a collection of html tags as functions. This allows you to create dinamically html pages and html components on the backend with python.

Quickstart

pip3 install htmgem

Bellow you have an html boilerplate created with htmgem:

from htmgem.tags import *


html_string = \
html({'lang':'en'}, [
    head([
        meta({'charset':'UTF-8'}),
        meta({'name':'viewport', 'content':'width=device-width, initial-scale=1.0'}),
        link({'rel':'stylesheet', 'href':'https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.1.2/tailwind.min.css'}), 
        script({'src':'https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js', 'defer':None})
    ]),

    body([
        h1("Interesting title"),

        p("A very long paragraph"),

        ul({"class": "somediv"}, [
            (li, "item1"),
            (li, "item2"),
            (li, {"id": "myid", "class":"important"}, "item3"),
        ])
    ])
])

The html equivalent would look like this:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.1.2/tailwind.min.css">
    <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
</head>
<body>

    <h1>Interesting title</h1>

    <p>A very long paragraph</p>

    <ul class="somediv">
        <li>item1</li>
        <li>item2</li>
        <li id="myid" class="important">item3</li>
    </ul>

</body>
</html>

As you can see html is a function and same is the case for body, p, ul, li etc.

Each tag function receives attributes and children as parameters.

Some examples:

p(attrs={'class':'bg-gray-100', 'id':'first-paragraph'}, children="My paragraph")
#'<p class="bg-gray-100" id="first-paragraph">My paragraph</p>'

p({'class':'bg-gray-100', 'id':'first-paragraph'})
# '<p class="bg-gray-100" id="first-paragraph"></p>'

p("My paragraph")
# '<p>My paragraph</p>'

p()
# '<p></p>'

div({'class': 'flex items-center'}, [p(f"My paragraph{nbr}") for nbr in range(3)])
# '<div class="flex items-center"><p>My paragraph0</p><p>My paragraph1</p><p>My paragraph2</p></div>'

div([p, p, p])
# '<div><p></p><p></p><p></p></div>'


input_({'type':'text', 'required':None})
# '<input type="text" required></input>' 
  • attrs : is a dictionary which contains attribute name (class, id, anything) and the corresponding value;
  • children: can be a string or a list of other tag functions (ul, li etc);
  • attrs and children are positional and optional parameters;

This is how you can create a component:

def Link(path, content):

    class_list = [
        'bg-blue-200',
        'p-4',
        'cursor-pointer'
    ]    

    return a(
        {"href": path, "class": class_list, 'style':'margin-left:2rem;'}, 
        content
    )


Link('/users', 'see users')
# '<a href="/users" class="bg-blue-200 p-4 cursor-pointer" style="margin-left:2rem;">see users</a>'

Now you can pass Link component in any other function tag (p, div, body etc).

This is how a generated list would look like:

data_list = [1,2,3,4] # something from your database

def DataNbrList():
    #Whatever operations you want to do on data

    htmli = []
    for item in data_list:
        htmli.append(li(item))

    return [
        h4(f"Currently saved names {len(htmli)}"),
        ul(htmli)
    ]


div(DataNbrList())
# '<div><h4>Currently saved names 4</h4><ul><li>1</li><li>2</li><li>3</li><li>4</li></ul></div>'

I quess by now you got a feel on how htmgem works, how you can integrate htmgem with Flask or other framework?

HTML is just text (more specifically text/html)

from flask import Flask, request
from functools import lru_cache # trick for max performance
from htmgem.tags import *


app = Flask(__name__)


# This can be imported from another file
Layout = lambda page_title, page_content: \
html({'lang':'en'}, [
    head([
        meta({'charset':'UTF-8'}),
        meta({'name':'viewport', 'content':'width=device-width, initial-scale=1.0'}),
        # Add Alpine js for a sprinkle of interactivity (modals, toggles etc)
        script({'src':'https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js', 'defer':None})
        title(page_title)
    ]),

    body(page_content)

])


@app.route("/")
def index():  
    return Layout(
        page_title   = "home page", 
        page_content = [ #list of components or just the component
            h1("The home page"),
            Link(path='/form-page', content="Go to form page")
        ]
    )


if __name__ == "__main__":
    app.run(debug=True)

In the examples folder you have a comparision between a list with 100_000 items generated with javascript (vannila js) and the same list generated with Python. Once the js is working on creating the list the page is blocked while when the html is sent by python you can still work on the page. Of course, the js code can be optimized, but same is the case for Python.

Why?

  • Freedom to have data as html([li(item) for item in items]);
  • No specific template syntax just python functions;
  • It's faster to sent HTML than sending JSON which is parsed later by Javascript;
  • Minimum javascript skills to create a dynamic component (no need to learn Vue, React, Angular, Svelte or other frontend framework);
  • No need to worry about the js bundle size (you just send html);
  • Better performance by decorating components with @functools.lru_cache(maxsize=None) or @functools.cache(3.9+) (read python docs);
  • For interactivity js features you can use either AlpineJs or LuciaJs;
  • Easy Lighthouse: Performance, SEO.

Submit any questions/issues you have! Fell free to fork it and improve it!

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

htmgem-0.0.2.tar.gz (11.1 kB view details)

Uploaded Source

Built Distribution

htmgem-0.0.2-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file htmgem-0.0.2.tar.gz.

File metadata

  • Download URL: htmgem-0.0.2.tar.gz
  • Upload date:
  • Size: 11.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/51.3.3 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.8.10

File hashes

Hashes for htmgem-0.0.2.tar.gz
Algorithm Hash digest
SHA256 df194f5c92442f7d974866df809b07c326bff10a42b2f35cc023ddbc6c5187f2
MD5 fec5429a2f6ec1eda503dc0f30291107
BLAKE2b-256 cbb7b1a6d4c85488aae21cc1bf3c0fc53b75a634077096cc0348ccde6d927f6d

See more details on using hashes here.

File details

Details for the file htmgem-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: htmgem-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 8.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.7.0 requests/2.25.1 setuptools/51.3.3 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.8.10

File hashes

Hashes for htmgem-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 600a62f64b0ada245f53ea7c429582b4627cd448fe585cbf8aea2a37217a3f80
MD5 1c74bf3500197e0b03f1b71596554c6c
BLAKE2b-256 1d8f01f16124ca3d0b3faf596f5d7196f65a3f080ff59e88e167f5b471d09b20

See more details on using hashes here.

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