Generate HTML with Python.
Pythoml provides a set of classes which render into HTML tags, allowing you to define HTML in code.
⚠️ CAUTION
It is important to sanitise any strings from an external source (e.g. a user input or database) which are used to render anything in a browser (e.g. HTML).
Pythoml does not provide any sanitisation. It is up to the developer to include that where necessary.
Pythonml can be installed from PyPI:
pip install pythoml
Alternatively, you can download a specific version from the Releases.
The Pythoml.html
module contains Tag
classes for each HTML5 tag.
The class names match their respective HTML tags, for example: html.A
for <a>...</a>
and html.Div
for <div>...</div>
.
For the complete list please see html.py.
Creating a Tag
:
from Pythoml import html
ex = html.P("This is a paragraph")
Calling render()
on a Tag
will return the HTML string:
print(ex.render())
Will print the following:
<p>
This is a paragraph
</p>
A Tag
constructor accepts any number of arguments and keyword arguments (i.e. *args, **kwargs
).
Arguments (*args
) are used for "content" which is nested inside the HTML tag.
For example, in the code above we passed the string "This is a paragraph" to html.P
, so it was rendered inside the HTML tag <p>...</p>
.
Keyword Arguments (**kwargs
) are used for the HTML tag attributes and flags.
When a keyword argument value is a string, it will be used as an attribute of the HTML tag. For example setting an attribute name
on a Div
:
ex = html.Div(name="myDiv")
print(ex.render())
"""
<div name="myDiv">
</div>
"""
ℹ️ INFO
There are some special keys to avoid collisions with Python keywords (most notablyclass_
for setting a tag class attribute).
Below is a list of the special keys, listed asPython
->HTML
:
class_
->class
for_
->for
id_
->id
type_
->type
When a keyword argument value is a boolean, it will be used as a flag on the HTML tag. For example setting a dialog
as open:
ex = html.Dialog(open=True)
print(ex.render())
"""
<dialog open>
</dialog>
"""
Tags
can be added to the "content" of other Tags
to achieve the typical nesting of HTML:
ex = html.Html(
html.Body(
html.H1("This is my Site"),
html.Hr(),
html.Div(
html.P("Welcome to my website!")
)
)
)
print(ex.render())
"""
<html>
<body>
<h1>
This is my Site
</h1>
<hr>
<div>
<p>
Welcome to my website!
</p>
</div>
</body>
</html>
"""
ℹ️ INFO
Tags
which represent "void elements" do not support adding content.
If you add content to such aTag
, you will recieve a warning.
You can lazily add
content or set
attributes on a Tag
:
ex = html.Div()
ex.add("Some content")
ex.set(name="myDiv")
print(ex.render())
"""
<div name="myDiv">
Some content
</div>
"""
The add
and set
methods both return Self
, which allows you to chain them:
ex = html.Div()
ex.add("Some content").set(name="myDiv")
print(ex.render())
"""
<div name="myDiv">
Some content
</div>
"""
Alternatively, you can also call the instanciated Tag
to update the content and attributes simultaneously:
ex = html.Div()
ex("Some content", name="myDiv")
print(ex.render())
"""
<div name="myDiv">
Some content
</div>
"""
The syntax shown up until now only uses the constructor to populate the Tag
content and attributes. Since the examples have been relatively simple, it does not highlight a minor grievance (at least to me...) with this approach.
Consider the following:
ex = html.Div(
html.H1("Title", class_="display-4")
html.Div(
html.H3("Subtitle"),
html.Div(
html.P("Some content"),
class_="card"
),
),
class_="container p-3" # <-- who does this belong to?
)
With big ugly nests like this, it gets difficult to determine which Tag
the attributes are associated with.
Since we can lazily add content, an alternative to the above is:
ex = html.Div(class_="container p-3")(
html.H1("Title", class_="display-4"),
html.Div(
html.H3("Subtitle"),
html.Div(class_="card")(
html.P("Some content"),
),
),
)