Light-weight Python Templating Engine
Project description
pypage is a document template engine for Python programmers with a short learning curve.
Why use pypage?
Easy to pick up. Syntax similar to Python’s.
You need an eval-based template engine.
pypage supports Python 3.x and 2.7, and has been tested (using test_cmd) on CPython and PyPy.
What does it look like?
<ul id="users">
{% for user in users %}
<li>
<a href="mailto: {{ html_ascii( user.email ) }}">{{ user.name }}</a>
</li>
{% endfor %}
</ul>
Installation
You can install pypage easily with pip:
pip install pypage
Try running pypage -h to see the command-line options available.
Embedding Code
In order to embed code in a document, you wrap Python code with {{ and }}. The {{ ... }} constructs are called code tags. There are two kinds of code tags: inline and multiline.
Execution Environment
All code is executed in a shared common environment. I.e., the locals and globals passed into eval and exec is a single shared dictionary, for all code tags in the same file.
As such, a variable instantiated in a code tag at the beginning of the document, will be available to all other code tags in the document. When pypage is invoked as library, an initial seed environment consisting of a Python dictionary mapping variable names to values, can be provided.
The write function
A write function similar to the Python 3’s print function is accessible from both kinds of code tags. It writes text into the document that substitutes/replaces the code tag it’s used in.
write(*object, sep=' ', end='\n')
Objects passed to it are stringified with str, concatenated together with sep, and terminated with end. The outputs of multiple calls to write in a code tag are concatenated together, and the resulting final output is injected in place of the code tag.
If write is called from an inline code tag, the result of evaluating the expression (a None, since write will return a None) is ignored, and the output of the write call is used instead.
Finer Details
Inheritance (with inject and exists)
The approach taken by pypage toward template inheritance is quite distinct from that of other templating engines (like Jinja’s). It’s a lot simpler. You call a pypage-provided function inject with the path of a pypage template you want to inject (i.e. “extend” in Jinja parlance), and pypage will process that template under the current scope (with all previously defined variables being available to the injected template), and the inject function will return its output.
A base template could look like this:
<html>
<head>
<title>
{% if exists('title') %}
{{title}}
{% else %}
No title
{% endif %}
</title>
</head>
<body>
{{body}}
</body>
</html>
A derived templates only needs to define body and optionally title, to “extend” the template above.
{% capture body %}
The HTML body content would go in here.
{% %}
{{ inject('...path to the base template...') }}
We didn’t specify a title above, but if we wanted to, we’d just need to make sure it was defined before inject was called. The base template checks whether a title variable exists by calling the function exists. As is obvious, the exists function simply takes a variable name as a string, and returns a boolean indicating whether the variable exists in the current scope.
This approach to inheritance is explicit and easy-to-grasp. Rather than have complex inheritance rules, with a default block definition that is optionally overridden by a derived template, we make things more explicit by using conditionals for cases where we want to provide a default/fallback definition. We error out if a definition is expected to be provided, and is not present. The output of the “dervied” template is clear and obvious, with this approach.
Whitespace & Indentation
Whitespace Removal
If a block tag is on a line by itself, surrounded only by whitespace, then that whitespace is automatically excluded from the output. This allows you indent your block tags without worrying about excess whitespace in the generated document.
Automatic Indentation
pypage smartly handles indentation for you. In a multi-line code tag, if you consistently indent your Python code with a specific amount of whitespace, that indentation will be stripped off before executing the code block (as Python is indentation-sensitive), and the resulting output of that code block will be re-indented with same whitespace that the initial code block was.
The whitespace preceding the second line of code determines the peripheral indentation for the entiee block. All subsequent lines (after second) must begin with exact same whitespace that preceded the second line, or be an empty line.
For example:
<p>
Lorem ipsum dolor sit amet
<ul>
{{
def foo():
write("Hello!")
foo()
}}
</ul>
consectetur adipisicing elit
</p>
would produce the following output:
<p>
Lorem ipsum dolor sit amet
<ul>
Hello!
</ul>
consectetur adipisicing elit
</p>
Note that the Hello! was indented with same whitespace that the code in the code block was.
pypage automatically intends the output of a multi-line tag to match the indentation level of the code tag. The number of whitespace characters at the beginning of the second line of the code block determines the indentation level for the whole block. All lines of code following the second line must at least have the same level of indentation as the second line (or else, a PypageSyntaxError exception will be thrown).
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Comments
Comment Tags
Anything bounded by {# and #} will be omitted from the output. For example:
Comment Blocks
You can also easily comment an existing block, by simply placing the word comment in front of it:
The comment keyword before the for above results in the entire block being commented out and omitted from the output.