Skip to main content

Templating recipe with remote resource support.

Project description

======================
slapos.recipe.template
======================

Template recipe which supports remote resource.

Inspired by collective.recipe.template, with minimum set of features, but with
(hopefully) safer buildout-based templating.

"jinja2" entry point allows rendering jinja2 templates.

Usage
=====

Getting started
---------------

You can start by a simple buildout::

>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template
... url = template.in
... output = template.out
...
... [section]
... option = value
... ''')

And a simple template::

>>> write('template.in', '${section:option}')

We run buildout::

>>> print system(join('bin', 'buildout')),
Installing template.

And the output file has been parsed by buildout itself::

>>> cat('template.out')
value

Full options
------------

There is two non required options:

``md5sum``
Check the integrity of the input file.

``mode``
Specify the filesystem permissions in octal notation.

Check file integrity
~~~~~~~~~~~~~~~~~~~~

Let's write a file template::

>>> write('template.in', '${buildout:parts}')

Compute its MD5 sum::

>>> import md5
>>> md5sum = md5.new(open('template.in', 'r').read()).hexdigest()

Write the ``buildout.cfg`` using slapos.recipe.template::

>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template
... url = template.in
... output = template.out
... md5sum = ''' + md5sum + '''
... ''')

And run buildout, and see the result::

>>> print system(join('bin', 'buildout')),
Uninstalling template.
Installing template.

>>> cat('template.out')
template

If the md5sum doesn't match, the buildout fail::

>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template
... url = template.in
... output = template.out
... md5sum = 0123456789abcdef0123456789abcdef
... ''')
>>> print system(join('bin', 'buildout')),
While:
Installing.
Getting section template.
Initializing part template.
Error: MD5 checksum mismatch for local resource at 'template.in'.


Specify filesystem permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can specify the mode of the written file::

>>> write('template.in', '${buildout:installed}')

>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template
... url = template.in
... output = template.out
... mode = 0627
... ''')

>>> print system(join('bin', 'buildout')),
Uninstalling template.
Installing template.

And the generated file with have the right permissions::

>>> import stat
>>> import os
>>> print oct(stat.S_IMODE(os.stat('template.out').st_mode))
0627

Section dependency
------------------

You can use other part of buildout in the template. This way this parts
will be installed as dependency::

>>> write('template.in', '${dependency:foobar}')
>>> write('buildout.cfg', '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template
... url = template.in
... output = template.out
...
... [dependency]
... foobar = dependency content
... recipe = zc.buildout:debug
... ''')

>>> print system(join('bin', 'buildout')),
Uninstalling template.
Installing dependency.
foobar='dependency content'
recipe='zc.buildout:debug'
Installing template.

This way you can get options which are computed in the ``__init__`` of
the dependent recipe.

Let's create a sample recipe modifying its option dict::

>>> write('setup.py',
... '''
... from setuptools import setup
...
... setup(name='samplerecipe',
... entry_points = {
... 'zc.buildout': [
... 'default = main:Recipe',
... ],
... }
... )
... ''')
>>> write('main.py',
... '''
... class Recipe(object):
...
... def __init__(self, buildout, name, options):
... options['data'] = 'foobar'
...
... def install(self):
... return []
... ''')

Let's just use ``buildout.cfg`` using this egg::

>>> write('template.in', '${sample:data}')
>>> write('buildout.cfg',
... '''
... [buildout]
... develop = .
... parts = template
...
... [template]
... recipe = slapos.recipe.template
... url = template.in
... output = template.out
...
... [sample]
... recipe = samplerecipe
... ''')
>>> print system(join('bin', 'buildout')),
Develop: ...
Uninstalling template.
Uninstalling dependency.
Installing sample.
Installing template.
>>> cat('template.out')
foobar

Jinja2 usage
============

Getting started
---------------

Example buildout demonstrating some types::

>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... template = foo.in
... rendered = foo
... context =
... key bar section:key
... key recipe :recipe
... raw knight Ni !
... json words ["Peng", "Neee-wom"]
... jsonkey later section:later-words
... import json_module json
... section param_dict parameter-collection
...
... [parameter-collection]
... foo = 1
... bar = bar
...
... [section]
... key = value
... later-words = "Ekke Ekke Ekke Ekke Ptangya Ziiinnggggggg Ni!"
... ''')

And according Jinja2 template (kept simple, control structures are possible)::

>>> write('foo.in',
... '{{bar}}\n'
... 'Knights who say "{{knight}}" also protect {{ words | join(", ") }}.\n'
... 'They later say {{later}}\n'
... '${this:is_literal}\n'
... '${foo:{{bar}}}\n'
... 'swallow: {{ json_module.dumps(("african", "european")) }}\n'
... 'parameters from section: {{ param_dict | dictsort }}\n'
... 'Rendered with {{recipe}}'
... )

We run buildout::

>>> print system(join('bin', 'buildout')),
Installing template.

And the template has been rendered::

>>> cat('foo')
value
Knights who say "Ni !" also protect Peng, Neee-wom.
They later say Ekke Ekke Ekke Ekke Ptangya Ziiinnggggggg Ni!
${this:is_literal}
${foo:value}
swallow: ["african", "european"]
parameters from section: [('bar', 'bar'), ('foo', '1')]
Rendered with slapos.recipe.template:jinja2

Parameters
----------

Mandatory:

``template``
Template url/path, as accepted by zc.buildout.download.Download.__call__ .

``rendered``
Where rendered template should be stored.

Optional:

``context``
Jinja2 context specification, one variable per line, with 3
whitespace-separated parts: type, name and expression. Available types are
described below. "name" is the variable name to declare. Expression semantic
varies depending on the type.

Available types:

``raw``
Immediate literal string.

``json``
Immediate json-encoded string.

``key``
Indirect literal string.

``jsonkey``
Indirect json-encoded string.

``import``
Import a python module.

``section``
Make a whole buildout section available to template, as a dictionary.

Indirection targets are specified as: [section]:key .
It is possible to use buildout's buit-in variable replacement instead instead
of ``key`` or ``jsonkey`` types, but keep in mind that different lines are
different variables for this recipe. It might be what you want (factorising
context chunk declarations), otherwise you should use indirect types.

``md5sum``
Template's MD5, for file integrity checking. By default, no integrity check
is done.

``umask``
Umask, in octal notation (no need for 0-prefix), to create output file with.
Defaults to system's umask at the time recipe is instanciated.

``extensions``
Jinja2 extensions to enable when rendering the template,
whitespace-separated. By default, none is loaded.

FAQ
---

Q: How do I generate ${foo:bar} where foo comes from a variable ?

A: ``{{ '${' ~ foo_var ~ ':bar}' }}``
This is required as jinja2 fails parsing "${{{ foo_var }}:bar}". Though,
jinja2 succeeds at parsing "${foo:{{ bar_var }}}" so this trick isn't
needed for that case.

Use jinja2 extensions
~~~~~~~~~~~~~~~~~~~~~

>>> write('foo.in',
... '''{% set foo = ['foo'] -%}
... {% do foo.append(bar) -%}
... {{ foo | join(', ') }}''')
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... template = foo.in
... rendered = foo
... context = key bar buildout:parts
... # We don't actually use all those extensions in this minimal example.
... extensions = jinja2.ext.do jinja2.ext.loopcontrols
... jinja2.ext.with_
... ''')
>>> print system(join('bin', 'buildout')),
Uninstalling template.
Installing template.

>>> cat('foo')
foo, template

Check file integrity
~~~~~~~~~~~~~~~~~~~~

Compute template's MD5 sum::

>>> write('foo.in', '{{bar}}')
>>> import md5
>>> md5sum = md5.new(open('foo.in', 'r').read()).hexdigest()
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... template = foo.in
... rendered = foo
... context = key bar buildout:parts
... md5sum = ''' + md5sum + '''
... ''')
>>> print system(join('bin', 'buildout')),
Uninstalling template.
Installing template.

>>> cat('foo')
template

If the md5sum doesn't match, the buildout fail::

>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... template = foo.in
... rendered = foo
... context = key bar buildout:parts
... md5sum = 0123456789abcdef0123456789abcdef
... ''')
>>> print system(join('bin', 'buildout')),
While:
Installing.
Getting section template.
Initializing part template.
Error: MD5 checksum mismatch for local resource at 'foo.in'.


Specify filesystem permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can specify the umask for rendered file::

>>> write('template.in', '{{bar}}')
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... template = foo.in
... rendered = foo
... context = key bar buildout:parts
... umask = 570
... ''')
>>> print system(join('bin', 'buildout')),
Uninstalling template.
Installing template.

And the generated file with have the right permissions::

>>> import stat
>>> import os
>>> print oct(stat.S_IMODE(os.stat('foo').st_mode))
0206

Section dependency
------------------

You can use other part of buildout in the template. This way this parts
will be installed as dependency::

>>> write('foo.in', '{{bar}}')
>>> write('buildout.cfg', '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... template = foo.in
... rendered = foo
... context = key bar dependency:foobar
...
... [dependency]
... foobar = dependency content
... recipe = zc.buildout:debug
... ''')

>>> print system(join('bin', 'buildout')),
Uninstalling template.
Installing dependency.
foobar='dependency content'
recipe='zc.buildout:debug'
Installing template.

This way you can get options which are computed in the ``__init__`` of
the dependent recipe.

Let's create a sample recipe modifying its option dict::

>>> write('setup.py',
... '''
... from setuptools import setup
...
... setup(name='samplerecipe',
... entry_points = {
... 'zc.buildout': [
... 'default = main:Recipe',
... ],
... }
... )
... ''')
>>> write('main.py',
... '''
... class Recipe(object):
...
... def __init__(self, buildout, name, options):
... options['data'] = 'foobar'
...
... def install(self):
... return []
... ''')

Let's just use ``buildout.cfg`` using this egg::

>>> write('foo.in', '{{bar}}')
>>> write('buildout.cfg',
... '''
... [buildout]
... develop = .
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... template = foo.in
... rendered = foo
... context = key bar sample:data
...
... [sample]
... recipe = samplerecipe
... ''')
>>> print system(join('bin', 'buildout')),
Develop: ...
Uninstalling template.
Uninstalling dependency.
Installing sample.
Installing template.
>>> cat('foo')
foobar


2.4.1 (2012-08-01)
================

* jinja2: Make "context" parameter really optional. [Vincent Pelletier]

2.4 (2012-06-01)
================

* Provide access to zc.buildout.buildout.dumps when it exists. [Vincent
Pelletier]

* Fix missing jinja2 entry point documentation in pacakge description [Vincent
Pelletier]

2.3 (2012-03-29)
================

* Add jinja2 entry point with jinja2 template support. [Vincent Pelletier]

2.2 (2011-10-12)
================

* Include missing files in package. [Łukasz Nowak]

2.1 (2011-10-12)
================

* Description update. [Łukasz Nowak]

2.0 (2011-10-12)
================

* Dropping collective.recipe.template dependency. [Romain Courteaud, Antoine
Catton]

1.1 (2011-05-30)
================

* Moved out from slapos.cookbook in order to minimise depenencies [Łukasz
Nowak]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slapos.recipe.template-2.4.1.tar.gz (10.1 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page