Welcome to Lina¶
Lina is a minimal template system for Python, modelled after Google’s CTemplate library. It is designed to provide fast, safe template evaluation to generate code or other text documents.
enum DataTypes {
{{#types:list-separator=,NEWLINE}} {{name}}={{value:hex}}{{/types}}
}
evaluated with:
types = [{'name':'Vector3i', 'value': 0x301}, {'name':'Vector3f', 'value': 0x302}]
will produce:
enum DataTypes {
Vector3i = 0x301,
Vector3f = 0x302
}
Overview¶
The base class in Lina is lina.Template
which must be initialized with the template contents. It can be then evaluated to a string using lina.Template.Render()
and lina.Template.RenderSimple()
.
Lina has two main directives, values and blocks. A value is something which is replaced directly by the provided value, while a block is used to iterate over collections. Both blocks and values can be optionally formatted using a formatter
, which allows for example to turn a string into uppercase inside the template.
Values are escaped using double curly braces:
Hello {{name}}!
Blocks have an additional prefix before the variable, #
for the block start and /
for the block end:
{{#users}}Hello {{name}}!{{/users}}
This requires to pass an array of named objects:
template.Render ({'users':[{'name':'Alice'}, {'name':'Bob'}]})
Element access¶
In some cases, accessing members by names is unnecessary complicated. Lina provides a special syntax to access the current element, using a single dot. Using a self-reference, the template above can be simplified to:
{{#users}}Hello {{.}}!{{/users}}
and rendered with:
template.Render ({'users': ['Alice', 'Bob']})
or even simpler using lina.Template.RenderSimple()
:
template.RenderSimple (users = ['Alice', 'Bob'])
Both self-references as well as values can also access fields of an object. Assuming the User
class has fields name
, age
, the following template will print the user name and age:
{{#users}}Hello {{.name}}, you are {{.age}} years old!{{/users}}
For an object, use {{item.field}}
. The field accessor syntax works for both fields as well as associative containers, that is, for Lina, the following two objects are equivalent:
u = {'name':'Alice'}
and:
class User:
def __init__(self, name):
self.name = name
u = User ('Alice')
It is also possible to directly reference indexed items using [0]
, [1]
, etc. For instance, the following template:
{{#vectors}}X: {{.[0]}}, Y: {{.[1]}}, Z: {{.[2]}}\n{{/vectors}}
rendered with:
template.RenderSimple (vectors = [[0, 1, 2], [3, 4, 5]])
will produce:
X: 0, Y: 1, Z: 2
X: 3, Y: 4, Z: 5
Blocks¶
For blocks, Lina provides additional modifiers to check whether the current block execution is the first, an intermediate or the last one:
{{#block}}{{variable}}{{#block#Separator}},{{/block#Separator}}{{/block}}
#First
will be only expanded for the first iteration, #Separator
will be expanded for every expansion which is neither first nor last and #Last
will be expanded for the last iteration only. If there is only one element, it will considered both first and last item of the sequence.
If a block variable is not found, or the block is None
, the block will be not expanded. It is possible to capture this case using !
blocks, which are only expanded if the variable is not present:
{{!users}}No users :({{/users}}
Rendered with template.Render ()
, this will yield No users :(
. This can be used to emulate conditional statements.
Formatters¶
Lina comes with a few formatters which can be used to modify values or block elements. The value formatters are:
width
,w
: This aligns a value to a specific width. Negative values align to the left. For example:{{name:w=-8}}
usingname='Ton'
will yield `` Ton``.prefix
adds a prefix to a value. For example:{{method:prefix=api_}}
withmethod='Copy'
yieldsapi_Copy
suffix
adds a suffix to a value. For example:{{method:suffix=_internal}}
withmethod='Copy'
yieldsCopy_internal
default
provides a default value in case the provided value isNone
.{{name:default=Unknown}}
withname=None
yieldsUnknown
.upper-case
,uc
converts the provided value to upper case.{{func:upper-case}}
withfunc=Copy
yieldsCOPY
.escape-newlines
escapes embedded newlines.{{s:escape-newlines}}
withs='foo\nbar'
yieldsfoo\\nbar
.escape-string
escapes newlines, tabs, and quotes.{{s:escape-string}}
withs=a "string"
yieldsa \"string\"
.wrap-string
wraps the provided string with quotes.{{s:wrap-string}}
withs=string
yields"string"
. If the value is not a string, it will not be wrapped.cbool
converts booleans totrue
orfalse
.{{enabled:cbool}}
withenabled=True
yieldstrue
. If the value is not a boolean, it will be returned as-is.hex
prints numbers in hexadecimal notation.{{i:hex}}
withi=127
yields0x7F
.
Lina provides the following block formatters:
indent
indents every line with a specified number of tabs.{{#block:indent=2}}{{.}}{{/block}}
withblock=[1,2]
yields\t\t1\t\t2
list-separator
,l-s
separates block repetitions using the provided value.{{#block:l-s=,}}{{.}}{{/block}}
withblock=[1,2]
yields1,2
.NEWLINE
is replaced with a new line, andSPACE
with a space.
Whitespace & special characters¶
Whitespace in Lina is preserved. If you want to explicitly insert whitespace, you can use {{_NEWLINE}}
to get a new line character inserted into the stream, and {{_SPACE}}
to get a blank space. To produce a left brace or right brace, use {{_LEFT_BRACE}}
and {{_RIGHT_BRACE}}
.
Contents¶
API reference¶
-
class
lina.
Formatter
(formatterType)[source]¶ Bases:
object
Base class for all formatters.
A formatter can be used to transform blocks/values during expansion.
-
class
lina.
FormatterType
[source]¶ Bases:
enum.Enum
The formatter type, either
Block
orValue
.-
Block
= 0¶
-
Value
= 1¶
-
-
exception
lina.
InvalidBlock
(message, position)[source]¶ Bases:
lina.TemplateException
An invalid block was encountered.
-
exception
lina.
InvalidFormatter
(message, position)[source]¶ Bases:
lina.TemplateException
An invalid formatter was encountered.
This exception is raised when a formatter could not be found or instantiated.
-
exception
lina.
InvalidNamedCharacterToken
(message, position)[source]¶ Bases:
lina.InvalidWhitespaceToken
An invalid named character token was encountered.
-
exception
lina.
InvalidToken
(message, position)[source]¶ Bases:
lina.TemplateException
An invalid token was encountered.
-
exception
lina.
InvalidWhitespaceToken
(message, position)[source]¶ Bases:
lina.TemplateException
Only for backwards compatibility. Will be removed in 2.x.
-
class
lina.
Template
(template, includeHandler=None, *, filename=None)[source]¶ Bases:
object
The main template class.
-
RenderSimple
(**items)[source]¶ Simple rendering function.
This is just a convenience function which creates the context from the passed items and forwards them to
Template.Render()
.
-
-
exception
lina.
TemplateException
(message, position)[source]¶ Bases:
Exception
Base class for all exceptions thrown by Lina.
-
class
lina.
TemplateRepository
(templateDirectory, suffix='')[source]¶ Bases:
lina.IncludeHandler
A file template repository.
This template repository will load files from a specified folder.
-
class
lina.
TextStream
(text, *, filename=None)[source]¶ Bases:
object
A read-only text stream.
The text stream is used for input only and keeps track of the current read pointer position in terms of line/column numbers.
-
class
lina.
Token
(name, start, end, position)[source]¶ Bases:
object
Represents a single token.
Each token may contain an optional list of flags, separated by colons. The grammar implemented here is:
[prefix]?[^:}]+(:[^:})+, for example: {{#Foo}} -> name = Foo, prefix = # {{Bar:width=8}} -> name = Bar, prefix = None, flags = {width:8}
The constructor checks if the formatter matches the token type. A block formatter can be only applied to a block token, and a value formatter only to a value.
Release notes¶
1.0.10¶
- Added
{{_LEFT_BRACE}}
and{{_RIGHT_BRACE}}
tokens.
1.0.9¶
- Applying block formatters to non-blocks and value formatters to non-values raises an error now. Previously, those were silently ignored.
1.0.8¶
- Packaging changes only.
1.0.7¶
- The formatter registration has been improved. Instead of a long
if-elif
cascade, it now jumps directly to the right formatter through a dictionary.
1.0.6¶
- Various build improvements.
1.0.5¶
- Handling of
None
blocks changed (i.e. set usingblock_name = None
.) Instead of treating them as empty, they are now completely ignored. Only blocks initialized to an empty container ({}
) are now treated as empty.
1.0.4¶
- Added a new
escape-string
formatter.
1.0.3¶
- Add support for negated blocks:
{{!block}}
1.0.2¶
- Add support for self references via
{{.[0]}}
.
1.0.1¶
- Allow block formatters to write a suffix
- Add support for field access via
{{item.member}}
.
1.0¶
Initial public release.