Skip to main content

tagz is a html tags builder

Project description

tests Coveralls Latest Version python wheel Python Versions license

tagz

tagz – is an extremely simple library for building html documents without using templates, just with python code.

from tagz import Page, StyleSheet, Style, html


page = Page(
    lang="en",
    body_element=html.body(
        html.h1("Hello"),
        html.div(
            html.strong("world"),
        ),
        html.a(
            "example link",
            html.i("with italic text"),
            href="https://example.com/"
        ),
    ),
    head_elements=(
        html.meta(charset="utf-8"),
        html.meta(name="viewport", content="width=device-width, initial-scale=1"),
        html.title("tagz example page"),
        html.link(href="/static/css/bootstrap.min.css", rel="stylesheet"),
        html.script(src="/static/js/bootstrap.bundle.min.js"),
        html.style(
            StyleSheet({
                "body": Style(padding="0", margin="0"),
                (".container", ".container-fluid"): Style(transition="opacity 600ms ease-in"),
            })
        )
    ),
)

# `pretty=False` should be faster but performs not a human-readable result
print(page.to_html5(pretty=True))

writes something like this:

<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8"/>
		<meta content="width=device-width, initial-scale=1" name="viewport"/>
		<title>
			tagz example page
		</title>
		<link href="/static/css/bootstrap.min.css" rel="stylesheet"/>
		<script src="/static/js/bootstrap.bundle.min.js">
		</script>
		<style>
			body {margin: 0; padding: 0;}
			.container, .container-fluid {transition: opacity 600ms ease-in;}
		</style>
	</head>
	<body>
		<h1>
			Hello
		</h1>
		<div>
			<strong>
				world
			</strong>
		</div>
		<a href="https://example.com/">
			example link
			<i>
				with italic text
			</i>
		</a>
	</body>
</html>

Features

tagz provides the following features:

Callable children and attributes

You can pass a function (callable) as a child or as an attribute value to any tag. This allows for lazy or dynamic content generation.

Callable children

from tagz import html

def child():
    return "hello"

tag = html.div(child)
assert str(tag) == "<div>hello</div>"

Or return another tag:

from tagz import html

# Callable child returning a tag
def child_tag():
    return html.span("world")

tag = html.div(child_tag)
assert str(tag) == "<div><span>world</span></div>"

You can also use append with callables:

from tagz import html

tag = html.div()
tag.append(lambda: "foo")
assert str(tag) == "<div>foo</div>"

Callable attribute values

You can use callables as attribute values. If the result is a not an string, it will be converted to string and escaped as an attribute value.

from tagz import html

def attr():
    return "bar"

tag = html.div(foo=attr)
assert str(tag) == '<div foo="bar"></div>', str(tag)

Custom tags is supported

Add custom tags by using underscore _ in the name:

from tagz import html
assert str(html.my_custom_tag("hello")) == "<my-custom-tag>hello</my-custom-tag>" 

Pretty printing html

You can pretty print the html output with to_string(pretty=True) or to_html5(pretty=True) methods:

from tagz import html

print(
    html.div(
        "Hello", html.strong("world"),
    ).to_string(pretty=True)
)
#<div>
#	Hello
#	<strong>
#		world
#	</strong>
#</div>

Style and StyleSheet helper objects

Style helper object encapsulating css styles:

from tagz import Style
assert str(Style(color="#ffffff")) == "color: #ffffff;"

StyleSheet helper object encapsulating css stylesheet:

from tagz import Style, StyleSheet

# body {padding: 0;margin: 0}
# a, div {transition: opacity 600ms ease-in}
print(
    str(
        StyleSheet({
            "body": Style(padding="0", margin="0"),
            ("div", "a"): Style(transition="opacity 600ms ease-in"),
        })
    )
)

Controlling Attribute Absence

You can use the special value ABSENT to dynamically remove an attribute from a tag. This is useful for callables that may want to omit an attribute based on logic.

from tagz import html, ABSENT

present = True

def attr():
     return "value" if present else ABSENT

tag = html.div(test=attr)
assert str(tag) == '<div test="value"></div>'

present = False
assert str(tag) == "<div></div>"

Raw Content in Script and Style

Content inside <script> and <style> tags is not escaped by default. This allows you to embed raw JS/CSS.

from tagz import html

style = html.style("body {margin: 0; padding: 0;}")
assert str(style) == "<style>body {margin: 0; padding: 0;}</style>"

script = html.script('''console.log(1 > 2 && 3 < 2 && "0" === '0');''')
assert str(script) == '''<script>console.log(1 > 2 && 3 < 2 && "0" === '0');</script>'''

Tag classes API

The classes property supports assignment via set, list, tuple, or space-separated string, and raises a TypeError for invalid types.

from tagz import html

tag = html.div()
tag.classes = "foo bar"
assert tag.classes == {"foo", "bar"}
tag.classes = ["baz"]
assert tag.classes == {"baz"}

You can use either class or classes as a keyword argument or attribute key. Both will be mapped to the HTML class attribute and the internal classes set. This allows for more natural Python code:

from tagz import html

tag = html.div(classes=["foo", "bar"])
# Classes are stored as a set internally
assert str(tag) == '<div class="bar foo"></div>', str(tag)

tag["class"] = "baz"
assert str(tag) == '<div class="baz"></div>', str(tag)

tag["classes"] = "spam eggs"
# Classes was splitted and are stored as a set internally
assert str(tag) == '<div class="eggs spam"></div>', str(tag)

Escaping Callable Children

If a callable child returns a string, it will be escaped by default (unless the tag disables escaping, e.g., <script> or <style>):

from tagz import html

def child():
    return "<script>alert('xss');</script>"

tag = html.div(child)
assert str(tag) == "<div>&lt;script&gt;alert(&#x27;xss&#x27;);&lt;/script&gt;</div>"

Boolean Attribute Handling

Setting an attribute to True renders it as a boolean attribute (e.g., <input checked>). Setting it to False removes the attribute:

from tagz import html

tag = html.input(type="checkbox", checked=True)
assert str(tag) == '<input checked type="checkbox"/>'
tag["checked"] = False
assert str(tag) == '<input type="checkbox"/>'

Data URI helpers

You can embed binary data directly in HTML attributes using data_uri and open_data_uri:

from tagz import data_uri

src = data_uri(b"hello world", media_type="text/plain")
assert src == "data:text/plain;base64,aGVsbG8gd29ybGQ="

Or use open_data_uri to read and encode a file directly:

from tempfile import NamedTemporaryFile
from tagz import open_data_uri, html

with NamedTemporaryFile("wb", suffix=".png") as f:
    # Write a minimal PNG file
    f.write(b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01"
            b"\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4"
            b"\x89\x00\x00\x00\nIDATx\xdac\xf8\x0f\x00\x01\x01"
            b"\x01\x00\x18\xdd\x03\xe2\x00\x00\x00\x00IEND\xaeB`\x82")
    f.flush()
    f.seek(0)

    src = open_data_uri(f.name, media_type="image/png")
    img_tag = html.img(src=src, alt="Nothing")
    assert str(img_tag).startswith(
        '<img alt="Nothing" src="data:image/png;base64,'
        'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcS'
        'JAAAACklEQVR42mP4DwABAQEAGN0D4gAAAABJRU5ErkJggg=='
    )

More examples

Building page from parts

You can reuse the code, and assemble the page piece by piece, to do this you can modify elements already added to the tags:

from tagz import html, Page

# Make an content element
content = html.div(id='content')

page = Page(
    lang="en",
    body_element=html.body(
        html.h1("Example page"),
        html.hr(),
        # Adding it to the page
        content,
    ),
    head_elements=(
        html.meta(charset="utf-8"),
        html.title("tagz partial page"),
    ),
)

content.append("Example page content")

print(page.to_html5(pretty=True))

This prints something like this:

<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8"/>
		<title>
			tagz example page
		</title>
	</head>
	<body>
		<h1>
			Example page
		</h1>
		<hr/>
		<div id="content">
			Example page content
		</div>
	</body>
</html>

Convert CSV to html table

from io import StringIO
from urllib.request import urlopen
from csv import reader
from tagz import html, Page, Style

url = (
    'https://media.githubusercontent.com/media/datablist/'
    'sample-csv-files/main/files/organizations/'
    'organizations-10000.csv'
)

csv = reader(StringIO(urlopen(url).read().decode()))
table = html.table(border='1', style=Style(border_collapse="collapse"))
content = list(csv)

# Make table header 
table.append(html.tr(*map(html.th, content[0])))

# Add table rows
for csv_row in content[1:]:
    table.append(html.tr(*map(html.td, csv_row)))

page = Page(
    lang="en",
    body_element=html.body(
        html.h1("Converted CSV"),
        table,
        "Content of this page has been automatically converted from",
        html.a(url, href=url),
    ),
    head_elements=(
        html.meta(charset="utf-8"),
        html.title("tagz csv example page"),
    ),
)

with open("/tmp/csv.html", "w") as fp:
    fp.write(page.to_html5())

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

tagz-0.6.0.tar.gz (9.2 kB view details)

Uploaded Source

Built Distribution

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

tagz-0.6.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file tagz-0.6.0.tar.gz.

File metadata

  • Download URL: tagz-0.6.0.tar.gz
  • Upload date:
  • Size: 9.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.20 {"installer":{"name":"uv","version":"0.9.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tagz-0.6.0.tar.gz
Algorithm Hash digest
SHA256 18e4a77451edfcef0d8de6628991ce216a50e22a26c510712d4bd82a32a1d211
MD5 685d33bf39fa04acb0ca175f4f0851a2
BLAKE2b-256 0e49f488d9320d44da3938b55b3757a1938ea6c72bffe6fba42ca8edb3442916

See more details on using hashes here.

File details

Details for the file tagz-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: tagz-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 8.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.20 {"installer":{"name":"uv","version":"0.9.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tagz-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 21953be853f73622f5f1e0f6c14e372b567c0bf11f437b3298d69e7b11372819
MD5 bd7ed5ffa4dd8c146c0a47ec01dac41b
BLAKE2b-256 cf57976efbad152e12665a3d095907ae8f9a30d9ef22f81a3c178f61ae0ffad3

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