summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2022-03-07 08:32:45 -0800
committerGitHub <noreply@github.com>2022-03-07 08:32:45 -0800
commita819eb37bd182a4e668ab2299f303630df0d7973 (patch)
tree273500fdb8ce764fb84855c8b9ac3230cbed64f5
parentd5b8f668f392710f80c3c62ad8b70ea7a8bbd44b (diff)
parent1c036cac2fe61486e3bb1cc27474bf3e09ee0cd4 (diff)
downloadjinja2-a819eb37bd182a4e668ab2299f303630df0d7973.tar.gz
Merge pull request #1564 from pallets/feature/pairs
Added items filter
-rw-r--r--CHANGES.rst1
-rw-r--r--src/jinja2/filters.py31
-rw-r--r--tests/test_filters.py11
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