{"id":13501227,"url":"https://github.com/kpfleming/jinjanator","last_synced_at":"2025-12-24T13:44:11.962Z","repository":{"id":181329703,"uuid":"663098683","full_name":"kpfleming/jinjanator","owner":"kpfleming","description":"Jinja2 Command-Line Tool, reworked, again","archived":false,"fork":false,"pushed_at":"2025-05-01T10:56:21.000Z","size":203,"stargazers_count":95,"open_issues_count":2,"forks_count":7,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-01T11:34:23.662Z","etag":null,"topics":["cli","jinja2","jinja2-cli","python","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"kolypto/j2cli","license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kpfleming.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.apache-2.0","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"kpfleming","liberapay":"kpfleming"}},"created_at":"2023-07-06T14:47:14.000Z","updated_at":"2025-05-01T10:56:24.000Z","dependencies_parsed_at":"2024-03-09T13:25:10.540Z","dependency_job_id":"1f5da5b5-3cad-42f8-a832-81fc08b09989","html_url":"https://github.com/kpfleming/jinjanator","commit_stats":null,"previous_names":["kpfleming/j2cli","kpfleming/jinjanator"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpfleming%2Fjinjanator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpfleming%2Fjinjanator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpfleming%2Fjinjanator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kpfleming%2Fjinjanator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kpfleming","download_url":"https://codeload.github.com/kpfleming/jinjanator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254485057,"owners_count":22078767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","jinja2","jinja2-cli","python","python3"],"created_at":"2024-07-31T22:01:29.865Z","updated_at":"2025-12-24T13:44:11.956Z","avatar_url":"https://github.com/kpfleming.png","language":"Python","funding_links":["https://github.com/sponsors/kpfleming","https://liberapay.com/kpfleming"],"categories":["Python"],"sub_categories":[],"readme":"# jinjanator\n\n\u003ca href=\"https://opensource.org\"\u003e\u003cimg height=\"150\" align=\"left\" src=\"https://opensource.org/files/OSIApprovedCropped.png\" alt=\"Open Source Initiative Approved License logo\"\u003e\u003c/a\u003e\n[![CI](https://github.com/kpfleming/jinjanator/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/kpfleming/jinjanator/actions/workflows/ci.yml)\n[![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/release/python-31019/)\n[![License - Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-9400d3.svg)](https://spdx.org/licenses/Apache-2.0.html)\n[![Types - Mypy](https://img.shields.io/badge/Types-Mypy-blue.svg)](https://github.com/python/mypy)\n[![Code Style and Quality - Ruff](https://img.shields.io/badge/Code%20Quality-Ruff-red.svg)](https://github.com/astral-sh/ruff)\n[![Project Management - Hatch](https://img.shields.io/badge/Project%20Management-Hatch-purple.svg)](https://github.com/pypa/hatch)\n[![Testing - Pytest](https://img.shields.io/badge/Testing-Pytest-orange.svg)](https://github.com/pytest-dev/pytest)\n\nThis repo contains `jinjanator`, a CLI tool to render\n[Jinja2](https://github.com/pallets/jinja/) templates. It is a fork of\n`j2cli`, which itself was a fork of `jinja2-cli`, both of which are no\nlonger actively maintained.\n\nOpen Source software: [Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html)\n\n## \u0026nbsp;\n\n\u003c!-- fancy-readme start --\u003e\nFeatures:\n\n* Jinja2 templating\n* INI, YAML, JSON data sources supported\n* Environment variables can be used with or without data files\n* Plugins can provide additional formats, filters, tests, extensions and global\n  functions (see\n  [jinjanator-plugins](https://github.com/kpfleming/jinjanator-plugins)\n  for details)\n\n## Installation\n\n```\npip install jinjanator\n```\n\n## Available Plugins\n\n* [jinjanator-plugin-ansible](https://pypi.org/project/jinjanator-plugin-ansible) -\n  makes Ansible's 'core' filters and tests available during template\n  rendering\n* [jinjanator-plugin-format-toml](https://pypi.org/project/jinjanator-plugin-format-toml) -\n  provides a TOML parser for input data files\n* [jinjanator-plugin-format-xml](https://pypi.org/project/jinjanator-plugin-format-xml) -\n  provides an XML parser for input data files\n\n## Tutorial\n\nSuppose you have an NGINX configuration file template, `nginx.j2`:\n\n```jinja2\nserver {\n  listen 80;\n  server_name {{ nginx.hostname }};\n\n  root {{ nginx.webroot }};\n  index index.htm;\n}\n```\n\nAnd you have a JSON file with the data, `nginx.json`:\n\n```json\n{\n    \"nginx\":{\n        \"hostname\": \"localhost\",\n        \"webroot\": \"/var/www/project\"\n    }\n}\n```\n\nThis is how you render it into a working configuration file:\n\n```bash\n$ jinjanate nginx.j2 nginx.json \u003e nginx.conf\n```\n\nThe output is saved to `nginx.conf`:\n\n```\nserver {\n  listen 80;\n  server_name localhost;\n\n  root /var/www/project;\n  index index.htm;\n}\n```\n\nAlternatively, you can use the `-o nginx.conf` or `--output-file\nnginx.conf`options to write directly to the file.\n\n## Tutorial with environment variables\n\nSuppose, you have a very simple template, `person.xml.j2`:\n\n```jinja2\n\u003cdata\u003e\u003cname\u003e{{ name }}\u003c/name\u003e\u003cage\u003e{{ age }}\u003c/age\u003e\u003c/data\u003e\n```\n\nWhat is the easiest way to use jinjanator here?\nUse environment variables in your Bash script:\n\n```bash\n$ export name=Andrew\n$ export age=31\n$ jinjanate /tmp/person.xml.j2\n\u003cdata\u003e\u003cname\u003eAndrew\u003c/name\u003e\u003cage\u003e31\u003c/age\u003e\u003c/data\u003e\n```\n\n## Using environment variables\n\nEven when you use a data file as the data source, you can always\naccess environment variables using the `env()` function:\n\n```jinja2\nUsername: {{ login }}\nPassword: {{ env(\"APP_PASSWORD\") }}\n```\n\nOr, if you prefer, as a filter:\n\n```jinja2\nUsername: {{ login }}\nPassword: {{ \"APP_PASSWORD\" | env }}\n```\n\n## CLI Reference\n`jinjanate` accepts the following arguments:\n\n* `template`: Jinja2 template file to render\n* `data`: (optional) path to the data used for rendering.\n    The default is `-`: use stdin.\n\nThere is some special behavior with environment variables:\n\n* When `data` is not provided (data is `-`), `--format` defaults to\n  `env` and thus reads environment variables.\n\n### Options:\n\n* `--format FMT, -f FMT`: format for the data file. The default is\n  `?`: guess from file extension. Supported formats are YAML (.yaml or\n  .yml), JSON (.json), INI (.ini), and dotenv (.env), plus any formats\n  provided by plugins you have installed.\n* `--format-option OPT`: option to be passed to the parser for the\n  data format selected with `--format` (or auto-selected). This can be\n  specified multiple times. Refer to the documentation for the format\n  itself to learn whether it supports any options.\n* `--help, -h`: generates a help message describing usage of the tool.\n* `--import-env VAR, -e VAR`: import all environment variables into\n    the template as `VAR`.  To import environment variables into the\n    global scope, give it an empty string: `--import-env=`.  (This\n    will overwrite any existing variables with the same names!)\n* `--output-file OUTFILE, -o OUTFILE`: Write rendered template to a\n  file.\n* `--quiet`: Avoid generating any output on stderr.\n* `--undefined`: Allow undefined variables to be used in templates (no\n  error will be raised).\n* `--version`: prints the version of the tool and the Jinja2 package installed.\n\n### Customization Options:\n\nFor details on the behavior of these options, see the\n[Customization](#customization) section.\n\n* `--filters PYTHON_FILE` - specify a file of Python source code,\n  containing additional Jinja2 filters as simple functions. You can\n  use this option more than once to include multiple files.\n\n  * NOTE: While this option's behavior matches the `j2cli`\n    documentation, it does not match the `j2cli` implementation. If\n    you are migrating from `j2cli` and use more than one filters file,\n    you will need to specify this option once for each file.\n\n* `--tests PYTHON_FILE` - specify a file of Python source code,\n  containing additional Jinja2 tests as simple functions. You can use\n  this option more than once to include multiple files.\n\n  * NOTE: While this option's behavior matches the `j2cli`\n    documentation, it does not match the `j2cli` implementation. If\n    you are migrating from `j2cli` and use more than one tests file,\n    you will need to specify this option once for each file.\n\n* `--customize PYTHON_FILE` - specify a file of Python source code\n  containing customization functions. This file can modify the Jinja2\n  context, add filters/tests, or change Jinja2's configuration. Unlike\n  `--filters` and `--tests`, this option can only be specified once.\n\n## Usage Examples\n\nRender a template using INI-file data source:\n\n    $ jinjanate config.j2 data.ini\n\nRender using JSON data source:\n\n    $ jinjanate config.j2 data.json\n\nRender using YAML data source:\n\n    $ jinjanate config.j2 data.yaml\n\nRender using JSON data on stdin:\n\n    $ curl http://example.com/service.json | jinjanate --format=json config.j2 -\n\nRender using environment variables:\n\n    $ jinjanate config.j2\n\nOr use environment variables from a file:\n\n    $ jinjanate config.j2 data.env\n\nOr pipe it: (note that you'll have to use \"-\" in this particular case):\n\n    $ jinjanate --format=env config.j2 - \u003c data.env\n\n\n## Data Formats\n\n### dotenv\nData input from environment variables.\n\n#### Options\n\nThis format does not support any options.\n\n#### Usage\n\nRender directly from the current environment variable values:\n\n    $ jinjanate config.j2\n\nOr alternatively, read the values from a dotenv file:\n\n```\nNGINX_HOSTNAME=localhost\nNGINX_WEBROOT=/var/www/project\nNGINX_LOGS=/var/log/nginx/\n```\n\nAnd render with:\n\n    $ jinjanate config.j2 data.env\n\nOr:\n\n    $ env | jinjanate --format=env config.j2\n\nIf you're going to pipe a dotenv file into `jinjanate`, you'll need to\nuse \"-\" as the second argument:\n\n    $ jinjanate config.j2 - \u003c data.env\n\n### INI\nINI data input format.\n\n#### Options\n\nThis format does not support any options.\n\n#### Usage\n\ndata.ini:\n\n```\n[nginx]\nhostname=localhost\nwebroot=/var/www/project\nlogs=/var/log/nginx\n```\n\nUsage:\n\n    $ jinjanate config.j2 data.ini\n\nOr:\n\n    $ cat data.ini | jinjanate --format=ini config.j2\n\n### JSON\nJSON data input format.\n\n#### Options\n\n* `array-name`: accepts a single string (e.g. `--format-option array-name=foo`), which\n  must be a valid Python identifier and not a Python keyword. If this\n  option is specified, and the JSON data provided is an `array`\n  (sequence, list), the specified name will be used to make the data\n  available to the Jinja2 template. Errors will be generated if\n  `array` data is provided and this option is not specified, or if\n  this option is specified and the data provided is an `object`.\n\n#### Usage\n\ndata.json:\n\n```\n{\n    \"nginx\":{\n        \"hostname\": \"localhost\",\n        \"webroot\": \"/var/www/project\",\n        \"logs\": \"/var/log/nginx\"\n    }\n}\n```\n\nUsage:\n\n    $ jinjanate config.j2 data.json\n\nOr:\n\n    $ cat data.json | jinjanate --format=json config.j2\n\n### YAML\nYAML data input format.\n\n#### Options\n\n* `sequence-name`: accepts a single string (e.g. `--format-option sequence-name=foo`),\n  which must be a valid Python identifier and not a Python keyword. If\n  this option is specified, and the YAML data provided is a `sequence`\n  (array, list), the specified name will be used to make the data\n  available to the Jinja2 template. Errors will be generated if\n  `sequence` data is provided and this option is not specified, or if\n  this option is specified and the data provided is a `mapping`.\n\n#### Usage\n\ndata.yaml:\n\n```\nnginx:\n  hostname: localhost\n  webroot: /var/www/project\n  logs: /var/log/nginx\n```\n\nUsage:\n\n    $ jinjanate config.j2 data.yml\n\nOr:\n\n    $ cat data.yml | jinjanate --format=yaml config.j2\n\n## Filters\n\n### `env(varname, default=None)`\nUse an environment variable's value in the template.\n\nThis filter is available even when your data source is something other\nthan the environment.\n\nExample:\n\n```jinja2\nUser: {{ user_login }}\nPass: {{ \"USER_PASSWORD\" | env }}\n```\n\nYou can provide a default value:\n\n```jinja2\nPass: {{ \"USER_PASSWORD\" | env(\"-none-\") }}\n```\n\nFor your convenience, it's also available as a global function:\n\n```jinja2\nUser: {{ user_login }}\nPass: {{ env(\"USER_PASSWORD\") }}\n```\n\nNotice that there must be quotes around the environment variable name\nwhen it is a literal string.\n\n## Customization\n\nJinjanator supports customizing Jinja2 template processing using two\nmethods - via simple files containing custom filters or tests, or via\na more advanced \"customizations\" file that allows you to do all of the\nabove as well as modify core configuration of the Jinja2 engine.\n\n### Using filters and tests files\n\nThe simplest way to add additional filters or tests is via \"filters\"\nand \"tests\" files. These files contain Python source code consisting\nof simple functions. Each function becomes a filter or test.\n\nExamples:\n\n`filters.py`\n\n```python\n# Simple filters file\n\ndef parentheses(message):\n    \"\"\" Put message in parenthesis \"\"\"\n    return f\"({message})\"\n```\n\n`tests.py`\n\n```python\n#  Example of simple tests file\n\ndef an_odd_number(number):\n    \"\"\" test if number is odd \"\"\"\n    return True if (number % 2) else False\n```\n\nAnd a template that uses them:\n\n```\n{% for x in range(4) %}\n{{x}} is: {% if x is an_odd_number %}\n    {{- \"odd\" | parentheses }}\n  {%- else %}\n    {{- \"even\" | parentheses }}\n  {%- endif %}\n{%- endfor %}\n```\n\nThe output is:\n\n```\n$ jinjanate --filter ./filters.py --test ./tests.py simple.j2\n\n0 is: (even)\n1 is: (odd)\n2 is: (even)\n3 is: (odd)\n```\n\nYou can include multiple functions in each file and/or use multiple\nfiles as needed.\n\n### Using a customizations file\n\nA more advanced way to customize your template processing is by using\na \"customizations\" file.\n\nCustomizations files allow you to:\n\n* Pass additional keywords to the Jinja2 environment\n* Modify the context before it is used for rendering\n* Register custom filters and tests\n\nThis is done through *hooks* that you implement in a customization\nfile in Python code. Each hook is a plain function at the module\nlevel with the exact name as shown below.\n\nThe following hooks are available:\n\n* `j2_environment_params() -\u003e dict`: returns a `dict` of additional parameters for\n  [Jinja2 Environment](https://jinja.pocoo.org/docs/2.10/api/#jinja2.Environment).\n\n* `j2_environment(env: Environment) -\u003e Environment`: lets you\n  customize the `Environment` object.\n\n* `alter_context(context: dict) -\u003e dict`: lets you modify the context\n  variables that are going to be used for template rendering. You can\n  do all sorts of pre-processing here.\n\n* `extra_filters() -\u003e dict`: returns a `dict` with extra filters for\n  Jinja2\n\n* `extra_tests() -\u003e dict`: returns a `dict` with extra tests for\n  Jinja2\n\nAll of them are optional.\n\nThe example `customization.py file` for your reference:\n\n```python\n#\n# Example customization.py file for jinjanator\n# Contains hooks that modify the way Jinja2 is initialized and used\n\ndef j2_environment_params():\n    \"\"\" Extra parameters for the Jinja2 Environment \"\"\"\n    # Jinja2 Environment configuration\n    # https://jinja.pocoo.org/docs/2.10/api/#jinja2.Environment\n    return dict(\n        # Just some examples\n\n        # Change block start/end strings\n        block_start_string='\u003c%',\n        block_end_string='%\u003e',\n        # Change variable strings\n        variable_start_string='\u003c\u003c',\n        variable_end_string='\u003e\u003e',\n        # Remove whitespace around blocks\n        trim_blocks=True,\n        lstrip_blocks=True,\n        # Enable line statements:\n        # http://jinja.pocoo.org/docs/2.10/templates/#line-statements\n        line_statement_prefix='#',\n        # Keep \\n at the end of a file\n        keep_trailing_newline=True,\n        # Enable custom extensions\n        # http://jinja.pocoo.org/docs/2.10/extensions/#jinja-extensions\n        extensions=('jinja2.ext.i18n',),\n    )\n\ndef j2_environment(env):\n    \"\"\" Modify Jinja2 environment\n\n    :param env: jinja2.environment.Environment\n    :rtype: jinja2.environment.Environment\n    \"\"\"\n    env.globals.update(\n        my_function=lambda v: 'my function says \"{}\"'.format(v)\n    )\n    return env\n\ndef alter_context(context):\n    \"\"\" Modify the context and return it \"\"\"\n    # An extra variable\n    context['ADD'] = '127'\n    return context\n\ndef extra_filters():\n    \"\"\" Declare some custom filters.\n\n        Returns: dict(name = function)\n    \"\"\"\n    return dict(\n        # Example: {{ var | parentheses }}\n        parentheses=lambda t: '(' + t + ')',\n    )\n\ndef extra_tests():\n    \"\"\" Declare some custom tests\n\n        Returns: dict(name = function)\n    \"\"\"\n    return dict(\n        # Example: {% if a|int is custom_odd %}odd{% endif %}\n        custom_odd=lambda n: True if (n % 2) else False\n    )\n```\n\n\u003c!-- fancy-readme end --\u003e\n## Chat\n\nIf you'd like to chat with the jinjanator community, join us on\n[Matrix](https://matrix.to/#/#jinjanator:km6g.us)!\n\n## Credits\n\nThis tool was created from [j2cli](https://github.com/kolypto/j2cli),\nwhich itself was created from\n[jinja2-cli](https://github.com/mattrobenolt/jinja2-cli). It was\ncreated to bring the project up to 'modern' Python coding, packaging,\nand project-management standards, and to support plugins to provide\nextensibility.\n\n[\"Standing on the shoulders of\ngiants\"](https://en.wikipedia.org/wiki/Standing_on_the_shoulders_of_giants)\ncould not be more true than it is in the Python community; this\nproject relies on many wonderful tools and libraries produced by the\nglobal open source software community, in addition to Python\nitself. I've listed many of them below, but if I've overlooked any\nplease do not be offended :-)\n\n* [Attrs](https://pypi.org/project/attrs)\n* [Hatch-Fancy-PyPI-Readme](https://pypi.org/project/hatch-fancy-pypi-readme)\n* [Hatch](https://pypi.org/project/hatch)\n* [Jinja2](https://pypi.org/project/jinja2/)\n* [Mypy](https://pypi.org/project/mypy)\n* [Pluggy](https://pypi.org/project/pluggy)\n* [pyproject-fmt](https://pypi.org/project/pyproject-fmt)\n* [Pytest](https://pypi.org/project/pytest)\n* [Ruff](https://pypi.org/project/ruff)\n* [Towncrier](https://pypi.org/project/towncrier)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpfleming%2Fjinjanator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkpfleming%2Fjinjanator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkpfleming%2Fjinjanator/lists"}