]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
add items filter 1564/head
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 1 Jan 2022 21:19:24 +0000 (22:19 +0100)
committerDavid Lord <davidism@gmail.com>
Mon, 7 Mar 2022 16:29:16 +0000 (08:29 -0800)
CHANGES.rst
src/jinja2/filters.py
tests/test_filters.py

index 3e8b56e475440ddfd7ac861a69ce63bba1fb049d..8c041f60de9a92e72aed53a006ef481f94ef03dd 100644 (file)
@@ -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
index 9ebd9911cb4623b194908691f2a9c7fbe00251e5..80ea6504e3cec85baa3ea49534f35c047cd6b39b 100644 (file)
@@ -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,
index 2195157c4f1d8f5ed647ab2ea1ed30dad2735228..43ddf59cfdad9896015b466651fc148fa8a89e33 100644 (file)
@@ -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