Skip to content

API

Extended Mako Templates for Python.

Makolator is not makulation. It extends the mako template engine by the following features:

  • Simple API
  • Keep timestamp of generated files, if content did not change (a gift for every build system user)
  • Easy hierarchical template usage
  • Inplace File Rendering

This is how to use it

Initialize

Just create your instance of Makolator

>>> from makolator import Makolator
>>> mklt = Makolator()

Configure

The config attribute contains all settings. See Config for a complete documentation. The most relevant settings are search paths for general templates ...

>>> mklt.config.template_paths
[]

... and the replacement strategy for outputfile.Existing files:

>>> mklt.config.existing
<Existing.KEEP_TIMESTAMP: 'keep_timestamp'>

If you want to place templates in the actual working directory - set it as search path:

>>> from pathlib import Path
>>> mklt.config.template_paths = [Path('.')]

If you like it verbose - try:

>>> mklt.config.verbose = True

Rendering

Assume you have this template in a file (please ignore the ... here - they are just there for a proper python example):

>>> Path('file.txt.mako').write_text('''\\
... <%def name="top(name)">\\
... generated-top: ${name}
... </%def>\\
... <%def name="bot(name, **kwargs)">\\
... generated-bot: ${name} ${kwargs}
... </%def>\\
... ${datamodel}
... ${top('foo')}\\
... ${bot('foo', b=4)}\\
... ''') and None

gen uses sys.stdout by default ...

>>> mklt.gen([Path('file.txt.mako')])
Datamodel()
generated-top: foo
generated-bot: foo {'b': 4}

... or use dest for file creation - the verbose mode shares some nice information:

>>> mklt.gen([Path('file.txt.mako')], dest=Path("file.txt"))
'file.txt'... CREATED.
>>> mklt.gen([Path('file.txt.mako')], dest=Path("file.txt"))
'file.txt'... identical. untouched.

Datamodel

The Datamodel is your container to forward data to the template.

>>> mklt.datamodel.mydata = {'a': 0, 'b': 1}

The output looks like that:

>>> mklt.gen([Path('file.txt.mako')])
Datamodel(mydata={'a': 0, 'b': 1})
generated-top: foo
generated-bot: foo {'b': 4}

and you get notified about the changed content:

>>> mklt.gen([Path('file.txt.mako')], dest=Path("file.txt"))
'file.txt'... UPDATED.

Differential output

If you like it even more verbose - try:

>>> mklt.config.diffout = print

This will send any diff of the updated files to stdout.

>>> mklt.datamodel.mydata['b'] = 2
>>> mklt.gen([Path('file.txt.mako')], dest=Path("file.txt")) # doctest: +SKIP
---
+++
@@ -1,3 +1,3 @@
-Datamodel(mydata={'a': 0, 'b': 1})
+Datamodel(mydata={'a': 0, 'b': 2})
generated-top: foo
generated-bot: foo {'b': 4}
<BLANKLINE>
'file.txt'... UPDATED.

By default this is disabled:

>>> mklt.config.diffout = None

Inplace Rendering

One key feature is the inplace rendering.

Assume you have a handwritten file and you just want to update/generate a part of it. GENERATE INPLACE will become your new friend:

>>> Path('inplace.txt').write_text('''\\
... I am a regular text file.
... This is handwritten text.
... GENERATE INPLACE BEGIN top('bar')
...   this line will be replaced
... GENERATE INPLACE END top
... This is handwritten text too.
... ''') and None

>>> mklt.inplace([Path('file.txt.mako')], Path("inplace.txt"))
'inplace.txt'... UPDATED.

>>> print(Path('inplace.txt').read_text())
\
I am a regular text file.
This is handwritten text.
GENERATE INPLACE BEGIN top('bar')
generated-top: bar
GENERATE INPLACE END top
This is handwritten text too.
<BLANKLINE>

If you like to warn your users about generated sections even more, you can add a comment by default

>>> mklt.config.inplace_eol_comment = "GENERATED"
>>> mklt.inplace([Path('file.txt.mako')], Path("inplace.txt"))
'inplace.txt'... UPDATED.

>>> print(Path('inplace.txt').read_text())
\
I am a regular text file.
This is handwritten text.
GENERATE INPLACE BEGIN top('bar')
generated-top: bar // GENERATED
GENERATE INPLACE END top
This is handwritten text too.
<BLANKLINE>

That's it.

Modules:

Name Description
cli

Command Line Interface.

config

Configuration Handling.

datamodel

Data Model.

escape

Escape reserved characters.

exceptions

Exceptions.

helper

Makolator Helper.

info

Makolator Information.

makolator

The Makolator.

tags

Tags.

tracker

Update Tracker.

Classes:

Name Description
Config

Configuration.

Datamodel

Datamodel.

MakolatorError

Just a Helper to Distinguish our Error.

Info

Makolator Information.

Makolator

The Makolator.

Tracker

Update Tracker.

Functions:

Name Description
tex

Escape (La)Tex.

indent

Indent Lines by number of text_or_int.

prefix

Add pre In Front of Every Line.

run

Run External Command And Use Command STDOUT as Result.

get_cli

Determine Command Line Invocation.

Config

Configuration.

Container For All Customization Options.

The following file extensions are known:

>>> for suffix, comment in Config().comment_map.items():
...     print(f"{suffix}: {comment}")
.c: //
.c++: //
.cpp: //
.ini: #
.py: #
.sv: //
.svh: //
.tex: %
.txt: //
.v: //
.vh: //

Attributes:

Name Type Description
template_paths list[Path]

Default Search Paths for Templates.

existing Existing

Behaviour in case of existing files.

diffout Callable[[str], None] | None

print function to handle differential output on changed files.

verbose bool

Enable Verbose Output.

create bool

Create Missing Inplace Files.

template_marker str

Search marker for template code within output file.

inplace_marker str

Search marker for output code within output file.

static_marker str

Marker for Static Code.

marker_fill str

Marker Filling.

marker_linelength int

Marker Line Length for Filling.

cache_path Path | None

Cache Directory.

comment_map dict[str, str]

Line Comment Symbols.

inplace_eol_comment str | None

End-Of-Line Comment Added On Every Inplace Generated Line

track bool

Track Changes.

tag_lines int

Number Of Lines Within A Files to look for Tags.

excludes tuple[str, ...]

Exclude Patterns for Directory Handling.

pre_create Hookup | None

Function called before opening a file for creating.

post_create Hookup | None

Function called after creating.

pre_update Hookup | None

Function called before opening a file for update.

post_update Hookup | None

Function called after writing.

pre_remove Hookup | None

Function called removing a file.

post_remove Hookup | None

Function called removing a file.

template_paths class-attribute instance-attribute

template_paths = []

Default Search Paths for Templates.

existing class-attribute instance-attribute

existing = KEEP_TIMESTAMP

Behaviour in case of existing files.

diffout class-attribute instance-attribute

diffout = None

print function to handle differential output on changed files.

verbose class-attribute instance-attribute

verbose = False

Enable Verbose Output.

create class-attribute instance-attribute

create = False

Create Missing Inplace Files.

template_marker class-attribute instance-attribute

template_marker = 'MAKO TEMPLATE'

Search marker for template code within output file.

inplace_marker class-attribute instance-attribute

inplace_marker = 'GENERATE INPLACE'

Search marker for output code within output file.

static_marker class-attribute instance-attribute

static_marker = 'STATIC'

Marker for Static Code.

marker_fill class-attribute instance-attribute

marker_fill = '='

Marker Filling.

marker_linelength class-attribute instance-attribute

marker_linelength = 0

Marker Line Length for Filling.

cache_path class-attribute instance-attribute

cache_path = None

Cache Directory.

Used to store converted templates. Use if you have many and/or large templates. Speeds up rendering. Share it between runs.

comment_map class-attribute instance-attribute

comment_map = COMMENT_MAP_DEFAULT

Line Comment Symbols.

File Suffix dependent comment starter.

inplace_eol_comment class-attribute instance-attribute

inplace_eol_comment = None

End-Of-Line Comment Added On Every Inplace Generated Line

track class-attribute instance-attribute

track = False

Track Changes.

tag_lines class-attribute instance-attribute

tag_lines = 50

Number Of Lines Within A Files to look for Tags.

excludes class-attribute instance-attribute

excludes = ()

Exclude Patterns for Directory Handling.

See https://docs.python.org/3/library/pathlib.html#pathlib-pattern-language

pre_create class-attribute instance-attribute

pre_create = None

Function called before opening a file for creating.

post_create class-attribute instance-attribute

post_create = None

Function called after creating.

pre_update class-attribute instance-attribute

pre_update = None

Function called before opening a file for update.

post_update class-attribute instance-attribute

post_update = None

Function called after writing.

pre_remove class-attribute instance-attribute

pre_remove = None

Function called removing a file.

post_remove class-attribute instance-attribute

post_remove = None

Function called removing a file.

Datamodel

Datamodel.

A simple container for all data attributes. Add attributes on your needs. That's it.

>>> Datamodel()
Datamodel()
>>> Datamodel(abc='def', item=4)
Datamodel(abc='def', item=4)
>>> datamodel = Datamodel(abc='def')
>>> datamodel
Datamodel(abc='def')
>>> datamodel.item=4
>>> datamodel
Datamodel(abc='def', item=4)

Methods:

Name Description
__init__

Datamodel.

__init__

__init__(**kwargs)

MakolatorError

Bases: RuntimeError

Just a Helper to Distinguish our Error.

Info

Makolator Information.

Attributes:

Name Type Description
cli str | None

Actual Command Line Interface Call.

genwarning str

Generated Code Warning.

inplacewarning str

Generated Inplace Code Warning.

cli class-attribute instance-attribute

cli = None

Actual Command Line Interface Call.

genwarning class-attribute instance-attribute

genwarning = "THIS FILE IS GENERATED!!! DO NOT EDIT MANUALLY. CHANGES ARE LOST."

Generated Code Warning.

inplacewarning class-attribute instance-attribute

inplacewarning = "THIS SECTION IS GENERATED!!! DO NOT EDIT MANUALLY. CHANGES ARE LOST."

Generated Inplace Code Warning.

Makolator

The Makolator.

A simple API to an improved http://www.makotemplates.org/

Methods:

Name Description
remove

Remove files or files in given directories.

open_outputfile

Open Outputfile and Return Context.

gen

Render template file.

inplace

Update generated code within filename between BEGIN/END markers.

clean

Remove Fully-Generated Files from Filepaths.

is_fully_generated

Check If File Is Fully Generated.

Attributes:

Name Type Description
config Config

The Configuration.

datamodel Datamodel

The Data Container.

info Info

Makolator Information.

tracker Tracker

File Change Tracker.

cache_path Path

Cache Path.

config class-attribute instance-attribute

config = field(factory=Config)

The Configuration.

datamodel class-attribute instance-attribute

datamodel = field(factory=Datamodel)

The Data Container.

info class-attribute instance-attribute

info = field(factory=Info)

Makolator Information.

tracker class-attribute instance-attribute

tracker = field(factory=Tracker)

File Change Tracker.

cache_path property

cache_path

Cache Path.

remove

remove(filepaths)

Remove files or files in given directories.

open_outputfile

open_outputfile(filepath, encoding='utf-8', **kwargs)

Open Outputfile and Return Context.

Parameters:

Name Type Description Default
filepath Path

path of the created/updated file.

required

Other Parameters:

Name Type Description
encoding str

Charset.

kwargs

Additional arguments are forwarded to open.

Example:

>>> mklt = Makolator(config=Config(verbose=True))
>>> with mklt.open_outputfile("myfile.txt") as file:
...     file.write("data")
'myfile.txt'... CREATED.
>>> with mklt.open_outputfile("myfile.txt") as file:
...     file.write("data")
'myfile.txt'... identical. untouched.

gen

gen(template_filepaths, dest=None, context=None)

Render template file.

Parameters:

Name Type Description Default
template_filepaths Paths

Templates.

required

Other Parameters:

Name Type Description
dest Path | None

Output File.

context dict | None

Key-Value Pairs pairs forwarded to the template.

inplace

inplace(
    template_filepaths,
    filepath,
    context=None,
    ignore_unknown=False,
)

Update generated code within filename between BEGIN/END markers.

Parameters:

Name Type Description Default
template_filepaths Paths

Templates.

required
filepath Path

File to update.

required

Other Parameters:

Name Type Description
context dict | None

Key-Value Pairs pairs forwarded to the template.

ignore_unknown bool

Ignore unknown inplace markers, instead of raising an error.

clean

clean(filepaths)

Remove Fully-Generated Files from Filepaths.

is_fully_generated

is_fully_generated(filepath)

Check If File Is Fully Generated.

Tracker

Update Tracker.

Methods:

Name Description
add

Add Information.

clear

Clear Information.

Attributes:

Name Type Description
total

Total Number of Files.

stat str

Status Summary.

updated int

Files Updated.

identical int

Files Identical.

created int

Files Created.

overwritten int

Files Overwritten.

existing int

Files Existing.

failed int

Files Failed.

removed int

Files Removed.

total property

total

Total Number of Files.

stat property

stat

Status Summary.

updated property

updated

Files Updated.

identical property

identical

Files Identical.

created property

created

Files Created.

overwritten property

overwritten

Files Overwritten.

existing property

existing

Files Existing.

failed property

failed

Files Failed.

removed property

removed

Files Removed.

add

add(path, state)

Add Information.

clear

clear()

Clear Information.

tex

tex(text)

Escape (La)Tex.

Parameters:

Name Type Description Default
text str | None

tex

required

Returns: escaped string.

>>> tex("Foo & Bar")
'Foo \\& Bar'
>>> tex(None)

indent

indent(text_or_int, rstrip=False)

Indent Lines by number of text_or_int.

>>> print(indent('''A # doctest: +SKIP
... B
... C'''))
  A
  B
  C

>>> print(indent(4)('''A # doctest: +SKIP
... B
... C'''))
    A
    B
    C

prefix

prefix(pre, rstrip=False)

Add pre In Front of Every Line.

>>> print(prefix('PRE-')('''A # doctest: +SKIP
... B
... C'''))
PRE-A
PRE-B
PRE-C

run

run(args, **kwargs)

Run External Command And Use Command STDOUT as Result.

:any:subprocess.run wrapper. STDOUT is taken as result.

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

get_cli

get_cli()

Determine Command Line Invocation.