The Sketch Templating Language

The sketch templating language is aiming to generate flexible output for arbitrary text file. It is designed for Pythonistas with statement marks inspired by ERB and a Python like syntax.

Example

<!DOCTYPE>
<html>
    <head>
        <title><%= await handler.get_page_title() %></title>
    </head>
    <body>
        <main>
            <% async for item in handler.db.items.find() %>
                <article>
                    <div class="title"><%= item["title"] %></div>
                    <div class="author"><%= item["author"] %></div>
                    <div class="content"><%= item["content"] %></div>
                </article>
            <% end %>
        </main>
    </body>
</html>

Contents between <% and %> may look quite similar to Python syntax. As a matter of fact, Sketchbook rewrites sketches into Python code and asks the Python interpreter to “compile” the plain Python code into Python byte code.

Hence, You can use (almost) all Python syntax in your sketches but with some modifications:

  1. All python style syntax is wrapped between <% and %>.

  2. Indentation is not indicated by : any more, but controlled by a special <% end %> statement.

Also, print() function still prints to sys.stdout by default. To print to the template, use SketchRuntime.write() or output statements.

Statement Mark

All the statements are wrapped under statement marks - <% and %>.

Statement Mark Escape

<%% and %%> escape <% and %> respectively. e.g.: The following sketch:

<%% is the begin mark, and <%r= "%%> is the end mark. " %>
<%r= "<% and" %> %> only need to be escaped whenever they
are ambiguous to the templating system.

will be drawn like:

<% is the begin mark, and %> is the end mark.
<% and %> only need to be escaped whenever they
have ambiguity of the templating system.

Output

The most commonly-used(perhaps) statement in the Templating System.

= is the statement keyword, and the expression following the keyword will be evaluated by the interpreter. You can also await a collections.abc.Awaitable:

Hello, <%= await user.get_user_name() %>.

The result will be passed to SketchRuntime.write() and escaped by the default escape function.

Raw Output

To output raw strings without escaping, use raw= (or r= for shorthand) as a keyword. In this case, raw is the escape function for statement instead of default.

For how to override the default escape function, define custom escape functions and view built-in escape functions, please see the documentation for BaseSketchContext.

Indentation

Indentation is used to control the flow on how the sketch is drawn. There’re three types of keywords to control the indentation:

  • Indent keywords: if, with, for, while, try and async.

  • Unident keyword: end.

  • Half-indent keywords: else, elif, except and finally.

Indent keywords is used to start an indentation, and the unindent keyword is used to finish the indentation created by the last indent keyword.

Half Indentation

Half-indent keywords is a special type. It unindents the last indentation, and establishes a new indentation at the same time.

Example:

<% if a == b %>
    <%= "They are the same." %>
<% else %>
    <%= "They are not the same." %>
<% end %>

The if statement creates an indenation as discussed above, and the else statement will automatically unident the if statement, and create a new indentation until another unindent statement or half-indent statement is reached.

Warning

Redundant or missing unindent statements will raise a SketchSyntaxError.

Inline

The statement represents a Python inline keyword.

Example:

<% from time import time as get_timestamp %>
<% import random %>

<% while True %>
    <%r= str(get_timestamp()) %>
    <% if random.choice(range(0, 2)) %>
        <% break %>
    <% end %>
<% end %>

This example will output timestamps until a positive value is selected by random.random().

Note

Keywords of inline statements are break, continue, import, from, raise, assert, nonlocal, and global.

Variable Assignment

In Python language, keyword = is used to assign values to variables. However, in order to set a variable in sketches, you have to use an additional keyword let:

<% try %>
    <%= a %>
<% except NameError %>
    Variable a is not set.
<% end %>

<% let a = "whatever" %>
Variable a is set to <%= a %>.

This should output

Variable a is not set.
Variable a is set to whatever.

Inclusion

Include another sketch into the current sketch.

Example:

header.html:

<header>
    <h1>Site Title</h1>
</header>

main.html:

<html>
    <head>
        <title>Main Page</title>
    </head>
    <body>
        <% include "header.html" %>
        <main>
            <p>Thank you for visiting.</p>
        </main>
    </body>
</html>

When main.html is being drawn, it will ask the finder to find header.html and draw header.html at the runtime, then append it to the result of main.html.

The result of the example above is:

<html>
    <head>
        <title>Main Page</title>
    </head>
    <body>
        <header>
            <h1>Site Title</h1>
        </header>
        <main>
            <p>Thank you for visiting.</p>
        </main>
    </body>
</html>

Inheritance

Inherit from other sketches. When a sketch with an inherit statement is being drawn, a subclass of BaseSketchFinder will find the parent sketch. The parent sketch will then being drawn with .SketchRuntime.body set to the output of the original sketch. The blocks of the parent sketch will be replaced with the ones in the child sketch.

Example:

layout.html:

<html>
    <head>
        <title><% block title %><% end %></title>
        <% block head %><% end %>
    </head>
    <body>
        <%r= self.body %>
    </body>
</html>

main.html:

<% inherit "layout.html" %>
<% block title %>Main Page<% end %>
<main>
    <p>Thank you for visiting.</p>
</main>

When main.html is being drawn, it will ask the sketch finder to find layout.html and update all the blocks in layout.html with blocks in main.html. Other content outside blocks in main.html can be accessed using self.body in layout.html.

Hint

If Inheritance is not enabled, the block statement has no effect.

Important

When drawing the self.body, make sure to use Raw Output, or the it may be escaped.

Warning

If the sketch being drawn is not a parent of another sketch, using self.body will raise an SketchDrawingError.

The result of the example above is:

<html>
    <head>
        <title>Main Page</title>
    </head>
    <body>
        <main>
    <p>Thank you for visiting.</p>
</main>
    </body>
</html>

Comment

Strings that will be removed from the result.

This is the content.
<%# This is a comment. %>

When the sketch is being drawn, the comment will be excluded.

The result of the example above is:

This is the content.