score.tpl

This module handles templates, i.e. text files that [usually] need to be pre-processed before they can be put to their actual use. This is not just limited to typical template languages like Jinja2 or Mako, but also include preprocessors for other formats like sass or coffescript.

Quickstart

This module is usally used in combination with other modules like score.jinja2, score.css, score.sass, score.js, etc. So add this module and the others you need to your initialization list and provide the root folder containing all your template files:

[score.init]
modules =
    score.tpl
    score.jinja2
    score.css

[tpl]
rootdir = ${here}/tpl

You can then render your templates:

>>> score.tpl.render('self-defense.jinja2', {'fruit' => 'banana'})

Configuration

score.tpl.init(confdict)[source]

Initializes this module acoording to our module initialization guidelines with the following configuration keys:

rootdirs None
Denotes the root folder containing all templates.

Details

File Types

This module primarily manages file types: classes of files identified by a common mime type. Every file type may have an associated list of file extensions, postprocessors and global variables.

You can define new file types accessing the FileType objects in the configured template module’s filetypes:

>>> tpl.filetypes['text/plain'].extensions.append('txt')
>>> def leetify(text):
...     return text\
...             .replace('e', '3')\
...             .replace('i', '1')\
...             .replace('l', '1')\
...             .replace('t', '7')\
...             .replace('o', '0')
...
>>> tpl.filetypes['text/plain'].postprocessors.append(leetify)

Your textual content will now always be rendered in leetspeak.

Loaders

Loaders are objects that can provide the contents of templates. They are always associated with a file extension and can be registered with this module. The following is a loader, that can provide all text files on your computer:

>>> tpl.loaders.append(FileSytemLoader('/', 'txt'))

If you also configure the leetify postprocessor found in the previous section, you can read all your text files in leetspeak after finalizing the module:

>>> tpl.render('/some/text/file.txt')
'H3110 W0r1d'

Renderers

It is also possible to register renderers for arbitrary file extensions. The module will the pass the template content to the renderer for processing and treat the return value as the new template content.

When registering renderers, they are not passed directly, but through a factory method, which is called the engine in this context. The following example creates a Renderer, that replaces all occurrences of the string FRUIT with the fruit variable, that was passed to the renderer:

class FruitRenderer(Renderer):

    def render_string(self, string, variables, path=None):
        return string.replace('FRUIT', variables['fruit'])

tpl.engines['txt'] = FruitRenderer

The reason for this additional layer of indirection is that some templating engines have separate modes for different file types: The Jinja2 renderer can be configured to automatically escape the HTML output, for example. This means, that the Jinja2 engine may choose to provide a different Renderer, if the target mime type is ‘text/html’.

Globals

It is also possible to provide variables, that are always present in certain file types. The following example provides the function now inside ‘text/html’ templates (for engines, that support calling functions):

>>> from datetime import datetime
>>> tpl.filetypes['text/plain'].add_global('now', datetime.now)

Your template can now always access the current time:

The current time is {{ now() }}.

Rendering Process

When instructed to render a template, the module needs to perform these steps:

  • Determine the loader to use
  • Determine the file type of the template
  • Determine the renderers to use

Normally, each of these decisions is trivial, but there are some cases that need a bit more explanation. Let’s look at these steps when loading the file myfile.css.jinja2, a Jinja2 file that renders content of the mime type ‘text/css’.

Determining the Loader

As each Loader is associated with an extension, we will need to be careful with file paths having more than one file extension. When loading file myfile.css.jinja2, the module would first look for a Loader registered with the extension css.jinja2. If there is none, it would next look for a Loader for jinja2 files. If that doesn’t exist either, the module will raise a TemplateNotFound exception.

Determining the File Type

A very similar process is performed for finding the file type of a template. This time the extensions are searched backwards, though: When looking for the file type of myfile.css.jinja2, the module will first check if any file type was registered for the extension css.jinja2. If it finds none, it will look for the mime type of the extension css.

Determining Renderers

When looking for the renderers to use, the module will first test if there is a renderer for the extension css.jinja2. If there is none, it will check for two other extensions: First jinja2, then css. So the possible engine list for our example path are:

  • Just one engine: the one registered for the extension css.jinja2
  • Both jinja2 and css engines.
  • Just jinja2
  • Just css

API

Configuration

score.tpl.init(confdict)[source]

Initializes this module acoording to our module initialization guidelines with the following configuration keys:

rootdirs None
Denotes the root folder containing all templates.
class score.tpl.ConfiguredTplModule[source]

This module’s configuration class.

filetypes

Mapping of mime type strings to FileType objects. Missing value will be created automatically, so the following code will work, even if no ‘text/html’ file type was defined yet:

>>> tpl.filetypes['text/plain'].extensions.append('txt')
loaders

Mapping of file extensions to Loader instances. You can modify this dict to your liking until the module is finalized.

engines

Mapping of file extensions to callbacks capable of creating Engine instances. The callback will be invoked once for every mime type, receiving the configured score.tpl module and the mime type to create the Renderer for.

Example: Assuming, that the “jinja2” engine is registered for the file extension “jinja2”, as is the default of score.jinja2. When the score.tpl module is instructed to render the template “foo.jinja2”, it will invoke the engine callback for the first time to create a Renderer for the “text/html” mime type:

>>> engine(tpl, "text/html")

If the score.css module was configured, it is possible to render a jinja2 template to construct a css file dynamically. So if the score.tpl module is instructed to render the file called “bar.css.jinja2”, it will invoke the engine again to obtain another jinja2 Renderer:

>>> engine(tpl, "text/css")
iter_paths(mimetype=None)[source]

Provides a generator iterating over all known template paths. If the optional parameter mimetype is present, only templates of that mime type will pe provided instead.

render(path, variables=None, *, apply_postprocessors=True)[source]

Renders give template path with the optional dict of variables. It is possible to prevent running the file type’s postprocessors by passing a falsey value for the apply_postprocessors parameter.

load(path)[source]

Loads given template path.

See Loader.load().

mimetype(path)[source]

Provides to mime type associated with given path.

See File Types.

hash(path)[source]

Provides a hash for given template path.

See Loader.hash().

class score.tpl.FileType[source]

Represents a known file type. Attributes may only be modified until finalization of the module.

mimetype

The mime type associated with this file type.

extensions

List of extensions associated with this file type. Extensions must not start with a period, but may contain them. Thus .foo is invalid, whereas foo and foo.bar are both valid.

postprocessors

List of postprocessor callbacks for this file type. Each postprocessor must accept a content string (the rendered template) and return the modified content.

globals

A list of namedtuples, each consisting of the parameters passed to add_global().

add_global(name, value, *, escape=True)[source]

Defines a new global variable with given name and value for this file type. The optional parameter escape determines, whether the value must be escaped in the final output.

Loader

class score.tpl.Loader[source]

Object capable of loading template content.

iter_paths()[source]

Provide a generator iterating all valid paths of this loader.

load(path)[source]

Load given path. Returns a 2-tuple, where the first value is a bool indicating whether the other value is a file path (True) or the contents of the template (False). Illustration of the return values:

(True, '/path/to/the/template.html')
(False, '<html>The contents of the template</html>')

Raises TemplateNotFound if this loader cannot load the given path.

is_valid(path)[source]

Whether given path is valid for this loader, i.e. whether a call to load() would return successfully.

hash(path)[source]

Provides a random str, that will always change whenever the file content changes. The default implementation uses the content’s xxHash.

class score.tpl.FileSystemLoader(rootdirs, extension)[source]

Loader searching for files with a given extension inside given folders.

class score.tpl.ChainLoader(loaders)[source]

A Loader that will wrap the other given loaders and simulate a loader, that combines the features of all of them. If this receives a Loader capable of loading ‘a.tpl’ and another Loader that can load ‘b.tpl’, this ChainLoader instance will be able to load ‘a.tpl’ and ‘b.tpl’.

Renderer

class score.tpl.Renderer(tpl_conf, filetype)[source]
render_file(file, variables, path=None)[source]

Renders given template file with the given variables dict.

render_string(string, variables, path=None)[source]

Renders the given template content string with the given variables dict.

Exceptions

class score.tpl.TemplateNotFound[source]

Thrown when a template was requested, but not found.