From: Armin Ronacher Date: Sat, 1 Jan 2022 21:19:24 +0000 (+0100) Subject: add items filter X-Git-Tag: 3.1.0~9^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1c036cac2fe61486e3bb1cc27474bf3e09ee0cd4;p=thirdparty%2Fjinja.git add items filter --- diff --git a/CHANGES.rst b/CHANGES.rst index 3e8b56e4..8c041f60 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -30,6 +30,7 @@ Unreleased :pr:`1571` - Filters and tests decorated with ``@async_variant`` are pickleable. :pr:`1612` +- Add ``items`` filter. :issue:`1561` Version 3.0.3 diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py index 9ebd9911..80ea6504 100644 --- a/src/jinja2/filters.py +++ b/src/jinja2/filters.py @@ -218,6 +218,36 @@ def do_lower(s: str) -> str: return soft_str(s).lower() +def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]: + """Return an iterator over the ``(key, value)`` items of a mapping. + + ``x|items`` is the same as ``x.items()``, except if ``x`` is + undefined an empty iterator is returned. + + This filter is useful if you expect the template to be rendered with + an implementation of Jinja in another programming language that does + not have a ``.items()`` method on its mapping type. + + .. code-block:: html+jinja + +
+ {% for key, value in my_dict|items %} +
{{ key }} +
{{ value }} + {% endfor %} +
+ + .. versionadded:: 3.1 + """ + if isinstance(value, Undefined): + return + + if not isinstance(value, abc.Mapping): + raise TypeError("Can only get item pairs from a mapping.") + + yield from value.items() + + @pass_eval_context def do_xmlattr( eval_ctx: "EvalContext", d: t.Mapping[str, t.Any], autospace: bool = True @@ -1739,6 +1769,7 @@ FILTERS = { "length": len, "list": do_list, "lower": do_lower, + "items": do_items, "map": do_map, "min": do_min, "max": do_max, diff --git a/tests/test_filters.py b/tests/test_filters.py index 2195157c..43ddf59c 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -251,6 +251,17 @@ class TestFilter: out = tmpl.render() assert out == "foo" + def test_items(self, env): + d = {i: c for i, c in enumerate("abc")} + tmpl = env.from_string("""{{ d|items|list }}""") + out = tmpl.render(d=d) + assert out == "[(0, 'a'), (1, 'b'), (2, 'c')]" + + def test_items_undefined(self, env): + tmpl = env.from_string("""{{ d|items|list }}""") + out = tmpl.render() + assert out == "[]" + def test_pprint(self, env): from pprint import pformat