makejinja 2.4.0__tar.gz → 2.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {makejinja-2.4.0 → makejinja-2.5.0}/PKG-INFO +1 -1
- {makejinja-2.4.0 → makejinja-2.5.0}/makejinja/app.py +45 -32
- {makejinja-2.4.0 → makejinja-2.5.0}/makejinja/config.py +19 -0
- {makejinja-2.4.0 → makejinja-2.5.0}/pyproject.toml +1 -1
- {makejinja-2.4.0 → makejinja-2.5.0}/LICENSE +0 -0
- {makejinja-2.4.0 → makejinja-2.5.0}/README.md +0 -0
- {makejinja-2.4.0 → makejinja-2.5.0}/makejinja/__init__.py +0 -0
- {makejinja-2.4.0 → makejinja-2.5.0}/makejinja/__main__.py +0 -0
- {makejinja-2.4.0 → makejinja-2.5.0}/makejinja/cli.py +0 -0
- {makejinja-2.4.0 → makejinja-2.5.0}/makejinja/plugin.py +0 -0
- {makejinja-2.4.0 → makejinja-2.5.0}/makejinja/py.typed +0 -0
|
@@ -2,6 +2,7 @@ import itertools
|
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
5
|
+
import subprocess
|
|
5
6
|
import sys
|
|
6
7
|
import tomllib
|
|
7
8
|
from collections import abc
|
|
@@ -20,18 +21,32 @@ from makejinja.plugin import Data, MutableData, PathFilter, Plugin
|
|
|
20
21
|
|
|
21
22
|
__all__ = ["makejinja"]
|
|
22
23
|
|
|
24
|
+
STDOUT_PATH = Path("/dev/stdout").resolve()
|
|
25
|
+
STDIN_PATH = Path("/dev/stdin").resolve()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def log(message: str, config: Config) -> None:
|
|
29
|
+
if not config.quiet and config.output != STDOUT_PATH:
|
|
30
|
+
print(message)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def exec(cmd: str) -> None:
|
|
34
|
+
subprocess.run(cmd, shell=True, check=True)
|
|
35
|
+
|
|
23
36
|
|
|
24
37
|
def makejinja(config: Config):
|
|
25
38
|
"""makejinja can be used to automatically generate files from [Jinja templates](https://jinja.palletsprojects.com/en/3.1.x/templates/)."""
|
|
26
39
|
|
|
40
|
+
for cmd in config.exec_pre:
|
|
41
|
+
exec(cmd)
|
|
42
|
+
|
|
27
43
|
for path in config.import_paths:
|
|
28
44
|
sys.path.append(str(path.resolve()))
|
|
29
45
|
|
|
30
46
|
data = load_data(config)
|
|
31
47
|
|
|
32
48
|
if config.output.is_dir() and config.clean:
|
|
33
|
-
|
|
34
|
-
print(f"Remove output '{config.output}'")
|
|
49
|
+
log(f"Remove output '{config.output}'", config)
|
|
35
50
|
|
|
36
51
|
shutil.rmtree(config.output)
|
|
37
52
|
|
|
@@ -61,7 +76,7 @@ def makejinja(config: Config):
|
|
|
61
76
|
rendered_dirs: dict[Path, Path] = {}
|
|
62
77
|
|
|
63
78
|
for user_input_path in config.inputs:
|
|
64
|
-
if user_input_path.is_file():
|
|
79
|
+
if user_input_path.is_file() or user_input_path == STDIN_PATH:
|
|
65
80
|
handle_input_file(user_input_path, config, env, rendered_files)
|
|
66
81
|
elif user_input_path.is_dir():
|
|
67
82
|
handle_input_dir(
|
|
@@ -75,6 +90,9 @@ def makejinja(config: Config):
|
|
|
75
90
|
|
|
76
91
|
postprocess_rendered_dirs(config, rendered_dirs)
|
|
77
92
|
|
|
93
|
+
for cmd in config.exec_post:
|
|
94
|
+
exec(cmd)
|
|
95
|
+
|
|
78
96
|
|
|
79
97
|
def postprocess_rendered_dirs(
|
|
80
98
|
config: Config,
|
|
@@ -85,24 +103,24 @@ def postprocess_rendered_dirs(
|
|
|
85
103
|
rendered_dirs.items(), key=lambda x: x[0], reverse=True
|
|
86
104
|
):
|
|
87
105
|
if not config.keep_empty and not any(output_path.iterdir()):
|
|
88
|
-
|
|
89
|
-
print(f"Remove empty dir '{output_path}'")
|
|
90
|
-
|
|
106
|
+
log(f"Remove empty dir '{output_path}'", config)
|
|
91
107
|
shutil.rmtree(output_path)
|
|
92
108
|
|
|
93
109
|
elif config.copy_metadata:
|
|
94
|
-
|
|
95
|
-
print(f"Copy dir metadata '{input_path}' -> '{output_path}'")
|
|
96
|
-
|
|
110
|
+
log(f"Copy dir metadata '{input_path}' -> '{output_path}'", config)
|
|
97
111
|
shutil.copystat(input_path, output_path)
|
|
98
112
|
|
|
99
113
|
|
|
100
114
|
def single_input_output_file(config: Config) -> bool:
|
|
101
115
|
"""Check if the user provided a single input and a single output"""
|
|
102
116
|
return (
|
|
103
|
-
len(config.inputs)
|
|
104
|
-
and config.inputs
|
|
105
|
-
and (
|
|
117
|
+
len(config.inputs) <= 1
|
|
118
|
+
and not any(path.is_dir() for path in config.inputs)
|
|
119
|
+
and (
|
|
120
|
+
config.output == STDOUT_PATH
|
|
121
|
+
or config.output.suffix != ""
|
|
122
|
+
or config.output.is_file()
|
|
123
|
+
)
|
|
106
124
|
and not config.output.is_dir()
|
|
107
125
|
)
|
|
108
126
|
|
|
@@ -154,8 +172,7 @@ def handle_input_dir(
|
|
|
154
172
|
not path_filter(input_path) for path_filter in plugin_path_filters
|
|
155
173
|
)
|
|
156
174
|
if exclude_pattern_match or path_filter_match:
|
|
157
|
-
|
|
158
|
-
print(f"Skip excluded path '{input_path}'")
|
|
175
|
+
log(f"Skip excluded path '{input_path}'", config)
|
|
159
176
|
|
|
160
177
|
elif input_path.is_file() and output_path not in rendered_files:
|
|
161
178
|
render_file(
|
|
@@ -190,7 +207,11 @@ def init_jinja_env(
|
|
|
190
207
|
data: Data,
|
|
191
208
|
) -> Environment:
|
|
192
209
|
file_loader = DictLoader(
|
|
193
|
-
{
|
|
210
|
+
{
|
|
211
|
+
path.name: path.read_text()
|
|
212
|
+
for path in config.inputs
|
|
213
|
+
if path.is_file() or path == STDIN_PATH
|
|
214
|
+
}
|
|
194
215
|
)
|
|
195
216
|
dir_loader = FileSystemLoader([path for path in config.inputs if path.is_dir()])
|
|
196
217
|
loaders: list[BaseLoader] = [file_loader, dir_loader]
|
|
@@ -297,13 +318,11 @@ def load_data(config: Config) -> dict[str, Any]:
|
|
|
297
318
|
|
|
298
319
|
for path in collect_files(config.data):
|
|
299
320
|
if loader := DATA_LOADERS.get(path.suffix):
|
|
300
|
-
|
|
301
|
-
print(f"Load data '{path}'")
|
|
321
|
+
log(f"Load data '{path}'", config)
|
|
302
322
|
|
|
303
323
|
data |= loader(path)
|
|
304
324
|
else:
|
|
305
|
-
|
|
306
|
-
print(f"Skip unsupported data '{path}'")
|
|
325
|
+
log(f"Skip unsupported data '{path}'", config)
|
|
307
326
|
|
|
308
327
|
for key, value in config.data_vars.items():
|
|
309
328
|
dict_nested_set(data, key, value)
|
|
@@ -355,11 +374,9 @@ def load_plugin(
|
|
|
355
374
|
|
|
356
375
|
def render_dir(input: Path, output: Path, config: Config) -> None:
|
|
357
376
|
if output.exists() and not config.force:
|
|
358
|
-
|
|
359
|
-
print(f"Skip existing dir '{output}'")
|
|
377
|
+
log(f"Skip existing dir '{output}'", config)
|
|
360
378
|
else:
|
|
361
|
-
|
|
362
|
-
print(f"Create dir '{input}' -> '{output}'")
|
|
379
|
+
log(f"Create dir '{input}' -> '{output}'", config)
|
|
363
380
|
|
|
364
381
|
output.mkdir(exist_ok=True)
|
|
365
382
|
|
|
@@ -372,9 +389,8 @@ def render_file(
|
|
|
372
389
|
env: Environment,
|
|
373
390
|
enforce_jinja_suffix: bool,
|
|
374
391
|
) -> None:
|
|
375
|
-
if output.exists() and not config.force:
|
|
376
|
-
|
|
377
|
-
print(f"Skip existing file '{output}'")
|
|
392
|
+
if output.exists() and not config.force and output != STDOUT_PATH:
|
|
393
|
+
log(f"Skip existing file '{output}'", config)
|
|
378
394
|
|
|
379
395
|
elif input.suffix == config.jinja_suffix or not enforce_jinja_suffix:
|
|
380
396
|
template = env.get_template(template_name)
|
|
@@ -383,11 +399,9 @@ def render_file(
|
|
|
383
399
|
# Write the rendered template if it has content
|
|
384
400
|
# Prevents empty macro definitions
|
|
385
401
|
if rendered.strip() == "" and not config.keep_empty:
|
|
386
|
-
|
|
387
|
-
print(f"Skip empty file '{input}'")
|
|
402
|
+
log(f"Skip empty file '{input}'", config)
|
|
388
403
|
else:
|
|
389
|
-
|
|
390
|
-
print(f"Render file '{input}' -> '{output}'")
|
|
404
|
+
log(f"Render file '{input}' -> '{output}'", config)
|
|
391
405
|
|
|
392
406
|
with output.open("w") as fp:
|
|
393
407
|
fp.write(rendered)
|
|
@@ -396,7 +410,6 @@ def render_file(
|
|
|
396
410
|
shutil.copystat(input, output)
|
|
397
411
|
|
|
398
412
|
else:
|
|
399
|
-
|
|
400
|
-
print(f"Copy file '{input}' -> '{output}'")
|
|
413
|
+
log(f"Copy file '{input}' -> '{output}'", config)
|
|
401
414
|
|
|
402
415
|
shutil.copy2(input, output)
|
|
@@ -322,6 +322,18 @@ class Config:
|
|
|
322
322
|
"""
|
|
323
323
|
),
|
|
324
324
|
)
|
|
325
|
+
exec_pre: tuple[str, ...] = ts.option(
|
|
326
|
+
default=tuple(),
|
|
327
|
+
help="""
|
|
328
|
+
Shell commands to execute before rendering.
|
|
329
|
+
""",
|
|
330
|
+
)
|
|
331
|
+
exec_post: tuple[str, ...] = ts.option(
|
|
332
|
+
default=tuple(),
|
|
333
|
+
help="""
|
|
334
|
+
Shell commands to execute after rendering.
|
|
335
|
+
""",
|
|
336
|
+
)
|
|
325
337
|
clean: bool = ts.option(
|
|
326
338
|
default=False,
|
|
327
339
|
click={"param_decls": ("--clean", "-c")},
|
|
@@ -402,5 +414,12 @@ OPTION_GROUPS = {
|
|
|
402
414
|
"--prefix-line-comment",
|
|
403
415
|
],
|
|
404
416
|
},
|
|
417
|
+
{
|
|
418
|
+
"name": "Shell Hooks",
|
|
419
|
+
"options": [
|
|
420
|
+
"--exec-pre",
|
|
421
|
+
"--exec-post",
|
|
422
|
+
],
|
|
423
|
+
},
|
|
405
424
|
]
|
|
406
425
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "makejinja"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.5.0"
|
|
4
4
|
description = "Generate entire directory structures using Jinja templates with support for external data and custom plugins."
|
|
5
5
|
authors = ["Mirko Lenz <mirko@mirkolenz.com>"]
|
|
6
6
|
license = "MIT"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|