Home Blog CV Projects Patterns Notes Book Colophon Search

Fast Python HTML Template

1 Oct, 2024

This is a simple way of rendering HTML in Python.

But I think Python HTML Tags is even better.

import html

class Tag:
    def __init__(self, name, attrs, children=None):
        self.name = name
        self.attrs = attrs
        self.children = children

tag = Tag

class Placeholder:
    def __init__(self, name):
        self.name = name

def render_attrs(attrs):
    all = []
    for key, value in attrs.items():
        if value is True:
            all.append(' ' + html.escape(key))
        elif value is False:
            pass
        else:
            all.append(f' {html.escape(key)}="{html.escape(value)}"')
    return ''.join(all)

def tag2template(t, indent='', parts=None, placeholders=None):
    if parts is None:
        parts = ['']
    if placeholders is None:
        placeholders = []
    escaped_name = html.escape(t.name)
    if t.attrs:
        parts[-1] += indent+'<' + escaped_name + render_attrs(t.attrs) + '>'
    else:
        parts[-1] += indent+'<' + escaped_name + '>'
    if t.children is not None:
        if type(t.children) is list:
            extra_indent = '    '
            if t.name == 'html':
                extra_indent = '' 
            for item in t.children:
                if type(item) is Tag:
                    child_parts, child_placeholders = tag2template(item, indent=indent + extra_indent)
                    parts[-1] += '\n' + child_parts[0]
                    parts += child_parts[1:]
                    placeholders += child_placeholders
                elif type(item) is str:
                    parts[-1] +=  indent+ extra_indent+ html.escape(item)
                elif type(item) is Placeholder:
                    parts[-1] += '\n'  + indent+ extra_indent 
                    placeholders.append(html.escape(item.name))
                    parts.append('')
            parts[-1] += '\n' + indent+'</' + escaped_name + '>'
        elif type(t.children) is str:
            parts[-1] += html.escape(t.children) + '</' + escaped_name + '>'
        elif type(t.children) is Placeholder:
            placeholders.append(html.escape(t.children.name))
            parts.append('</' + escaped_name + '>')
    return parts, placeholders

def tag2html(t):
    parts, placeholders = tag2template(t)
    assert placeholders == []
    return parts[0]

def render_template(template, values=None):
    if values is None:
        values = {}
    result = ''
    parts, placeholders = template
    result += parts[0]
    for i, part in enumerate(parts[1:]):
        result += values[placeholders[i]] + part
    return result

if __name__ == '__main__':
    import timeit

    template = tag2template(tag('html', {'lang': 'en'}, [
        tag('head', {}, [
            tag('title', {}, Placeholder('title')),
            tag('meta', {'name': "viewport", 'content': "width=device-width, initial-scale=1.0"}),
            tag('meta', {'charset': "UTF-8"}),
        ]),
        tag('body', {}, [
            tag('h1', {}, Placeholder('title')),
            Placeholder('body'),
        ])
    ]))
    body = 'This is the body'
    def run():
        return render_template(template, dict(title='This is the title', body=body))
    print(run())
    number = 1000000
    print(number / timeit.timeit(run, number=number))

Comments

Be the first to comment.

Add Comment





Copyright James Gardner 1996-2020 All Rights Reserved. Admin.