James Kuszmaul | 55d9fc7 | 2020-05-10 18:58:08 -0700 | [diff] [blame] | 1 | #!/usr/bin/python3 |
| 2 | |
| 3 | import argparse |
Philipp Schrader | af21324 | 2024-04-03 11:51:25 -0700 | [diff] [blame] | 4 | import importlib.util |
James Kuszmaul | 55d9fc7 | 2020-05-10 18:58:08 -0700 | [diff] [blame] | 5 | import json |
| 6 | import sys |
Philipp Schrader | 75f1c26 | 2024-04-03 11:32:58 -0700 | [diff] [blame] | 7 | from pathlib import Path |
James Kuszmaul | 55d9fc7 | 2020-05-10 18:58:08 -0700 | [diff] [blame] | 8 | |
| 9 | import jinja2 |
| 10 | |
| 11 | |
Philipp Schrader | af21324 | 2024-04-03 11:51:25 -0700 | [diff] [blame] | 12 | def load_filter_file(filename: Path, env: jinja2.Environment): |
| 13 | """Adds filters specified in the .py file. |
| 14 | |
| 15 | The .py file has to define a `register_filters` function that will be |
| 16 | invoked. The function will be passed the jinja2 environment. |
| 17 | |
| 18 | def register_filters(env: jinja2.Environment): |
| 19 | env.filters["custom_filter"] = ... |
| 20 | |
| 21 | Then you can use it in the template. |
| 22 | |
| 23 | Hello {{ "world" | custom_filter }}! |
| 24 | |
| 25 | Based on https://stackoverflow.com/a/51575312. |
| 26 | |
| 27 | Args: |
| 28 | filename: The .py file to load. |
| 29 | env: The environment to pass to the `register_filters` function. |
| 30 | """ |
| 31 | spec = importlib.util.spec_from_file_location("filter_module", filename) |
| 32 | filter_module = importlib.util.module_from_spec(spec) |
| 33 | spec.loader.exec_module(filter_module) |
| 34 | filter_module.register_filters(env) |
| 35 | |
| 36 | |
James Kuszmaul | 55d9fc7 | 2020-05-10 18:58:08 -0700 | [diff] [blame] | 37 | def main(): |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame] | 38 | # Note: this is a pretty transparent interface to jinja2--there's no reason |
| 39 | # this script couldn't be renamed and then used to generate any config from |
| 40 | # a template. |
| 41 | parser = argparse.ArgumentParser( |
| 42 | description="Generates the raspberry pi configs from a template.") |
Philipp Schrader | 75f1c26 | 2024-04-03 11:32:58 -0700 | [diff] [blame] | 43 | parser.add_argument("template", |
| 44 | type=Path, |
| 45 | help="File to use for template.") |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame] | 46 | parser.add_argument( |
Philipp Schrader | 4187e17 | 2024-04-03 11:45:19 -0700 | [diff] [blame] | 47 | "--replacements_file", |
| 48 | type=Path, |
| 49 | help=("File containing a dictionary of parameters to replace " |
| 50 | "in the template. The behaviour is undefined if keys are " |
| 51 | "duplicated between this file and the `replacements` argument."), |
| 52 | ) |
| 53 | parser.add_argument( |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame] | 54 | "replacements", |
| 55 | type=json.loads, |
| 56 | help="Dictionary of parameters to replace in the template.") |
James Kuszmaul | ba43b06 | 2024-01-14 17:29:21 -0800 | [diff] [blame] | 57 | parser.add_argument( |
Philipp Schrader | 75f1c26 | 2024-04-03 11:32:58 -0700 | [diff] [blame] | 58 | "--include_dir", |
| 59 | action="append", |
| 60 | type=Path, |
| 61 | default=[], |
| 62 | help="One or more search directories for {% include %} blocks.", |
| 63 | ) |
| 64 | parser.add_argument("output", type=Path, help="Output file to create.") |
Philipp Schrader | af21324 | 2024-04-03 11:51:25 -0700 | [diff] [blame] | 65 | parser.add_argument( |
| 66 | "--filter_file", |
| 67 | action="append", |
| 68 | type=str, |
| 69 | default=[], |
| 70 | help=("A .py file with a register_filters() function for custom " |
| 71 | "jinja2 filters."), |
| 72 | ) |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame] | 73 | args = parser.parse_args(sys.argv[1:]) |
James Kuszmaul | 55d9fc7 | 2020-05-10 18:58:08 -0700 | [diff] [blame] | 74 | |
Philipp Schrader | 75f1c26 | 2024-04-03 11:32:58 -0700 | [diff] [blame] | 75 | env = jinja2.Environment(loader=jinja2.FileSystemLoader(args.include_dir)) |
Philipp Schrader | af21324 | 2024-04-03 11:51:25 -0700 | [diff] [blame] | 76 | for filename in args.filter_file: |
| 77 | load_filter_file(filename, env) |
| 78 | |
Philipp Schrader | 75f1c26 | 2024-04-03 11:32:58 -0700 | [diff] [blame] | 79 | template = env.from_string(args.template.read_text()) |
James Kuszmaul | 55d9fc7 | 2020-05-10 18:58:08 -0700 | [diff] [blame] | 80 | |
Philipp Schrader | 4187e17 | 2024-04-03 11:45:19 -0700 | [diff] [blame] | 81 | replacements = args.replacements.copy() |
| 82 | if args.replacements_file: |
| 83 | with args.replacements_file.open() as file: |
| 84 | replacements.update(json.load(file)) |
| 85 | |
| 86 | args.output.write_text(template.render(replacements)) |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame] | 87 | |
James Kuszmaul | 55d9fc7 | 2020-05-10 18:58:08 -0700 | [diff] [blame] | 88 | |
| 89 | if __name__ == '__main__': |
Ravago Jones | 5127ccc | 2022-07-31 16:32:45 -0700 | [diff] [blame] | 90 | main() |