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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: makejinja
3
- Version: 2.4.0
3
+ Version: 2.5.0
4
4
  Summary: Generate entire directory structures using Jinja templates with support for external data and custom plugins.
5
5
  Home-page: https://mirkolenz.github.io/makejinja/
6
6
  License: MIT
@@ -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
- if not config.quiet:
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
- if not config.quiet:
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
- if not config.quiet:
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) == 1
104
- and config.inputs[0].is_file()
105
- and (config.output.suffix != "" or config.output.is_file())
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
- if not config.quiet:
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
- {path.name: path.read_text() for path in config.inputs if path.is_file()}
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
- if not config.quiet:
301
- print(f"Load data '{path}'")
321
+ log(f"Load data '{path}'", config)
302
322
 
303
323
  data |= loader(path)
304
324
  else:
305
- if not config.quiet:
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
- if not config.quiet:
359
- print(f"Skip existing dir '{output}'")
377
+ log(f"Skip existing dir '{output}'", config)
360
378
  else:
361
- if not config.quiet:
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
- if not config.quiet:
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
- if not config.quiet:
387
- print(f"Skip empty file '{input}'")
402
+ log(f"Skip empty file '{input}'", config)
388
403
  else:
389
- if not config.quiet:
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
- if not config.quiet:
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.4.0"
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