Tempita
+++++++
.. CXREF:
https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge
.. image:: https://github.com/doblabs/tempita-hotoffthehamster/actions/workflows/checks-unspecial.yml/badge.svg?branch=release
:target: https://github.com/doblabs/tempita-hotoffthehamster/actions/workflows/checks-unspecial.yml/badge.svg?branch=release
:alt: Build Status
.. CXREF: https://app.codecov.io/gh/doblabs/tempita-hotoffthehamster/settings/badge
.. image:: https://codecov.io/gh/doblabs/tempita-hotoffthehamster/graph/badge.svg?token=E0cpXPQExg
:target: https://app.codecov.io/gh/doblabs/tempita-hotoffthehamster
:alt: Coverage Status
.. image:: https://readthedocs.org/projects/tempita-hotoffthehamster/badge/?version=latest
:target: https://tempita-hotoffthehamster.readthedocs.io/en/latest/
:alt: Documentation Status
.. image:: https://img.shields.io/github/v/release/doblabs/tempita-hotoffthehamster.svg?style=flat
:target: https://github.com/doblabs/tempita-hotoffthehamster/releases
:alt: GitHub Release Status
.. image:: https://img.shields.io/pypi/v/tempita-hotoffthehamster.svg
:target: https://pypi.org/project/tempita-hotoffthehamster/
:alt: PyPI Release Status
.. image:: https://img.shields.io/pypi/pyversions/tempita-hotoffthehamster.svg
:target: https://pypi.org/project/tempita-hotoffthehamster/
:alt: PyPI Supported Python Versions
.. image:: https://img.shields.io/github/license/doblabs/tempita-hotoffthehamster.svg?style=flat
:target: https://github.com/doblabs/tempita-hotoffthehamster/blob/release/LICENSE
:alt: License Status
.. |tempita-hotoffthehamster| replace:: ``tempita-hotoffthehamster``
.. _tempita-hotoffthehamster: https://github.com/doblabs/tempita-hotoffthehamster
.. |pipx| replace:: ``pipx``
.. _pipx: https://pypa.github.io/pipx/
|
.. .. toctree::
.. :maxdepth: 1
..
.. license
.. modules/tempita
.. .. contents::
:author: Ian Bicking <ianb@colorstudy.com>
Status & License
================
Tempita is available under an `MIT-style license <license.html>`_.
It is ~actively~ developed, but not an ambitious project. It does not
seek to take over the templating world, or adopt many new features.
I just wanted a small templating language for cases when ``%`` and
``string.Template`` weren't enough.
Discussion should go the the `Paste-users mailing list
<http://pythonpaste.org/community/mailing-list.html>`_ and bugs in the
`Paste Trac instance <http://trac.pythonpaste.org/>`_.
Why Another Templating Language
===============================
Surely the world has enough templating languages? So why did I write
another.
I initially used `Cheetah <http://cheetahtemplate.org/>`_ as the
templating language for `Paste Script
<http://pythonpaste.org/script/>`_, but this caused quite a few
problems. People frequently had problems installing Cheetah because
it includes a C extension. Also, the errors and invocation can be a
little confusing. This might be okay for something that used
Cheetah's features extensively, except that the templating was a very
minor feature of the system, and many people didn't even understand or
care about where templating came into the system.
At the same time, I was starting to create reusable WSGI components
that had some templating in them. Not a lot of templating, but enough
that ``string.Template`` had become too complicated -- I need if
statements and loops.
Given this, I started looking around for a very small templating
language, and I didn't like anything I found. Many of them seemed
awkward or like toys that were more about the novelty of the
implementation than the utility of the language.
So one night when I felt like coding but didn't feel like working on
anything I was already working on, I wrote this. It was first called
``paste.util.template``, but I decided it deserved a life of its own,
hence Tempita.
The Interface
=============
The interface is intended to look a lot like ``string.Template``. You
can create a template object like::
>>> import tempita
>>> tmpl = tempita.Template("""Hello {{name}}""")
>>> tmpl.substitute(name='Bob')
'Hello Bob'
Or if you want to skip the class::
>>> tempita.sub("Hello {{name}}", name='Alice')
'Hello Alice'
Note that the language allows arbitrary Python to be executed, so
your templates must be trusted.
You can give a name to your template, which is handy when there is an
error (the name will be displayed)::
>>> tmpl = tempita.Template('Hi {{name}}', name='tmpl')
>>> tmpl.substitute()
Traceback (most recent call last):
...
NameError: name 'name' is not defined at line 1 column 6 in file tmpl
You can also give a namespace to use by default, which
``.substitute(...)`` will augment::
>>> tmpl = tempita.Template(
... 'Hi {{upper(name)}}',
... namespace=dict(upper=lambda s: s.upper()))
>>> tmpl.substitute(name='Joe')
'Hi JOE'
Lastly, you can give a dictionary-like object as the argument to
``.substitute``, like::
>>> name = 'Jane'
>>> tmpl.substitute(locals())
'Hi JANE'
There's also an `HTMLTemplate`_ class that is more appropriate for
templates that produce HTML.
You can also instantiate a template from a filename with
``Template.from_filename(filename, namespace={}, encoding=None)``.
This is like calling::
Template(open(filename, 'rb').read().decode(encoding),
name=filename, namespace=namespace)
Unicode
-------
Tempita tries to handle unicode gracefully, for some value of
"graceful". ``Template`` objects have a ``default_encoding``
attribute. It will try to use that encoding whenever ``unicode`` and
``str`` objects are mixed in the template. E.g.::
>>> tmpl = tempita.Template(u'Hi {{name}}')
>>> import sys
>>> if sys.version.startswith('2'): # unicode is the default in 3 -> failing test
... val = tmpl.substitute(name='Jos\xc3\xa9')
... comparison = val == u'Hi Jos\xe9'
... else:
... comparison = True
>>> comparison
True
>>> tmpl = tempita.Template('Hi {{name}}')
>>> print (tmpl.substitute(name=u'Jos\xe9'))
Hi José
The default encoding is UTF8.
The Language
============
The language is fairly simple; all the constructs look like
``{{stuff}}``.
Substitution
------------
To insert a variable or expression, use ``{{expression}}``. You can't
use ``}}`` in your expression, but if it comes up just use ``} }``
(put a space between them). You can pass your expression through
*filters* with ``{{expression | filter}}``, for instance
``{{expression | repr}}``. This is entirely equivalent to
``{{repr(expression)}}``. But it might look nicer to some people; I
took it from Django because I liked it. There's a shared namespace,
so ``repr`` is just an object in the namespace.
If you want to have ``{{`` or ``}}`` in your template, you must use
the built-in variables like ``{{start_braces}}`` and
``{{end_braces}}``. There's no escape character.
You may also specify the delimiters as an argument to the Template
``__init__`` method::
>>> tempita.Template(
content='Hello ${name}',
delimiters=('${', '}')
).substitute(name='world')
'Hello world'
The delimiters argument must be of length two and both items must be strings.
None, as a special case, is substituted as the empty string.
Also there is a command for setting default values in your template::
{{default width = 100}}
You can use this so that the ``width`` variable will always have a
value in your template (the number ``100``). If someone calls
``tmpl.substitute(width=200)`` then this will have no effect; only if
the variable is undefined will this default matter. You can use any
expression to the right of the ``=``.
if
--
You can do an if statement with::
{{if condition}}
true stuff
{{elif other_condition}}
other stuff
{{else}}
final stuff
{{endif}}
Some of the blank lines will be removed when, as in this case, they
only contain a single directive. A trailing ``:`` is optional (like
``{{if condition:}}``).
for
---
Loops should be unsurprising::
{{for a, b in items}}
{{a}} = {{b | repr}}
{{endfor}}
See? Unsurprising. Note that nested tuples (like ``for a, (b, c)
in...``) are not supported (patches welcome).
inherit & def
-------------
You can do template inheritance. To inherit from another template
do::
{{inherit "some_other_file"}}
From Python you must also pass in, to `Template`, a `get_template`
function; the implementation for ``Template.from_filename(...)`` is::
def get_file_template(name, from_template):
path = os.path.join(os.path.dirname(from_template.name), name)
return from_template.__class__.from_filename(
path, namespace=from_template.namespace,
get_template=from_template.get_template)
You can also pass in a constructor argument `default_inherit`, which
will be the inherited template name when no ``{{inherit}}`` is in the
template.
The inherited template is executed with a variable ``self``, which
includes ``self.body`` which is the text of the child template. You
can also put in definitions in the child, like::
{{def sidebar}}
sidebar links...
{{enddef}}
Then in the parent/inherited template::
{{self.sidebar}}
If you want to make the sidebar method optional, in the inherited
template use::
{{self.get.sidebar}}
If ``sidebar`` is not defined then this will just result in an object
that shows up as the empty string (but is also callable).
This can be called like ``self.sidebar`` or ``self.sidebar()`` -- defs
can have arguments (like ``{{def sidebar(name)}}``), but when there
are no arguments you can leave off ``()`` (in the call and
definition).
Python blocks
-------------
For anything more complicated, you can use blocks of Python code,
like::
{{py:x = 1}}
{{py:
lots of code
}}
The first form allows statements, like an assignment or raising an
exception. The second form is for multiple lines. If you have
multiple lines, then ``{{py:`` must be on a line of its own and the
code can't start out indented (but if you have something like ``def
x():`` you would indent the body).
These blocks of code can't output any values, but they can calculate
values and define functions. So you can do something like::
{{py:
def pad(s):
return s + ' '*(20-len(s))
}}
{{for name, value in kw.items()}}
{{s | pad}} {{value | repr}}
{{endfor}}
As a last detail ``{{# comments...}}`` doesn't do anything at all,
because it is a comment.
bunch and looper
----------------
There's two kinds of objects provided to help you in your templates.
The first is ``tempita.bunch``, which is just a dictionary that also
lets you use attributes::
>>> bunch = tempita.bunch(a=1)
>>> bunch.a
1
>>> list(bunch.items())
[('a', 1)]
>>> bunch.default = None
>>> print (bunch.b)
None
This can be nice for passing options into a template.
The other object is for use inside the template, and is part of the
default namespace, ``looper``. This can be used in ``for`` loops in
some convenient ways. You basically use it like::
{{for loop, item in looper(seq)}}
...
{{endfor}}
The ``loop`` object has a bunch of useful methods and attributes:
``.index``
The index of the current item (like you'd get with
``enumerate()``)
``.number``
The number: ``.index + 1``
``.item``
The item you are looking at. Which you probably already have,
but it's there if you want it.
``.next``
The next item in the sequence, or None if it's the last item.
``.previous``
The previous item in the sequence, or None if it's the first
item.
``.odd``
True if this is an odd item. The first item is even.
``.even``
True if it's even.
``.first``
True if this is the first item.
``.last``
True if this is the last item.
``.length``
The total length of the sequence.
``.first_group(getter=None)``
Returns true if this item is the first in the group, where the
group is either of equal objects (probably boring), or when you
give a getter. getter can be ``'.attribute'``, like
``'.last_name'`` -- this lets you group people by their last
name. Or a method, like ``'.birth_year()'`` -- which calls the
method. If it's just a string, it is expected to be a key in a
dictionary, like ``'name'`` which groups on ``item['name']``.
Or you can give a function which returns the value to group on.
This always returns true when ``.first`` returns true.
``.last_group(getter=None)``
Like ``first_group``, only returns True when it's the last of
the group. This always returns true when ``.last`` returns true.
Note that there's currently a limitation in the templating language,
so you can't do ``{{for loop, (key, value) in looper(d.items())}}``.
You'll have to do::
{{for loop, key_value in looper(d.items())}}
{{py:key, value = key_value}}
...
{{endfor}}
HTMLTemplate
============
In addition to ``Template`` there is a template specialized for HTML,
``HTMLTemplate`` (and the substitution function ``sub_html``).
The basic thing that it adds is automatic HTML quoting. All values
substituted into your template will be quoted unless they are
specially marked.
You mark objects as instances of ``tempita.html``. The easiest way is
``{{some_string | html}}``, though you can also use
``tempita.html(string)`` in your functions.
An example::
>>> tmpl = tempita.HTMLTemplate('''\
... Hi {{name}}!
... <a href="{{href}}">{{title|html}}</a>''')
>>> name = tempita.html('<img src="bob.jpg">')
>>> href = 'Attack!">'
>>> title = '<i>Homepage</i>'
>>> tmpl.substitute(locals())
'Hi <img src="bob.jpg">!\n<a href="Attack!">"><i>Homepage</i></a>'
It also adds a couple handy builtins:
``html_quote(value)``:
HTML quotes the value. Turns all unicode values into
character references, so it always returns ASCII text. Also
it calls ``str(value)`` or ``unicode(value)``, so you can do
things like ``html_quote(1)``.
``url(value)``:
Does URL quoting, similar to ``html_quote()``.
``attr(**kw)``:
Inserts attributes. Use like::
<div {{attr(width=width, class_=div_class)}}>
Then it'll put in something like ``width="{{width}}"
class={{div_class}}``. Any attribute with a value of None is
left out entirely.
Extending Tempita
=================
It's not really meant for extension. Instead you should just write
Python functions and classes that do what you want, and use them in
the template. You can either add the namespace to the constructor, or
extend ``default_namespace`` in your own subclass.
The extension that ``HTMLTemplate`` uses is to subclass and override
the ``_repr(value, pos)`` function. This is called on each object
just before inserting it in the template.
Two other methods you might want to look at are ``_eval(code, ns,
pos)`` and ``_exec(code, ns, pos)``, which evaluate and execute
expressions and statements. You could probably make this language
safe with appropriate implementations of those methods.
Command-line Use
================
There's also a command-line version of the program. In Python 2.5+
you can run ``python -m tempita``; in previous versions you must run
``python path/to/tempita/__init__.py``.
The usage::
Usage: __init__.py [OPTIONS] TEMPLATE arg=value
Use py:arg=value to set a Python value; otherwise all values are
strings.
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-o FILENAME, --output=FILENAME
File to write output to (default stdout)
--html Use HTML style filling (including automatic HTML
quoting)
--env Put the environment in as top-level variables
So you can use it like::
$ python -m tempita --html mytemplate.tmpl \
> var1="$var1" var2="$var2" > mytemplate.html
Still To Do
===========
* Currently nested structures in ``for`` loop assignments don't work,
like ``for (a, b), c in x``. They should.
* There's no way to handle exceptions, except in your ``py:`` code.
I'm not sure what there should be, if anything.
* Probably I should try to dedent ``py:`` code.
* There should be some way of calling a function with a chunk of the
template. Maybe like::
{{call expr}}
template code...
{{endcall}}
That would mean ``{{expr(result_of_template_code)}}``. But maybe
there should be another assignment form too, if you don't want to
immediately put the output in the code (``{{x =
call}}...{{endcall}}?``). For now defs could be used for this,
like::
{{def something}}
template code...
{{enddef}}
{{expr(something())}}
News
====
0.5
---
* Python 3 compatible.
* Fixed bug where file-relative filenames wouldn't work well.
* Fixed the stripping of empty lines.
0.4
---
* Added a ``line_offset`` constructor argument, which can be used to
adjust the line numbers reported in error messages (e.g., if a
template is embedded in a file).
* Allow non-dictionary namespace objects (with
``tmpl.substitute(namespace)`` (in Python 2.5+).
* Instead of defining ``__name__`` in template namespaces (which has special
rules, and must be a module name) the template name is put into
``__template_name__``. This became important in Python 2.5.
* Fix some issues with \r
0.3
---
* Added ``{{inherit}}`` and ``{{def}}`` for doing template inheritance.
* Make error message annotation slightly more robust.
* Fix whitespace stripping for the beginning and end of lines.
0.2
---
* Added ``html_quote`` to default functions provided in
``HTMLTemplate``.
* HTML literals have an ``.__html__()`` method, and the presence of
that method is used to determine if values need to be quoted in
``HTMLTemplate``.
Raw data
{
"_id": null,
"home_page": "",
"name": "tempita-hotoffthehamster",
"maintainer": "Tally Bark LLC",
"docs_url": null,
"requires_python": ">=3.8.1,<4.0.0",
"maintainer_email": "doblabs@tallybark.com",
"keywords": "templating,template,language,html",
"author": "Landon Bouma",
"author_email": "doblabs@tallybark.com",
"download_url": "https://files.pythonhosted.org/packages/8c/dd/558348f03e3294e5e8d7fb189de872ab8d6db5748bc016c04438f8686c8e/tempita_hotoffthehamster-1.0.3.tar.gz",
"platform": null,
"description": "Tempita\n+++++++\n\n.. CXREF:\n https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge\n\n.. image:: https://github.com/doblabs/tempita-hotoffthehamster/actions/workflows/checks-unspecial.yml/badge.svg?branch=release\n :target: https://github.com/doblabs/tempita-hotoffthehamster/actions/workflows/checks-unspecial.yml/badge.svg?branch=release\n :alt: Build Status\n\n.. CXREF: https://app.codecov.io/gh/doblabs/tempita-hotoffthehamster/settings/badge\n\n.. image:: https://codecov.io/gh/doblabs/tempita-hotoffthehamster/graph/badge.svg?token=E0cpXPQExg\n :target: https://app.codecov.io/gh/doblabs/tempita-hotoffthehamster\n :alt: Coverage Status\n\n.. image:: https://readthedocs.org/projects/tempita-hotoffthehamster/badge/?version=latest\n :target: https://tempita-hotoffthehamster.readthedocs.io/en/latest/\n :alt: Documentation Status\n\n.. image:: https://img.shields.io/github/v/release/doblabs/tempita-hotoffthehamster.svg?style=flat\n :target: https://github.com/doblabs/tempita-hotoffthehamster/releases\n :alt: GitHub Release Status\n\n.. image:: https://img.shields.io/pypi/v/tempita-hotoffthehamster.svg\n :target: https://pypi.org/project/tempita-hotoffthehamster/\n :alt: PyPI Release Status\n\n.. image:: https://img.shields.io/pypi/pyversions/tempita-hotoffthehamster.svg\n :target: https://pypi.org/project/tempita-hotoffthehamster/\n :alt: PyPI Supported Python Versions\n\n.. image:: https://img.shields.io/github/license/doblabs/tempita-hotoffthehamster.svg?style=flat\n :target: https://github.com/doblabs/tempita-hotoffthehamster/blob/release/LICENSE\n :alt: License Status\n\n.. |tempita-hotoffthehamster| replace:: ``tempita-hotoffthehamster``\n.. _tempita-hotoffthehamster: https://github.com/doblabs/tempita-hotoffthehamster\n\n.. |pipx| replace:: ``pipx``\n.. _pipx: https://pypa.github.io/pipx/\n\n|\n\n.. .. toctree::\n.. :maxdepth: 1\n..\n.. license\n.. modules/tempita\n\n.. .. contents::\n\n:author: Ian Bicking <ianb@colorstudy.com>\n\nStatus & License\n================\n\nTempita is available under an `MIT-style license <license.html>`_.\n\nIt is ~actively~ developed, but not an ambitious project. It does not\nseek to take over the templating world, or adopt many new features.\nI just wanted a small templating language for cases when ``%`` and\n``string.Template`` weren't enough.\n\nDiscussion should go the the `Paste-users mailing list\n<http://pythonpaste.org/community/mailing-list.html>`_ and bugs in the\n`Paste Trac instance <http://trac.pythonpaste.org/>`_.\n\nWhy Another Templating Language\n===============================\n\nSurely the world has enough templating languages? So why did I write\nanother.\n\nI initially used `Cheetah <http://cheetahtemplate.org/>`_ as the\ntemplating language for `Paste Script\n<http://pythonpaste.org/script/>`_, but this caused quite a few\nproblems. People frequently had problems installing Cheetah because\nit includes a C extension. Also, the errors and invocation can be a\nlittle confusing. This might be okay for something that used\nCheetah's features extensively, except that the templating was a very\nminor feature of the system, and many people didn't even understand or\ncare about where templating came into the system.\n\nAt the same time, I was starting to create reusable WSGI components\nthat had some templating in them. Not a lot of templating, but enough\nthat ``string.Template`` had become too complicated -- I need if\nstatements and loops.\n\nGiven this, I started looking around for a very small templating\nlanguage, and I didn't like anything I found. Many of them seemed\nawkward or like toys that were more about the novelty of the\nimplementation than the utility of the language.\n\nSo one night when I felt like coding but didn't feel like working on\nanything I was already working on, I wrote this. It was first called\n``paste.util.template``, but I decided it deserved a life of its own,\nhence Tempita.\n\nThe Interface\n=============\n\nThe interface is intended to look a lot like ``string.Template``. You\ncan create a template object like::\n\n >>> import tempita\n >>> tmpl = tempita.Template(\"\"\"Hello {{name}}\"\"\")\n >>> tmpl.substitute(name='Bob')\n 'Hello Bob'\n\nOr if you want to skip the class::\n\n >>> tempita.sub(\"Hello {{name}}\", name='Alice')\n 'Hello Alice'\n\nNote that the language allows arbitrary Python to be executed, so\nyour templates must be trusted.\n\nYou can give a name to your template, which is handy when there is an\nerror (the name will be displayed)::\n\n >>> tmpl = tempita.Template('Hi {{name}}', name='tmpl')\n >>> tmpl.substitute()\n Traceback (most recent call last):\n ...\n NameError: name 'name' is not defined at line 1 column 6 in file tmpl\n\nYou can also give a namespace to use by default, which\n``.substitute(...)`` will augment::\n\n >>> tmpl = tempita.Template(\n ... 'Hi {{upper(name)}}',\n ... namespace=dict(upper=lambda s: s.upper()))\n >>> tmpl.substitute(name='Joe')\n 'Hi JOE'\n\nLastly, you can give a dictionary-like object as the argument to\n``.substitute``, like::\n\n >>> name = 'Jane'\n >>> tmpl.substitute(locals())\n 'Hi JANE'\n\nThere's also an `HTMLTemplate`_ class that is more appropriate for\ntemplates that produce HTML.\n\nYou can also instantiate a template from a filename with\n``Template.from_filename(filename, namespace={}, encoding=None)``.\nThis is like calling::\n\n Template(open(filename, 'rb').read().decode(encoding),\n name=filename, namespace=namespace)\n\nUnicode\n-------\n\nTempita tries to handle unicode gracefully, for some value of\n\"graceful\". ``Template`` objects have a ``default_encoding``\nattribute. It will try to use that encoding whenever ``unicode`` and\n``str`` objects are mixed in the template. E.g.::\n\n >>> tmpl = tempita.Template(u'Hi {{name}}')\n >>> import sys\n >>> if sys.version.startswith('2'): # unicode is the default in 3 -> failing test\n ... val = tmpl.substitute(name='Jos\\xc3\\xa9')\n ... comparison = val == u'Hi Jos\\xe9'\n ... else:\n ... comparison = True\n >>> comparison\n True\n >>> tmpl = tempita.Template('Hi {{name}}')\n >>> print (tmpl.substitute(name=u'Jos\\xe9'))\n Hi Jos\u00e9\n\nThe default encoding is UTF8.\n\nThe Language\n============\n\nThe language is fairly simple; all the constructs look like\n``{{stuff}}``.\n\nSubstitution\n------------\n\nTo insert a variable or expression, use ``{{expression}}``. You can't\nuse ``}}`` in your expression, but if it comes up just use ``} }``\n(put a space between them). You can pass your expression through\n*filters* with ``{{expression | filter}}``, for instance\n``{{expression | repr}}``. This is entirely equivalent to\n``{{repr(expression)}}``. But it might look nicer to some people; I\ntook it from Django because I liked it. There's a shared namespace,\nso ``repr`` is just an object in the namespace.\n\nIf you want to have ``{{`` or ``}}`` in your template, you must use\nthe built-in variables like ``{{start_braces}}`` and\n``{{end_braces}}``. There's no escape character.\n\nYou may also specify the delimiters as an argument to the Template\n``__init__`` method::\n\n >>> tempita.Template(\n content='Hello ${name}',\n delimiters=('${', '}')\n ).substitute(name='world')\n\n 'Hello world'\n\nThe delimiters argument must be of length two and both items must be strings.\n\nNone, as a special case, is substituted as the empty string.\n\nAlso there is a command for setting default values in your template::\n\n {{default width = 100}}\n\nYou can use this so that the ``width`` variable will always have a\nvalue in your template (the number ``100``). If someone calls\n``tmpl.substitute(width=200)`` then this will have no effect; only if\nthe variable is undefined will this default matter. You can use any\nexpression to the right of the ``=``.\n\nif\n--\n\nYou can do an if statement with::\n\n {{if condition}}\n true stuff\n {{elif other_condition}}\n other stuff\n {{else}}\n final stuff\n {{endif}}\n\nSome of the blank lines will be removed when, as in this case, they\nonly contain a single directive. A trailing ``:`` is optional (like\n``{{if condition:}}``).\n\nfor\n---\n\nLoops should be unsurprising::\n\n {{for a, b in items}}\n {{a}} = {{b | repr}}\n {{endfor}}\n\nSee? Unsurprising. Note that nested tuples (like ``for a, (b, c)\nin...``) are not supported (patches welcome).\n\ninherit & def\n-------------\n\nYou can do template inheritance. To inherit from another template\ndo::\n\n {{inherit \"some_other_file\"}}\n\nFrom Python you must also pass in, to `Template`, a `get_template`\nfunction; the implementation for ``Template.from_filename(...)`` is::\n\n def get_file_template(name, from_template):\n path = os.path.join(os.path.dirname(from_template.name), name)\n return from_template.__class__.from_filename(\n path, namespace=from_template.namespace,\n get_template=from_template.get_template)\n\nYou can also pass in a constructor argument `default_inherit`, which\nwill be the inherited template name when no ``{{inherit}}`` is in the\ntemplate.\n\nThe inherited template is executed with a variable ``self``, which\nincludes ``self.body`` which is the text of the child template. You\ncan also put in definitions in the child, like::\n\n {{def sidebar}}\n sidebar links...\n {{enddef}}\n\nThen in the parent/inherited template::\n\n {{self.sidebar}}\n\nIf you want to make the sidebar method optional, in the inherited\ntemplate use::\n\n {{self.get.sidebar}}\n\nIf ``sidebar`` is not defined then this will just result in an object\nthat shows up as the empty string (but is also callable).\n\nThis can be called like ``self.sidebar`` or ``self.sidebar()`` -- defs\ncan have arguments (like ``{{def sidebar(name)}}``), but when there\nare no arguments you can leave off ``()`` (in the call and\ndefinition).\n\nPython blocks\n-------------\n\nFor anything more complicated, you can use blocks of Python code,\nlike::\n\n {{py:x = 1}}\n\n {{py:\n lots of code\n }}\n\nThe first form allows statements, like an assignment or raising an\nexception. The second form is for multiple lines. If you have\nmultiple lines, then ``{{py:`` must be on a line of its own and the\ncode can't start out indented (but if you have something like ``def\nx():`` you would indent the body).\n\nThese blocks of code can't output any values, but they can calculate\nvalues and define functions. So you can do something like::\n\n {{py:\n def pad(s):\n return s + ' '*(20-len(s))\n }}\n {{for name, value in kw.items()}}\n {{s | pad}} {{value | repr}}\n {{endfor}}\n\nAs a last detail ``{{# comments...}}`` doesn't do anything at all,\nbecause it is a comment.\n\nbunch and looper\n----------------\n\nThere's two kinds of objects provided to help you in your templates.\nThe first is ``tempita.bunch``, which is just a dictionary that also\nlets you use attributes::\n\n >>> bunch = tempita.bunch(a=1)\n >>> bunch.a\n 1\n >>> list(bunch.items())\n [('a', 1)]\n >>> bunch.default = None\n >>> print (bunch.b)\n None\n\nThis can be nice for passing options into a template.\n\nThe other object is for use inside the template, and is part of the\ndefault namespace, ``looper``. This can be used in ``for`` loops in\nsome convenient ways. You basically use it like::\n\n {{for loop, item in looper(seq)}}\n ...\n {{endfor}}\n\nThe ``loop`` object has a bunch of useful methods and attributes:\n\n ``.index``\n The index of the current item (like you'd get with\n ``enumerate()``)\n ``.number``\n The number: ``.index + 1``\n ``.item``\n The item you are looking at. Which you probably already have,\n but it's there if you want it.\n ``.next``\n The next item in the sequence, or None if it's the last item.\n ``.previous``\n The previous item in the sequence, or None if it's the first\n item.\n ``.odd``\n True if this is an odd item. The first item is even.\n ``.even``\n True if it's even.\n ``.first``\n True if this is the first item.\n ``.last``\n True if this is the last item.\n ``.length``\n The total length of the sequence.\n ``.first_group(getter=None)``\n Returns true if this item is the first in the group, where the\n group is either of equal objects (probably boring), or when you\n give a getter. getter can be ``'.attribute'``, like\n ``'.last_name'`` -- this lets you group people by their last\n name. Or a method, like ``'.birth_year()'`` -- which calls the\n method. If it's just a string, it is expected to be a key in a\n dictionary, like ``'name'`` which groups on ``item['name']``.\n Or you can give a function which returns the value to group on.\n This always returns true when ``.first`` returns true.\n ``.last_group(getter=None)``\n Like ``first_group``, only returns True when it's the last of\n the group. This always returns true when ``.last`` returns true.\n\nNote that there's currently a limitation in the templating language,\nso you can't do ``{{for loop, (key, value) in looper(d.items())}}``.\nYou'll have to do::\n\n {{for loop, key_value in looper(d.items())}}\n {{py:key, value = key_value}}\n ...\n {{endfor}}\n\nHTMLTemplate\n============\n\nIn addition to ``Template`` there is a template specialized for HTML,\n``HTMLTemplate`` (and the substitution function ``sub_html``).\n\nThe basic thing that it adds is automatic HTML quoting. All values\nsubstituted into your template will be quoted unless they are\nspecially marked.\n\nYou mark objects as instances of ``tempita.html``. The easiest way is\n``{{some_string | html}}``, though you can also use\n``tempita.html(string)`` in your functions.\n\nAn example::\n\n >>> tmpl = tempita.HTMLTemplate('''\\\n ... Hi {{name}}!\n ... <a href=\"{{href}}\">{{title|html}}</a>''')\n >>> name = tempita.html('<img src=\"bob.jpg\">')\n >>> href = 'Attack!\">'\n >>> title = '<i>Homepage</i>'\n >>> tmpl.substitute(locals())\n 'Hi <img src=\"bob.jpg\">!\\n<a href=\"Attack!">\"><i>Homepage</i></a>'\n\nIt also adds a couple handy builtins:\n\n ``html_quote(value)``:\n HTML quotes the value. Turns all unicode values into\n character references, so it always returns ASCII text. Also\n it calls ``str(value)`` or ``unicode(value)``, so you can do\n things like ``html_quote(1)``.\n\n ``url(value)``:\n Does URL quoting, similar to ``html_quote()``.\n\n ``attr(**kw)``:\n Inserts attributes. Use like::\n\n <div {{attr(width=width, class_=div_class)}}>\n\n Then it'll put in something like ``width=\"{{width}}\"\n class={{div_class}}``. Any attribute with a value of None is\n left out entirely.\n\nExtending Tempita\n=================\n\nIt's not really meant for extension. Instead you should just write\nPython functions and classes that do what you want, and use them in\nthe template. You can either add the namespace to the constructor, or\nextend ``default_namespace`` in your own subclass.\n\nThe extension that ``HTMLTemplate`` uses is to subclass and override\nthe ``_repr(value, pos)`` function. This is called on each object\njust before inserting it in the template.\n\nTwo other methods you might want to look at are ``_eval(code, ns,\npos)`` and ``_exec(code, ns, pos)``, which evaluate and execute\nexpressions and statements. You could probably make this language\nsafe with appropriate implementations of those methods.\n\nCommand-line Use\n================\n\nThere's also a command-line version of the program. In Python 2.5+\nyou can run ``python -m tempita``; in previous versions you must run\n``python path/to/tempita/__init__.py``.\n\nThe usage::\n\n Usage: __init__.py [OPTIONS] TEMPLATE arg=value\n\n Use py:arg=value to set a Python value; otherwise all values are\n strings.\n\n\n Options:\n --version show program's version number and exit\n -h, --help show this help message and exit\n -o FILENAME, --output=FILENAME\n File to write output to (default stdout)\n --html Use HTML style filling (including automatic HTML\n quoting)\n --env Put the environment in as top-level variables\n\nSo you can use it like::\n\n $ python -m tempita --html mytemplate.tmpl \\\n > var1=\"$var1\" var2=\"$var2\" > mytemplate.html\n\n\nStill To Do\n===========\n\n* Currently nested structures in ``for`` loop assignments don't work,\n like ``for (a, b), c in x``. They should.\n\n* There's no way to handle exceptions, except in your ``py:`` code.\n I'm not sure what there should be, if anything.\n\n* Probably I should try to dedent ``py:`` code.\n\n* There should be some way of calling a function with a chunk of the\n template. Maybe like::\n\n {{call expr}}\n template code...\n {{endcall}}\n\n That would mean ``{{expr(result_of_template_code)}}``. But maybe\n there should be another assignment form too, if you don't want to\n immediately put the output in the code (``{{x =\n call}}...{{endcall}}?``). For now defs could be used for this,\n like::\n\n {{def something}}\n template code...\n {{enddef}}\n {{expr(something())}}\n\nNews\n====\n\n0.5\n---\n\n* Python 3 compatible.\n\n* Fixed bug where file-relative filenames wouldn't work well.\n\n* Fixed the stripping of empty lines.\n\n0.4\n---\n\n* Added a ``line_offset`` constructor argument, which can be used to\n adjust the line numbers reported in error messages (e.g., if a\n template is embedded in a file).\n\n* Allow non-dictionary namespace objects (with\n ``tmpl.substitute(namespace)`` (in Python 2.5+).\n\n* Instead of defining ``__name__`` in template namespaces (which has special\n rules, and must be a module name) the template name is put into\n ``__template_name__``. This became important in Python 2.5.\n\n* Fix some issues with \\r\n\n0.3\n---\n\n* Added ``{{inherit}}`` and ``{{def}}`` for doing template inheritance.\n\n* Make error message annotation slightly more robust.\n\n* Fix whitespace stripping for the beginning and end of lines.\n\n0.2\n---\n\n* Added ``html_quote`` to default functions provided in\n ``HTMLTemplate``.\n\n* HTML literals have an ``.__html__()`` method, and the presence of\n that method is used to determine if values need to be quoted in\n ``HTMLTemplate``.\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A very small text templating language",
"version": "1.0.3",
"project_urls": {
"documentation": "https://tempita-hotoffthehamster.readthedocs.io/en/latest",
"download": "https://pypi.org/project/tempita-hotoffthehamster/#files",
"history": "https://github.com/doblabs/tempita-hotoffthehamster/blob/release/HISTORY.rst",
"homepage": "https://github.com/doblabs/tempita-hotoffthehamster#\ud83e\udd93",
"issues": "https://github.com/doblabs/tempita-hotoffthehamster/issues"
},
"split_keywords": [
"templating",
"template",
"language",
"html"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0881e0948e34196f884fdf3eead8a10c4d1430d8ceab23c118e8d5039ab42c81",
"md5": "24b997d83c5c1e952dd1e76fbc16371f",
"sha256": "8e575bd2b314659daccf29a8b9dfe36d4692fb9cd656aaa1eef6d176c3135f4e"
},
"downloads": -1,
"filename": "tempita_hotoffthehamster-1.0.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "24b997d83c5c1e952dd1e76fbc16371f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8.1,<4.0.0",
"size": 20538,
"upload_time": "2023-12-28T05:04:18",
"upload_time_iso_8601": "2023-12-28T05:04:18.756950Z",
"url": "https://files.pythonhosted.org/packages/08/81/e0948e34196f884fdf3eead8a10c4d1430d8ceab23c118e8d5039ab42c81/tempita_hotoffthehamster-1.0.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "8cdd558348f03e3294e5e8d7fb189de872ab8d6db5748bc016c04438f8686c8e",
"md5": "4d8aba5d7802f82c431cc3c2b6f165ad",
"sha256": "39621b855e7863b2873de9ac662821725c1e89b271f26992b6780bdf6a49883d"
},
"downloads": -1,
"filename": "tempita_hotoffthehamster-1.0.3.tar.gz",
"has_sig": false,
"md5_digest": "4d8aba5d7802f82c431cc3c2b6f165ad",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8.1,<4.0.0",
"size": 49047,
"upload_time": "2023-12-28T05:04:20",
"upload_time_iso_8601": "2023-12-28T05:04:20.372557Z",
"url": "https://files.pythonhosted.org/packages/8c/dd/558348f03e3294e5e8d7fb189de872ab8d6db5748bc016c04438f8686c8e/tempita_hotoffthehamster-1.0.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-12-28 05:04:20",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "doblabs",
"github_project": "tempita-hotoffthehamster",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "tempita-hotoffthehamster"
}