From 5f8ea85dd567cf1c1d7964bb2474236ed12ddb40 Mon Sep 17 00:00:00 2001 From: George Spalding Date: Mon, 25 Mar 2019 10:54:12 +0100 Subject: [PATCH 1/5] #75 Add -i --includes option that extends the paths to search for jinja templates This is useful for reuse when a set of shared templates are maintained in another directory by using {% include 'lib/header.j2' %} style includes. --- .gitignore | 2 ++ README.md | 7 ++++++- jinja2cli/cli.py | 13 ++++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9f431dd..55a2fb1 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ nosetests.xml .pytest_cache dist/ +env/ +*.iml \ No newline at end of file diff --git a/README.md b/README.md index 13579e6..e38ccbb 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,20 @@ 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 + -I INCLUDES, --includes=INCLUDES + extra jinja2 template directory to search for + (included) templates -D key=value Define template variable in the form of key=value -s SECTION, --section=SECTION Use only this section from the configuration --strict Disallow undefined variables to be used within the template + -o FILE, --outfile=FILE + File to use for output. Default is stdout. ``` ## Optional YAML support diff --git a/jinja2cli/cli.py b/jinja2cli/cli.py index 95a2b68..619c69d 100644 --- a/jinja2cli/cli.py +++ b/jinja2cli/cli.py @@ -212,11 +212,11 @@ def _parse_env(data): } -def render(template_path, data, extensions, strict=False): +def render(template_path, data, extensions, strict=False, includes=[]): from jinja2 import Environment, FileSystemLoader, StrictUndefined env = Environment( - loader=FileSystemLoader(os.path.dirname(template_path)), + loader=FileSystemLoader([os.path.dirname(template_path)] + includes), extensions=extensions, keep_trailing_newline=True, ) @@ -311,7 +311,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, includes=opts.includes)) out.flush() return 0 @@ -378,6 +378,13 @@ def main(): action="append", default=["do", "with_", "autoescape", "loopcontrols"], ) + parser.add_option( + "-I", + "--includes", + help="extra jinja2 template directory to search for (included) templates", + dest="includes", + action="append", + ) parser.add_option( "-D", help="Define template variable in the form of key=value", From d8d2fd648b78fc3a7260310d80e7fa36a21498ff Mon Sep 17 00:00:00 2001 From: George Spalding Date: Thu, 28 Mar 2019 00:49:22 +0100 Subject: [PATCH 2/5] Bugfix for when -I is not given on the cli --- jinja2cli/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jinja2cli/cli.py b/jinja2cli/cli.py index 619c69d..deb831f 100644 --- a/jinja2cli/cli.py +++ b/jinja2cli/cli.py @@ -384,6 +384,7 @@ def main(): help="extra jinja2 template directory to search for (included) templates", dest="includes", action="append", + default=[], ) parser.add_option( "-D", From 591d538ec0ab7bdb9959923cd019a850d899dbf8 Mon Sep 17 00:00:00 2001 From: George Spalding Date: Tue, 17 Aug 2021 16:33:29 +0200 Subject: [PATCH 3/5] Avoid using mutable default value in parameter See also https://github.com/mattrobenolt/jinja2-cli/pull/76#pullrequestreview-731755068 > bad idea to pass empty list as a default argument > explanation here: https://web.archive.org/web/20200221224620/http://effbot.org/zone/default-values.htm > should be something like that: > > ``` > def render(template_path, data, extensions, strict=False, includes=None): > includes = [] if includes is None else includes > ``` --- jinja2cli/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jinja2cli/cli.py b/jinja2cli/cli.py index deb831f..552dc02 100644 --- a/jinja2cli/cli.py +++ b/jinja2cli/cli.py @@ -212,7 +212,9 @@ def _parse_env(data): } -def render(template_path, data, extensions, strict=False, includes=[]): +def render(template_path, data, extensions, strict=False, includes=None): + includes = [] if includes is None else includes + from jinja2 import Environment, FileSystemLoader, StrictUndefined env = Environment( From e824bf7a570d3fc45ee96231425235cf7eaf8eca Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 11 Sep 2021 17:34:15 +0300 Subject: [PATCH 4/5] fix(tests): add tests that call jinja2-cli cmd with and without --includes flag; some autopeping from tools; add .python-version (pyenv) to .gitignore --- .gitignore | 3 +- jinja2cli/cli.py | 6 ++- tests/files/maindir_template.j2 | 1 + tests/files/subdir/subdir_template.j2 | 1 + tests/test_jinja2cli.py | 59 +++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/files/maindir_template.j2 create mode 100644 tests/files/subdir/subdir_template.j2 diff --git a/.gitignore b/.gitignore index 55a2fb1..8308954 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ nosetests.xml .pytest_cache dist/ env/ -*.iml \ No newline at end of file +*.iml +.python-version diff --git a/jinja2cli/cli.py b/jinja2cli/cli.py index 552dc02..b84abf6 100644 --- a/jinja2cli/cli.py +++ b/jinja2cli/cli.py @@ -142,7 +142,7 @@ def _load_querystring(): import urllib.parse as urlparse def _parse_qs(data): - """ Extend urlparse to allow objects in dot syntax. + """Extend urlparse to allow objects in dot syntax. >>> _parse_qs('user.first_name=Matt&user.last_name=Robenolt') {'user': {'first_name': 'Matt', 'last_name': 'Robenolt'}} @@ -313,7 +313,9 @@ def cli(opts, args): out = codecs.getwriter("utf8")(out) - out.write(render(template_path, data, extensions, opts.strict, includes=opts.includes)) + out.write( + render(template_path, data, extensions, opts.strict, includes=opts.includes) + ) out.flush() return 0 diff --git a/tests/files/maindir_template.j2 b/tests/files/maindir_template.j2 new file mode 100644 index 0000000..7917609 --- /dev/null +++ b/tests/files/maindir_template.j2 @@ -0,0 +1 @@ +{% include 'template.j2' %} diff --git a/tests/files/subdir/subdir_template.j2 b/tests/files/subdir/subdir_template.j2 new file mode 100644 index 0000000..7917609 --- /dev/null +++ b/tests/files/subdir/subdir_template.j2 @@ -0,0 +1 @@ +{% include 'template.j2' %} diff --git a/tests/test_jinja2cli.py b/tests/test_jinja2cli.py index 181a8bd..0204cee 100644 --- a/tests/test_jinja2cli.py +++ b/tests/test_jinja2cli.py @@ -1,4 +1,6 @@ import os +import json +import subprocess from jinja2cli import cli @@ -23,3 +25,60 @@ def test_absolute_path(): output = cli.render(path, {"title": title}, []) assert output == title assert type(output) == cli.text_type + + +def test_sys_call_succeeds(): + template = "files/maindir_template.j2" + title = "Greatest Title 1" + ctx = json.dumps({"title": title}) + echo_proc = subprocess.Popen(["echo", ctx], stdout=subprocess.PIPE, shell=False) + jinja_proc = subprocess.Popen( + ["jinja2", template], + stdin=echo_proc.stdout, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=False, + ) + assert jinja_proc.wait() == 0 + assert jinja_proc.stderr.readlines() == [] + assert jinja_proc.stdout.readlines() == [(title + "\n").encode()] + + +def test_sys_call_fails_without_includes_flag(): + template = "files/subdir/subdir_template.j2" + title = "Greatest Title 2" + context = json.dumps({"title": title}) + echo_args = ["echo", context] + jinja_args = ["jinja2", template] + echo_proc = subprocess.Popen(echo_args, stdout=subprocess.PIPE, shell=False) + jinja_proc = subprocess.Popen( + jinja_args, + stdin=echo_proc.stdout, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=False, + ) + assert jinja_proc.wait() == 1 + jinja_err = jinja_proc.stderr.readlines() + assert "jinja2.exceptions.TemplateNotFound" in jinja_err[-1].decode("utf-8") + assert jinja_proc.stdout.readlines() == [] + + +def test_sys_call_succeeds_with_includes_flag(): + template = "files/subdir/subdir_template.j2" + title = "Greatest Title 3" + ctx = json.dumps({"title": title}) + template_root = "files" + echo_args = ["echo", ctx] + jinja_args = ["jinja2", template, "--includes", template_root] + echo_proc = subprocess.Popen(echo_args, stdout=subprocess.PIPE, shell=False) + jinja_proc = subprocess.Popen( + jinja_args, + stdin=echo_proc.stdout, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=False, + ) + assert jinja_proc.wait() == 0 + assert jinja_proc.stderr.readlines() == [] + assert jinja_proc.stdout.readlines() == [(title + "\n").encode()] From 1d9219d11e048129562e09fcc936f5d0fc70d980 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 11 Sep 2021 18:29:26 +0300 Subject: [PATCH 5/5] fix(tests): shorter test names --- tests/test_jinja2cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_jinja2cli.py b/tests/test_jinja2cli.py index 0204cee..0b44167 100644 --- a/tests/test_jinja2cli.py +++ b/tests/test_jinja2cli.py @@ -27,7 +27,7 @@ def test_absolute_path(): assert type(output) == cli.text_type -def test_sys_call_succeeds(): +def test_cmd_succeeds(): template = "files/maindir_template.j2" title = "Greatest Title 1" ctx = json.dumps({"title": title}) @@ -44,7 +44,7 @@ def test_sys_call_succeeds(): assert jinja_proc.stdout.readlines() == [(title + "\n").encode()] -def test_sys_call_fails_without_includes_flag(): +def test_cmd_fails_without_includes_flag(): template = "files/subdir/subdir_template.j2" title = "Greatest Title 2" context = json.dumps({"title": title}) @@ -64,7 +64,7 @@ def test_sys_call_fails_without_includes_flag(): assert jinja_proc.stdout.readlines() == [] -def test_sys_call_succeeds_with_includes_flag(): +def test_cmd_succeeds_with_includes_flag(): template = "files/subdir/subdir_template.j2" title = "Greatest Title 3" ctx = json.dumps({"title": title})