makejinja 2.8.1__tar.gz → 2.8.3__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.8.1/src/makejinja.egg-info → makejinja-2.8.3}/PKG-INFO +18 -20
- {makejinja-2.8.1 → makejinja-2.8.3}/README.md +1 -1
- {makejinja-2.8.1 → makejinja-2.8.3}/pyproject.toml +38 -34
- makejinja-2.8.3/pyproject.toml.orig +76 -0
- {makejinja-2.8.1 → makejinja-2.8.3}/src/makejinja/__init__.py +1 -1
- {makejinja-2.8.1 → makejinja-2.8.3}/src/makejinja/app.py +32 -16
- {makejinja-2.8.1 → makejinja-2.8.3}/src/makejinja/cli.py +3 -3
- {makejinja-2.8.1 → makejinja-2.8.3}/src/makejinja/config.py +23 -22
- makejinja-2.8.3/src/makejinja/plugin.py +76 -0
- makejinja-2.8.1/LICENSE +0 -21
- makejinja-2.8.1/PKG-INFO +0 -137
- makejinja-2.8.1/setup.cfg +0 -4
- makejinja-2.8.1/src/makejinja/plugin.py +0 -58
- makejinja-2.8.1/src/makejinja.egg-info/SOURCES.txt +0 -17
- makejinja-2.8.1/src/makejinja.egg-info/dependency_links.txt +0 -1
- makejinja-2.8.1/src/makejinja.egg-info/entry_points.txt +0 -2
- makejinja-2.8.1/src/makejinja.egg-info/requires.txt +0 -5
- makejinja-2.8.1/src/makejinja.egg-info/top_level.txt +0 -1
- makejinja-2.8.1/tests/test_makejinja.py +0 -152
- {makejinja-2.8.1 → makejinja-2.8.3}/src/makejinja/__main__.py +0 -0
- {makejinja-2.8.1 → makejinja-2.8.3}/src/makejinja/py.typed +0 -0
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: makejinja
|
|
3
|
-
Version: 2.8.
|
|
3
|
+
Version: 2.8.3
|
|
4
4
|
Summary: Generate entire directory structures using Jinja templates with support for external data and custom plugins
|
|
5
|
+
Keywords: cli,dashboard,generator,hassio,home-assistant,jinja2,library,lovelace,template,tool
|
|
6
|
+
Author: Mirko Lenz
|
|
5
7
|
Author-email: Mirko Lenz <mirko@mirkolenz.com>
|
|
6
|
-
|
|
7
|
-
Project-URL: Homepage, https://mirkolenz.github.io/makejinja/
|
|
8
|
-
Project-URL: Documentation, https://mirkolenz.github.io/makejinja/makejinja/cli.html
|
|
9
|
-
Project-URL: Issues, https://github.com/mirkolenz/makejinja/issues
|
|
10
|
-
Project-URL: Changelog, https://github.com/mirkolenz/makejinja/releases
|
|
11
|
-
Keywords: jinja2,home-assistant,hassio,dashboard,lovelace,template,generator,cli,tool,library
|
|
8
|
+
License-Expression: MIT
|
|
12
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
10
|
Classifier: Environment :: Console
|
|
14
11
|
Classifier: Framework :: Pytest
|
|
15
12
|
Classifier: Intended Audience :: Developers
|
|
16
13
|
Classifier: Intended Audience :: End Users/Desktop
|
|
17
14
|
Classifier: Intended Audience :: System Administrators
|
|
18
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
19
15
|
Classifier: Natural Language :: English
|
|
20
16
|
Classifier: Operating System :: OS Independent
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
24
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
20
|
Classifier: Topic :: File Formats
|
|
26
21
|
Classifier: Topic :: Home Automation
|
|
27
22
|
Classifier: Topic :: Software Development :: Code Generators
|
|
@@ -31,15 +26,18 @@ Classifier: Topic :: System :: Systems Administration
|
|
|
31
26
|
Classifier: Topic :: Text Processing :: Markup
|
|
32
27
|
Classifier: Topic :: Utilities
|
|
33
28
|
Classifier: Typing :: Typed
|
|
34
|
-
Requires-
|
|
29
|
+
Requires-Dist: frozendict>=2,<3
|
|
30
|
+
Requires-Dist: jinja2>=3,<4
|
|
31
|
+
Requires-Dist: pyyaml>=6,<7
|
|
32
|
+
Requires-Dist: rich-click>=1,<2
|
|
33
|
+
Requires-Dist: typed-settings[attrs,cattrs,click]>=25,<27
|
|
34
|
+
Requires-Python: >=3.13, <4
|
|
35
|
+
Project-URL: Changelog, https://github.com/mirkolenz/makejinja/releases
|
|
36
|
+
Project-URL: Documentation, https://mirkolenz.github.io/makejinja/makejinja/cli.html
|
|
37
|
+
Project-URL: Homepage, https://mirkolenz.github.io/makejinja/
|
|
38
|
+
Project-URL: Issues, https://github.com/mirkolenz/makejinja/issues
|
|
39
|
+
Project-URL: Repository, https://github.com/mirkolenz/makejinja
|
|
35
40
|
Description-Content-Type: text/markdown
|
|
36
|
-
License-File: LICENSE
|
|
37
|
-
Requires-Dist: jinja2<4,>=3
|
|
38
|
-
Requires-Dist: pyyaml<7,>=6
|
|
39
|
-
Requires-Dist: rich-click<2,>=1
|
|
40
|
-
Requires-Dist: typed-settings[attrs,cattrs,click]<26,>=23
|
|
41
|
-
Requires-Dist: immutables<1,>=0.20
|
|
42
|
-
Dynamic: license-file
|
|
43
41
|
|
|
44
42
|
<!-- markdownlint-disable MD033 MD041 -->
|
|
45
43
|
<h1><p align="center">makejinja</p></h1>
|
|
@@ -82,7 +80,7 @@ Our comprehensive [Home Assistant example](https://github.com/mirkolenz/makejinj
|
|
|
82
80
|
- **Multi-Source Data Integration**: Load variables from YAML, TOML, and Python files, with support for file-specific data sources and runtime variable injection.
|
|
83
81
|
- **Custom Template Delimiters**: Configure Jinja delimiters (e.g., `<% %>` instead of `{{ }}`) to avoid conflicts with target file formats like Home Assistant, Kubernetes, or Terraform.
|
|
84
82
|
- **Flexible Directory Processing**: Process multiple input directories with complex nested structures, preserving hierarchy while applying powerful template transformations.
|
|
85
|
-
- **Extensible Plugin System**: Create custom [plugins](https://mirkolenz.github.io/makejinja/makejinja/plugin.html#Plugin) with filters, functions, and path filtering logic for specialized requirements.
|
|
83
|
+
- **Extensible Plugin System**: Create custom [plugins](https://mirkolenz.github.io/makejinja/makejinja/plugin.html#Plugin) with filters, functions, data, tests, policies, extensions, and path filtering logic for specialized requirements.
|
|
86
84
|
- **Production-Ready**: Comprehensive CLI interface, configuration file support, and Python library API for seamless workflow integration.
|
|
87
85
|
|
|
88
86
|
## Use Cases
|
|
@@ -39,7 +39,7 @@ Our comprehensive [Home Assistant example](https://github.com/mirkolenz/makejinj
|
|
|
39
39
|
- **Multi-Source Data Integration**: Load variables from YAML, TOML, and Python files, with support for file-specific data sources and runtime variable injection.
|
|
40
40
|
- **Custom Template Delimiters**: Configure Jinja delimiters (e.g., `<% %>` instead of `{{ }}`) to avoid conflicts with target file formats like Home Assistant, Kubernetes, or Terraform.
|
|
41
41
|
- **Flexible Directory Processing**: Process multiple input directories with complex nested structures, preserving hierarchy while applying powerful template transformations.
|
|
42
|
-
- **Extensible Plugin System**: Create custom [plugins](https://mirkolenz.github.io/makejinja/makejinja/plugin.html#Plugin) with filters, functions, and path filtering logic for specialized requirements.
|
|
42
|
+
- **Extensible Plugin System**: Create custom [plugins](https://mirkolenz.github.io/makejinja/makejinja/plugin.html#Plugin) with filters, functions, data, tests, policies, extensions, and path filtering logic for specialized requirements.
|
|
43
43
|
- **Production-Ready**: Comprehensive CLI interface, configuration file support, and Python library API for seamless workflow integration.
|
|
44
44
|
|
|
45
45
|
## Use Cases
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "makejinja"
|
|
3
|
-
version = "2.8.
|
|
3
|
+
version = "2.8.3"
|
|
4
4
|
description = "Generate entire directory structures using Jinja templates with support for external data and custom plugins"
|
|
5
|
-
authors = [{ name = "Mirko Lenz", email = "mirko@mirkolenz.com" }]
|
|
6
5
|
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13,<4"
|
|
7
|
+
license = "MIT"
|
|
7
8
|
keywords = [
|
|
8
|
-
"
|
|
9
|
-
"home-assistant",
|
|
10
|
-
"hassio",
|
|
9
|
+
"cli",
|
|
11
10
|
"dashboard",
|
|
11
|
+
"generator",
|
|
12
|
+
"hassio",
|
|
13
|
+
"home-assistant",
|
|
14
|
+
"jinja2",
|
|
15
|
+
"library",
|
|
12
16
|
"lovelace",
|
|
13
17
|
"template",
|
|
14
|
-
"generator",
|
|
15
|
-
"cli",
|
|
16
18
|
"tool",
|
|
17
|
-
"library",
|
|
18
19
|
]
|
|
19
20
|
classifiers = [
|
|
20
21
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -23,13 +24,11 @@ classifiers = [
|
|
|
23
24
|
"Intended Audience :: Developers",
|
|
24
25
|
"Intended Audience :: End Users/Desktop",
|
|
25
26
|
"Intended Audience :: System Administrators",
|
|
26
|
-
"License :: OSI Approved :: MIT License",
|
|
27
27
|
"Natural Language :: English",
|
|
28
28
|
"Operating System :: OS Independent",
|
|
29
|
-
"Programming Language :: Python :: 3.11",
|
|
30
|
-
"Programming Language :: Python :: 3.12",
|
|
31
|
-
"Programming Language :: Python :: 3.13",
|
|
32
29
|
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Programming Language :: Python :: 3.14",
|
|
33
32
|
"Topic :: File Formats",
|
|
34
33
|
"Topic :: Home Automation",
|
|
35
34
|
"Topic :: Software Development :: Code Generators",
|
|
@@ -40,45 +39,50 @@ classifiers = [
|
|
|
40
39
|
"Topic :: Utilities",
|
|
41
40
|
"Typing :: Typed",
|
|
42
41
|
]
|
|
43
|
-
requires-python = ">=3.11"
|
|
44
42
|
dependencies = [
|
|
43
|
+
"frozendict>=2,<3",
|
|
45
44
|
"jinja2>=3,<4",
|
|
46
45
|
"pyyaml>=6,<7",
|
|
47
46
|
"rich-click>=1,<2",
|
|
48
|
-
"typed-settings[attrs,cattrs,click]>=
|
|
49
|
-
"immutables>=0.20,<1",
|
|
47
|
+
"typed-settings[attrs,cattrs,click]>=25,<27",
|
|
50
48
|
]
|
|
51
49
|
|
|
50
|
+
[[project.authors]]
|
|
51
|
+
name = "Mirko Lenz"
|
|
52
|
+
email = "mirko@mirkolenz.com"
|
|
53
|
+
|
|
52
54
|
[project.urls]
|
|
53
|
-
|
|
54
|
-
Homepage = "https://mirkolenz.github.io/makejinja/"
|
|
55
|
+
Changelog = "https://github.com/mirkolenz/makejinja/releases"
|
|
55
56
|
Documentation = "https://mirkolenz.github.io/makejinja/makejinja/cli.html"
|
|
57
|
+
Homepage = "https://mirkolenz.github.io/makejinja/"
|
|
56
58
|
Issues = "https://github.com/mirkolenz/makejinja/issues"
|
|
57
|
-
|
|
59
|
+
Repository = "https://github.com/mirkolenz/makejinja"
|
|
58
60
|
|
|
59
61
|
[project.scripts]
|
|
60
62
|
makejinja = "makejinja.cli:makejinja_cli"
|
|
61
63
|
|
|
62
64
|
[dependency-groups]
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
docs = ["pdoc>=16,<17"]
|
|
66
|
+
test = [
|
|
67
|
+
"pytest>=9,<10",
|
|
68
|
+
"pytest-cov>=7,<8",
|
|
69
|
+
]
|
|
65
70
|
|
|
66
71
|
[build-system]
|
|
67
|
-
requires = ["
|
|
68
|
-
build-backend = "
|
|
72
|
+
requires = ["uv-build>=0.12,<1"]
|
|
73
|
+
build-backend = "uv_build"
|
|
69
74
|
|
|
70
|
-
[tool.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[tool.ruff]
|
|
77
|
-
target-version = "py311"
|
|
78
|
-
|
|
79
|
-
[tool.ruff.lint]
|
|
80
|
-
select = ["E", "W", "F", "B", "UP"]
|
|
81
|
-
ignore = ["E501"]
|
|
75
|
+
[tool.pytest]
|
|
76
|
+
addopts = [
|
|
77
|
+
"--cov=makejinja",
|
|
78
|
+
"--cov-report=term-missing",
|
|
79
|
+
]
|
|
82
80
|
|
|
83
81
|
[tool.ruff.lint.pydocstyle]
|
|
84
82
|
convention = "google"
|
|
83
|
+
|
|
84
|
+
[tool.uv]
|
|
85
|
+
default-groups = [
|
|
86
|
+
"test",
|
|
87
|
+
"docs",
|
|
88
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "makejinja"
|
|
3
|
+
version = "2.8.3"
|
|
4
|
+
description = "Generate entire directory structures using Jinja templates with support for external data and custom plugins"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13,<4"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [{ name = "Mirko Lenz", email = "mirko@mirkolenz.com" }]
|
|
9
|
+
keywords = [
|
|
10
|
+
"cli",
|
|
11
|
+
"dashboard",
|
|
12
|
+
"generator",
|
|
13
|
+
"hassio",
|
|
14
|
+
"home-assistant",
|
|
15
|
+
"jinja2",
|
|
16
|
+
"library",
|
|
17
|
+
"lovelace",
|
|
18
|
+
"template",
|
|
19
|
+
"tool",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 5 - Production/Stable",
|
|
23
|
+
"Environment :: Console",
|
|
24
|
+
"Framework :: Pytest",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Intended Audience :: End Users/Desktop",
|
|
27
|
+
"Intended Audience :: System Administrators",
|
|
28
|
+
"Natural Language :: English",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Programming Language :: Python :: 3.14",
|
|
33
|
+
"Topic :: File Formats",
|
|
34
|
+
"Topic :: Home Automation",
|
|
35
|
+
"Topic :: Software Development :: Code Generators",
|
|
36
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
37
|
+
"Topic :: System :: Software Distribution",
|
|
38
|
+
"Topic :: System :: Systems Administration",
|
|
39
|
+
"Topic :: Text Processing :: Markup",
|
|
40
|
+
"Topic :: Utilities",
|
|
41
|
+
"Typing :: Typed",
|
|
42
|
+
]
|
|
43
|
+
dependencies = [
|
|
44
|
+
"frozendict>=2,<3",
|
|
45
|
+
"jinja2>=3,<4",
|
|
46
|
+
"pyyaml>=6,<7",
|
|
47
|
+
"rich-click>=1,<2",
|
|
48
|
+
"typed-settings[attrs,cattrs,click]>=25,<27",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[project.urls]
|
|
52
|
+
Changelog = "https://github.com/mirkolenz/makejinja/releases"
|
|
53
|
+
Documentation = "https://mirkolenz.github.io/makejinja/makejinja/cli.html"
|
|
54
|
+
Homepage = "https://mirkolenz.github.io/makejinja/"
|
|
55
|
+
Issues = "https://github.com/mirkolenz/makejinja/issues"
|
|
56
|
+
Repository = "https://github.com/mirkolenz/makejinja"
|
|
57
|
+
|
|
58
|
+
[project.scripts]
|
|
59
|
+
makejinja = "makejinja.cli:makejinja_cli"
|
|
60
|
+
|
|
61
|
+
[dependency-groups]
|
|
62
|
+
docs = ["pdoc>=16,<17"]
|
|
63
|
+
test = ["pytest>=9,<10", "pytest-cov>=7,<8"]
|
|
64
|
+
|
|
65
|
+
[build-system]
|
|
66
|
+
requires = ["uv-build>=0.12,<1"]
|
|
67
|
+
build-backend = "uv_build"
|
|
68
|
+
|
|
69
|
+
[tool.pytest]
|
|
70
|
+
addopts = ["--cov=makejinja", "--cov-report=term-missing"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint.pydocstyle]
|
|
73
|
+
convention = "google"
|
|
74
|
+
|
|
75
|
+
[tool.uv]
|
|
76
|
+
default-groups = ["test", "docs"]
|
|
@@ -8,16 +8,15 @@ import tomllib
|
|
|
8
8
|
from collections import abc
|
|
9
9
|
from inspect import signature
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any
|
|
11
|
+
from typing import Any, cast
|
|
12
12
|
|
|
13
|
+
import rich_click as click
|
|
13
14
|
import yaml
|
|
14
15
|
from jinja2 import BaseLoader, ChoiceLoader, DictLoader, Environment, FileSystemLoader
|
|
15
|
-
from jinja2.environment import load_extensions
|
|
16
16
|
from jinja2.utils import import_string
|
|
17
|
-
from rich import print
|
|
18
17
|
|
|
19
18
|
from makejinja.config import Config
|
|
20
|
-
from makejinja.plugin import Data, MutableData, PathFilter, Plugin
|
|
19
|
+
from makejinja.plugin import Data, Functions, MutableData, PathFilter, Plugin
|
|
21
20
|
|
|
22
21
|
__all__ = ["makejinja"]
|
|
23
22
|
|
|
@@ -26,15 +25,15 @@ STDIN_PATH = Path("/dev/stdin").resolve()
|
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
def log(message: str, config: Config) -> None:
|
|
29
|
-
if not config.quiet
|
|
30
|
-
|
|
28
|
+
if not config.quiet:
|
|
29
|
+
click.echo(message, err=True)
|
|
31
30
|
|
|
32
31
|
|
|
33
32
|
def exec(cmd: str) -> None:
|
|
34
33
|
subprocess.run(cmd, shell=True, check=True)
|
|
35
34
|
|
|
36
35
|
|
|
37
|
-
def makejinja(config: Config):
|
|
36
|
+
def makejinja(config: Config) -> None:
|
|
38
37
|
"""makejinja can be used to automatically generate files from [Jinja templates](https://jinja.palletsprojects.com/en/3.1.x/templates/)."""
|
|
39
38
|
|
|
40
39
|
for cmd in config.exec_pre:
|
|
@@ -231,7 +230,7 @@ def init_jinja_env(
|
|
|
231
230
|
line_comment_prefix=config.prefix.line_comment,
|
|
232
231
|
trim_blocks=config.whitespace.trim_blocks,
|
|
233
232
|
lstrip_blocks=config.whitespace.lstrip_blocks,
|
|
234
|
-
newline_sequence=config.whitespace.newline_sequence,
|
|
233
|
+
newline_sequence=config.whitespace.newline_sequence,
|
|
235
234
|
keep_trailing_newline=config.whitespace.keep_trailing_newline,
|
|
236
235
|
optimized=config.internal.optimized,
|
|
237
236
|
undefined=config.undefined.value,
|
|
@@ -244,7 +243,7 @@ def init_jinja_env(
|
|
|
244
243
|
)
|
|
245
244
|
|
|
246
245
|
env.globals.update(data)
|
|
247
|
-
env.globals["env"] = os.environ
|
|
246
|
+
cast(dict[str, Any], env.globals)["env"] = os.environ
|
|
248
247
|
|
|
249
248
|
return env
|
|
250
249
|
|
|
@@ -254,7 +253,12 @@ def from_yaml(path: Path) -> dict[str, Any]:
|
|
|
254
253
|
|
|
255
254
|
with path.open("rb") as fp:
|
|
256
255
|
for doc in yaml.safe_load_all(fp):
|
|
257
|
-
|
|
256
|
+
if isinstance(doc, abc.Mapping):
|
|
257
|
+
data |= doc
|
|
258
|
+
else:
|
|
259
|
+
raise TypeError(
|
|
260
|
+
f"Expected YAML documents in '{path}' to be mappings but found {type(doc).__name__}"
|
|
261
|
+
)
|
|
258
262
|
|
|
259
263
|
return data
|
|
260
264
|
|
|
@@ -266,7 +270,14 @@ def from_toml(path: Path) -> dict[str, Any]:
|
|
|
266
270
|
|
|
267
271
|
def from_json(path: Path) -> dict[str, Any]:
|
|
268
272
|
with path.open("rb") as fp:
|
|
269
|
-
|
|
273
|
+
data = json.load(fp)
|
|
274
|
+
|
|
275
|
+
if isinstance(data, abc.Mapping):
|
|
276
|
+
return dict(data)
|
|
277
|
+
|
|
278
|
+
raise TypeError(
|
|
279
|
+
f"Expected JSON documents in '{path}' to be mappings but found {type(data).__name__}"
|
|
280
|
+
)
|
|
270
281
|
|
|
271
282
|
|
|
272
283
|
DATA_LOADERS: dict[str, abc.Callable[[Path], dict[str, Any]]] = {
|
|
@@ -352,6 +363,10 @@ def load_file_data(template_name: str, config: Config) -> dict[str, Any]:
|
|
|
352
363
|
return file_data
|
|
353
364
|
|
|
354
365
|
|
|
366
|
+
def _named_functions(functions: Functions) -> dict[str, Any]:
|
|
367
|
+
return {cast(Any, func).__name__: func for func in functions}
|
|
368
|
+
|
|
369
|
+
|
|
355
370
|
def load_plugin(
|
|
356
371
|
plugin_name: str, env: Environment, data: Data, config: Config
|
|
357
372
|
) -> Plugin:
|
|
@@ -371,22 +386,23 @@ def load_plugin(
|
|
|
371
386
|
plugin = cls(**params)
|
|
372
387
|
|
|
373
388
|
if hasattr(plugin, "globals"):
|
|
374
|
-
env.globals.update(
|
|
389
|
+
env.globals.update(_named_functions(plugin.globals()))
|
|
375
390
|
|
|
376
391
|
if hasattr(plugin, "functions"):
|
|
377
|
-
env.globals.update(
|
|
392
|
+
env.globals.update(_named_functions(plugin.functions()))
|
|
378
393
|
|
|
379
394
|
if hasattr(plugin, "data"):
|
|
380
395
|
env.globals.update(plugin.data())
|
|
381
396
|
|
|
382
397
|
if hasattr(plugin, "extensions"):
|
|
383
|
-
|
|
398
|
+
for extension in plugin.extensions():
|
|
399
|
+
env.add_extension(extension)
|
|
384
400
|
|
|
385
401
|
if hasattr(plugin, "filters"):
|
|
386
|
-
env.filters.update(
|
|
402
|
+
env.filters.update(_named_functions(plugin.filters()))
|
|
387
403
|
|
|
388
404
|
if hasattr(plugin, "tests"):
|
|
389
|
-
env.tests.update(
|
|
405
|
+
env.tests.update(_named_functions(plugin.tests()))
|
|
390
406
|
|
|
391
407
|
if hasattr(plugin, "policies"):
|
|
392
408
|
env.policies.update(plugin.policies())
|
|
@@ -13,7 +13,7 @@ from .app import makejinja
|
|
|
13
13
|
|
|
14
14
|
__all__: list[str] = []
|
|
15
15
|
|
|
16
|
-
click.rich_click.
|
|
16
|
+
click.rich_click.TEXT_MARKUP = "markdown"
|
|
17
17
|
click.rich_click.OPTION_GROUPS = OPTION_GROUPS
|
|
18
18
|
|
|
19
19
|
_ts_loaders = ts.default_loaders(
|
|
@@ -24,12 +24,12 @@ _ts_loaders = ts.default_loaders(
|
|
|
24
24
|
@click.command("makejinja", context_settings={"help_option_names": ("--help", "-h")})
|
|
25
25
|
@click.version_option(None, "--version", "-v")
|
|
26
26
|
@ts.click_options(Config, _ts_loaders)
|
|
27
|
-
def makejinja_cli(config: Config):
|
|
27
|
+
def makejinja_cli(config: Config) -> None:
|
|
28
28
|
"""makejinja can be used to automatically generate files from [Jinja templates](https://jinja.palletsprojects.com/en/3.1.x/templates/).
|
|
29
29
|
|
|
30
30
|
Instead of passing CLI options, you can also write them to a file called `makejinja.toml` in your working directory.
|
|
31
31
|
**Note**: In this file, options may be named differently.
|
|
32
|
-
Please refer to the file [`makejinja/config.py`](https://github.com/mirkolenz/makejinja/blob/main/makejinja/config.py) to see their actual names.
|
|
32
|
+
Please refer to the file [`makejinja/config.py`](https://github.com/mirkolenz/makejinja/blob/main/src/makejinja/config.py) to see their actual names.
|
|
33
33
|
You will also find an example here: [`makejinja/tests/data/makejinja.toml`](https://github.com/mirkolenz/makejinja/blob/main/tests/data/makejinja.toml).
|
|
34
34
|
To override its location, you can set the environment variable `MAKEJINJA_SETTINGS` to the path of your config file.
|
|
35
35
|
"""
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
from collections import abc
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import Literal
|
|
4
5
|
|
|
5
|
-
import immutables
|
|
6
6
|
import rich_click as click
|
|
7
7
|
import typed_settings as ts
|
|
8
|
+
from frozendict import frozendict
|
|
8
9
|
from jinja2 import (
|
|
9
10
|
ChainableUndefined,
|
|
10
11
|
DebugUndefined,
|
|
11
12
|
StrictUndefined,
|
|
12
13
|
)
|
|
13
|
-
from jinja2 import
|
|
14
|
-
Undefined as DefaultUndefined,
|
|
15
|
-
)
|
|
14
|
+
from jinja2 import Undefined as DefaultUndefined
|
|
16
15
|
from jinja2.defaults import (
|
|
17
16
|
BLOCK_END_STRING,
|
|
18
17
|
BLOCK_START_STRING,
|
|
@@ -24,9 +23,9 @@ from jinja2.defaults import (
|
|
|
24
23
|
VARIABLE_END_STRING,
|
|
25
24
|
VARIABLE_START_STRING,
|
|
26
25
|
)
|
|
26
|
+
from rich_click.utils import OptionGroupDict
|
|
27
27
|
|
|
28
|
-
__all__ = ["Config", "Delimiter", "Internal", "Prefix", "
|
|
29
|
-
frozendict = immutables.Map
|
|
28
|
+
__all__ = ["Config", "Delimiter", "Internal", "Prefix", "Undefined", "Whitespace"]
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class Undefined(Enum):
|
|
@@ -40,9 +39,10 @@ class Undefined(Enum):
|
|
|
40
39
|
|
|
41
40
|
def _exclude_patterns_validator(instance, attribute, value) -> None:
|
|
42
41
|
if any("**" in pattern for pattern in value):
|
|
43
|
-
#
|
|
44
|
-
|
|
42
|
+
# todo: for next major release, raise ValueError instead of printing a warning
|
|
43
|
+
click.echo(
|
|
45
44
|
"The recursive wildcard `**` is not supported by `exclude_patterns` (it acts like non-recursive `*`).",
|
|
45
|
+
err=True,
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
|
|
@@ -153,7 +153,7 @@ class Whitespace:
|
|
|
153
153
|
If this is set to `True`, leading spaces and tabs are stripped from the start of a line to a block.
|
|
154
154
|
""",
|
|
155
155
|
)
|
|
156
|
-
newline_sequence:
|
|
156
|
+
newline_sequence: Literal["\n", "\r\n", "\r"] = ts.option(
|
|
157
157
|
default=NEWLINE_SEQUENCE,
|
|
158
158
|
click={"param_decls": "--newline-sequence"},
|
|
159
159
|
help="""
|
|
@@ -182,7 +182,7 @@ class Config:
|
|
|
182
182
|
Path to a directory containing template files or a single template file.
|
|
183
183
|
It is passed to Jinja's [FileSystemLoader](https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.FileSystemLoader) when creating the environment.
|
|
184
184
|
**Note:** This option may be passed multiple times to pass a list of values.
|
|
185
|
-
If a template exists in multiple inputs, the
|
|
185
|
+
If a template exists in multiple inputs, the first matching template in the provided order will be used.
|
|
186
186
|
""",
|
|
187
187
|
)
|
|
188
188
|
output: Path = ts.option(
|
|
@@ -207,7 +207,7 @@ class Config:
|
|
|
207
207
|
""",
|
|
208
208
|
)
|
|
209
209
|
exclude_patterns: tuple[str, ...] = ts.option(
|
|
210
|
-
default=
|
|
210
|
+
default=(),
|
|
211
211
|
click={"param_decls": ("--exclude-pattern", "--exclude", "-E")},
|
|
212
212
|
validator=_exclude_patterns_validator,
|
|
213
213
|
help="""
|
|
@@ -251,7 +251,7 @@ class Config:
|
|
|
251
251
|
""",
|
|
252
252
|
)
|
|
253
253
|
data: tuple[Path, ...] = ts.option(
|
|
254
|
-
default=
|
|
254
|
+
default=(),
|
|
255
255
|
click={
|
|
256
256
|
"type": click.Path(exists=True, path_type=Path),
|
|
257
257
|
"param_decls": ("--data", "-d"),
|
|
@@ -291,7 +291,7 @@ class Config:
|
|
|
291
291
|
},
|
|
292
292
|
)
|
|
293
293
|
loaders: tuple[str, ...] = ts.option(
|
|
294
|
-
default=
|
|
294
|
+
default=(),
|
|
295
295
|
click={
|
|
296
296
|
"param_decls": ("--loader", "-l"),
|
|
297
297
|
"hidden": True,
|
|
@@ -299,17 +299,18 @@ class Config:
|
|
|
299
299
|
help="Deprecated, use `--plugin` instead.",
|
|
300
300
|
)
|
|
301
301
|
plugins: tuple[str, ...] = ts.option(
|
|
302
|
-
default=
|
|
302
|
+
default=(),
|
|
303
303
|
click={
|
|
304
304
|
"param_decls": ("--plugin", "-p"),
|
|
305
305
|
},
|
|
306
306
|
help="""
|
|
307
307
|
Use custom Python code to adjust the used Jinja environment to your needs.
|
|
308
308
|
The specified Python file should export a **class** containing a subset of the following functions:
|
|
309
|
-
`filters`, `
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
`filters`, `functions`, `data`, `tests`, `policies`, `extensions`, and `path_filters`.
|
|
310
|
+
The `globals` method is deprecated, use `functions` and `data` instead.
|
|
311
|
+
An optional `__init__` method can receive any subset of the keyword arguments `env` (or `environment`), `data`, and `config`.
|
|
312
|
+
These provide the Jinja environment, loaded global data, and makejinja configuration.
|
|
313
|
+
Before templates are loaded, the constructor can configure environment features such as `finalize`, custom loaders, and bytecode caching.
|
|
313
314
|
An import path can be specified either in dotted notation (`your.custom.Plugin`)
|
|
314
315
|
or with a colon as object delimiter (`your.custom:Plugin`).
|
|
315
316
|
**Note:** This option may be passed multiple times to pass a list of values.
|
|
@@ -328,7 +329,7 @@ class Config:
|
|
|
328
329
|
""",
|
|
329
330
|
)
|
|
330
331
|
extensions: tuple[str, ...] = ts.option(
|
|
331
|
-
default=
|
|
332
|
+
default=(),
|
|
332
333
|
click={"param_decls": ("--extension", "-e")},
|
|
333
334
|
help="""
|
|
334
335
|
List of Jinja extensions to use as strings of import paths.
|
|
@@ -347,13 +348,13 @@ class Config:
|
|
|
347
348
|
),
|
|
348
349
|
)
|
|
349
350
|
exec_pre: tuple[str, ...] = ts.option(
|
|
350
|
-
default=
|
|
351
|
+
default=(),
|
|
351
352
|
help="""
|
|
352
353
|
Shell commands to execute before rendering.
|
|
353
354
|
""",
|
|
354
355
|
)
|
|
355
356
|
exec_post: tuple[str, ...] = ts.option(
|
|
356
|
-
default=
|
|
357
|
+
default=(),
|
|
357
358
|
help="""
|
|
358
359
|
Shell commands to execute after rendering.
|
|
359
360
|
""",
|
|
@@ -385,7 +386,7 @@ class Config:
|
|
|
385
386
|
internal: Internal = Internal()
|
|
386
387
|
|
|
387
388
|
|
|
388
|
-
OPTION_GROUPS = {
|
|
389
|
+
OPTION_GROUPS: dict[str, list[OptionGroupDict]] = {
|
|
389
390
|
"makejinja": [
|
|
390
391
|
{
|
|
391
392
|
"name": "Input/Output",
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from collections import abc
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Protocol
|
|
4
|
+
|
|
5
|
+
from jinja2 import Environment
|
|
6
|
+
from jinja2.ext import Extension
|
|
7
|
+
|
|
8
|
+
from makejinja.config import Config
|
|
9
|
+
|
|
10
|
+
__all__ = ["Plugin"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
type Extensions = abc.Sequence[type[Extension]]
|
|
14
|
+
type Filter = abc.Callable[..., Any]
|
|
15
|
+
type Filters = abc.Sequence[Filter]
|
|
16
|
+
type Function = abc.Callable[..., Any]
|
|
17
|
+
type Functions = abc.Sequence[Function]
|
|
18
|
+
type Test = abc.Callable[..., Any]
|
|
19
|
+
type Tests = abc.Sequence[Test]
|
|
20
|
+
type Policies = abc.Mapping[str, Any]
|
|
21
|
+
type MutableData = abc.MutableMapping[str, Any]
|
|
22
|
+
type Data = abc.Mapping[str, Any]
|
|
23
|
+
type PathFilter = abc.Callable[[Path], bool]
|
|
24
|
+
type PathFilters = abc.Sequence[PathFilter]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Plugin(Protocol):
|
|
28
|
+
"""Extend makejinja by implementing any subset of the plugin methods.
|
|
29
|
+
|
|
30
|
+
The optional constructor receives supported parameters by keyword.
|
|
31
|
+
It may request `env` (or `environment`), `data`, and `config`.
|
|
32
|
+
Configure the environment before loading any templates, including changes to
|
|
33
|
+
`finalize`, custom loaders, or bytecode caching.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, *, env: Environment, data: Data, config: Config) -> None:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def functions(self) -> Functions:
|
|
40
|
+
"""Return global template callables registered by their function names."""
|
|
41
|
+
return []
|
|
42
|
+
|
|
43
|
+
def data(self) -> Data:
|
|
44
|
+
"""Return global template values that override existing values."""
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
def filters(self) -> Filters:
|
|
48
|
+
"""Return template filters registered by their function names."""
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
def tests(self) -> Tests:
|
|
52
|
+
"""Return template tests registered by their function names."""
|
|
53
|
+
return []
|
|
54
|
+
|
|
55
|
+
def policies(self) -> Policies:
|
|
56
|
+
"""Return overrides for Jinja environment policies."""
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
def extensions(self) -> Extensions:
|
|
60
|
+
"""Return Jinja extension classes to register on the environment."""
|
|
61
|
+
return []
|
|
62
|
+
|
|
63
|
+
def path_filters(self) -> PathFilters:
|
|
64
|
+
"""Return predicates that exclude discovered paths when they return false."""
|
|
65
|
+
return []
|
|
66
|
+
|
|
67
|
+
def globals(self) -> Functions:
|
|
68
|
+
"""Return global callables.
|
|
69
|
+
|
|
70
|
+
Deprecated:
|
|
71
|
+
Use `functions()` and `data()` instead.
|
|
72
|
+
"""
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
AbstractLoader = Plugin
|
makejinja-2.8.1/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022 Mirko Lenz
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
makejinja-2.8.1/PKG-INFO
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: makejinja
|
|
3
|
-
Version: 2.8.1
|
|
4
|
-
Summary: Generate entire directory structures using Jinja templates with support for external data and custom plugins
|
|
5
|
-
Author-email: Mirko Lenz <mirko@mirkolenz.com>
|
|
6
|
-
Project-URL: Repository, https://github.com/mirkolenz/makejinja
|
|
7
|
-
Project-URL: Homepage, https://mirkolenz.github.io/makejinja/
|
|
8
|
-
Project-URL: Documentation, https://mirkolenz.github.io/makejinja/makejinja/cli.html
|
|
9
|
-
Project-URL: Issues, https://github.com/mirkolenz/makejinja/issues
|
|
10
|
-
Project-URL: Changelog, https://github.com/mirkolenz/makejinja/releases
|
|
11
|
-
Keywords: jinja2,home-assistant,hassio,dashboard,lovelace,template,generator,cli,tool,library
|
|
12
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
-
Classifier: Environment :: Console
|
|
14
|
-
Classifier: Framework :: Pytest
|
|
15
|
-
Classifier: Intended Audience :: Developers
|
|
16
|
-
Classifier: Intended Audience :: End Users/Desktop
|
|
17
|
-
Classifier: Intended Audience :: System Administrators
|
|
18
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
-
Classifier: Natural Language :: English
|
|
20
|
-
Classifier: Operating System :: OS Independent
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
-
Classifier: Programming Language :: Python :: 3
|
|
25
|
-
Classifier: Topic :: File Formats
|
|
26
|
-
Classifier: Topic :: Home Automation
|
|
27
|
-
Classifier: Topic :: Software Development :: Code Generators
|
|
28
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
|
-
Classifier: Topic :: System :: Software Distribution
|
|
30
|
-
Classifier: Topic :: System :: Systems Administration
|
|
31
|
-
Classifier: Topic :: Text Processing :: Markup
|
|
32
|
-
Classifier: Topic :: Utilities
|
|
33
|
-
Classifier: Typing :: Typed
|
|
34
|
-
Requires-Python: >=3.11
|
|
35
|
-
Description-Content-Type: text/markdown
|
|
36
|
-
License-File: LICENSE
|
|
37
|
-
Requires-Dist: jinja2<4,>=3
|
|
38
|
-
Requires-Dist: pyyaml<7,>=6
|
|
39
|
-
Requires-Dist: rich-click<2,>=1
|
|
40
|
-
Requires-Dist: typed-settings[attrs,cattrs,click]<26,>=23
|
|
41
|
-
Requires-Dist: immutables<1,>=0.20
|
|
42
|
-
Dynamic: license-file
|
|
43
|
-
|
|
44
|
-
<!-- markdownlint-disable MD033 MD041 -->
|
|
45
|
-
<h1><p align="center">makejinja</p></h1>
|
|
46
|
-
|
|
47
|
-
<p align="center">
|
|
48
|
-
<img width="256px" alt="makejinja logo" src="https://raw.githubusercontent.com/mirkolenz/makejinja/main/assets/logo.png" />
|
|
49
|
-
</p>
|
|
50
|
-
|
|
51
|
-
<p align="center">
|
|
52
|
-
<a href="https://pypi.org/project/makejinja/">PyPI</a> |
|
|
53
|
-
<a href="https://github.com/users/mirkolenz/packages/container/package/makejinja">Docker</a> |
|
|
54
|
-
<a href="https://mirkolenz.github.io/makejinja">Docs</a> |
|
|
55
|
-
<a href="https://github.com/mirkolenz/makejinja/tree/main/tests/data">Example</a> |
|
|
56
|
-
<a href="https://jinja.palletsprojects.com/en/3.1.x/templates">Jinja reference</a>
|
|
57
|
-
</p>
|
|
58
|
-
|
|
59
|
-
<p align="center">
|
|
60
|
-
Generate entire directory structures using Jinja templates with support for external data and custom plugins.
|
|
61
|
-
</p>
|
|
62
|
-
|
|
63
|
-
<p align="center">
|
|
64
|
-
<img alt="makejinja demonstration" src="https://mirkolenz.github.io/makejinja/assets/demo.gif" />
|
|
65
|
-
</p>
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
<!-- PDOC_START -->
|
|
70
|
-
|
|
71
|
-
makejinja can be used to automatically generate files from [Jinja templates](https://jinja.palletsprojects.com/en/3.1.x/templates).
|
|
72
|
-
It is conceptually similar to [Ansible templates](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html) since both are built on top of Jinja.
|
|
73
|
-
However, makejinja is a standalone tool that can be used without Ansible and offers some advanced features like custom plugins.
|
|
74
|
-
|
|
75
|
-
A popular use case is generating config files for [Home Assistant](https://www.home-assistant.io/):
|
|
76
|
-
Using the same Jinja language that Home Assistant's built-in templates use, you can greatly simplify your configuration management.
|
|
77
|
-
makejinja's custom delimiter support prevents conflicts with Home Assistant's own template syntax, while file-specific data loading enables modular dashboard and automation generation.
|
|
78
|
-
Our comprehensive [Home Assistant example](https://github.com/mirkolenz/makejinja/tree/main/tests/data) demonstrates dashboard generation with custom plugins, multiple data sources, and advanced template organization.
|
|
79
|
-
|
|
80
|
-
## Key Features
|
|
81
|
-
|
|
82
|
-
- **Multi-Source Data Integration**: Load variables from YAML, TOML, and Python files, with support for file-specific data sources and runtime variable injection.
|
|
83
|
-
- **Custom Template Delimiters**: Configure Jinja delimiters (e.g., `<% %>` instead of `{{ }}`) to avoid conflicts with target file formats like Home Assistant, Kubernetes, or Terraform.
|
|
84
|
-
- **Flexible Directory Processing**: Process multiple input directories with complex nested structures, preserving hierarchy while applying powerful template transformations.
|
|
85
|
-
- **Extensible Plugin System**: Create custom [plugins](https://mirkolenz.github.io/makejinja/makejinja/plugin.html#Plugin) with filters, functions, and path filtering logic for specialized requirements.
|
|
86
|
-
- **Production-Ready**: Comprehensive CLI interface, configuration file support, and Python library API for seamless workflow integration.
|
|
87
|
-
|
|
88
|
-
## Use Cases
|
|
89
|
-
|
|
90
|
-
- **Configuration Management**: Generate environment-specific configs (dev/staging/prod) from shared templates with different data sources.
|
|
91
|
-
- **Home Assistant Dashboards**: Create complex dashboards and automations using Jinja syntax without conflicts. See our [complete example](https://github.com/mirkolenz/makejinja/tree/main/tests/data).
|
|
92
|
-
- **Infrastructure as Code**: Generate Kubernetes manifests, Terraform modules, or Docker Compose files with consistent patterns across environments.
|
|
93
|
-
- **Web Development**: Generate HTML pages, CSS files, or JavaScript configs from data sources for static sites or multi-tenant applications.
|
|
94
|
-
- **Database Schemas**: Create SQL migration scripts, database configurations, or ORM models based on structured schema definitions.
|
|
95
|
-
- **Network Configuration**: Generate router configs, firewall rules, or network device settings from centralized network topology data.
|
|
96
|
-
- **Monitoring & Alerting**: Create Grafana dashboards, Prometheus rules, or alerting configurations from service inventories.
|
|
97
|
-
- **Documentation & CI/CD**: Create project docs, API specifications, or pipeline definitions from structured data sources.
|
|
98
|
-
|
|
99
|
-
## Installation
|
|
100
|
-
|
|
101
|
-
The tool is written in Python and can be installed via uv, nix, and docker.
|
|
102
|
-
It can be used as a CLI tool or as a Python library.
|
|
103
|
-
|
|
104
|
-
### UV
|
|
105
|
-
|
|
106
|
-
makejinja is available on [PyPI](https://pypi.org/project/makejinja/) and can be installed via `uv`:
|
|
107
|
-
|
|
108
|
-
```shell
|
|
109
|
-
uv tool install makejinja
|
|
110
|
-
makejinja -i ./input -o ./output
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Nix
|
|
114
|
-
|
|
115
|
-
makejinja is packaged in nixpkgs.
|
|
116
|
-
To use the most recent version, you can run it via `nix run`:
|
|
117
|
-
|
|
118
|
-
```shell
|
|
119
|
-
nix run github:mirkolenz/makejinja -- -i ./input -o ./output
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Alternatively, you can add this repository as an input to your flake and use `makejinja.packages.${system}.default`.
|
|
123
|
-
|
|
124
|
-
### Docker
|
|
125
|
-
|
|
126
|
-
We automatically publish an image to the [GitHub Container Registry](https://ghcr.io/mirkolenz/makejinja).
|
|
127
|
-
To use it, mount a directory to the container and pass the options as the command:
|
|
128
|
-
|
|
129
|
-
```shell
|
|
130
|
-
docker run --rm -v $(pwd)/data:/data ghcr.io/mirkolenz/makejinja:latest -i /data/input -o /data/output
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Usage in Terminal / Command Line
|
|
134
|
-
|
|
135
|
-
In its default configuration, makejinja searches the input directory recursively for files ending in `.jinja`.
|
|
136
|
-
It then renders these files and writes them to the output directory, preserving the directory structure.
|
|
137
|
-
Our [documentation](https://mirkolenz.github.io/makejinja/makejinja/cli.html) contains a detailed description of all options and can also be accessed via `makejinja --help`.
|
makejinja-2.8.1/setup.cfg
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
from collections import abc
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import Any, Protocol
|
|
4
|
-
|
|
5
|
-
from jinja2 import Environment
|
|
6
|
-
from jinja2.ext import Extension
|
|
7
|
-
|
|
8
|
-
from makejinja.config import Config
|
|
9
|
-
|
|
10
|
-
__all__ = ["Plugin"]
|
|
11
|
-
|
|
12
|
-
Extensions = abc.Sequence[type[Extension]]
|
|
13
|
-
Filter = abc.Callable[[Any], Any]
|
|
14
|
-
Filters = abc.Sequence[Filter]
|
|
15
|
-
Function = abc.Callable[..., Any]
|
|
16
|
-
Functions = abc.Sequence[Function]
|
|
17
|
-
Test = abc.Callable[..., Any]
|
|
18
|
-
Tests = abc.Sequence[Test]
|
|
19
|
-
Policies = abc.Mapping[str, Any]
|
|
20
|
-
MutableData = abc.MutableMapping[str, Any]
|
|
21
|
-
Data = abc.Mapping[str, Any]
|
|
22
|
-
PathFilter = abc.Callable[[Path], bool]
|
|
23
|
-
PathFilters = abc.Sequence[PathFilter]
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class Plugin(Protocol):
|
|
27
|
-
"""Extend the functionality of makejinja with a plugin implementing a subset of this protocol."""
|
|
28
|
-
|
|
29
|
-
def __init__(self, *, env: Environment, data: Data, config: Config) -> None:
|
|
30
|
-
pass
|
|
31
|
-
|
|
32
|
-
def functions(self) -> Functions:
|
|
33
|
-
return []
|
|
34
|
-
|
|
35
|
-
def data(self) -> Data:
|
|
36
|
-
return {}
|
|
37
|
-
|
|
38
|
-
def filters(self) -> Filters:
|
|
39
|
-
return []
|
|
40
|
-
|
|
41
|
-
def tests(self) -> Tests:
|
|
42
|
-
return []
|
|
43
|
-
|
|
44
|
-
def policies(self) -> Policies:
|
|
45
|
-
return {}
|
|
46
|
-
|
|
47
|
-
def extensions(self) -> Extensions:
|
|
48
|
-
return []
|
|
49
|
-
|
|
50
|
-
def path_filters(self) -> PathFilters:
|
|
51
|
-
return []
|
|
52
|
-
|
|
53
|
-
# Deprecated: Use functions() and data() instead
|
|
54
|
-
def globals(self) -> Functions:
|
|
55
|
-
return []
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
AbstractLoader = Plugin
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
pyproject.toml
|
|
4
|
-
src/makejinja/__init__.py
|
|
5
|
-
src/makejinja/__main__.py
|
|
6
|
-
src/makejinja/app.py
|
|
7
|
-
src/makejinja/cli.py
|
|
8
|
-
src/makejinja/config.py
|
|
9
|
-
src/makejinja/plugin.py
|
|
10
|
-
src/makejinja/py.typed
|
|
11
|
-
src/makejinja.egg-info/PKG-INFO
|
|
12
|
-
src/makejinja.egg-info/SOURCES.txt
|
|
13
|
-
src/makejinja.egg-info/dependency_links.txt
|
|
14
|
-
src/makejinja.egg-info/entry_points.txt
|
|
15
|
-
src/makejinja.egg-info/requires.txt
|
|
16
|
-
src/makejinja.egg-info/top_level.txt
|
|
17
|
-
tests/test_makejinja.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
makejinja
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
import pytest
|
|
5
|
-
from click.testing import CliRunner
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@dataclass(slots=True, frozen=True)
|
|
9
|
-
class MakejinjaPaths:
|
|
10
|
-
"""Paths used in makejinja test execution.
|
|
11
|
-
|
|
12
|
-
Attributes:
|
|
13
|
-
input: Path to input template directories
|
|
14
|
-
baseline: Path to expected output files
|
|
15
|
-
output: Path to actual generated output files
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
input: Path
|
|
19
|
-
baseline: Path
|
|
20
|
-
output: Path
|
|
21
|
-
|
|
22
|
-
def __repr__(self) -> str:
|
|
23
|
-
return "makejinja"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@pytest.fixture(scope="session")
|
|
27
|
-
def test_run(tmp_path_factory: pytest.TempPathFactory) -> MakejinjaPaths:
|
|
28
|
-
"""Execute makejinja on test data and return paths to input, expected, and actual output."""
|
|
29
|
-
assert __package__ is not None
|
|
30
|
-
data_path = Path(__package__, "data")
|
|
31
|
-
input_path = data_path / "input"
|
|
32
|
-
baseline_path = data_path / "output"
|
|
33
|
-
output_path = tmp_path_factory.mktemp("data")
|
|
34
|
-
|
|
35
|
-
with pytest.MonkeyPatch.context() as m:
|
|
36
|
-
m.chdir(data_path)
|
|
37
|
-
runner = CliRunner()
|
|
38
|
-
|
|
39
|
-
# Need to import it AFTER chdir
|
|
40
|
-
from makejinja.cli import makejinja_cli
|
|
41
|
-
|
|
42
|
-
runner.invoke(
|
|
43
|
-
makejinja_cli,
|
|
44
|
-
[
|
|
45
|
-
# Override it here to use our tmp_path
|
|
46
|
-
"--output",
|
|
47
|
-
str(output_path),
|
|
48
|
-
],
|
|
49
|
-
catch_exceptions=False,
|
|
50
|
-
color=True,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
return MakejinjaPaths(input_path, baseline_path, output_path)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _dir_content(path: Path) -> set[Path]:
|
|
57
|
-
return {item.relative_to(path) for item in path.rglob("*")}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def test_output_structure(test_run: MakejinjaPaths):
|
|
61
|
-
"""Test that makejinja generates the expected directory structure."""
|
|
62
|
-
expected_files = _dir_content(test_run.baseline)
|
|
63
|
-
actual_files = _dir_content(test_run.output)
|
|
64
|
-
|
|
65
|
-
if expected_files != actual_files:
|
|
66
|
-
missing = sorted(expected_files - actual_files)
|
|
67
|
-
extra = sorted(actual_files - expected_files)
|
|
68
|
-
pytest.fail(f"Directory structure mismatch. Missing: {missing}, Extra: {extra}")
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def test_template_rendering(test_run: MakejinjaPaths):
|
|
72
|
-
"""Test that makejinja renders all templates with correct content."""
|
|
73
|
-
paths = _dir_content(test_run.baseline)
|
|
74
|
-
|
|
75
|
-
for item in paths:
|
|
76
|
-
baseline_path = test_run.baseline / item
|
|
77
|
-
output_path = test_run.output / item
|
|
78
|
-
|
|
79
|
-
if baseline_path.is_file() and output_path.is_file():
|
|
80
|
-
expected_content = baseline_path.read_text()
|
|
81
|
-
actual_content = output_path.read_text()
|
|
82
|
-
|
|
83
|
-
if expected_content.strip() != actual_content.strip():
|
|
84
|
-
pytest.fail(
|
|
85
|
-
f"Content mismatch in {item}: expected '{expected_content.strip()}', got '{actual_content.strip()}'"
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def test_custom_delimiters(test_run: MakejinjaPaths):
|
|
90
|
-
"""Test that custom Jinja delimiters (<< >>, <% %>) work correctly."""
|
|
91
|
-
not_empty_file = test_run.output / "not-empty.yaml"
|
|
92
|
-
content = not_empty_file.read_text()
|
|
93
|
-
|
|
94
|
-
assert "NOT EMPTY" in content, (
|
|
95
|
-
f"Custom delimiters not working. Expected 'NOT EMPTY' in {not_empty_file}"
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def test_file_specific_data(test_run: MakejinjaPaths):
|
|
100
|
-
"""Test that file-specific data loading works correctly."""
|
|
101
|
-
file_specific_output = test_run.output / "file-specific.yaml"
|
|
102
|
-
content = file_specific_output.read_text()
|
|
103
|
-
|
|
104
|
-
# Should contain data from data1.yaml
|
|
105
|
-
assert "Value from data1" in content, (
|
|
106
|
-
f"File-specific data not loaded. Expected 'Value from data1' in {file_specific_output}"
|
|
107
|
-
)
|
|
108
|
-
assert "item1" in content and "item2" in content, (
|
|
109
|
-
f"File-specific list data not loaded correctly in {file_specific_output}"
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def test_plugin_features(test_run: MakejinjaPaths):
|
|
114
|
-
"""Test that custom plugin filters and functions work."""
|
|
115
|
-
# Test that secret files are excluded by plugin path filter
|
|
116
|
-
secret_file = test_run.output / "secret.yaml"
|
|
117
|
-
assert not secret_file.exists(), (
|
|
118
|
-
f"Secret file should be excluded by plugin but exists: {secret_file}"
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def test_partial_inclusion(test_run: MakejinjaPaths):
|
|
123
|
-
"""Test that partial template inclusion works."""
|
|
124
|
-
ui_lovelace_file = test_run.output / "ui-lovelace.yaml"
|
|
125
|
-
content = ui_lovelace_file.read_text()
|
|
126
|
-
|
|
127
|
-
# Should contain content from include.yaml.partial
|
|
128
|
-
assert "icon: mdi:home" in content, (
|
|
129
|
-
f"Partial inclusion not working. Expected 'icon: mdi:home' in {ui_lovelace_file}"
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def test_empty_template_handling(test_run: MakejinjaPaths):
|
|
134
|
-
"""Test that empty templates are handled correctly."""
|
|
135
|
-
# The empty.yaml.jinja should not produce output (contains only comments)
|
|
136
|
-
empty_file = test_run.output / "empty.yaml"
|
|
137
|
-
assert not empty_file.exists(), (
|
|
138
|
-
f"Empty template should not generate output file but {empty_file} exists"
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def test_nested_directory_processing(test_run: MakejinjaPaths):
|
|
143
|
-
"""Test that nested directories are processed correctly."""
|
|
144
|
-
nested_file = test_run.output / "views" / "home.yaml"
|
|
145
|
-
assert nested_file.exists(), (
|
|
146
|
-
f"Nested directory processing failed. Expected {nested_file} to exist"
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
content = nested_file.read_text()
|
|
150
|
-
assert len(content.strip()) > 0, (
|
|
151
|
-
f"Nested template should have content but {nested_file} is empty"
|
|
152
|
-
)
|
|
File without changes
|
|
File without changes
|