Package: htmldoom
htmldoomIntroducing htmldoomhtmldoomis an HTML rendering framework for Python that helps you define your
HTML documents, templates, layouts and contents in an organised way, gives you
the flexibility to use many different syntaxes, option to declare and debug
different components and pages separately and more... all without making you
worry about the runtime performance.How does it work?Understanding the working principle will get pretty intuitive if we go one
feature at a time. Let's start with HTML escaping.HTML escapingIn the center of htmldoom's functionality is the HTML escaping logic. Texts to
be escaped are represented usingstr, while texts that should not be escaped
are represented usingbytes. Thehtmldoom.renders()function receives the
texts as inputs, then for each input, it decides whether to escape it or not
based on if it isstrorbytesand returns the rendered text instrformat.We can usehtmldoom.txt()to convert some string into HTML escaped text, andhtmldoom.raw()to convert some string into HTML unescaped text. It should be
noted that both the functions will return text formatted inbyteswhich can be
passed to thehtmldoom.render()to render it back tostr.HTML elementsHTML elements or tags can be defined using thehtmldoom.composite_tag()and
thehtmldoom.leaf_tag()functions. Any tag that can have child elements (e.g.<p></p>) are defined usinghtmldoom.composite_tag().For example:p = composite_tag("p")Tags that cannot have child elements are defined usinghtmldoom.leaf_tag().For example:input_ = leaf_tag("input")So when we callp(class_="red")("foo", "bar")orinput_(type_="button"),
we getbytesencoded<p class="red">foobar</p>or<input type="button" />respectively. Thehtmldoom.render()function will
render them back tostr.HTML components / layoutsHTML components i.e. combination of several elements (tags and texts) can be
defined using the@htmldoom.renders()decorator like below.from htmldoom import renders, elements as e

@renders(
    e.p(class_="red")("{key1}", "{key2}"),
    e._input(type_="button")
)
def my_component(value1, value2):
    return {"key1": value1, "key2": value2}Now we can render the component by calling it with the required arguments e.g.my_document(value1="&amp;", value2=b"&amp;")This will return abytesencoded text. Thevalue1will be escaped to&amp;amp;since
it isstr.value2is inbytes, hence it will remain the same and wil be
rendered in the browser as&.Calling this will returnbytesencoded text<p class="red">foobar</p><input type="button" />which can be rendered into string usinghtmldoom.render().This mechanism renders the template text during module load time. Hence there should be
no performance drop in runtime.NOTE: It is important to note that every{and}should be escaped with{{and}}when it is not being used as a placeholder for some variable.It is also possible to define the components usingyamlsyntax like below.some:
  demo:
    p:
    - class: red
    - - "{key1}"
      - "{key2}"
    input:
    - type: buttonAnd then define the component renderer like below.from htmldoom import renders
from htmldoom.yaml_loader import loadyaml

@renders(loadyaml("path/to/component.yaml", ("some", "demo")))
def my_component(value1, value2):
    return {"key1": value1, "key2": value2}We can also use escaped and raw loaders with thehtmldoom.loadtxt()andhtmldoom.loadraw()functions respectively. We only need to pass the file path as shown in theyamlexample.We can use the same syntax to define reusable layouts.Separating values from templateshtmldoom provides us with a friendly way to separate values from layouts or components
templates. Thehtmldoom.value_loader.loadvalues()function scans the given directory and
returns a nested namedtuple with the file or directory names as element names and the loaded
contents as the values. We can pass this to components like below.from htmldoom import renders, elements as e
from htmldoom.value_loader import loadvalues

values = loadvalues("path/to/values")

@renders(
    e.p()("{v.title}"),
    e.p()("{v.content.line1}")
    e.p()("{v.content.line2}")
    e.p()("{v.content.line3}")
)
def my_component():
    return {"v": values}So we need a directory structure like below.values
├── title.txt
└── content
    ├── line1.txt
    ├── line2.html
    └── line3.ymlAs you mught have guessed, this can load the values based of file types. You can use
your own renderer for file types such asmdorrstby extending thehtmldoom.value_loader.EXTENSION_RENDERERSmap and passing is to thehtmldoom.value_loader.loadvalues()function like below.from htmldoom import raw
from htmldoom.value_loader import loadvalues, EXTENSION_RENDERERS

def markdown_to_html(path):
    """Some function that reads the file, converts the content to HTML,
    and returns the converted data as string.
    """
    # The logic goes here...

MY_EXTENSION_RENDERERS = dict(
    md=lambda path: raw(markdown_to_html(path)),
    **EXTENSION_RENDERERS
)

values = loadvalues("path/to/values", extension_renderers=MY_EXTENSION_RENDERERS)In fact, this documentation is generated using the same method.