Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
319 lines (236 sloc) 8.31 KB

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 grammar. You should feel familiar with Sketch if you are familiar with Python.

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>

The contents between <% and %> look quite similar to the Python syntax. Actually, 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) any Python syntax in your sketches but with some modifications:

  1. All the python style syntax is wrapped between <% and %>.
  2. Indent Is not indicated by : any more, but controlled by a special <% end %> statement.

Also, :func:`print` function still prints to sys.stdout by default. To print to the template, use :meth:`.SketchRuntime.write` or output statements.

Statement Mark

All the statements of sketches 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
have ambiguity of 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 simplest and the most common-used(perhaps) statement in the whole Templating System.

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

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

The result will be passed to :meth:`.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 :class:`.BaseSketchContext`.

Indentation

Indentation is used to control the flow 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 establish an new indentation until another unindent statement or half-indent statement is reached.

Warning

Redundant unindent statements will raise a :class:`.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 time stamps until a positive value is selected by random function.

Note

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

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.

Include

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 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 :class:`.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 being drawn, it will ask the sketch finder to find layout.html and update all the blocks in layout.html with the blocks in main.html. The other content outside the blocks in main.html can be accessed using self.body in layout.html.

Hint

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

Important

When drawing the self.body, make sure to use :ref:`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 :class:`.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 the comment. %>

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

The result of the example above is:

This is the content.