Templating

The goal of bricks.html5 is to replace your template engine by Python code that generates HTML fragments. This approach removes the constraints imposed by the template language and makes integration with surrounding Python code trivial.

I know what you are thinking: “it is a really bad idea to mix template with logic”. Bricks obviously doesn’t prevent you from shooting yourself on the foot and you can make really messy code if you want. However, things can be very smooth if you stick to focused and simple components that adopt a more functional style.

Our advice is: break your code in small pieces and compose these pieces in simple and predictable ways. Incidentally, this is a good advice for any form of code ;).

The fact is that our good old friend “a function” is probably simpler to use and composes much better than anything a templating engine has come up with.

Let us dive in!

We want to implement a little Bootstrap element that shows a menu with actions (this is a random example taken from Bootstrap website).

<div class="btn-group">
  <button type="button"
          class="btn btn-default dropdown-toggle"
          data-toggle="dropdown"
          aria-haspopup="true"
          aria-expanded="false">
    Action <span class="caret"></span>
  </button>
  <ul class="dropdown-menu">
    <li><a href="#">Action</a></li>
    <li><a href="#">Another action</a></li>
    <li><a href="#">Something else here</a></li>
    <li role="separator" class="divider"></li>
    <li><a href="#">Separated link</a></li>
  </ul>
</div>

Of course we could translate this directly into bricks code by calling the corresponding div()‘s, button()‘s, etc. But first, let us break up this mess into smaller pieces.

from bricks.html5 import button, div, p, ul, li, span

def menu_button(name, caret=True):
    return \
        button(type='button',
               class_='btn btn-default dropdown-toggle',
               data_toggle="dropdown",
               aria_haspopup="true",
               aria-expanded="false")[
            name,
            span(class_='caret') if caret else None,  # Nones are ignored
        ]

Ok, it looks like it’s a lot of trouble for a simple component. But now we can reuse this piece and easily make as many buttons as we like: menu_button('File'), menu_button('Edit'), .... The next step is to create a function that takes a list of strings and return the corresponding menu (in the real world we might also want to control the href attribute). We are also going to be clever and use the Ellipsis (...) as a menu separator.

def menu_data(values):
    def do_item(x):
        if x is ...:
            return li(role='separator', class='divider')
        else:
            # This could parse the href from string, or take a tuple
            # input, or whatever you like. The bricks.helpers.link function
            # can be handy here.
            return li[a(href='#')[x]]

    return \
        ul(class_='dropdown-menu')[
            map(do_item, values)
        ]

We glue both together...

def menu(name, values, caret=True):
    return \
        div(class_='btn-group')[
            menu_button(name, caret=True),
            menu_data(values),
        ]

... and create as many new menu buttons as we like:

menubar = \
    div(id='menubar')[
        menu('File', ['New', 'Open', ..., 'Exit']),
        menu('Edit', ['Copy', 'Paste', ..., 'Preferences']),
        menu('Help', ['Manual', 'Topics', ..., 'About']),
    ]

Look how nice it is now :)

The with statement

If you have more complex logic the “with” syntax can be handy.

>>> with div(class_='card') as fragment:
...     +h1('Multi-hello')
...     for i in range(1, 4):
...         +p('hello %s' % i)
>>> print(fragment.pretty())
<div class="card">
  <h1>Multi-hello</h1>
  <p>hello 1</p>
  <p>hello 2</p>
  <p>hello 3</p>
</div>

The unary + operator says “add me to the node defined in the last with statement”. Nested with statements are also supported.

How does it work?

Bricks HTML syntax is obviously just regular Python wrapped in a HTML-wannabe DSL. How does it work?

Take the example:

element = \
    div(class_="contact-card")[
        span("john", class_="contact-name"),
        span("555-1234", class_="contact-phone"),
    ]

The first positional argument is a single child element or a list of children. Keyword arguments are interpreted as tag attributes. Notice we did not use class as an argument name because it is a reserved keyword in Python. Bricks, however, ignores all trailing underscores and converts underscores in the middle of the argument to dashes.

If your tag uses underscore in any attribute name or if you happen to have the attributes to values stored in a dictionary, just use the attrs argument of a tag constructor.

# <div my_attr="1" attrs="2" data-attr="3">foo</div>

div('foo', attrs={'my_attr': 1, 'attrs': 2}, data_attr=3)

Imperative API

The contact-card element above could have been created in a more regular imperative fashion:

element = div(class_="contact-card")
span1 = span("john", class_="contact-name")
span2 = span("555-1234", class_="contact-phone")
element.children.extend([span1, span2])

This is not as expressive as the first case and forces us to think imperative instead of thinking in declarative markup. This is not very natural for HTML and also tends to be more verbose. The “square bracket syntax” is just regular Python indexing syntax abused to call .children.extend to insert child elements into the tag’s children attribute.

More specifically, the tag[args] creates a copy of the original tag, flatten all list and tuple arguments, insert them into the copied object, and return it. The same hack is applied to the metaclass and this allow us to call tags that do not define any attribute like this:

element = \
    div[
        span('Foo'),
        span('Bar'),
    ]

And since lists, tuples, mappings, and generators are flattened, we can also define a tag’s children with list comprehensions and maps:

element = \
    div[
        [span(x) for x in words],
        map(lambda x, y: a(x, href=b), words, hyperlinks),
    ]

Since square brackets were already taken to define the children elements of a tag, we cannot use them to directly access the children elements of a tag. Instead, this must be done explicitly using the tag.children interface. It behaves just as a regular list so you can do things as

>>> elem = div('foo', class_='elem')
>>> elem.children.append('Hello world')
>>> first = elem.children.pop(0)
>>> print(elem)
<div class="elem">Hello world</div>

Similarly to children, attributes are also exposed in a special attribute named attrs that behaves like a dictionary:

>>> elem = div('foo', class_='elem')
>>> elem.attrs['data-answer'] = 42
>>> elem.attrs.keys()
dict_keys(['class', 'data-answer'])

The attrs dictionary also exposes the id and class elements as read-only values. id is also exposed as an attribute and class is constructed from the list of classes in the tag.classes attribute.

>>> elem = div('foo', class_='class', id='id')
>>> elem.id, elem.classes
('id', ['class'])
>>> elem.id = 'new-id'
>>> print(elem)
<div id="new-id" class="class">foo</div>