Extended Mako Templates for Python

Programming Interface

Command Line

makolator has two sub-commands gen and inplace:

usage: makolator [-h] {gen,inplace} ...

Mako Templates (https://www.makotemplates.org/) extended.

positional arguments:
  {gen,inplace}
    gen          Generate File
    inplace      Update File Inplace

optional arguments:
  -h, --help     show this help message and exit

Generate

usage: makolator gen [-h] [--verbose] [--show-diff]
                     [--existing {error,keep,overwrite,keep_timestamp}]
                     [--template-path TEMPLATE_PATH]
                     [--marker-fill MARKER_FILL]
                     [--marker-linelength MARKER_LINELENGTH]
                     templates [templates ...] output

positional arguments:
  templates             Template Files. At least one must exist
  output                Output File

optional arguments:
  -h, --help            show this help message and exit
  --verbose, -v         Tell what happens to the file.
  --show-diff, -s       Show what lines changed.
  --existing {error,keep,overwrite,keep_timestamp}, -e {error,keep,overwrite,keep_timestamp}
                        What if the file exists. Default is 'keep_timestamp'
  --template-path TEMPLATE_PATH, -T TEMPLATE_PATH
                        Directories with templates referred by include/inherit/...
  --marker-fill MARKER_FILL
                        Static Code, Inplace and Template Marker are filled with this given value until reaching line length of --marker-linelength.
  --marker-linelength MARKER_LINELENGTH
                        Static Code, Inplace and Template Marker are filled until --marker-linelength.

Generate a file from a template:

    makolator gen test.txt.mako test.txt

Generate a file from a template and fallback to 'default.txt.mako' if 'test.txt.mako' is missing:

    makolator gen test.txt.mako default.txt.mako test.txt

Inplace

usage: makolator inplace [-h] [--ignore-unknown] [--eol EOL] [--verbose]
                         [--show-diff]
                         [--existing {error,keep,overwrite,keep_timestamp}]
                         [--template-path TEMPLATE_PATH]
                         [--marker-fill MARKER_FILL]
                         [--marker-linelength MARKER_LINELENGTH]
                         [templates ...] inplace

positional arguments:
  templates             Optional Template Files
  inplace               Updated File

optional arguments:
  -h, --help            show this help message and exit
  --ignore-unknown, -i  Ignore unknown template function calls.
  --eol EOL, -E EOL     EOL comment on generated lines
  --verbose, -v         Tell what happens to the file.
  --show-diff, -s       Show what lines changed.
  --existing {error,keep,overwrite,keep_timestamp}, -e {error,keep,overwrite,keep_timestamp}
                        What if the file exists. Default is 'keep_timestamp'
  --template-path TEMPLATE_PATH, -T TEMPLATE_PATH
                        Directories with templates referred by include/inherit/...
  --marker-fill MARKER_FILL
                        Static Code, Inplace and Template Marker are filled with this given value until reaching line length of --marker-linelength.
  --marker-linelength MARKER_LINELENGTH
                        Static Code, Inplace and Template Marker are filled until --marker-linelength.

Update with inplace template only:

    makolator inplace test.txt

Update a file from a template:

    makolator inplace test.txt.mako test.txt

Update a file from a template and fallback to 'default.txt.mako' if 'test.txt.mako' is missing:

    makolator inplace test.txt.mako default.txt.mako test.txt

Template Writing

https://www.makotemplates.org/ documents the template language. Within the template the following symbols are available

Template Symbols

Symbol

Description

datamodel

Datamodel - the data container

output_filepath

pathlib.Path with outputfile path

makolator

Makolator - Makolator Engine

makolator.open_outputfile

Makolator.open_outputfile - Create file handle with timestamp preserving.

makolator.gen

Makolator.gen - generate file

makolator.inplace

Makolator.inplace - update file

makolator.info

Makolator.info - Information Container

run

run - Identical to subprocess.run and capture stdout.

Examples:

  • run(['echo', '"Hello World"'])

  • run('echo "Hello World"', shell=True)

  • run('mycmd --output file && cat file', shell=True)

The variable ${TMPDIR} in the arguments will be replaced by a temporary directory path.

indent

Indent single line or multiple lines by two spaces:

${text | indent}

indent(spaces)

Indent single line or multiple lines by given number of spaces:

${text | indent(4)}

prefix(text)

Prefix single line or multiple lines by given text:

${text | prefix('// ')}

tex

Escape latex special characters:

${text | tex}

File Generation

The template test.txt.mako:

<%def name="afunc(pos, opt=None)">\
${output_filepath.name}
pos=${pos}
% if opt:
options: ${opt}
% endif

</%def>
# ${makolator.info.genwarning}
# This file is updated by '${makolator.info.cli}'

Text
${afunc('abc')}
${afunc('def', opt=5)}

A generate (makolator gen test.txt.mako test.txt) will result in:

# THIS FILE IS GENERATED!!! DO NOT EDIT MANUALLY. CHANGES ARE LOST.
# This file is updated by 'makolator gen test.txt.mako test.txt'

Text
test.txt
pos=abc


test.txt
pos=def
options: 5

Inplace Code Generation

Assume the following file:

This is just some handwritten text.

    GENERATE INPLACE BEGIN afunc("foo")
obsolete
    GENERATE INPLACE END afunc

This is also something inbetween.

// ==== GENERATE INPLACE BEGIN afunc("foo", "bar") ====
    will be updated
// ==== GENERATE INPLACE END afunc ====

The lines between GENERATE INPLACE BEGIN and GENERATE INPLACE END can be filled via a template like:

<%def name="afunc(pos, opt=None)">\
${output_filepath.name}
pos=${pos}
% if opt:
options: ${opt}
% endif

</%def>

An inplace update (makolator inplace test.txt.mako file.txt) will result in:

This is just some handwritten text.

    GENERATE INPLACE BEGIN afunc("foo")
    inplace.txt
    pos=foo

    GENERATE INPLACE END afunc

This is also something inbetween.

// ==== GENERATE INPLACE BEGIN afunc("foo", "bar") ====
inplace.txt
pos=foo
options: bar

// ==== GENERATE INPLACE END afunc ====

Inplace Template

The file can contain templates too:

You can define your own template in the source code

// MAKO TEMPLATE BEGIN
// <%def name="repeat(num)">\
// # ${makolator.info.inplacewarning}
// # This section is updated via '${makolator.info.cli}'
// ${output_filepath.name}
// % for idx in range(num):
// item${idx}
// % endfor
// </%def>
// MAKO TEMPLATE END


GENERATE INPLACE BEGIN repeat(2)
obsolete
GENERATE INPLACE END repeat


GENERATE INPLACE BEGIN repeat(5)
obsolete
GENERATE INPLACE END repeat

The inplace update (makolator inplace file.txt) will result in:

You can define your own template in the source code

// MAKO TEMPLATE BEGIN
// <%def name="repeat(num)">\
// # ${makolator.info.inplacewarning}
// # This section is updated via '${makolator.info.cli}'
// ${output_filepath.name}
// % for idx in range(num):
// item${idx}
// % endfor
// </%def>
// MAKO TEMPLATE END


GENERATE INPLACE BEGIN repeat(2)
# THIS SECTION IS GENERATED!!! DO NOT EDIT MANUALLY. CHANGES ARE LOST.
# This section is updated via 'makolator inplace inplace-mako.txt'
inplace-mako.txt
item0
item1
GENERATE INPLACE END repeat


GENERATE INPLACE BEGIN repeat(5)
# THIS SECTION IS GENERATED!!! DO NOT EDIT MANUALLY. CHANGES ARE LOST.
# This section is updated via 'makolator inplace inplace-mako.txt'
inplace-mako.txt
item0
item1
item2
item3
item4
GENERATE INPLACE END repeat

Static Code

Fully and inplace generated files might want to leave space for user manipulation, which is kept even on update. These locations need to be prepared by ${staticcode('name')}, where name is a unique identifier within the target file.

Assume the following template:

<%def name="example(greet='Hello')">\
${greet} before

${staticcode('a', default='obsolete a')}

${greet} middle

${staticcode('b')}

${greet} after

</%def>
${example()}

and an outdated generated file:

Outdated before

// STATIC BEGIN a
kept a
// STATIC END a

Outdated middle

// STATIC BEGIN b ignored
kept b
// STATIC END b

Outdated after

An update (makolator gen file.txt.mako file.txt) will result in:

Hello before

// STATIC BEGIN a
kept a
// STATIC END a

Hello middle

// STATIC BEGIN b
kept b
// STATIC END b

Hello after

Indices and tables