blob: 30432ea240fc0a51d7d57dbc59a9328850df1756 [file] [log] [blame]
James Kuszmaul55d9fc72020-05-10 18:58:08 -07001#!/usr/bin/python3
2
3import argparse
Philipp Schraderaf213242024-04-03 11:51:25 -07004import importlib.util
James Kuszmaul55d9fc72020-05-10 18:58:08 -07005import json
6import sys
Philipp Schrader75f1c262024-04-03 11:32:58 -07007from pathlib import Path
James Kuszmaul55d9fc72020-05-10 18:58:08 -07008
9import jinja2
10
11
Philipp Schraderaf213242024-04-03 11:51:25 -070012def 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 Kuszmaul55d9fc72020-05-10 18:58:08 -070037def main():
Ravago Jones5127ccc2022-07-31 16:32:45 -070038 # 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 Schrader75f1c262024-04-03 11:32:58 -070043 parser.add_argument("template",
44 type=Path,
45 help="File to use for template.")
Ravago Jones5127ccc2022-07-31 16:32:45 -070046 parser.add_argument(
Philipp Schrader4187e172024-04-03 11:45:19 -070047 "--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 Jones5127ccc2022-07-31 16:32:45 -070054 "replacements",
55 type=json.loads,
56 help="Dictionary of parameters to replace in the template.")
James Kuszmaulba43b062024-01-14 17:29:21 -080057 parser.add_argument(
Philipp Schrader75f1c262024-04-03 11:32:58 -070058 "--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 Schraderaf213242024-04-03 11:51:25 -070065 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 Jones5127ccc2022-07-31 16:32:45 -070073 args = parser.parse_args(sys.argv[1:])
James Kuszmaul55d9fc72020-05-10 18:58:08 -070074
Philipp Schrader75f1c262024-04-03 11:32:58 -070075 env = jinja2.Environment(loader=jinja2.FileSystemLoader(args.include_dir))
Philipp Schraderaf213242024-04-03 11:51:25 -070076 for filename in args.filter_file:
77 load_filter_file(filename, env)
78
Philipp Schrader75f1c262024-04-03 11:32:58 -070079 template = env.from_string(args.template.read_text())
James Kuszmaul55d9fc72020-05-10 18:58:08 -070080
Philipp Schrader4187e172024-04-03 11:45:19 -070081 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 Jones5127ccc2022-07-31 16:32:45 -070087
James Kuszmaul55d9fc72020-05-10 18:58:08 -070088
89if __name__ == '__main__':
Ravago Jones5127ccc2022-07-31 16:32:45 -070090 main()