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 |
prefix |
Add |
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
|
|
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
Default Search Paths for Templates.
existing
class-attribute
instance-attribute
Behaviour in case of existing files.
diffout
class-attribute
instance-attribute
print function to handle differential output on changed files.
template_marker
class-attribute
instance-attribute
Search marker for template code within output file.
inplace_marker
class-attribute
instance-attribute
Search marker for output code within output file.
marker_linelength
class-attribute
instance-attribute
Marker Line Length for Filling.
cache_path
class-attribute
instance-attribute
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
Line Comment Symbols.
File Suffix dependent comment starter.
inplace_eol_comment
class-attribute
instance-attribute
End-Of-Line Comment Added On Every Inplace Generated Line
tag_lines
class-attribute
instance-attribute
Number Of Lines Within A Files to look for Tags.
excludes
class-attribute
instance-attribute
Exclude Patterns for Directory Handling.
See https://docs.python.org/3/library/pathlib.html#pathlib-pattern-language
pre_create
class-attribute
instance-attribute
Function called before opening a file for creating.
pre_update
class-attribute
instance-attribute
Function called before opening a file for update.
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. |
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. |
genwarning
class-attribute
instance-attribute
Generated 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 |
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. |
datamodel
class-attribute
instance-attribute
datamodel = field(factory=Datamodel)
The Data Container.
open_outputfile
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
inplace
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. |
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. |
tex
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 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
Add pre In Front of Every Line.
>>> print(prefix('PRE-')('''A # doctest: +SKIP
... B
... C'''))
PRE-A
PRE-B
PRE-C
run
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.