diff --git a/README.md b/README.md index 13579e6..9a4a88d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Usage: jinja2 [options] Options: --version show program's version number and exit -h, --help show this help message and exit - --format=FORMAT format of input variables: auto, ini, json, + --format=FORMAT format of input variables: auto, env, ini, json, querystring, yaml, yml -e EXTENSIONS, --extension=EXTENSIONS extra jinja2 extensions to load @@ -26,8 +26,16 @@ Options: Use only this section from the configuration --strict Disallow undefined variables to be used within the template + -a, --ansible Enable support for Ansible filters + -o FILE, --outfile=FILE + File to use for output. Default is stdout. ``` +## Optional Ansible support +If `Ansible Core` is present, you can use Ansible filters within your templates. + +`$ pip install jinja2-cli[ansible]` + ## Optional YAML support If `PyYAML` is present, you can use YAML as an input data source. diff --git a/jinja2cli/cli.py b/jinja2cli/cli.py index fa57ad9..d161c46 100644 --- a/jinja2cli/cli.py +++ b/jinja2cli/cli.py @@ -220,7 +220,55 @@ def _parse_env(data): } -def render(template_path, data, extensions, strict=False): +def _ansible_filter_loader(base_module, module_path=None): + from pkgutil import iter_modules + from jinja2.utils import import_string + + if module_path is not None: + try: + parent_module = import_string(f"{base_module.__name__}.{module_path}.plugins.filter") + except ImportError: + print(f"Could not find collection '{module_path}', did you mean 'ansible.{module_path}'?") + raise + else: + parent_module = import_string(f"{base_module.__name__}.plugins.filter") + + for module in iter_modules(parent_module.__path__): + filter_module = import_string(f"{parent_module.__name__}.{module.name}") + yield filter_module.FilterModule().filters() + + +def _load_ansible_filters(collections=None): + try: + import ansible + except ImportError: + print("This feature requires the `ansible-core` package.") + raise + + filters = dict() + for filter in _ansible_filter_loader(ansible): + filters.update(filter) + + if collections is None: + return filters + + collections_paths = os.environ.get("ANSIBLE_COLLECTIONS") or \ + f"{os.path.expanduser('~/.ansible/collections')}:/usr/share/ansible/collections" + sys.path.extend(collections_paths.split(':')) + try: + import ansible_collections + except ImportError: + print("Could not find the `ansible_collections` module, please check the ANSIBLE_COLLECTIONS environment variable.") + raise + + for collection in collections: + for filter in _ansible_filter_loader(ansible_collections, collection): + filters.update(filter) + + return filters + + +def render(template_path, data, extensions, strict=False, ansible=None): from jinja2 import ( __version__ as jinja_version, Environment, @@ -246,6 +294,10 @@ def render(template_path, data, extensions, strict=False): ) if strict: env.undefined = StrictUndefined + if ansible: + collections = set(ansible) + collections.discard('core') # only used because optparse doesn't support 0+ values + env.filters.update(_load_ansible_filters(collections)) # Add environ global env.globals["environ"] = lambda key: force_text(os.environ.get(key)) @@ -336,7 +388,7 @@ def cli(opts, args): out = codecs.getwriter("utf8")(out) - out.write(render(template_path, data, extensions, opts.strict)) + out.write(render(template_path, data, extensions, opts.strict, opts.ansible)) out.flush() return 0 @@ -422,6 +474,14 @@ def main(): dest="strict", action="store_true", ) + parser.add_option( + "-a", + "--ansible", + help="Enable support for Ansible filters (--ansible core or --ansible )", + dest="ansible", + action="append", + default=[], + ) parser.add_option( "-o", "--outfile", diff --git a/setup.py b/setup.py index ccade5d..c4bd972 100755 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ license="BSD", install_requires=install_requires, extras_require={ + "ansible": install_requires + ["ansible-core"], "yaml": install_requires + ["pyyaml"], "toml": install_requires + ["toml"], "xml": install_requires + ["xmltodict"],