Skip to main content

Automatic code generation from Jinja2 templates

Project description

StempelWerk

Automatic code generation from Jinja templates

Illustration of an old printing machine Illustration by Narcisse Navellier (obtained from Wikimedia Commons)

Introduction

StempelWerk has been created to prevent copy-and-paste errors and reduce tedious manual work. This can lead to a tremendous saving in development time and cost.

  • Lean and easy to use
    • Template system is easy to understand and lightweight
    • Templates can be introduced gradually
    • Any file can be switched back to manual editing by deleting its template
  • Mature and powerful
    • All functionality is covered by unit tests
    • The template language Jinja2 is very mature, and has an excellent Template Designer Documentation
    • A single template can create multiple files
    • Templates can undertake surprisingly complex tasks
    • Jinja2 (and thus StempelWerk) can be extended using Python
  • Permissive licensing

Case study

I have used StempelWerk in a professional project to generate most code (SQL) and part of the documentation (Markdown). StempelWerk even helped me during a migration by creating code for two different types of database. I logged and offset time gained against time spent: after nine months, I had saved 100+ hours of working time!

Installation

StempelWerk is currently developed in Python 3.12. Older versions of Python used to work, and some probably still do.

PyPI package

  • For flexibility, use pip:
# bash
python3 -m pip install stempelwerk
  • For simplicity, use pipx:
# bash
pipx install --upgrade stempelwerk

Repository

  1. Install uv

  2. Open the project directory

  3. Setup the development environment

    • Linux shell:
    ./script/bootstrap
    
    • Windows PowerShell:
    .\script\bootstrap.ps1
    
    • Anywhere:
    # edit `.python-version` to match your installed Python version,
    # or remove `--no-managed-python` to let uv install Python
    uv sync --no-managed-python
    

Execution

  • pip and pipx:
stempelwerk [ARGUMENTS] SETTINGS_FILE_PATH
  • uv:
uv run stempelwerk [ARGUMENTS] SETTINGS_FILE_PATH
  • Python package:
from stempelwerk.StempelWerk import StempelWerk

StempelWerk(...)

For help on the command line, simply call:

[uv run] stempelwerk --help

Command line argument --globals

Path to a JSON file or a JSON-formatted string containing a dictionary of global variables:

{
  "spam": "eggs"
}

StempelWerk forces explicit use of globals by grouping them under the environment variable globals. In other words, you have to use globals.spam or globals['spam'] to access your global variable spam.

Jinja supports several approaches of loading global variables. In case it matters, StempelWerk loads globals when calling jinja2.Environment.get_template.

For a simple demonstration of globals, please render the provided example templates with --globals '{"NO_cast": true}'.

Command line argument --only-modified

By default, StempelWerk renders all template files it finds in the specified template directory.

When you use the command line argument --only-modified, however, StempelWerk tries to process only the template files that have changed since the last successful run.

This logic is not infallible: some file systems update modification times in a weird manner, and changes to master templates (called "stencils" in StempelWerk) are currently not handled. However, in such a case you can simply use StempelWerk without the --only-modified argument.

Do not use this command line argument in CI/CD pipelines!

Command line argument --ultraquiet and --quiet

Adding one of these command line arguments will display less information. Great when working on slow consoles.

Command line argument --verbose

Adding this command line argument will display additional information, such as settings, loaded templates and added extensions. Very useful for debugging.

Settings

StempelWerk reads its settings from a JSON file (see settings_example.json for an example) . The path to this file is specified as command line argument, and is relative to the current working directory.

For cross-platform compatibility, I recommend using forward slashes in settings for path separators: /spam/eggs. StempelWerk will handle path separator conversions for you.

root_dir

Path to root directory, relative to the current working directory. All other paths are relative to this directory. This simplifies the setting up of paths.

template_dir

Path to root of template directory, relative to root_dir. This directory is scanned recursively, and all files matching the setting included_file_names will be rendered using Jinja.

output_dir

Path to root of output directory, relative to root_dir. Rendered files will be saved in this directory.

stencil_dir_name

Default value: None

Name of the directory containing stencils (master templates). The name must not contain slashes or backslashes.

Files in directories matching this name will not be rendered. If this setting is specified and no stencils are found, StempelWerk will exit with an error.

There may be one or more directories with this name, and they must be located somewhere under template_dir. This allows stencils to be loaded into Jinja, and to be referenced from templates at runtime.

create_directories

Default value: False

StempelWerk ensures that template_dir and output_dir exist. In case they are not found, StempelWerk will exit with an error.

Previously, these directories were created automatically, but that interfered with debugging.

When this option is set to yes, all missing directories in the output directory will be created automatically. This ensures that rendered files can always be written.

Depending on your use case, automatically created directories may be just awkward or a full-blown security issue. This option is therefore disabled by default, and I encourage you to leave it that way.

included_file_names

List containing file specifications such as *.sql.jinja. Only files with a matching glob are considered to be templates and will be passed to Jinja.

jinja_options

Default value: {}

Dictionary containing initialization parameters for the Jinja environment.

Most default values work well for me, but I always enable trim_blocks:

  "jinja_options": {
    "trim_blocks": true
  },

jinja_extensions

Default value: []

List containing Jinja extensions that will be loaded into the Jinja environment.

custom_modules

Default value: []

List of Python modules, each containing a CustomCode class inheriting from StempelWerk.CustomCodeTemplate. See directory tests/tintin/custom for examples.

After creating the Jinja environment and loading Jinja extensions, each module will be imported. An instance of CustomCode will be created and its method update_environment() will be called. This method must return a Jinja environment.

Use custom modules to add filters and tests to the environment, or perform any other task Python is capable of.

Warning: there are no security checks to prevent you from deleting all of your files and doing other mischief, so please be careful!

last_run_file

Default value: .last_run

Path to the file for storing a time stamp of the last successful run. The path is relative to root_dir.

If your operating system handles temporary directories correctly (Windows does not), you could store this file in one of them (e.g. /tmp/). With --only-modified, all template files would be rendered once after starting the system, and afterwards only when they are updated.

marker_new_file and marker_content

Default values: ### New file: and ### Content:

Each time these strings are encountered in the rendered output of a template, a new file is created. This allows you to create multiple files from a single template.

The code relies on the following order: new file marker, optional whitespace, path to the output file, optional whitespace, content marker, optional whitespace, and contents of the output file:

### New file: spam/eggs.py
### Content:
def spam():
    return 'eggs'

You can insert these markers by using a template filter provided by StempelWerk:

{{- ('directory/' ~ filename) | start_new_file -}}

or adding a macro to the template:

{%- macro add_file_markers(filename) %}
### New file: {{ filename }}
### Content:
{% endmacro -%}

{{- add_file_markers('directory/' ~ filename) -}}

Good file separators strike a balance between performance (brevity) and reliability (uniqueness). Please see the example files to see them in action.

newline

Default value: None

Rendered files will be written using the operating system's standard newline character. Change this setting to use another newline character, such as \r\n.

StempelWerk overrides this setting for certain files, such as Windows Batch files. If you want to change this behavior, please create an instance of StempelWerk in Python and override its public member variable newline_exceptions.

Code of conduct

Please read the code of conduct before asking for help, filing bug reports or contributing to this project. Thanks!

License

Copyright (c) 2020-2025 Martin Zuther

This program is free software and licensed under the terms of the BSD 3-Clause License.

Thank you for using free software!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stempelwerk-1.1.0.tar.gz (136.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stempelwerk-1.1.0-py3-none-any.whl (14.9 kB view details)

Uploaded Python 3

File details

Details for the file stempelwerk-1.1.0.tar.gz.

File metadata

  • Download URL: stempelwerk-1.1.0.tar.gz
  • Upload date:
  • Size: 136.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.13

File hashes

Hashes for stempelwerk-1.1.0.tar.gz
Algorithm Hash digest
SHA256 3b2bf59cb96dfb1895d806cf6375046f4e27ecd6ab2f2dcd92e257c5594d217e
MD5 b045b5dd9c14af9f4e54faa660e7098d
BLAKE2b-256 f04d88a2ff8ccc850078ec8c89bbb80c81611bc8d7b145c232294295668e9971

See more details on using hashes here.

File details

Details for the file stempelwerk-1.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for stempelwerk-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 72e2730e04c257fa00b996e72b81a4f80ff67a8b3aa4ee2f0868947ae1a06fd7
MD5 6d5767e7c4e7b8c1f5549338169a3288
BLAKE2b-256 ddd05aa895b803ba9ebad807c22bcf3a9fd01dfd91b7614d58092bd5d541dec2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page