diff options
author | David Lord <davidism@gmail.com> | 2022-03-07 08:32:45 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-07 08:32:45 -0800 |
commit | a819eb37bd182a4e668ab2299f303630df0d7973 (patch) | |
tree | 273500fdb8ce764fb84855c8b9ac3230cbed64f5 | |
parent | d5b8f668f392710f80c3c62ad8b70ea7a8bbd44b (diff) | |
parent | 1c036cac2fe61486e3bb1cc27474bf3e09ee0cd4 (diff) | |
download | jinja2-a819eb37bd182a4e668ab2299f303630df0d7973.tar.gz |
Merge pull request #1564 from pallets/feature/pairs
Added items filter
-rw-r--r-- | CHANGES.rst | 1 | ||||
-rw-r--r-- | src/jinja2/filters.py | 31 | ||||
-rw-r--r-- | tests/test_filters.py | 11 |
3 files changed, 43 insertions, 0 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 3e8b56e..8c041f6 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 9ebd991..80ea650 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 + + <dl> + {% for key, value in my_dict|items %} + <dt>{{ key }} + <dd>{{ value }} + {% endfor %} + </dl> + + .. 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 2195157..43ddf59 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 |