1e31aef6aSopenharmony_ci"""Built-in template filters used with the ``|`` operator.""" 2e31aef6aSopenharmony_ciimport math 3e31aef6aSopenharmony_ciimport random 4e31aef6aSopenharmony_ciimport re 5e31aef6aSopenharmony_ciimport typing 6e31aef6aSopenharmony_ciimport typing as t 7e31aef6aSopenharmony_cifrom collections import abc 8e31aef6aSopenharmony_cifrom itertools import chain 9e31aef6aSopenharmony_cifrom itertools import groupby 10e31aef6aSopenharmony_ci 11e31aef6aSopenharmony_cifrom markupsafe import escape 12e31aef6aSopenharmony_cifrom markupsafe import Markup 13e31aef6aSopenharmony_cifrom markupsafe import soft_str 14e31aef6aSopenharmony_ci 15e31aef6aSopenharmony_cifrom .async_utils import async_variant 16e31aef6aSopenharmony_cifrom .async_utils import auto_aiter 17e31aef6aSopenharmony_cifrom .async_utils import auto_await 18e31aef6aSopenharmony_cifrom .async_utils import auto_to_list 19e31aef6aSopenharmony_cifrom .exceptions import FilterArgumentError 20e31aef6aSopenharmony_cifrom .runtime import Undefined 21e31aef6aSopenharmony_cifrom .utils import htmlsafe_json_dumps 22e31aef6aSopenharmony_cifrom .utils import pass_context 23e31aef6aSopenharmony_cifrom .utils import pass_environment 24e31aef6aSopenharmony_cifrom .utils import pass_eval_context 25e31aef6aSopenharmony_cifrom .utils import pformat 26e31aef6aSopenharmony_cifrom .utils import url_quote 27e31aef6aSopenharmony_cifrom .utils import urlize 28e31aef6aSopenharmony_ci 29e31aef6aSopenharmony_ciif t.TYPE_CHECKING: 30e31aef6aSopenharmony_ci import typing_extensions as te 31e31aef6aSopenharmony_ci from .environment import Environment 32e31aef6aSopenharmony_ci from .nodes import EvalContext 33e31aef6aSopenharmony_ci from .runtime import Context 34e31aef6aSopenharmony_ci from .sandbox import SandboxedEnvironment # noqa: F401 35e31aef6aSopenharmony_ci 36e31aef6aSopenharmony_ci class HasHTML(te.Protocol): 37e31aef6aSopenharmony_ci def __html__(self) -> str: 38e31aef6aSopenharmony_ci pass 39e31aef6aSopenharmony_ci 40e31aef6aSopenharmony_ci 41e31aef6aSopenharmony_ciF = t.TypeVar("F", bound=t.Callable[..., t.Any]) 42e31aef6aSopenharmony_ciK = t.TypeVar("K") 43e31aef6aSopenharmony_ciV = t.TypeVar("V") 44e31aef6aSopenharmony_ci 45e31aef6aSopenharmony_ci 46e31aef6aSopenharmony_cidef ignore_case(value: V) -> V: 47e31aef6aSopenharmony_ci """For use as a postprocessor for :func:`make_attrgetter`. Converts strings 48e31aef6aSopenharmony_ci to lowercase and returns other types as-is.""" 49e31aef6aSopenharmony_ci if isinstance(value, str): 50e31aef6aSopenharmony_ci return t.cast(V, value.lower()) 51e31aef6aSopenharmony_ci 52e31aef6aSopenharmony_ci return value 53e31aef6aSopenharmony_ci 54e31aef6aSopenharmony_ci 55e31aef6aSopenharmony_cidef make_attrgetter( 56e31aef6aSopenharmony_ci environment: "Environment", 57e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]], 58e31aef6aSopenharmony_ci postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None, 59e31aef6aSopenharmony_ci default: t.Optional[t.Any] = None, 60e31aef6aSopenharmony_ci) -> t.Callable[[t.Any], t.Any]: 61e31aef6aSopenharmony_ci """Returns a callable that looks up the given attribute from a 62e31aef6aSopenharmony_ci passed object with the rules of the environment. Dots are allowed 63e31aef6aSopenharmony_ci to access attributes of attributes. Integer parts in paths are 64e31aef6aSopenharmony_ci looked up as integers. 65e31aef6aSopenharmony_ci """ 66e31aef6aSopenharmony_ci parts = _prepare_attribute_parts(attribute) 67e31aef6aSopenharmony_ci 68e31aef6aSopenharmony_ci def attrgetter(item: t.Any) -> t.Any: 69e31aef6aSopenharmony_ci for part in parts: 70e31aef6aSopenharmony_ci item = environment.getitem(item, part) 71e31aef6aSopenharmony_ci 72e31aef6aSopenharmony_ci if default is not None and isinstance(item, Undefined): 73e31aef6aSopenharmony_ci item = default 74e31aef6aSopenharmony_ci 75e31aef6aSopenharmony_ci if postprocess is not None: 76e31aef6aSopenharmony_ci item = postprocess(item) 77e31aef6aSopenharmony_ci 78e31aef6aSopenharmony_ci return item 79e31aef6aSopenharmony_ci 80e31aef6aSopenharmony_ci return attrgetter 81e31aef6aSopenharmony_ci 82e31aef6aSopenharmony_ci 83e31aef6aSopenharmony_cidef make_multi_attrgetter( 84e31aef6aSopenharmony_ci environment: "Environment", 85e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]], 86e31aef6aSopenharmony_ci postprocess: t.Optional[t.Callable[[t.Any], t.Any]] = None, 87e31aef6aSopenharmony_ci) -> t.Callable[[t.Any], t.List[t.Any]]: 88e31aef6aSopenharmony_ci """Returns a callable that looks up the given comma separated 89e31aef6aSopenharmony_ci attributes from a passed object with the rules of the environment. 90e31aef6aSopenharmony_ci Dots are allowed to access attributes of each attribute. Integer 91e31aef6aSopenharmony_ci parts in paths are looked up as integers. 92e31aef6aSopenharmony_ci 93e31aef6aSopenharmony_ci The value returned by the returned callable is a list of extracted 94e31aef6aSopenharmony_ci attribute values. 95e31aef6aSopenharmony_ci 96e31aef6aSopenharmony_ci Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc. 97e31aef6aSopenharmony_ci """ 98e31aef6aSopenharmony_ci if isinstance(attribute, str): 99e31aef6aSopenharmony_ci split: t.Sequence[t.Union[str, int, None]] = attribute.split(",") 100e31aef6aSopenharmony_ci else: 101e31aef6aSopenharmony_ci split = [attribute] 102e31aef6aSopenharmony_ci 103e31aef6aSopenharmony_ci parts = [_prepare_attribute_parts(item) for item in split] 104e31aef6aSopenharmony_ci 105e31aef6aSopenharmony_ci def attrgetter(item: t.Any) -> t.List[t.Any]: 106e31aef6aSopenharmony_ci items = [None] * len(parts) 107e31aef6aSopenharmony_ci 108e31aef6aSopenharmony_ci for i, attribute_part in enumerate(parts): 109e31aef6aSopenharmony_ci item_i = item 110e31aef6aSopenharmony_ci 111e31aef6aSopenharmony_ci for part in attribute_part: 112e31aef6aSopenharmony_ci item_i = environment.getitem(item_i, part) 113e31aef6aSopenharmony_ci 114e31aef6aSopenharmony_ci if postprocess is not None: 115e31aef6aSopenharmony_ci item_i = postprocess(item_i) 116e31aef6aSopenharmony_ci 117e31aef6aSopenharmony_ci items[i] = item_i 118e31aef6aSopenharmony_ci 119e31aef6aSopenharmony_ci return items 120e31aef6aSopenharmony_ci 121e31aef6aSopenharmony_ci return attrgetter 122e31aef6aSopenharmony_ci 123e31aef6aSopenharmony_ci 124e31aef6aSopenharmony_cidef _prepare_attribute_parts( 125e31aef6aSopenharmony_ci attr: t.Optional[t.Union[str, int]] 126e31aef6aSopenharmony_ci) -> t.List[t.Union[str, int]]: 127e31aef6aSopenharmony_ci if attr is None: 128e31aef6aSopenharmony_ci return [] 129e31aef6aSopenharmony_ci 130e31aef6aSopenharmony_ci if isinstance(attr, str): 131e31aef6aSopenharmony_ci return [int(x) if x.isdigit() else x for x in attr.split(".")] 132e31aef6aSopenharmony_ci 133e31aef6aSopenharmony_ci return [attr] 134e31aef6aSopenharmony_ci 135e31aef6aSopenharmony_ci 136e31aef6aSopenharmony_cidef do_forceescape(value: "t.Union[str, HasHTML]") -> Markup: 137e31aef6aSopenharmony_ci """Enforce HTML escaping. This will probably double escape variables.""" 138e31aef6aSopenharmony_ci if hasattr(value, "__html__"): 139e31aef6aSopenharmony_ci value = t.cast("HasHTML", value).__html__() 140e31aef6aSopenharmony_ci 141e31aef6aSopenharmony_ci return escape(str(value)) 142e31aef6aSopenharmony_ci 143e31aef6aSopenharmony_ci 144e31aef6aSopenharmony_cidef do_urlencode( 145e31aef6aSopenharmony_ci value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]] 146e31aef6aSopenharmony_ci) -> str: 147e31aef6aSopenharmony_ci """Quote data for use in a URL path or query using UTF-8. 148e31aef6aSopenharmony_ci 149e31aef6aSopenharmony_ci Basic wrapper around :func:`urllib.parse.quote` when given a 150e31aef6aSopenharmony_ci string, or :func:`urllib.parse.urlencode` for a dict or iterable. 151e31aef6aSopenharmony_ci 152e31aef6aSopenharmony_ci :param value: Data to quote. A string will be quoted directly. A 153e31aef6aSopenharmony_ci dict or iterable of ``(key, value)`` pairs will be joined as a 154e31aef6aSopenharmony_ci query string. 155e31aef6aSopenharmony_ci 156e31aef6aSopenharmony_ci When given a string, "/" is not quoted. HTTP servers treat "/" and 157e31aef6aSopenharmony_ci "%2F" equivalently in paths. If you need quoted slashes, use the 158e31aef6aSopenharmony_ci ``|replace("/", "%2F")`` filter. 159e31aef6aSopenharmony_ci 160e31aef6aSopenharmony_ci .. versionadded:: 2.7 161e31aef6aSopenharmony_ci """ 162e31aef6aSopenharmony_ci if isinstance(value, str) or not isinstance(value, abc.Iterable): 163e31aef6aSopenharmony_ci return url_quote(value) 164e31aef6aSopenharmony_ci 165e31aef6aSopenharmony_ci if isinstance(value, dict): 166e31aef6aSopenharmony_ci items: t.Iterable[t.Tuple[str, t.Any]] = value.items() 167e31aef6aSopenharmony_ci else: 168e31aef6aSopenharmony_ci items = value # type: ignore 169e31aef6aSopenharmony_ci 170e31aef6aSopenharmony_ci return "&".join( 171e31aef6aSopenharmony_ci f"{url_quote(k, for_qs=True)}={url_quote(v, for_qs=True)}" for k, v in items 172e31aef6aSopenharmony_ci ) 173e31aef6aSopenharmony_ci 174e31aef6aSopenharmony_ci 175e31aef6aSopenharmony_ci@pass_eval_context 176e31aef6aSopenharmony_cidef do_replace( 177e31aef6aSopenharmony_ci eval_ctx: "EvalContext", s: str, old: str, new: str, count: t.Optional[int] = None 178e31aef6aSopenharmony_ci) -> str: 179e31aef6aSopenharmony_ci """Return a copy of the value with all occurrences of a substring 180e31aef6aSopenharmony_ci replaced with a new one. The first argument is the substring 181e31aef6aSopenharmony_ci that should be replaced, the second is the replacement string. 182e31aef6aSopenharmony_ci If the optional third argument ``count`` is given, only the first 183e31aef6aSopenharmony_ci ``count`` occurrences are replaced: 184e31aef6aSopenharmony_ci 185e31aef6aSopenharmony_ci .. sourcecode:: jinja 186e31aef6aSopenharmony_ci 187e31aef6aSopenharmony_ci {{ "Hello World"|replace("Hello", "Goodbye") }} 188e31aef6aSopenharmony_ci -> Goodbye World 189e31aef6aSopenharmony_ci 190e31aef6aSopenharmony_ci {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} 191e31aef6aSopenharmony_ci -> d'oh, d'oh, aaargh 192e31aef6aSopenharmony_ci """ 193e31aef6aSopenharmony_ci if count is None: 194e31aef6aSopenharmony_ci count = -1 195e31aef6aSopenharmony_ci 196e31aef6aSopenharmony_ci if not eval_ctx.autoescape: 197e31aef6aSopenharmony_ci return str(s).replace(str(old), str(new), count) 198e31aef6aSopenharmony_ci 199e31aef6aSopenharmony_ci if ( 200e31aef6aSopenharmony_ci hasattr(old, "__html__") 201e31aef6aSopenharmony_ci or hasattr(new, "__html__") 202e31aef6aSopenharmony_ci and not hasattr(s, "__html__") 203e31aef6aSopenharmony_ci ): 204e31aef6aSopenharmony_ci s = escape(s) 205e31aef6aSopenharmony_ci else: 206e31aef6aSopenharmony_ci s = soft_str(s) 207e31aef6aSopenharmony_ci 208e31aef6aSopenharmony_ci return s.replace(soft_str(old), soft_str(new), count) 209e31aef6aSopenharmony_ci 210e31aef6aSopenharmony_ci 211e31aef6aSopenharmony_cidef do_upper(s: str) -> str: 212e31aef6aSopenharmony_ci """Convert a value to uppercase.""" 213e31aef6aSopenharmony_ci return soft_str(s).upper() 214e31aef6aSopenharmony_ci 215e31aef6aSopenharmony_ci 216e31aef6aSopenharmony_cidef do_lower(s: str) -> str: 217e31aef6aSopenharmony_ci """Convert a value to lowercase.""" 218e31aef6aSopenharmony_ci return soft_str(s).lower() 219e31aef6aSopenharmony_ci 220e31aef6aSopenharmony_ci 221e31aef6aSopenharmony_cidef do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]: 222e31aef6aSopenharmony_ci """Return an iterator over the ``(key, value)`` items of a mapping. 223e31aef6aSopenharmony_ci 224e31aef6aSopenharmony_ci ``x|items`` is the same as ``x.items()``, except if ``x`` is 225e31aef6aSopenharmony_ci undefined an empty iterator is returned. 226e31aef6aSopenharmony_ci 227e31aef6aSopenharmony_ci This filter is useful if you expect the template to be rendered with 228e31aef6aSopenharmony_ci an implementation of Jinja in another programming language that does 229e31aef6aSopenharmony_ci not have a ``.items()`` method on its mapping type. 230e31aef6aSopenharmony_ci 231e31aef6aSopenharmony_ci .. code-block:: html+jinja 232e31aef6aSopenharmony_ci 233e31aef6aSopenharmony_ci <dl> 234e31aef6aSopenharmony_ci {% for key, value in my_dict|items %} 235e31aef6aSopenharmony_ci <dt>{{ key }} 236e31aef6aSopenharmony_ci <dd>{{ value }} 237e31aef6aSopenharmony_ci {% endfor %} 238e31aef6aSopenharmony_ci </dl> 239e31aef6aSopenharmony_ci 240e31aef6aSopenharmony_ci .. versionadded:: 3.1 241e31aef6aSopenharmony_ci """ 242e31aef6aSopenharmony_ci if isinstance(value, Undefined): 243e31aef6aSopenharmony_ci return 244e31aef6aSopenharmony_ci 245e31aef6aSopenharmony_ci if not isinstance(value, abc.Mapping): 246e31aef6aSopenharmony_ci raise TypeError("Can only get item pairs from a mapping.") 247e31aef6aSopenharmony_ci 248e31aef6aSopenharmony_ci yield from value.items() 249e31aef6aSopenharmony_ci 250e31aef6aSopenharmony_ci 251e31aef6aSopenharmony_ci_space_re = re.compile(r"\s", flags=re.ASCII) 252e31aef6aSopenharmony_ci 253e31aef6aSopenharmony_ci 254e31aef6aSopenharmony_ci@pass_eval_context 255e31aef6aSopenharmony_cidef do_xmlattr( 256e31aef6aSopenharmony_ci eval_ctx: "EvalContext", d: t.Mapping[str, t.Any], autospace: bool = True 257e31aef6aSopenharmony_ci) -> str: 258e31aef6aSopenharmony_ci """Create an SGML/XML attribute string based on the items in a dict. 259e31aef6aSopenharmony_ci 260e31aef6aSopenharmony_ci If any key contains a space, this fails with a ``ValueError``. Values that 261e31aef6aSopenharmony_ci are neither ``none`` nor ``undefined`` are automatically escaped. 262e31aef6aSopenharmony_ci 263e31aef6aSopenharmony_ci .. sourcecode:: html+jinja 264e31aef6aSopenharmony_ci 265e31aef6aSopenharmony_ci <ul{{ {'class': 'my_list', 'missing': none, 266e31aef6aSopenharmony_ci 'id': 'list-%d'|format(variable)}|xmlattr }}> 267e31aef6aSopenharmony_ci ... 268e31aef6aSopenharmony_ci </ul> 269e31aef6aSopenharmony_ci 270e31aef6aSopenharmony_ci Results in something like this: 271e31aef6aSopenharmony_ci 272e31aef6aSopenharmony_ci .. sourcecode:: html 273e31aef6aSopenharmony_ci 274e31aef6aSopenharmony_ci <ul class="my_list" id="list-42"> 275e31aef6aSopenharmony_ci ... 276e31aef6aSopenharmony_ci </ul> 277e31aef6aSopenharmony_ci 278e31aef6aSopenharmony_ci As you can see it automatically prepends a space in front of the item 279e31aef6aSopenharmony_ci if the filter returned something unless the second parameter is false. 280e31aef6aSopenharmony_ci 281e31aef6aSopenharmony_ci .. versionchanged:: 3.1.3 282e31aef6aSopenharmony_ci Keys with spaces are not allowed. 283e31aef6aSopenharmony_ci """ 284e31aef6aSopenharmony_ci items = [] 285e31aef6aSopenharmony_ci 286e31aef6aSopenharmony_ci for key, value in d.items(): 287e31aef6aSopenharmony_ci if value is None or isinstance(value, Undefined): 288e31aef6aSopenharmony_ci continue 289e31aef6aSopenharmony_ci 290e31aef6aSopenharmony_ci if _space_re.search(key) is not None: 291e31aef6aSopenharmony_ci raise ValueError(f"Spaces are not allowed in attributes: '{key}'") 292e31aef6aSopenharmony_ci 293e31aef6aSopenharmony_ci items.append(f'{escape(key)}="{escape(value)}"') 294e31aef6aSopenharmony_ci 295e31aef6aSopenharmony_ci rv = " ".join(items) 296e31aef6aSopenharmony_ci 297e31aef6aSopenharmony_ci if autospace and rv: 298e31aef6aSopenharmony_ci rv = " " + rv 299e31aef6aSopenharmony_ci 300e31aef6aSopenharmony_ci if eval_ctx.autoescape: 301e31aef6aSopenharmony_ci rv = Markup(rv) 302e31aef6aSopenharmony_ci 303e31aef6aSopenharmony_ci return rv 304e31aef6aSopenharmony_ci 305e31aef6aSopenharmony_ci 306e31aef6aSopenharmony_cidef do_capitalize(s: str) -> str: 307e31aef6aSopenharmony_ci """Capitalize a value. The first character will be uppercase, all others 308e31aef6aSopenharmony_ci lowercase. 309e31aef6aSopenharmony_ci """ 310e31aef6aSopenharmony_ci return soft_str(s).capitalize() 311e31aef6aSopenharmony_ci 312e31aef6aSopenharmony_ci 313e31aef6aSopenharmony_ci_word_beginning_split_re = re.compile(r"([-\s({\[<]+)") 314e31aef6aSopenharmony_ci 315e31aef6aSopenharmony_ci 316e31aef6aSopenharmony_cidef do_title(s: str) -> str: 317e31aef6aSopenharmony_ci """Return a titlecased version of the value. I.e. words will start with 318e31aef6aSopenharmony_ci uppercase letters, all remaining characters are lowercase. 319e31aef6aSopenharmony_ci """ 320e31aef6aSopenharmony_ci return "".join( 321e31aef6aSopenharmony_ci [ 322e31aef6aSopenharmony_ci item[0].upper() + item[1:].lower() 323e31aef6aSopenharmony_ci for item in _word_beginning_split_re.split(soft_str(s)) 324e31aef6aSopenharmony_ci if item 325e31aef6aSopenharmony_ci ] 326e31aef6aSopenharmony_ci ) 327e31aef6aSopenharmony_ci 328e31aef6aSopenharmony_ci 329e31aef6aSopenharmony_cidef do_dictsort( 330e31aef6aSopenharmony_ci value: t.Mapping[K, V], 331e31aef6aSopenharmony_ci case_sensitive: bool = False, 332e31aef6aSopenharmony_ci by: 'te.Literal["key", "value"]' = "key", 333e31aef6aSopenharmony_ci reverse: bool = False, 334e31aef6aSopenharmony_ci) -> t.List[t.Tuple[K, V]]: 335e31aef6aSopenharmony_ci """Sort a dict and yield (key, value) pairs. Python dicts may not 336e31aef6aSopenharmony_ci be in the order you want to display them in, so sort them first. 337e31aef6aSopenharmony_ci 338e31aef6aSopenharmony_ci .. sourcecode:: jinja 339e31aef6aSopenharmony_ci 340e31aef6aSopenharmony_ci {% for key, value in mydict|dictsort %} 341e31aef6aSopenharmony_ci sort the dict by key, case insensitive 342e31aef6aSopenharmony_ci 343e31aef6aSopenharmony_ci {% for key, value in mydict|dictsort(reverse=true) %} 344e31aef6aSopenharmony_ci sort the dict by key, case insensitive, reverse order 345e31aef6aSopenharmony_ci 346e31aef6aSopenharmony_ci {% for key, value in mydict|dictsort(true) %} 347e31aef6aSopenharmony_ci sort the dict by key, case sensitive 348e31aef6aSopenharmony_ci 349e31aef6aSopenharmony_ci {% for key, value in mydict|dictsort(false, 'value') %} 350e31aef6aSopenharmony_ci sort the dict by value, case insensitive 351e31aef6aSopenharmony_ci """ 352e31aef6aSopenharmony_ci if by == "key": 353e31aef6aSopenharmony_ci pos = 0 354e31aef6aSopenharmony_ci elif by == "value": 355e31aef6aSopenharmony_ci pos = 1 356e31aef6aSopenharmony_ci else: 357e31aef6aSopenharmony_ci raise FilterArgumentError('You can only sort by either "key" or "value"') 358e31aef6aSopenharmony_ci 359e31aef6aSopenharmony_ci def sort_func(item: t.Tuple[t.Any, t.Any]) -> t.Any: 360e31aef6aSopenharmony_ci value = item[pos] 361e31aef6aSopenharmony_ci 362e31aef6aSopenharmony_ci if not case_sensitive: 363e31aef6aSopenharmony_ci value = ignore_case(value) 364e31aef6aSopenharmony_ci 365e31aef6aSopenharmony_ci return value 366e31aef6aSopenharmony_ci 367e31aef6aSopenharmony_ci return sorted(value.items(), key=sort_func, reverse=reverse) 368e31aef6aSopenharmony_ci 369e31aef6aSopenharmony_ci 370e31aef6aSopenharmony_ci@pass_environment 371e31aef6aSopenharmony_cidef do_sort( 372e31aef6aSopenharmony_ci environment: "Environment", 373e31aef6aSopenharmony_ci value: "t.Iterable[V]", 374e31aef6aSopenharmony_ci reverse: bool = False, 375e31aef6aSopenharmony_ci case_sensitive: bool = False, 376e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 377e31aef6aSopenharmony_ci) -> "t.List[V]": 378e31aef6aSopenharmony_ci """Sort an iterable using Python's :func:`sorted`. 379e31aef6aSopenharmony_ci 380e31aef6aSopenharmony_ci .. sourcecode:: jinja 381e31aef6aSopenharmony_ci 382e31aef6aSopenharmony_ci {% for city in cities|sort %} 383e31aef6aSopenharmony_ci ... 384e31aef6aSopenharmony_ci {% endfor %} 385e31aef6aSopenharmony_ci 386e31aef6aSopenharmony_ci :param reverse: Sort descending instead of ascending. 387e31aef6aSopenharmony_ci :param case_sensitive: When sorting strings, sort upper and lower 388e31aef6aSopenharmony_ci case separately. 389e31aef6aSopenharmony_ci :param attribute: When sorting objects or dicts, an attribute or 390e31aef6aSopenharmony_ci key to sort by. Can use dot notation like ``"address.city"``. 391e31aef6aSopenharmony_ci Can be a list of attributes like ``"age,name"``. 392e31aef6aSopenharmony_ci 393e31aef6aSopenharmony_ci The sort is stable, it does not change the relative order of 394e31aef6aSopenharmony_ci elements that compare equal. This makes it is possible to chain 395e31aef6aSopenharmony_ci sorts on different attributes and ordering. 396e31aef6aSopenharmony_ci 397e31aef6aSopenharmony_ci .. sourcecode:: jinja 398e31aef6aSopenharmony_ci 399e31aef6aSopenharmony_ci {% for user in users|sort(attribute="name") 400e31aef6aSopenharmony_ci |sort(reverse=true, attribute="age") %} 401e31aef6aSopenharmony_ci ... 402e31aef6aSopenharmony_ci {% endfor %} 403e31aef6aSopenharmony_ci 404e31aef6aSopenharmony_ci As a shortcut to chaining when the direction is the same for all 405e31aef6aSopenharmony_ci attributes, pass a comma separate list of attributes. 406e31aef6aSopenharmony_ci 407e31aef6aSopenharmony_ci .. sourcecode:: jinja 408e31aef6aSopenharmony_ci 409e31aef6aSopenharmony_ci {% for user in users|sort(attribute="age,name") %} 410e31aef6aSopenharmony_ci ... 411e31aef6aSopenharmony_ci {% endfor %} 412e31aef6aSopenharmony_ci 413e31aef6aSopenharmony_ci .. versionchanged:: 2.11.0 414e31aef6aSopenharmony_ci The ``attribute`` parameter can be a comma separated list of 415e31aef6aSopenharmony_ci attributes, e.g. ``"age,name"``. 416e31aef6aSopenharmony_ci 417e31aef6aSopenharmony_ci .. versionchanged:: 2.6 418e31aef6aSopenharmony_ci The ``attribute`` parameter was added. 419e31aef6aSopenharmony_ci """ 420e31aef6aSopenharmony_ci key_func = make_multi_attrgetter( 421e31aef6aSopenharmony_ci environment, attribute, postprocess=ignore_case if not case_sensitive else None 422e31aef6aSopenharmony_ci ) 423e31aef6aSopenharmony_ci return sorted(value, key=key_func, reverse=reverse) 424e31aef6aSopenharmony_ci 425e31aef6aSopenharmony_ci 426e31aef6aSopenharmony_ci@pass_environment 427e31aef6aSopenharmony_cidef do_unique( 428e31aef6aSopenharmony_ci environment: "Environment", 429e31aef6aSopenharmony_ci value: "t.Iterable[V]", 430e31aef6aSopenharmony_ci case_sensitive: bool = False, 431e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 432e31aef6aSopenharmony_ci) -> "t.Iterator[V]": 433e31aef6aSopenharmony_ci """Returns a list of unique items from the given iterable. 434e31aef6aSopenharmony_ci 435e31aef6aSopenharmony_ci .. sourcecode:: jinja 436e31aef6aSopenharmony_ci 437e31aef6aSopenharmony_ci {{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }} 438e31aef6aSopenharmony_ci -> ['foo', 'bar', 'foobar'] 439e31aef6aSopenharmony_ci 440e31aef6aSopenharmony_ci The unique items are yielded in the same order as their first occurrence in 441e31aef6aSopenharmony_ci the iterable passed to the filter. 442e31aef6aSopenharmony_ci 443e31aef6aSopenharmony_ci :param case_sensitive: Treat upper and lower case strings as distinct. 444e31aef6aSopenharmony_ci :param attribute: Filter objects with unique values for this attribute. 445e31aef6aSopenharmony_ci """ 446e31aef6aSopenharmony_ci getter = make_attrgetter( 447e31aef6aSopenharmony_ci environment, attribute, postprocess=ignore_case if not case_sensitive else None 448e31aef6aSopenharmony_ci ) 449e31aef6aSopenharmony_ci seen = set() 450e31aef6aSopenharmony_ci 451e31aef6aSopenharmony_ci for item in value: 452e31aef6aSopenharmony_ci key = getter(item) 453e31aef6aSopenharmony_ci 454e31aef6aSopenharmony_ci if key not in seen: 455e31aef6aSopenharmony_ci seen.add(key) 456e31aef6aSopenharmony_ci yield item 457e31aef6aSopenharmony_ci 458e31aef6aSopenharmony_ci 459e31aef6aSopenharmony_cidef _min_or_max( 460e31aef6aSopenharmony_ci environment: "Environment", 461e31aef6aSopenharmony_ci value: "t.Iterable[V]", 462e31aef6aSopenharmony_ci func: "t.Callable[..., V]", 463e31aef6aSopenharmony_ci case_sensitive: bool, 464e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]], 465e31aef6aSopenharmony_ci) -> "t.Union[V, Undefined]": 466e31aef6aSopenharmony_ci it = iter(value) 467e31aef6aSopenharmony_ci 468e31aef6aSopenharmony_ci try: 469e31aef6aSopenharmony_ci first = next(it) 470e31aef6aSopenharmony_ci except StopIteration: 471e31aef6aSopenharmony_ci return environment.undefined("No aggregated item, sequence was empty.") 472e31aef6aSopenharmony_ci 473e31aef6aSopenharmony_ci key_func = make_attrgetter( 474e31aef6aSopenharmony_ci environment, attribute, postprocess=ignore_case if not case_sensitive else None 475e31aef6aSopenharmony_ci ) 476e31aef6aSopenharmony_ci return func(chain([first], it), key=key_func) 477e31aef6aSopenharmony_ci 478e31aef6aSopenharmony_ci 479e31aef6aSopenharmony_ci@pass_environment 480e31aef6aSopenharmony_cidef do_min( 481e31aef6aSopenharmony_ci environment: "Environment", 482e31aef6aSopenharmony_ci value: "t.Iterable[V]", 483e31aef6aSopenharmony_ci case_sensitive: bool = False, 484e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 485e31aef6aSopenharmony_ci) -> "t.Union[V, Undefined]": 486e31aef6aSopenharmony_ci """Return the smallest item from the sequence. 487e31aef6aSopenharmony_ci 488e31aef6aSopenharmony_ci .. sourcecode:: jinja 489e31aef6aSopenharmony_ci 490e31aef6aSopenharmony_ci {{ [1, 2, 3]|min }} 491e31aef6aSopenharmony_ci -> 1 492e31aef6aSopenharmony_ci 493e31aef6aSopenharmony_ci :param case_sensitive: Treat upper and lower case strings as distinct. 494e31aef6aSopenharmony_ci :param attribute: Get the object with the min value of this attribute. 495e31aef6aSopenharmony_ci """ 496e31aef6aSopenharmony_ci return _min_or_max(environment, value, min, case_sensitive, attribute) 497e31aef6aSopenharmony_ci 498e31aef6aSopenharmony_ci 499e31aef6aSopenharmony_ci@pass_environment 500e31aef6aSopenharmony_cidef do_max( 501e31aef6aSopenharmony_ci environment: "Environment", 502e31aef6aSopenharmony_ci value: "t.Iterable[V]", 503e31aef6aSopenharmony_ci case_sensitive: bool = False, 504e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 505e31aef6aSopenharmony_ci) -> "t.Union[V, Undefined]": 506e31aef6aSopenharmony_ci """Return the largest item from the sequence. 507e31aef6aSopenharmony_ci 508e31aef6aSopenharmony_ci .. sourcecode:: jinja 509e31aef6aSopenharmony_ci 510e31aef6aSopenharmony_ci {{ [1, 2, 3]|max }} 511e31aef6aSopenharmony_ci -> 3 512e31aef6aSopenharmony_ci 513e31aef6aSopenharmony_ci :param case_sensitive: Treat upper and lower case strings as distinct. 514e31aef6aSopenharmony_ci :param attribute: Get the object with the max value of this attribute. 515e31aef6aSopenharmony_ci """ 516e31aef6aSopenharmony_ci return _min_or_max(environment, value, max, case_sensitive, attribute) 517e31aef6aSopenharmony_ci 518e31aef6aSopenharmony_ci 519e31aef6aSopenharmony_cidef do_default( 520e31aef6aSopenharmony_ci value: V, 521e31aef6aSopenharmony_ci default_value: V = "", # type: ignore 522e31aef6aSopenharmony_ci boolean: bool = False, 523e31aef6aSopenharmony_ci) -> V: 524e31aef6aSopenharmony_ci """If the value is undefined it will return the passed default value, 525e31aef6aSopenharmony_ci otherwise the value of the variable: 526e31aef6aSopenharmony_ci 527e31aef6aSopenharmony_ci .. sourcecode:: jinja 528e31aef6aSopenharmony_ci 529e31aef6aSopenharmony_ci {{ my_variable|default('my_variable is not defined') }} 530e31aef6aSopenharmony_ci 531e31aef6aSopenharmony_ci This will output the value of ``my_variable`` if the variable was 532e31aef6aSopenharmony_ci defined, otherwise ``'my_variable is not defined'``. If you want 533e31aef6aSopenharmony_ci to use default with variables that evaluate to false you have to 534e31aef6aSopenharmony_ci set the second parameter to `true`: 535e31aef6aSopenharmony_ci 536e31aef6aSopenharmony_ci .. sourcecode:: jinja 537e31aef6aSopenharmony_ci 538e31aef6aSopenharmony_ci {{ ''|default('the string was empty', true) }} 539e31aef6aSopenharmony_ci 540e31aef6aSopenharmony_ci .. versionchanged:: 2.11 541e31aef6aSopenharmony_ci It's now possible to configure the :class:`~jinja2.Environment` with 542e31aef6aSopenharmony_ci :class:`~jinja2.ChainableUndefined` to make the `default` filter work 543e31aef6aSopenharmony_ci on nested elements and attributes that may contain undefined values 544e31aef6aSopenharmony_ci in the chain without getting an :exc:`~jinja2.UndefinedError`. 545e31aef6aSopenharmony_ci """ 546e31aef6aSopenharmony_ci if isinstance(value, Undefined) or (boolean and not value): 547e31aef6aSopenharmony_ci return default_value 548e31aef6aSopenharmony_ci 549e31aef6aSopenharmony_ci return value 550e31aef6aSopenharmony_ci 551e31aef6aSopenharmony_ci 552e31aef6aSopenharmony_ci@pass_eval_context 553e31aef6aSopenharmony_cidef sync_do_join( 554e31aef6aSopenharmony_ci eval_ctx: "EvalContext", 555e31aef6aSopenharmony_ci value: t.Iterable, 556e31aef6aSopenharmony_ci d: str = "", 557e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 558e31aef6aSopenharmony_ci) -> str: 559e31aef6aSopenharmony_ci """Return a string which is the concatenation of the strings in the 560e31aef6aSopenharmony_ci sequence. The separator between elements is an empty string per 561e31aef6aSopenharmony_ci default, you can define it with the optional parameter: 562e31aef6aSopenharmony_ci 563e31aef6aSopenharmony_ci .. sourcecode:: jinja 564e31aef6aSopenharmony_ci 565e31aef6aSopenharmony_ci {{ [1, 2, 3]|join('|') }} 566e31aef6aSopenharmony_ci -> 1|2|3 567e31aef6aSopenharmony_ci 568e31aef6aSopenharmony_ci {{ [1, 2, 3]|join }} 569e31aef6aSopenharmony_ci -> 123 570e31aef6aSopenharmony_ci 571e31aef6aSopenharmony_ci It is also possible to join certain attributes of an object: 572e31aef6aSopenharmony_ci 573e31aef6aSopenharmony_ci .. sourcecode:: jinja 574e31aef6aSopenharmony_ci 575e31aef6aSopenharmony_ci {{ users|join(', ', attribute='username') }} 576e31aef6aSopenharmony_ci 577e31aef6aSopenharmony_ci .. versionadded:: 2.6 578e31aef6aSopenharmony_ci The `attribute` parameter was added. 579e31aef6aSopenharmony_ci """ 580e31aef6aSopenharmony_ci if attribute is not None: 581e31aef6aSopenharmony_ci value = map(make_attrgetter(eval_ctx.environment, attribute), value) 582e31aef6aSopenharmony_ci 583e31aef6aSopenharmony_ci # no automatic escaping? joining is a lot easier then 584e31aef6aSopenharmony_ci if not eval_ctx.autoescape: 585e31aef6aSopenharmony_ci return str(d).join(map(str, value)) 586e31aef6aSopenharmony_ci 587e31aef6aSopenharmony_ci # if the delimiter doesn't have an html representation we check 588e31aef6aSopenharmony_ci # if any of the items has. If yes we do a coercion to Markup 589e31aef6aSopenharmony_ci if not hasattr(d, "__html__"): 590e31aef6aSopenharmony_ci value = list(value) 591e31aef6aSopenharmony_ci do_escape = False 592e31aef6aSopenharmony_ci 593e31aef6aSopenharmony_ci for idx, item in enumerate(value): 594e31aef6aSopenharmony_ci if hasattr(item, "__html__"): 595e31aef6aSopenharmony_ci do_escape = True 596e31aef6aSopenharmony_ci else: 597e31aef6aSopenharmony_ci value[idx] = str(item) 598e31aef6aSopenharmony_ci 599e31aef6aSopenharmony_ci if do_escape: 600e31aef6aSopenharmony_ci d = escape(d) 601e31aef6aSopenharmony_ci else: 602e31aef6aSopenharmony_ci d = str(d) 603e31aef6aSopenharmony_ci 604e31aef6aSopenharmony_ci return d.join(value) 605e31aef6aSopenharmony_ci 606e31aef6aSopenharmony_ci # no html involved, to normal joining 607e31aef6aSopenharmony_ci return soft_str(d).join(map(soft_str, value)) 608e31aef6aSopenharmony_ci 609e31aef6aSopenharmony_ci 610e31aef6aSopenharmony_ci@async_variant(sync_do_join) # type: ignore 611e31aef6aSopenharmony_ciasync def do_join( 612e31aef6aSopenharmony_ci eval_ctx: "EvalContext", 613e31aef6aSopenharmony_ci value: t.Union[t.AsyncIterable, t.Iterable], 614e31aef6aSopenharmony_ci d: str = "", 615e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 616e31aef6aSopenharmony_ci) -> str: 617e31aef6aSopenharmony_ci return sync_do_join(eval_ctx, await auto_to_list(value), d, attribute) 618e31aef6aSopenharmony_ci 619e31aef6aSopenharmony_ci 620e31aef6aSopenharmony_cidef do_center(value: str, width: int = 80) -> str: 621e31aef6aSopenharmony_ci """Centers the value in a field of a given width.""" 622e31aef6aSopenharmony_ci return soft_str(value).center(width) 623e31aef6aSopenharmony_ci 624e31aef6aSopenharmony_ci 625e31aef6aSopenharmony_ci@pass_environment 626e31aef6aSopenharmony_cidef sync_do_first( 627e31aef6aSopenharmony_ci environment: "Environment", seq: "t.Iterable[V]" 628e31aef6aSopenharmony_ci) -> "t.Union[V, Undefined]": 629e31aef6aSopenharmony_ci """Return the first item of a sequence.""" 630e31aef6aSopenharmony_ci try: 631e31aef6aSopenharmony_ci return next(iter(seq)) 632e31aef6aSopenharmony_ci except StopIteration: 633e31aef6aSopenharmony_ci return environment.undefined("No first item, sequence was empty.") 634e31aef6aSopenharmony_ci 635e31aef6aSopenharmony_ci 636e31aef6aSopenharmony_ci@async_variant(sync_do_first) # type: ignore 637e31aef6aSopenharmony_ciasync def do_first( 638e31aef6aSopenharmony_ci environment: "Environment", seq: "t.Union[t.AsyncIterable[V], t.Iterable[V]]" 639e31aef6aSopenharmony_ci) -> "t.Union[V, Undefined]": 640e31aef6aSopenharmony_ci try: 641e31aef6aSopenharmony_ci return await auto_aiter(seq).__anext__() 642e31aef6aSopenharmony_ci except StopAsyncIteration: 643e31aef6aSopenharmony_ci return environment.undefined("No first item, sequence was empty.") 644e31aef6aSopenharmony_ci 645e31aef6aSopenharmony_ci 646e31aef6aSopenharmony_ci@pass_environment 647e31aef6aSopenharmony_cidef do_last( 648e31aef6aSopenharmony_ci environment: "Environment", seq: "t.Reversible[V]" 649e31aef6aSopenharmony_ci) -> "t.Union[V, Undefined]": 650e31aef6aSopenharmony_ci """Return the last item of a sequence. 651e31aef6aSopenharmony_ci 652e31aef6aSopenharmony_ci Note: Does not work with generators. You may want to explicitly 653e31aef6aSopenharmony_ci convert it to a list: 654e31aef6aSopenharmony_ci 655e31aef6aSopenharmony_ci .. sourcecode:: jinja 656e31aef6aSopenharmony_ci 657e31aef6aSopenharmony_ci {{ data | selectattr('name', '==', 'Jinja') | list | last }} 658e31aef6aSopenharmony_ci """ 659e31aef6aSopenharmony_ci try: 660e31aef6aSopenharmony_ci return next(iter(reversed(seq))) 661e31aef6aSopenharmony_ci except StopIteration: 662e31aef6aSopenharmony_ci return environment.undefined("No last item, sequence was empty.") 663e31aef6aSopenharmony_ci 664e31aef6aSopenharmony_ci 665e31aef6aSopenharmony_ci# No async do_last, it may not be safe in async mode. 666e31aef6aSopenharmony_ci 667e31aef6aSopenharmony_ci 668e31aef6aSopenharmony_ci@pass_context 669e31aef6aSopenharmony_cidef do_random(context: "Context", seq: "t.Sequence[V]") -> "t.Union[V, Undefined]": 670e31aef6aSopenharmony_ci """Return a random item from the sequence.""" 671e31aef6aSopenharmony_ci try: 672e31aef6aSopenharmony_ci return random.choice(seq) 673e31aef6aSopenharmony_ci except IndexError: 674e31aef6aSopenharmony_ci return context.environment.undefined("No random item, sequence was empty.") 675e31aef6aSopenharmony_ci 676e31aef6aSopenharmony_ci 677e31aef6aSopenharmony_cidef do_filesizeformat(value: t.Union[str, float, int], binary: bool = False) -> str: 678e31aef6aSopenharmony_ci """Format the value like a 'human-readable' file size (i.e. 13 kB, 679e31aef6aSopenharmony_ci 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, 680e31aef6aSopenharmony_ci Giga, etc.), if the second parameter is set to `True` the binary 681e31aef6aSopenharmony_ci prefixes are used (Mebi, Gibi). 682e31aef6aSopenharmony_ci """ 683e31aef6aSopenharmony_ci bytes = float(value) 684e31aef6aSopenharmony_ci base = 1024 if binary else 1000 685e31aef6aSopenharmony_ci prefixes = [ 686e31aef6aSopenharmony_ci ("KiB" if binary else "kB"), 687e31aef6aSopenharmony_ci ("MiB" if binary else "MB"), 688e31aef6aSopenharmony_ci ("GiB" if binary else "GB"), 689e31aef6aSopenharmony_ci ("TiB" if binary else "TB"), 690e31aef6aSopenharmony_ci ("PiB" if binary else "PB"), 691e31aef6aSopenharmony_ci ("EiB" if binary else "EB"), 692e31aef6aSopenharmony_ci ("ZiB" if binary else "ZB"), 693e31aef6aSopenharmony_ci ("YiB" if binary else "YB"), 694e31aef6aSopenharmony_ci ] 695e31aef6aSopenharmony_ci 696e31aef6aSopenharmony_ci if bytes == 1: 697e31aef6aSopenharmony_ci return "1 Byte" 698e31aef6aSopenharmony_ci elif bytes < base: 699e31aef6aSopenharmony_ci return f"{int(bytes)} Bytes" 700e31aef6aSopenharmony_ci else: 701e31aef6aSopenharmony_ci for i, prefix in enumerate(prefixes): 702e31aef6aSopenharmony_ci unit = base ** (i + 2) 703e31aef6aSopenharmony_ci 704e31aef6aSopenharmony_ci if bytes < unit: 705e31aef6aSopenharmony_ci return f"{base * bytes / unit:.1f} {prefix}" 706e31aef6aSopenharmony_ci 707e31aef6aSopenharmony_ci return f"{base * bytes / unit:.1f} {prefix}" 708e31aef6aSopenharmony_ci 709e31aef6aSopenharmony_ci 710e31aef6aSopenharmony_cidef do_pprint(value: t.Any) -> str: 711e31aef6aSopenharmony_ci """Pretty print a variable. Useful for debugging.""" 712e31aef6aSopenharmony_ci return pformat(value) 713e31aef6aSopenharmony_ci 714e31aef6aSopenharmony_ci 715e31aef6aSopenharmony_ci_uri_scheme_re = re.compile(r"^([\w.+-]{2,}:(/){0,2})$") 716e31aef6aSopenharmony_ci 717e31aef6aSopenharmony_ci 718e31aef6aSopenharmony_ci@pass_eval_context 719e31aef6aSopenharmony_cidef do_urlize( 720e31aef6aSopenharmony_ci eval_ctx: "EvalContext", 721e31aef6aSopenharmony_ci value: str, 722e31aef6aSopenharmony_ci trim_url_limit: t.Optional[int] = None, 723e31aef6aSopenharmony_ci nofollow: bool = False, 724e31aef6aSopenharmony_ci target: t.Optional[str] = None, 725e31aef6aSopenharmony_ci rel: t.Optional[str] = None, 726e31aef6aSopenharmony_ci extra_schemes: t.Optional[t.Iterable[str]] = None, 727e31aef6aSopenharmony_ci) -> str: 728e31aef6aSopenharmony_ci """Convert URLs in text into clickable links. 729e31aef6aSopenharmony_ci 730e31aef6aSopenharmony_ci This may not recognize links in some situations. Usually, a more 731e31aef6aSopenharmony_ci comprehensive formatter, such as a Markdown library, is a better 732e31aef6aSopenharmony_ci choice. 733e31aef6aSopenharmony_ci 734e31aef6aSopenharmony_ci Works on ``http://``, ``https://``, ``www.``, ``mailto:``, and email 735e31aef6aSopenharmony_ci addresses. Links with trailing punctuation (periods, commas, closing 736e31aef6aSopenharmony_ci parentheses) and leading punctuation (opening parentheses) are 737e31aef6aSopenharmony_ci recognized excluding the punctuation. Email addresses that include 738e31aef6aSopenharmony_ci header fields are not recognized (for example, 739e31aef6aSopenharmony_ci ``mailto:address@example.com?cc=copy@example.com``). 740e31aef6aSopenharmony_ci 741e31aef6aSopenharmony_ci :param value: Original text containing URLs to link. 742e31aef6aSopenharmony_ci :param trim_url_limit: Shorten displayed URL values to this length. 743e31aef6aSopenharmony_ci :param nofollow: Add the ``rel=nofollow`` attribute to links. 744e31aef6aSopenharmony_ci :param target: Add the ``target`` attribute to links. 745e31aef6aSopenharmony_ci :param rel: Add the ``rel`` attribute to links. 746e31aef6aSopenharmony_ci :param extra_schemes: Recognize URLs that start with these schemes 747e31aef6aSopenharmony_ci in addition to the default behavior. Defaults to 748e31aef6aSopenharmony_ci ``env.policies["urlize.extra_schemes"]``, which defaults to no 749e31aef6aSopenharmony_ci extra schemes. 750e31aef6aSopenharmony_ci 751e31aef6aSopenharmony_ci .. versionchanged:: 3.0 752e31aef6aSopenharmony_ci The ``extra_schemes`` parameter was added. 753e31aef6aSopenharmony_ci 754e31aef6aSopenharmony_ci .. versionchanged:: 3.0 755e31aef6aSopenharmony_ci Generate ``https://`` links for URLs without a scheme. 756e31aef6aSopenharmony_ci 757e31aef6aSopenharmony_ci .. versionchanged:: 3.0 758e31aef6aSopenharmony_ci The parsing rules were updated. Recognize email addresses with 759e31aef6aSopenharmony_ci or without the ``mailto:`` scheme. Validate IP addresses. Ignore 760e31aef6aSopenharmony_ci parentheses and brackets in more cases. 761e31aef6aSopenharmony_ci 762e31aef6aSopenharmony_ci .. versionchanged:: 2.8 763e31aef6aSopenharmony_ci The ``target`` parameter was added. 764e31aef6aSopenharmony_ci """ 765e31aef6aSopenharmony_ci policies = eval_ctx.environment.policies 766e31aef6aSopenharmony_ci rel_parts = set((rel or "").split()) 767e31aef6aSopenharmony_ci 768e31aef6aSopenharmony_ci if nofollow: 769e31aef6aSopenharmony_ci rel_parts.add("nofollow") 770e31aef6aSopenharmony_ci 771e31aef6aSopenharmony_ci rel_parts.update((policies["urlize.rel"] or "").split()) 772e31aef6aSopenharmony_ci rel = " ".join(sorted(rel_parts)) or None 773e31aef6aSopenharmony_ci 774e31aef6aSopenharmony_ci if target is None: 775e31aef6aSopenharmony_ci target = policies["urlize.target"] 776e31aef6aSopenharmony_ci 777e31aef6aSopenharmony_ci if extra_schemes is None: 778e31aef6aSopenharmony_ci extra_schemes = policies["urlize.extra_schemes"] or () 779e31aef6aSopenharmony_ci 780e31aef6aSopenharmony_ci for scheme in extra_schemes: 781e31aef6aSopenharmony_ci if _uri_scheme_re.fullmatch(scheme) is None: 782e31aef6aSopenharmony_ci raise FilterArgumentError(f"{scheme!r} is not a valid URI scheme prefix.") 783e31aef6aSopenharmony_ci 784e31aef6aSopenharmony_ci rv = urlize( 785e31aef6aSopenharmony_ci value, 786e31aef6aSopenharmony_ci trim_url_limit=trim_url_limit, 787e31aef6aSopenharmony_ci rel=rel, 788e31aef6aSopenharmony_ci target=target, 789e31aef6aSopenharmony_ci extra_schemes=extra_schemes, 790e31aef6aSopenharmony_ci ) 791e31aef6aSopenharmony_ci 792e31aef6aSopenharmony_ci if eval_ctx.autoescape: 793e31aef6aSopenharmony_ci rv = Markup(rv) 794e31aef6aSopenharmony_ci 795e31aef6aSopenharmony_ci return rv 796e31aef6aSopenharmony_ci 797e31aef6aSopenharmony_ci 798e31aef6aSopenharmony_cidef do_indent( 799e31aef6aSopenharmony_ci s: str, width: t.Union[int, str] = 4, first: bool = False, blank: bool = False 800e31aef6aSopenharmony_ci) -> str: 801e31aef6aSopenharmony_ci """Return a copy of the string with each line indented by 4 spaces. The 802e31aef6aSopenharmony_ci first line and blank lines are not indented by default. 803e31aef6aSopenharmony_ci 804e31aef6aSopenharmony_ci :param width: Number of spaces, or a string, to indent by. 805e31aef6aSopenharmony_ci :param first: Don't skip indenting the first line. 806e31aef6aSopenharmony_ci :param blank: Don't skip indenting empty lines. 807e31aef6aSopenharmony_ci 808e31aef6aSopenharmony_ci .. versionchanged:: 3.0 809e31aef6aSopenharmony_ci ``width`` can be a string. 810e31aef6aSopenharmony_ci 811e31aef6aSopenharmony_ci .. versionchanged:: 2.10 812e31aef6aSopenharmony_ci Blank lines are not indented by default. 813e31aef6aSopenharmony_ci 814e31aef6aSopenharmony_ci Rename the ``indentfirst`` argument to ``first``. 815e31aef6aSopenharmony_ci """ 816e31aef6aSopenharmony_ci if isinstance(width, str): 817e31aef6aSopenharmony_ci indention = width 818e31aef6aSopenharmony_ci else: 819e31aef6aSopenharmony_ci indention = " " * width 820e31aef6aSopenharmony_ci 821e31aef6aSopenharmony_ci newline = "\n" 822e31aef6aSopenharmony_ci 823e31aef6aSopenharmony_ci if isinstance(s, Markup): 824e31aef6aSopenharmony_ci indention = Markup(indention) 825e31aef6aSopenharmony_ci newline = Markup(newline) 826e31aef6aSopenharmony_ci 827e31aef6aSopenharmony_ci s += newline # this quirk is necessary for splitlines method 828e31aef6aSopenharmony_ci 829e31aef6aSopenharmony_ci if blank: 830e31aef6aSopenharmony_ci rv = (newline + indention).join(s.splitlines()) 831e31aef6aSopenharmony_ci else: 832e31aef6aSopenharmony_ci lines = s.splitlines() 833e31aef6aSopenharmony_ci rv = lines.pop(0) 834e31aef6aSopenharmony_ci 835e31aef6aSopenharmony_ci if lines: 836e31aef6aSopenharmony_ci rv += newline + newline.join( 837e31aef6aSopenharmony_ci indention + line if line else line for line in lines 838e31aef6aSopenharmony_ci ) 839e31aef6aSopenharmony_ci 840e31aef6aSopenharmony_ci if first: 841e31aef6aSopenharmony_ci rv = indention + rv 842e31aef6aSopenharmony_ci 843e31aef6aSopenharmony_ci return rv 844e31aef6aSopenharmony_ci 845e31aef6aSopenharmony_ci 846e31aef6aSopenharmony_ci@pass_environment 847e31aef6aSopenharmony_cidef do_truncate( 848e31aef6aSopenharmony_ci env: "Environment", 849e31aef6aSopenharmony_ci s: str, 850e31aef6aSopenharmony_ci length: int = 255, 851e31aef6aSopenharmony_ci killwords: bool = False, 852e31aef6aSopenharmony_ci end: str = "...", 853e31aef6aSopenharmony_ci leeway: t.Optional[int] = None, 854e31aef6aSopenharmony_ci) -> str: 855e31aef6aSopenharmony_ci """Return a truncated copy of the string. The length is specified 856e31aef6aSopenharmony_ci with the first parameter which defaults to ``255``. If the second 857e31aef6aSopenharmony_ci parameter is ``true`` the filter will cut the text at length. Otherwise 858e31aef6aSopenharmony_ci it will discard the last word. If the text was in fact 859e31aef6aSopenharmony_ci truncated it will append an ellipsis sign (``"..."``). If you want a 860e31aef6aSopenharmony_ci different ellipsis sign than ``"..."`` you can specify it using the 861e31aef6aSopenharmony_ci third parameter. Strings that only exceed the length by the tolerance 862e31aef6aSopenharmony_ci margin given in the fourth parameter will not be truncated. 863e31aef6aSopenharmony_ci 864e31aef6aSopenharmony_ci .. sourcecode:: jinja 865e31aef6aSopenharmony_ci 866e31aef6aSopenharmony_ci {{ "foo bar baz qux"|truncate(9) }} 867e31aef6aSopenharmony_ci -> "foo..." 868e31aef6aSopenharmony_ci {{ "foo bar baz qux"|truncate(9, True) }} 869e31aef6aSopenharmony_ci -> "foo ba..." 870e31aef6aSopenharmony_ci {{ "foo bar baz qux"|truncate(11) }} 871e31aef6aSopenharmony_ci -> "foo bar baz qux" 872e31aef6aSopenharmony_ci {{ "foo bar baz qux"|truncate(11, False, '...', 0) }} 873e31aef6aSopenharmony_ci -> "foo bar..." 874e31aef6aSopenharmony_ci 875e31aef6aSopenharmony_ci The default leeway on newer Jinja versions is 5 and was 0 before but 876e31aef6aSopenharmony_ci can be reconfigured globally. 877e31aef6aSopenharmony_ci """ 878e31aef6aSopenharmony_ci if leeway is None: 879e31aef6aSopenharmony_ci leeway = env.policies["truncate.leeway"] 880e31aef6aSopenharmony_ci 881e31aef6aSopenharmony_ci assert length >= len(end), f"expected length >= {len(end)}, got {length}" 882e31aef6aSopenharmony_ci assert leeway >= 0, f"expected leeway >= 0, got {leeway}" 883e31aef6aSopenharmony_ci 884e31aef6aSopenharmony_ci if len(s) <= length + leeway: 885e31aef6aSopenharmony_ci return s 886e31aef6aSopenharmony_ci 887e31aef6aSopenharmony_ci if killwords: 888e31aef6aSopenharmony_ci return s[: length - len(end)] + end 889e31aef6aSopenharmony_ci 890e31aef6aSopenharmony_ci result = s[: length - len(end)].rsplit(" ", 1)[0] 891e31aef6aSopenharmony_ci return result + end 892e31aef6aSopenharmony_ci 893e31aef6aSopenharmony_ci 894e31aef6aSopenharmony_ci@pass_environment 895e31aef6aSopenharmony_cidef do_wordwrap( 896e31aef6aSopenharmony_ci environment: "Environment", 897e31aef6aSopenharmony_ci s: str, 898e31aef6aSopenharmony_ci width: int = 79, 899e31aef6aSopenharmony_ci break_long_words: bool = True, 900e31aef6aSopenharmony_ci wrapstring: t.Optional[str] = None, 901e31aef6aSopenharmony_ci break_on_hyphens: bool = True, 902e31aef6aSopenharmony_ci) -> str: 903e31aef6aSopenharmony_ci """Wrap a string to the given width. Existing newlines are treated 904e31aef6aSopenharmony_ci as paragraphs to be wrapped separately. 905e31aef6aSopenharmony_ci 906e31aef6aSopenharmony_ci :param s: Original text to wrap. 907e31aef6aSopenharmony_ci :param width: Maximum length of wrapped lines. 908e31aef6aSopenharmony_ci :param break_long_words: If a word is longer than ``width``, break 909e31aef6aSopenharmony_ci it across lines. 910e31aef6aSopenharmony_ci :param break_on_hyphens: If a word contains hyphens, it may be split 911e31aef6aSopenharmony_ci across lines. 912e31aef6aSopenharmony_ci :param wrapstring: String to join each wrapped line. Defaults to 913e31aef6aSopenharmony_ci :attr:`Environment.newline_sequence`. 914e31aef6aSopenharmony_ci 915e31aef6aSopenharmony_ci .. versionchanged:: 2.11 916e31aef6aSopenharmony_ci Existing newlines are treated as paragraphs wrapped separately. 917e31aef6aSopenharmony_ci 918e31aef6aSopenharmony_ci .. versionchanged:: 2.11 919e31aef6aSopenharmony_ci Added the ``break_on_hyphens`` parameter. 920e31aef6aSopenharmony_ci 921e31aef6aSopenharmony_ci .. versionchanged:: 2.7 922e31aef6aSopenharmony_ci Added the ``wrapstring`` parameter. 923e31aef6aSopenharmony_ci """ 924e31aef6aSopenharmony_ci import textwrap 925e31aef6aSopenharmony_ci 926e31aef6aSopenharmony_ci if wrapstring is None: 927e31aef6aSopenharmony_ci wrapstring = environment.newline_sequence 928e31aef6aSopenharmony_ci 929e31aef6aSopenharmony_ci # textwrap.wrap doesn't consider existing newlines when wrapping. 930e31aef6aSopenharmony_ci # If the string has a newline before width, wrap will still insert 931e31aef6aSopenharmony_ci # a newline at width, resulting in a short line. Instead, split and 932e31aef6aSopenharmony_ci # wrap each paragraph individually. 933e31aef6aSopenharmony_ci return wrapstring.join( 934e31aef6aSopenharmony_ci [ 935e31aef6aSopenharmony_ci wrapstring.join( 936e31aef6aSopenharmony_ci textwrap.wrap( 937e31aef6aSopenharmony_ci line, 938e31aef6aSopenharmony_ci width=width, 939e31aef6aSopenharmony_ci expand_tabs=False, 940e31aef6aSopenharmony_ci replace_whitespace=False, 941e31aef6aSopenharmony_ci break_long_words=break_long_words, 942e31aef6aSopenharmony_ci break_on_hyphens=break_on_hyphens, 943e31aef6aSopenharmony_ci ) 944e31aef6aSopenharmony_ci ) 945e31aef6aSopenharmony_ci for line in s.splitlines() 946e31aef6aSopenharmony_ci ] 947e31aef6aSopenharmony_ci ) 948e31aef6aSopenharmony_ci 949e31aef6aSopenharmony_ci 950e31aef6aSopenharmony_ci_word_re = re.compile(r"\w+") 951e31aef6aSopenharmony_ci 952e31aef6aSopenharmony_ci 953e31aef6aSopenharmony_cidef do_wordcount(s: str) -> int: 954e31aef6aSopenharmony_ci """Count the words in that string.""" 955e31aef6aSopenharmony_ci return len(_word_re.findall(soft_str(s))) 956e31aef6aSopenharmony_ci 957e31aef6aSopenharmony_ci 958e31aef6aSopenharmony_cidef do_int(value: t.Any, default: int = 0, base: int = 10) -> int: 959e31aef6aSopenharmony_ci """Convert the value into an integer. If the 960e31aef6aSopenharmony_ci conversion doesn't work it will return ``0``. You can 961e31aef6aSopenharmony_ci override this default using the first parameter. You 962e31aef6aSopenharmony_ci can also override the default base (10) in the second 963e31aef6aSopenharmony_ci parameter, which handles input with prefixes such as 964e31aef6aSopenharmony_ci 0b, 0o and 0x for bases 2, 8 and 16 respectively. 965e31aef6aSopenharmony_ci The base is ignored for decimal numbers and non-string values. 966e31aef6aSopenharmony_ci """ 967e31aef6aSopenharmony_ci try: 968e31aef6aSopenharmony_ci if isinstance(value, str): 969e31aef6aSopenharmony_ci return int(value, base) 970e31aef6aSopenharmony_ci 971e31aef6aSopenharmony_ci return int(value) 972e31aef6aSopenharmony_ci except (TypeError, ValueError): 973e31aef6aSopenharmony_ci # this quirk is necessary so that "42.23"|int gives 42. 974e31aef6aSopenharmony_ci try: 975e31aef6aSopenharmony_ci return int(float(value)) 976e31aef6aSopenharmony_ci except (TypeError, ValueError): 977e31aef6aSopenharmony_ci return default 978e31aef6aSopenharmony_ci 979e31aef6aSopenharmony_ci 980e31aef6aSopenharmony_cidef do_float(value: t.Any, default: float = 0.0) -> float: 981e31aef6aSopenharmony_ci """Convert the value into a floating point number. If the 982e31aef6aSopenharmony_ci conversion doesn't work it will return ``0.0``. You can 983e31aef6aSopenharmony_ci override this default using the first parameter. 984e31aef6aSopenharmony_ci """ 985e31aef6aSopenharmony_ci try: 986e31aef6aSopenharmony_ci return float(value) 987e31aef6aSopenharmony_ci except (TypeError, ValueError): 988e31aef6aSopenharmony_ci return default 989e31aef6aSopenharmony_ci 990e31aef6aSopenharmony_ci 991e31aef6aSopenharmony_cidef do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str: 992e31aef6aSopenharmony_ci """Apply the given values to a `printf-style`_ format string, like 993e31aef6aSopenharmony_ci ``string % values``. 994e31aef6aSopenharmony_ci 995e31aef6aSopenharmony_ci .. sourcecode:: jinja 996e31aef6aSopenharmony_ci 997e31aef6aSopenharmony_ci {{ "%s, %s!"|format(greeting, name) }} 998e31aef6aSopenharmony_ci Hello, World! 999e31aef6aSopenharmony_ci 1000e31aef6aSopenharmony_ci In most cases it should be more convenient and efficient to use the 1001e31aef6aSopenharmony_ci ``%`` operator or :meth:`str.format`. 1002e31aef6aSopenharmony_ci 1003e31aef6aSopenharmony_ci .. code-block:: text 1004e31aef6aSopenharmony_ci 1005e31aef6aSopenharmony_ci {{ "%s, %s!" % (greeting, name) }} 1006e31aef6aSopenharmony_ci {{ "{}, {}!".format(greeting, name) }} 1007e31aef6aSopenharmony_ci 1008e31aef6aSopenharmony_ci .. _printf-style: https://docs.python.org/library/stdtypes.html 1009e31aef6aSopenharmony_ci #printf-style-string-formatting 1010e31aef6aSopenharmony_ci """ 1011e31aef6aSopenharmony_ci if args and kwargs: 1012e31aef6aSopenharmony_ci raise FilterArgumentError( 1013e31aef6aSopenharmony_ci "can't handle positional and keyword arguments at the same time" 1014e31aef6aSopenharmony_ci ) 1015e31aef6aSopenharmony_ci 1016e31aef6aSopenharmony_ci return soft_str(value) % (kwargs or args) 1017e31aef6aSopenharmony_ci 1018e31aef6aSopenharmony_ci 1019e31aef6aSopenharmony_cidef do_trim(value: str, chars: t.Optional[str] = None) -> str: 1020e31aef6aSopenharmony_ci """Strip leading and trailing characters, by default whitespace.""" 1021e31aef6aSopenharmony_ci return soft_str(value).strip(chars) 1022e31aef6aSopenharmony_ci 1023e31aef6aSopenharmony_ci 1024e31aef6aSopenharmony_cidef do_striptags(value: "t.Union[str, HasHTML]") -> str: 1025e31aef6aSopenharmony_ci """Strip SGML/XML tags and replace adjacent whitespace by one space.""" 1026e31aef6aSopenharmony_ci if hasattr(value, "__html__"): 1027e31aef6aSopenharmony_ci value = t.cast("HasHTML", value).__html__() 1028e31aef6aSopenharmony_ci 1029e31aef6aSopenharmony_ci return Markup(str(value)).striptags() 1030e31aef6aSopenharmony_ci 1031e31aef6aSopenharmony_ci 1032e31aef6aSopenharmony_cidef sync_do_slice( 1033e31aef6aSopenharmony_ci value: "t.Collection[V]", slices: int, fill_with: "t.Optional[V]" = None 1034e31aef6aSopenharmony_ci) -> "t.Iterator[t.List[V]]": 1035e31aef6aSopenharmony_ci """Slice an iterator and return a list of lists containing 1036e31aef6aSopenharmony_ci those items. Useful if you want to create a div containing 1037e31aef6aSopenharmony_ci three ul tags that represent columns: 1038e31aef6aSopenharmony_ci 1039e31aef6aSopenharmony_ci .. sourcecode:: html+jinja 1040e31aef6aSopenharmony_ci 1041e31aef6aSopenharmony_ci <div class="columnwrapper"> 1042e31aef6aSopenharmony_ci {%- for column in items|slice(3) %} 1043e31aef6aSopenharmony_ci <ul class="column-{{ loop.index }}"> 1044e31aef6aSopenharmony_ci {%- for item in column %} 1045e31aef6aSopenharmony_ci <li>{{ item }}</li> 1046e31aef6aSopenharmony_ci {%- endfor %} 1047e31aef6aSopenharmony_ci </ul> 1048e31aef6aSopenharmony_ci {%- endfor %} 1049e31aef6aSopenharmony_ci </div> 1050e31aef6aSopenharmony_ci 1051e31aef6aSopenharmony_ci If you pass it a second argument it's used to fill missing 1052e31aef6aSopenharmony_ci values on the last iteration. 1053e31aef6aSopenharmony_ci """ 1054e31aef6aSopenharmony_ci seq = list(value) 1055e31aef6aSopenharmony_ci length = len(seq) 1056e31aef6aSopenharmony_ci items_per_slice = length // slices 1057e31aef6aSopenharmony_ci slices_with_extra = length % slices 1058e31aef6aSopenharmony_ci offset = 0 1059e31aef6aSopenharmony_ci 1060e31aef6aSopenharmony_ci for slice_number in range(slices): 1061e31aef6aSopenharmony_ci start = offset + slice_number * items_per_slice 1062e31aef6aSopenharmony_ci 1063e31aef6aSopenharmony_ci if slice_number < slices_with_extra: 1064e31aef6aSopenharmony_ci offset += 1 1065e31aef6aSopenharmony_ci 1066e31aef6aSopenharmony_ci end = offset + (slice_number + 1) * items_per_slice 1067e31aef6aSopenharmony_ci tmp = seq[start:end] 1068e31aef6aSopenharmony_ci 1069e31aef6aSopenharmony_ci if fill_with is not None and slice_number >= slices_with_extra: 1070e31aef6aSopenharmony_ci tmp.append(fill_with) 1071e31aef6aSopenharmony_ci 1072e31aef6aSopenharmony_ci yield tmp 1073e31aef6aSopenharmony_ci 1074e31aef6aSopenharmony_ci 1075e31aef6aSopenharmony_ci@async_variant(sync_do_slice) # type: ignore 1076e31aef6aSopenharmony_ciasync def do_slice( 1077e31aef6aSopenharmony_ci value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1078e31aef6aSopenharmony_ci slices: int, 1079e31aef6aSopenharmony_ci fill_with: t.Optional[t.Any] = None, 1080e31aef6aSopenharmony_ci) -> "t.Iterator[t.List[V]]": 1081e31aef6aSopenharmony_ci return sync_do_slice(await auto_to_list(value), slices, fill_with) 1082e31aef6aSopenharmony_ci 1083e31aef6aSopenharmony_ci 1084e31aef6aSopenharmony_cidef do_batch( 1085e31aef6aSopenharmony_ci value: "t.Iterable[V]", linecount: int, fill_with: "t.Optional[V]" = None 1086e31aef6aSopenharmony_ci) -> "t.Iterator[t.List[V]]": 1087e31aef6aSopenharmony_ci """ 1088e31aef6aSopenharmony_ci A filter that batches items. It works pretty much like `slice` 1089e31aef6aSopenharmony_ci just the other way round. It returns a list of lists with the 1090e31aef6aSopenharmony_ci given number of items. If you provide a second parameter this 1091e31aef6aSopenharmony_ci is used to fill up missing items. See this example: 1092e31aef6aSopenharmony_ci 1093e31aef6aSopenharmony_ci .. sourcecode:: html+jinja 1094e31aef6aSopenharmony_ci 1095e31aef6aSopenharmony_ci <table> 1096e31aef6aSopenharmony_ci {%- for row in items|batch(3, ' ') %} 1097e31aef6aSopenharmony_ci <tr> 1098e31aef6aSopenharmony_ci {%- for column in row %} 1099e31aef6aSopenharmony_ci <td>{{ column }}</td> 1100e31aef6aSopenharmony_ci {%- endfor %} 1101e31aef6aSopenharmony_ci </tr> 1102e31aef6aSopenharmony_ci {%- endfor %} 1103e31aef6aSopenharmony_ci </table> 1104e31aef6aSopenharmony_ci """ 1105e31aef6aSopenharmony_ci tmp: "t.List[V]" = [] 1106e31aef6aSopenharmony_ci 1107e31aef6aSopenharmony_ci for item in value: 1108e31aef6aSopenharmony_ci if len(tmp) == linecount: 1109e31aef6aSopenharmony_ci yield tmp 1110e31aef6aSopenharmony_ci tmp = [] 1111e31aef6aSopenharmony_ci 1112e31aef6aSopenharmony_ci tmp.append(item) 1113e31aef6aSopenharmony_ci 1114e31aef6aSopenharmony_ci if tmp: 1115e31aef6aSopenharmony_ci if fill_with is not None and len(tmp) < linecount: 1116e31aef6aSopenharmony_ci tmp += [fill_with] * (linecount - len(tmp)) 1117e31aef6aSopenharmony_ci 1118e31aef6aSopenharmony_ci yield tmp 1119e31aef6aSopenharmony_ci 1120e31aef6aSopenharmony_ci 1121e31aef6aSopenharmony_cidef do_round( 1122e31aef6aSopenharmony_ci value: float, 1123e31aef6aSopenharmony_ci precision: int = 0, 1124e31aef6aSopenharmony_ci method: 'te.Literal["common", "ceil", "floor"]' = "common", 1125e31aef6aSopenharmony_ci) -> float: 1126e31aef6aSopenharmony_ci """Round the number to a given precision. The first 1127e31aef6aSopenharmony_ci parameter specifies the precision (default is ``0``), the 1128e31aef6aSopenharmony_ci second the rounding method: 1129e31aef6aSopenharmony_ci 1130e31aef6aSopenharmony_ci - ``'common'`` rounds either up or down 1131e31aef6aSopenharmony_ci - ``'ceil'`` always rounds up 1132e31aef6aSopenharmony_ci - ``'floor'`` always rounds down 1133e31aef6aSopenharmony_ci 1134e31aef6aSopenharmony_ci If you don't specify a method ``'common'`` is used. 1135e31aef6aSopenharmony_ci 1136e31aef6aSopenharmony_ci .. sourcecode:: jinja 1137e31aef6aSopenharmony_ci 1138e31aef6aSopenharmony_ci {{ 42.55|round }} 1139e31aef6aSopenharmony_ci -> 43.0 1140e31aef6aSopenharmony_ci {{ 42.55|round(1, 'floor') }} 1141e31aef6aSopenharmony_ci -> 42.5 1142e31aef6aSopenharmony_ci 1143e31aef6aSopenharmony_ci Note that even if rounded to 0 precision, a float is returned. If 1144e31aef6aSopenharmony_ci you need a real integer, pipe it through `int`: 1145e31aef6aSopenharmony_ci 1146e31aef6aSopenharmony_ci .. sourcecode:: jinja 1147e31aef6aSopenharmony_ci 1148e31aef6aSopenharmony_ci {{ 42.55|round|int }} 1149e31aef6aSopenharmony_ci -> 43 1150e31aef6aSopenharmony_ci """ 1151e31aef6aSopenharmony_ci if method not in {"common", "ceil", "floor"}: 1152e31aef6aSopenharmony_ci raise FilterArgumentError("method must be common, ceil or floor") 1153e31aef6aSopenharmony_ci 1154e31aef6aSopenharmony_ci if method == "common": 1155e31aef6aSopenharmony_ci return round(value, precision) 1156e31aef6aSopenharmony_ci 1157e31aef6aSopenharmony_ci func = getattr(math, method) 1158e31aef6aSopenharmony_ci return t.cast(float, func(value * (10**precision)) / (10**precision)) 1159e31aef6aSopenharmony_ci 1160e31aef6aSopenharmony_ci 1161e31aef6aSopenharmony_ciclass _GroupTuple(t.NamedTuple): 1162e31aef6aSopenharmony_ci grouper: t.Any 1163e31aef6aSopenharmony_ci list: t.List 1164e31aef6aSopenharmony_ci 1165e31aef6aSopenharmony_ci # Use the regular tuple repr to hide this subclass if users print 1166e31aef6aSopenharmony_ci # out the value during debugging. 1167e31aef6aSopenharmony_ci def __repr__(self) -> str: 1168e31aef6aSopenharmony_ci return tuple.__repr__(self) 1169e31aef6aSopenharmony_ci 1170e31aef6aSopenharmony_ci def __str__(self) -> str: 1171e31aef6aSopenharmony_ci return tuple.__str__(self) 1172e31aef6aSopenharmony_ci 1173e31aef6aSopenharmony_ci 1174e31aef6aSopenharmony_ci@pass_environment 1175e31aef6aSopenharmony_cidef sync_do_groupby( 1176e31aef6aSopenharmony_ci environment: "Environment", 1177e31aef6aSopenharmony_ci value: "t.Iterable[V]", 1178e31aef6aSopenharmony_ci attribute: t.Union[str, int], 1179e31aef6aSopenharmony_ci default: t.Optional[t.Any] = None, 1180e31aef6aSopenharmony_ci case_sensitive: bool = False, 1181e31aef6aSopenharmony_ci) -> "t.List[_GroupTuple]": 1182e31aef6aSopenharmony_ci """Group a sequence of objects by an attribute using Python's 1183e31aef6aSopenharmony_ci :func:`itertools.groupby`. The attribute can use dot notation for 1184e31aef6aSopenharmony_ci nested access, like ``"address.city"``. Unlike Python's ``groupby``, 1185e31aef6aSopenharmony_ci the values are sorted first so only one group is returned for each 1186e31aef6aSopenharmony_ci unique value. 1187e31aef6aSopenharmony_ci 1188e31aef6aSopenharmony_ci For example, a list of ``User`` objects with a ``city`` attribute 1189e31aef6aSopenharmony_ci can be rendered in groups. In this example, ``grouper`` refers to 1190e31aef6aSopenharmony_ci the ``city`` value of the group. 1191e31aef6aSopenharmony_ci 1192e31aef6aSopenharmony_ci .. sourcecode:: html+jinja 1193e31aef6aSopenharmony_ci 1194e31aef6aSopenharmony_ci <ul>{% for city, items in users|groupby("city") %} 1195e31aef6aSopenharmony_ci <li>{{ city }} 1196e31aef6aSopenharmony_ci <ul>{% for user in items %} 1197e31aef6aSopenharmony_ci <li>{{ user.name }} 1198e31aef6aSopenharmony_ci {% endfor %}</ul> 1199e31aef6aSopenharmony_ci </li> 1200e31aef6aSopenharmony_ci {% endfor %}</ul> 1201e31aef6aSopenharmony_ci 1202e31aef6aSopenharmony_ci ``groupby`` yields namedtuples of ``(grouper, list)``, which 1203e31aef6aSopenharmony_ci can be used instead of the tuple unpacking above. ``grouper`` is the 1204e31aef6aSopenharmony_ci value of the attribute, and ``list`` is the items with that value. 1205e31aef6aSopenharmony_ci 1206e31aef6aSopenharmony_ci .. sourcecode:: html+jinja 1207e31aef6aSopenharmony_ci 1208e31aef6aSopenharmony_ci <ul>{% for group in users|groupby("city") %} 1209e31aef6aSopenharmony_ci <li>{{ group.grouper }}: {{ group.list|join(", ") }} 1210e31aef6aSopenharmony_ci {% endfor %}</ul> 1211e31aef6aSopenharmony_ci 1212e31aef6aSopenharmony_ci You can specify a ``default`` value to use if an object in the list 1213e31aef6aSopenharmony_ci does not have the given attribute. 1214e31aef6aSopenharmony_ci 1215e31aef6aSopenharmony_ci .. sourcecode:: jinja 1216e31aef6aSopenharmony_ci 1217e31aef6aSopenharmony_ci <ul>{% for city, items in users|groupby("city", default="NY") %} 1218e31aef6aSopenharmony_ci <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li> 1219e31aef6aSopenharmony_ci {% endfor %}</ul> 1220e31aef6aSopenharmony_ci 1221e31aef6aSopenharmony_ci Like the :func:`~jinja-filters.sort` filter, sorting and grouping is 1222e31aef6aSopenharmony_ci case-insensitive by default. The ``key`` for each group will have 1223e31aef6aSopenharmony_ci the case of the first item in that group of values. For example, if 1224e31aef6aSopenharmony_ci a list of users has cities ``["CA", "NY", "ca"]``, the "CA" group 1225e31aef6aSopenharmony_ci will have two values. This can be disabled by passing 1226e31aef6aSopenharmony_ci ``case_sensitive=True``. 1227e31aef6aSopenharmony_ci 1228e31aef6aSopenharmony_ci .. versionchanged:: 3.1 1229e31aef6aSopenharmony_ci Added the ``case_sensitive`` parameter. Sorting and grouping is 1230e31aef6aSopenharmony_ci case-insensitive by default, matching other filters that do 1231e31aef6aSopenharmony_ci comparisons. 1232e31aef6aSopenharmony_ci 1233e31aef6aSopenharmony_ci .. versionchanged:: 3.0 1234e31aef6aSopenharmony_ci Added the ``default`` parameter. 1235e31aef6aSopenharmony_ci 1236e31aef6aSopenharmony_ci .. versionchanged:: 2.6 1237e31aef6aSopenharmony_ci The attribute supports dot notation for nested access. 1238e31aef6aSopenharmony_ci """ 1239e31aef6aSopenharmony_ci expr = make_attrgetter( 1240e31aef6aSopenharmony_ci environment, 1241e31aef6aSopenharmony_ci attribute, 1242e31aef6aSopenharmony_ci postprocess=ignore_case if not case_sensitive else None, 1243e31aef6aSopenharmony_ci default=default, 1244e31aef6aSopenharmony_ci ) 1245e31aef6aSopenharmony_ci out = [ 1246e31aef6aSopenharmony_ci _GroupTuple(key, list(values)) 1247e31aef6aSopenharmony_ci for key, values in groupby(sorted(value, key=expr), expr) 1248e31aef6aSopenharmony_ci ] 1249e31aef6aSopenharmony_ci 1250e31aef6aSopenharmony_ci if not case_sensitive: 1251e31aef6aSopenharmony_ci # Return the real key from the first value instead of the lowercase key. 1252e31aef6aSopenharmony_ci output_expr = make_attrgetter(environment, attribute, default=default) 1253e31aef6aSopenharmony_ci out = [_GroupTuple(output_expr(values[0]), values) for _, values in out] 1254e31aef6aSopenharmony_ci 1255e31aef6aSopenharmony_ci return out 1256e31aef6aSopenharmony_ci 1257e31aef6aSopenharmony_ci 1258e31aef6aSopenharmony_ci@async_variant(sync_do_groupby) # type: ignore 1259e31aef6aSopenharmony_ciasync def do_groupby( 1260e31aef6aSopenharmony_ci environment: "Environment", 1261e31aef6aSopenharmony_ci value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1262e31aef6aSopenharmony_ci attribute: t.Union[str, int], 1263e31aef6aSopenharmony_ci default: t.Optional[t.Any] = None, 1264e31aef6aSopenharmony_ci case_sensitive: bool = False, 1265e31aef6aSopenharmony_ci) -> "t.List[_GroupTuple]": 1266e31aef6aSopenharmony_ci expr = make_attrgetter( 1267e31aef6aSopenharmony_ci environment, 1268e31aef6aSopenharmony_ci attribute, 1269e31aef6aSopenharmony_ci postprocess=ignore_case if not case_sensitive else None, 1270e31aef6aSopenharmony_ci default=default, 1271e31aef6aSopenharmony_ci ) 1272e31aef6aSopenharmony_ci out = [ 1273e31aef6aSopenharmony_ci _GroupTuple(key, await auto_to_list(values)) 1274e31aef6aSopenharmony_ci for key, values in groupby(sorted(await auto_to_list(value), key=expr), expr) 1275e31aef6aSopenharmony_ci ] 1276e31aef6aSopenharmony_ci 1277e31aef6aSopenharmony_ci if not case_sensitive: 1278e31aef6aSopenharmony_ci # Return the real key from the first value instead of the lowercase key. 1279e31aef6aSopenharmony_ci output_expr = make_attrgetter(environment, attribute, default=default) 1280e31aef6aSopenharmony_ci out = [_GroupTuple(output_expr(values[0]), values) for _, values in out] 1281e31aef6aSopenharmony_ci 1282e31aef6aSopenharmony_ci return out 1283e31aef6aSopenharmony_ci 1284e31aef6aSopenharmony_ci 1285e31aef6aSopenharmony_ci@pass_environment 1286e31aef6aSopenharmony_cidef sync_do_sum( 1287e31aef6aSopenharmony_ci environment: "Environment", 1288e31aef6aSopenharmony_ci iterable: "t.Iterable[V]", 1289e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 1290e31aef6aSopenharmony_ci start: V = 0, # type: ignore 1291e31aef6aSopenharmony_ci) -> V: 1292e31aef6aSopenharmony_ci """Returns the sum of a sequence of numbers plus the value of parameter 1293e31aef6aSopenharmony_ci 'start' (which defaults to 0). When the sequence is empty it returns 1294e31aef6aSopenharmony_ci start. 1295e31aef6aSopenharmony_ci 1296e31aef6aSopenharmony_ci It is also possible to sum up only certain attributes: 1297e31aef6aSopenharmony_ci 1298e31aef6aSopenharmony_ci .. sourcecode:: jinja 1299e31aef6aSopenharmony_ci 1300e31aef6aSopenharmony_ci Total: {{ items|sum(attribute='price') }} 1301e31aef6aSopenharmony_ci 1302e31aef6aSopenharmony_ci .. versionchanged:: 2.6 1303e31aef6aSopenharmony_ci The ``attribute`` parameter was added to allow summing up over 1304e31aef6aSopenharmony_ci attributes. Also the ``start`` parameter was moved on to the right. 1305e31aef6aSopenharmony_ci """ 1306e31aef6aSopenharmony_ci if attribute is not None: 1307e31aef6aSopenharmony_ci iterable = map(make_attrgetter(environment, attribute), iterable) 1308e31aef6aSopenharmony_ci 1309e31aef6aSopenharmony_ci return sum(iterable, start) # type: ignore[no-any-return, call-overload] 1310e31aef6aSopenharmony_ci 1311e31aef6aSopenharmony_ci 1312e31aef6aSopenharmony_ci@async_variant(sync_do_sum) # type: ignore 1313e31aef6aSopenharmony_ciasync def do_sum( 1314e31aef6aSopenharmony_ci environment: "Environment", 1315e31aef6aSopenharmony_ci iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1316e31aef6aSopenharmony_ci attribute: t.Optional[t.Union[str, int]] = None, 1317e31aef6aSopenharmony_ci start: V = 0, # type: ignore 1318e31aef6aSopenharmony_ci) -> V: 1319e31aef6aSopenharmony_ci rv = start 1320e31aef6aSopenharmony_ci 1321e31aef6aSopenharmony_ci if attribute is not None: 1322e31aef6aSopenharmony_ci func = make_attrgetter(environment, attribute) 1323e31aef6aSopenharmony_ci else: 1324e31aef6aSopenharmony_ci 1325e31aef6aSopenharmony_ci def func(x: V) -> V: 1326e31aef6aSopenharmony_ci return x 1327e31aef6aSopenharmony_ci 1328e31aef6aSopenharmony_ci async for item in auto_aiter(iterable): 1329e31aef6aSopenharmony_ci rv += func(item) 1330e31aef6aSopenharmony_ci 1331e31aef6aSopenharmony_ci return rv 1332e31aef6aSopenharmony_ci 1333e31aef6aSopenharmony_ci 1334e31aef6aSopenharmony_cidef sync_do_list(value: "t.Iterable[V]") -> "t.List[V]": 1335e31aef6aSopenharmony_ci """Convert the value into a list. If it was a string the returned list 1336e31aef6aSopenharmony_ci will be a list of characters. 1337e31aef6aSopenharmony_ci """ 1338e31aef6aSopenharmony_ci return list(value) 1339e31aef6aSopenharmony_ci 1340e31aef6aSopenharmony_ci 1341e31aef6aSopenharmony_ci@async_variant(sync_do_list) # type: ignore 1342e31aef6aSopenharmony_ciasync def do_list(value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]") -> "t.List[V]": 1343e31aef6aSopenharmony_ci return await auto_to_list(value) 1344e31aef6aSopenharmony_ci 1345e31aef6aSopenharmony_ci 1346e31aef6aSopenharmony_cidef do_mark_safe(value: str) -> Markup: 1347e31aef6aSopenharmony_ci """Mark the value as safe which means that in an environment with automatic 1348e31aef6aSopenharmony_ci escaping enabled this variable will not be escaped. 1349e31aef6aSopenharmony_ci """ 1350e31aef6aSopenharmony_ci return Markup(value) 1351e31aef6aSopenharmony_ci 1352e31aef6aSopenharmony_ci 1353e31aef6aSopenharmony_cidef do_mark_unsafe(value: str) -> str: 1354e31aef6aSopenharmony_ci """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" 1355e31aef6aSopenharmony_ci return str(value) 1356e31aef6aSopenharmony_ci 1357e31aef6aSopenharmony_ci 1358e31aef6aSopenharmony_ci@typing.overload 1359e31aef6aSopenharmony_cidef do_reverse(value: str) -> str: 1360e31aef6aSopenharmony_ci ... 1361e31aef6aSopenharmony_ci 1362e31aef6aSopenharmony_ci 1363e31aef6aSopenharmony_ci@typing.overload 1364e31aef6aSopenharmony_cidef do_reverse(value: "t.Iterable[V]") -> "t.Iterable[V]": 1365e31aef6aSopenharmony_ci ... 1366e31aef6aSopenharmony_ci 1367e31aef6aSopenharmony_ci 1368e31aef6aSopenharmony_cidef do_reverse(value: t.Union[str, t.Iterable[V]]) -> t.Union[str, t.Iterable[V]]: 1369e31aef6aSopenharmony_ci """Reverse the object or return an iterator that iterates over it the other 1370e31aef6aSopenharmony_ci way round. 1371e31aef6aSopenharmony_ci """ 1372e31aef6aSopenharmony_ci if isinstance(value, str): 1373e31aef6aSopenharmony_ci return value[::-1] 1374e31aef6aSopenharmony_ci 1375e31aef6aSopenharmony_ci try: 1376e31aef6aSopenharmony_ci return reversed(value) # type: ignore 1377e31aef6aSopenharmony_ci except TypeError: 1378e31aef6aSopenharmony_ci try: 1379e31aef6aSopenharmony_ci rv = list(value) 1380e31aef6aSopenharmony_ci rv.reverse() 1381e31aef6aSopenharmony_ci return rv 1382e31aef6aSopenharmony_ci except TypeError as e: 1383e31aef6aSopenharmony_ci raise FilterArgumentError("argument must be iterable") from e 1384e31aef6aSopenharmony_ci 1385e31aef6aSopenharmony_ci 1386e31aef6aSopenharmony_ci@pass_environment 1387e31aef6aSopenharmony_cidef do_attr( 1388e31aef6aSopenharmony_ci environment: "Environment", obj: t.Any, name: str 1389e31aef6aSopenharmony_ci) -> t.Union[Undefined, t.Any]: 1390e31aef6aSopenharmony_ci """Get an attribute of an object. ``foo|attr("bar")`` works like 1391e31aef6aSopenharmony_ci ``foo.bar`` just that always an attribute is returned and items are not 1392e31aef6aSopenharmony_ci looked up. 1393e31aef6aSopenharmony_ci 1394e31aef6aSopenharmony_ci See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. 1395e31aef6aSopenharmony_ci """ 1396e31aef6aSopenharmony_ci try: 1397e31aef6aSopenharmony_ci name = str(name) 1398e31aef6aSopenharmony_ci except UnicodeError: 1399e31aef6aSopenharmony_ci pass 1400e31aef6aSopenharmony_ci else: 1401e31aef6aSopenharmony_ci try: 1402e31aef6aSopenharmony_ci value = getattr(obj, name) 1403e31aef6aSopenharmony_ci except AttributeError: 1404e31aef6aSopenharmony_ci pass 1405e31aef6aSopenharmony_ci else: 1406e31aef6aSopenharmony_ci if environment.sandboxed: 1407e31aef6aSopenharmony_ci environment = t.cast("SandboxedEnvironment", environment) 1408e31aef6aSopenharmony_ci 1409e31aef6aSopenharmony_ci if not environment.is_safe_attribute(obj, name, value): 1410e31aef6aSopenharmony_ci return environment.unsafe_undefined(obj, name) 1411e31aef6aSopenharmony_ci 1412e31aef6aSopenharmony_ci return value 1413e31aef6aSopenharmony_ci 1414e31aef6aSopenharmony_ci return environment.undefined(obj=obj, name=name) 1415e31aef6aSopenharmony_ci 1416e31aef6aSopenharmony_ci 1417e31aef6aSopenharmony_ci@typing.overload 1418e31aef6aSopenharmony_cidef sync_do_map( 1419e31aef6aSopenharmony_ci context: "Context", value: t.Iterable, name: str, *args: t.Any, **kwargs: t.Any 1420e31aef6aSopenharmony_ci) -> t.Iterable: 1421e31aef6aSopenharmony_ci ... 1422e31aef6aSopenharmony_ci 1423e31aef6aSopenharmony_ci 1424e31aef6aSopenharmony_ci@typing.overload 1425e31aef6aSopenharmony_cidef sync_do_map( 1426e31aef6aSopenharmony_ci context: "Context", 1427e31aef6aSopenharmony_ci value: t.Iterable, 1428e31aef6aSopenharmony_ci *, 1429e31aef6aSopenharmony_ci attribute: str = ..., 1430e31aef6aSopenharmony_ci default: t.Optional[t.Any] = None, 1431e31aef6aSopenharmony_ci) -> t.Iterable: 1432e31aef6aSopenharmony_ci ... 1433e31aef6aSopenharmony_ci 1434e31aef6aSopenharmony_ci 1435e31aef6aSopenharmony_ci@pass_context 1436e31aef6aSopenharmony_cidef sync_do_map( 1437e31aef6aSopenharmony_ci context: "Context", value: t.Iterable, *args: t.Any, **kwargs: t.Any 1438e31aef6aSopenharmony_ci) -> t.Iterable: 1439e31aef6aSopenharmony_ci """Applies a filter on a sequence of objects or looks up an attribute. 1440e31aef6aSopenharmony_ci This is useful when dealing with lists of objects but you are really 1441e31aef6aSopenharmony_ci only interested in a certain value of it. 1442e31aef6aSopenharmony_ci 1443e31aef6aSopenharmony_ci The basic usage is mapping on an attribute. Imagine you have a list 1444e31aef6aSopenharmony_ci of users but you are only interested in a list of usernames: 1445e31aef6aSopenharmony_ci 1446e31aef6aSopenharmony_ci .. sourcecode:: jinja 1447e31aef6aSopenharmony_ci 1448e31aef6aSopenharmony_ci Users on this page: {{ users|map(attribute='username')|join(', ') }} 1449e31aef6aSopenharmony_ci 1450e31aef6aSopenharmony_ci You can specify a ``default`` value to use if an object in the list 1451e31aef6aSopenharmony_ci does not have the given attribute. 1452e31aef6aSopenharmony_ci 1453e31aef6aSopenharmony_ci .. sourcecode:: jinja 1454e31aef6aSopenharmony_ci 1455e31aef6aSopenharmony_ci {{ users|map(attribute="username", default="Anonymous")|join(", ") }} 1456e31aef6aSopenharmony_ci 1457e31aef6aSopenharmony_ci Alternatively you can let it invoke a filter by passing the name of the 1458e31aef6aSopenharmony_ci filter and the arguments afterwards. A good example would be applying a 1459e31aef6aSopenharmony_ci text conversion filter on a sequence: 1460e31aef6aSopenharmony_ci 1461e31aef6aSopenharmony_ci .. sourcecode:: jinja 1462e31aef6aSopenharmony_ci 1463e31aef6aSopenharmony_ci Users on this page: {{ titles|map('lower')|join(', ') }} 1464e31aef6aSopenharmony_ci 1465e31aef6aSopenharmony_ci Similar to a generator comprehension such as: 1466e31aef6aSopenharmony_ci 1467e31aef6aSopenharmony_ci .. code-block:: python 1468e31aef6aSopenharmony_ci 1469e31aef6aSopenharmony_ci (u.username for u in users) 1470e31aef6aSopenharmony_ci (getattr(u, "username", "Anonymous") for u in users) 1471e31aef6aSopenharmony_ci (do_lower(x) for x in titles) 1472e31aef6aSopenharmony_ci 1473e31aef6aSopenharmony_ci .. versionchanged:: 2.11.0 1474e31aef6aSopenharmony_ci Added the ``default`` parameter. 1475e31aef6aSopenharmony_ci 1476e31aef6aSopenharmony_ci .. versionadded:: 2.7 1477e31aef6aSopenharmony_ci """ 1478e31aef6aSopenharmony_ci if value: 1479e31aef6aSopenharmony_ci func = prepare_map(context, args, kwargs) 1480e31aef6aSopenharmony_ci 1481e31aef6aSopenharmony_ci for item in value: 1482e31aef6aSopenharmony_ci yield func(item) 1483e31aef6aSopenharmony_ci 1484e31aef6aSopenharmony_ci 1485e31aef6aSopenharmony_ci@typing.overload 1486e31aef6aSopenharmony_cidef do_map( 1487e31aef6aSopenharmony_ci context: "Context", 1488e31aef6aSopenharmony_ci value: t.Union[t.AsyncIterable, t.Iterable], 1489e31aef6aSopenharmony_ci name: str, 1490e31aef6aSopenharmony_ci *args: t.Any, 1491e31aef6aSopenharmony_ci **kwargs: t.Any, 1492e31aef6aSopenharmony_ci) -> t.Iterable: 1493e31aef6aSopenharmony_ci ... 1494e31aef6aSopenharmony_ci 1495e31aef6aSopenharmony_ci 1496e31aef6aSopenharmony_ci@typing.overload 1497e31aef6aSopenharmony_cidef do_map( 1498e31aef6aSopenharmony_ci context: "Context", 1499e31aef6aSopenharmony_ci value: t.Union[t.AsyncIterable, t.Iterable], 1500e31aef6aSopenharmony_ci *, 1501e31aef6aSopenharmony_ci attribute: str = ..., 1502e31aef6aSopenharmony_ci default: t.Optional[t.Any] = None, 1503e31aef6aSopenharmony_ci) -> t.Iterable: 1504e31aef6aSopenharmony_ci ... 1505e31aef6aSopenharmony_ci 1506e31aef6aSopenharmony_ci 1507e31aef6aSopenharmony_ci@async_variant(sync_do_map) # type: ignore 1508e31aef6aSopenharmony_ciasync def do_map( 1509e31aef6aSopenharmony_ci context: "Context", 1510e31aef6aSopenharmony_ci value: t.Union[t.AsyncIterable, t.Iterable], 1511e31aef6aSopenharmony_ci *args: t.Any, 1512e31aef6aSopenharmony_ci **kwargs: t.Any, 1513e31aef6aSopenharmony_ci) -> t.AsyncIterable: 1514e31aef6aSopenharmony_ci if value: 1515e31aef6aSopenharmony_ci func = prepare_map(context, args, kwargs) 1516e31aef6aSopenharmony_ci 1517e31aef6aSopenharmony_ci async for item in auto_aiter(value): 1518e31aef6aSopenharmony_ci yield await auto_await(func(item)) 1519e31aef6aSopenharmony_ci 1520e31aef6aSopenharmony_ci 1521e31aef6aSopenharmony_ci@pass_context 1522e31aef6aSopenharmony_cidef sync_do_select( 1523e31aef6aSopenharmony_ci context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1524e31aef6aSopenharmony_ci) -> "t.Iterator[V]": 1525e31aef6aSopenharmony_ci """Filters a sequence of objects by applying a test to each object, 1526e31aef6aSopenharmony_ci and only selecting the objects with the test succeeding. 1527e31aef6aSopenharmony_ci 1528e31aef6aSopenharmony_ci If no test is specified, each object will be evaluated as a boolean. 1529e31aef6aSopenharmony_ci 1530e31aef6aSopenharmony_ci Example usage: 1531e31aef6aSopenharmony_ci 1532e31aef6aSopenharmony_ci .. sourcecode:: jinja 1533e31aef6aSopenharmony_ci 1534e31aef6aSopenharmony_ci {{ numbers|select("odd") }} 1535e31aef6aSopenharmony_ci {{ numbers|select("odd") }} 1536e31aef6aSopenharmony_ci {{ numbers|select("divisibleby", 3) }} 1537e31aef6aSopenharmony_ci {{ numbers|select("lessthan", 42) }} 1538e31aef6aSopenharmony_ci {{ strings|select("equalto", "mystring") }} 1539e31aef6aSopenharmony_ci 1540e31aef6aSopenharmony_ci Similar to a generator comprehension such as: 1541e31aef6aSopenharmony_ci 1542e31aef6aSopenharmony_ci .. code-block:: python 1543e31aef6aSopenharmony_ci 1544e31aef6aSopenharmony_ci (n for n in numbers if test_odd(n)) 1545e31aef6aSopenharmony_ci (n for n in numbers if test_divisibleby(n, 3)) 1546e31aef6aSopenharmony_ci 1547e31aef6aSopenharmony_ci .. versionadded:: 2.7 1548e31aef6aSopenharmony_ci """ 1549e31aef6aSopenharmony_ci return select_or_reject(context, value, args, kwargs, lambda x: x, False) 1550e31aef6aSopenharmony_ci 1551e31aef6aSopenharmony_ci 1552e31aef6aSopenharmony_ci@async_variant(sync_do_select) # type: ignore 1553e31aef6aSopenharmony_ciasync def do_select( 1554e31aef6aSopenharmony_ci context: "Context", 1555e31aef6aSopenharmony_ci value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1556e31aef6aSopenharmony_ci *args: t.Any, 1557e31aef6aSopenharmony_ci **kwargs: t.Any, 1558e31aef6aSopenharmony_ci) -> "t.AsyncIterator[V]": 1559e31aef6aSopenharmony_ci return async_select_or_reject(context, value, args, kwargs, lambda x: x, False) 1560e31aef6aSopenharmony_ci 1561e31aef6aSopenharmony_ci 1562e31aef6aSopenharmony_ci@pass_context 1563e31aef6aSopenharmony_cidef sync_do_reject( 1564e31aef6aSopenharmony_ci context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1565e31aef6aSopenharmony_ci) -> "t.Iterator[V]": 1566e31aef6aSopenharmony_ci """Filters a sequence of objects by applying a test to each object, 1567e31aef6aSopenharmony_ci and rejecting the objects with the test succeeding. 1568e31aef6aSopenharmony_ci 1569e31aef6aSopenharmony_ci If no test is specified, each object will be evaluated as a boolean. 1570e31aef6aSopenharmony_ci 1571e31aef6aSopenharmony_ci Example usage: 1572e31aef6aSopenharmony_ci 1573e31aef6aSopenharmony_ci .. sourcecode:: jinja 1574e31aef6aSopenharmony_ci 1575e31aef6aSopenharmony_ci {{ numbers|reject("odd") }} 1576e31aef6aSopenharmony_ci 1577e31aef6aSopenharmony_ci Similar to a generator comprehension such as: 1578e31aef6aSopenharmony_ci 1579e31aef6aSopenharmony_ci .. code-block:: python 1580e31aef6aSopenharmony_ci 1581e31aef6aSopenharmony_ci (n for n in numbers if not test_odd(n)) 1582e31aef6aSopenharmony_ci 1583e31aef6aSopenharmony_ci .. versionadded:: 2.7 1584e31aef6aSopenharmony_ci """ 1585e31aef6aSopenharmony_ci return select_or_reject(context, value, args, kwargs, lambda x: not x, False) 1586e31aef6aSopenharmony_ci 1587e31aef6aSopenharmony_ci 1588e31aef6aSopenharmony_ci@async_variant(sync_do_reject) # type: ignore 1589e31aef6aSopenharmony_ciasync def do_reject( 1590e31aef6aSopenharmony_ci context: "Context", 1591e31aef6aSopenharmony_ci value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1592e31aef6aSopenharmony_ci *args: t.Any, 1593e31aef6aSopenharmony_ci **kwargs: t.Any, 1594e31aef6aSopenharmony_ci) -> "t.AsyncIterator[V]": 1595e31aef6aSopenharmony_ci return async_select_or_reject(context, value, args, kwargs, lambda x: not x, False) 1596e31aef6aSopenharmony_ci 1597e31aef6aSopenharmony_ci 1598e31aef6aSopenharmony_ci@pass_context 1599e31aef6aSopenharmony_cidef sync_do_selectattr( 1600e31aef6aSopenharmony_ci context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1601e31aef6aSopenharmony_ci) -> "t.Iterator[V]": 1602e31aef6aSopenharmony_ci """Filters a sequence of objects by applying a test to the specified 1603e31aef6aSopenharmony_ci attribute of each object, and only selecting the objects with the 1604e31aef6aSopenharmony_ci test succeeding. 1605e31aef6aSopenharmony_ci 1606e31aef6aSopenharmony_ci If no test is specified, the attribute's value will be evaluated as 1607e31aef6aSopenharmony_ci a boolean. 1608e31aef6aSopenharmony_ci 1609e31aef6aSopenharmony_ci Example usage: 1610e31aef6aSopenharmony_ci 1611e31aef6aSopenharmony_ci .. sourcecode:: jinja 1612e31aef6aSopenharmony_ci 1613e31aef6aSopenharmony_ci {{ users|selectattr("is_active") }} 1614e31aef6aSopenharmony_ci {{ users|selectattr("email", "none") }} 1615e31aef6aSopenharmony_ci 1616e31aef6aSopenharmony_ci Similar to a generator comprehension such as: 1617e31aef6aSopenharmony_ci 1618e31aef6aSopenharmony_ci .. code-block:: python 1619e31aef6aSopenharmony_ci 1620e31aef6aSopenharmony_ci (u for user in users if user.is_active) 1621e31aef6aSopenharmony_ci (u for user in users if test_none(user.email)) 1622e31aef6aSopenharmony_ci 1623e31aef6aSopenharmony_ci .. versionadded:: 2.7 1624e31aef6aSopenharmony_ci """ 1625e31aef6aSopenharmony_ci return select_or_reject(context, value, args, kwargs, lambda x: x, True) 1626e31aef6aSopenharmony_ci 1627e31aef6aSopenharmony_ci 1628e31aef6aSopenharmony_ci@async_variant(sync_do_selectattr) # type: ignore 1629e31aef6aSopenharmony_ciasync def do_selectattr( 1630e31aef6aSopenharmony_ci context: "Context", 1631e31aef6aSopenharmony_ci value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1632e31aef6aSopenharmony_ci *args: t.Any, 1633e31aef6aSopenharmony_ci **kwargs: t.Any, 1634e31aef6aSopenharmony_ci) -> "t.AsyncIterator[V]": 1635e31aef6aSopenharmony_ci return async_select_or_reject(context, value, args, kwargs, lambda x: x, True) 1636e31aef6aSopenharmony_ci 1637e31aef6aSopenharmony_ci 1638e31aef6aSopenharmony_ci@pass_context 1639e31aef6aSopenharmony_cidef sync_do_rejectattr( 1640e31aef6aSopenharmony_ci context: "Context", value: "t.Iterable[V]", *args: t.Any, **kwargs: t.Any 1641e31aef6aSopenharmony_ci) -> "t.Iterator[V]": 1642e31aef6aSopenharmony_ci """Filters a sequence of objects by applying a test to the specified 1643e31aef6aSopenharmony_ci attribute of each object, and rejecting the objects with the test 1644e31aef6aSopenharmony_ci succeeding. 1645e31aef6aSopenharmony_ci 1646e31aef6aSopenharmony_ci If no test is specified, the attribute's value will be evaluated as 1647e31aef6aSopenharmony_ci a boolean. 1648e31aef6aSopenharmony_ci 1649e31aef6aSopenharmony_ci .. sourcecode:: jinja 1650e31aef6aSopenharmony_ci 1651e31aef6aSopenharmony_ci {{ users|rejectattr("is_active") }} 1652e31aef6aSopenharmony_ci {{ users|rejectattr("email", "none") }} 1653e31aef6aSopenharmony_ci 1654e31aef6aSopenharmony_ci Similar to a generator comprehension such as: 1655e31aef6aSopenharmony_ci 1656e31aef6aSopenharmony_ci .. code-block:: python 1657e31aef6aSopenharmony_ci 1658e31aef6aSopenharmony_ci (u for user in users if not user.is_active) 1659e31aef6aSopenharmony_ci (u for user in users if not test_none(user.email)) 1660e31aef6aSopenharmony_ci 1661e31aef6aSopenharmony_ci .. versionadded:: 2.7 1662e31aef6aSopenharmony_ci """ 1663e31aef6aSopenharmony_ci return select_or_reject(context, value, args, kwargs, lambda x: not x, True) 1664e31aef6aSopenharmony_ci 1665e31aef6aSopenharmony_ci 1666e31aef6aSopenharmony_ci@async_variant(sync_do_rejectattr) # type: ignore 1667e31aef6aSopenharmony_ciasync def do_rejectattr( 1668e31aef6aSopenharmony_ci context: "Context", 1669e31aef6aSopenharmony_ci value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1670e31aef6aSopenharmony_ci *args: t.Any, 1671e31aef6aSopenharmony_ci **kwargs: t.Any, 1672e31aef6aSopenharmony_ci) -> "t.AsyncIterator[V]": 1673e31aef6aSopenharmony_ci return async_select_or_reject(context, value, args, kwargs, lambda x: not x, True) 1674e31aef6aSopenharmony_ci 1675e31aef6aSopenharmony_ci 1676e31aef6aSopenharmony_ci@pass_eval_context 1677e31aef6aSopenharmony_cidef do_tojson( 1678e31aef6aSopenharmony_ci eval_ctx: "EvalContext", value: t.Any, indent: t.Optional[int] = None 1679e31aef6aSopenharmony_ci) -> Markup: 1680e31aef6aSopenharmony_ci """Serialize an object to a string of JSON, and mark it safe to 1681e31aef6aSopenharmony_ci render in HTML. This filter is only for use in HTML documents. 1682e31aef6aSopenharmony_ci 1683e31aef6aSopenharmony_ci The returned string is safe to render in HTML documents and 1684e31aef6aSopenharmony_ci ``<script>`` tags. The exception is in HTML attributes that are 1685e31aef6aSopenharmony_ci double quoted; either use single quotes or the ``|forceescape`` 1686e31aef6aSopenharmony_ci filter. 1687e31aef6aSopenharmony_ci 1688e31aef6aSopenharmony_ci :param value: The object to serialize to JSON. 1689e31aef6aSopenharmony_ci :param indent: The ``indent`` parameter passed to ``dumps``, for 1690e31aef6aSopenharmony_ci pretty-printing the value. 1691e31aef6aSopenharmony_ci 1692e31aef6aSopenharmony_ci .. versionadded:: 2.9 1693e31aef6aSopenharmony_ci """ 1694e31aef6aSopenharmony_ci policies = eval_ctx.environment.policies 1695e31aef6aSopenharmony_ci dumps = policies["json.dumps_function"] 1696e31aef6aSopenharmony_ci kwargs = policies["json.dumps_kwargs"] 1697e31aef6aSopenharmony_ci 1698e31aef6aSopenharmony_ci if indent is not None: 1699e31aef6aSopenharmony_ci kwargs = kwargs.copy() 1700e31aef6aSopenharmony_ci kwargs["indent"] = indent 1701e31aef6aSopenharmony_ci 1702e31aef6aSopenharmony_ci return htmlsafe_json_dumps(value, dumps=dumps, **kwargs) 1703e31aef6aSopenharmony_ci 1704e31aef6aSopenharmony_ci 1705e31aef6aSopenharmony_cidef prepare_map( 1706e31aef6aSopenharmony_ci context: "Context", args: t.Tuple, kwargs: t.Dict[str, t.Any] 1707e31aef6aSopenharmony_ci) -> t.Callable[[t.Any], t.Any]: 1708e31aef6aSopenharmony_ci if not args and "attribute" in kwargs: 1709e31aef6aSopenharmony_ci attribute = kwargs.pop("attribute") 1710e31aef6aSopenharmony_ci default = kwargs.pop("default", None) 1711e31aef6aSopenharmony_ci 1712e31aef6aSopenharmony_ci if kwargs: 1713e31aef6aSopenharmony_ci raise FilterArgumentError( 1714e31aef6aSopenharmony_ci f"Unexpected keyword argument {next(iter(kwargs))!r}" 1715e31aef6aSopenharmony_ci ) 1716e31aef6aSopenharmony_ci 1717e31aef6aSopenharmony_ci func = make_attrgetter(context.environment, attribute, default=default) 1718e31aef6aSopenharmony_ci else: 1719e31aef6aSopenharmony_ci try: 1720e31aef6aSopenharmony_ci name = args[0] 1721e31aef6aSopenharmony_ci args = args[1:] 1722e31aef6aSopenharmony_ci except LookupError: 1723e31aef6aSopenharmony_ci raise FilterArgumentError("map requires a filter argument") from None 1724e31aef6aSopenharmony_ci 1725e31aef6aSopenharmony_ci def func(item: t.Any) -> t.Any: 1726e31aef6aSopenharmony_ci return context.environment.call_filter( 1727e31aef6aSopenharmony_ci name, item, args, kwargs, context=context 1728e31aef6aSopenharmony_ci ) 1729e31aef6aSopenharmony_ci 1730e31aef6aSopenharmony_ci return func 1731e31aef6aSopenharmony_ci 1732e31aef6aSopenharmony_ci 1733e31aef6aSopenharmony_cidef prepare_select_or_reject( 1734e31aef6aSopenharmony_ci context: "Context", 1735e31aef6aSopenharmony_ci args: t.Tuple, 1736e31aef6aSopenharmony_ci kwargs: t.Dict[str, t.Any], 1737e31aef6aSopenharmony_ci modfunc: t.Callable[[t.Any], t.Any], 1738e31aef6aSopenharmony_ci lookup_attr: bool, 1739e31aef6aSopenharmony_ci) -> t.Callable[[t.Any], t.Any]: 1740e31aef6aSopenharmony_ci if lookup_attr: 1741e31aef6aSopenharmony_ci try: 1742e31aef6aSopenharmony_ci attr = args[0] 1743e31aef6aSopenharmony_ci except LookupError: 1744e31aef6aSopenharmony_ci raise FilterArgumentError("Missing parameter for attribute name") from None 1745e31aef6aSopenharmony_ci 1746e31aef6aSopenharmony_ci transfunc = make_attrgetter(context.environment, attr) 1747e31aef6aSopenharmony_ci off = 1 1748e31aef6aSopenharmony_ci else: 1749e31aef6aSopenharmony_ci off = 0 1750e31aef6aSopenharmony_ci 1751e31aef6aSopenharmony_ci def transfunc(x: V) -> V: 1752e31aef6aSopenharmony_ci return x 1753e31aef6aSopenharmony_ci 1754e31aef6aSopenharmony_ci try: 1755e31aef6aSopenharmony_ci name = args[off] 1756e31aef6aSopenharmony_ci args = args[1 + off :] 1757e31aef6aSopenharmony_ci 1758e31aef6aSopenharmony_ci def func(item: t.Any) -> t.Any: 1759e31aef6aSopenharmony_ci return context.environment.call_test(name, item, args, kwargs) 1760e31aef6aSopenharmony_ci 1761e31aef6aSopenharmony_ci except LookupError: 1762e31aef6aSopenharmony_ci func = bool # type: ignore 1763e31aef6aSopenharmony_ci 1764e31aef6aSopenharmony_ci return lambda item: modfunc(func(transfunc(item))) 1765e31aef6aSopenharmony_ci 1766e31aef6aSopenharmony_ci 1767e31aef6aSopenharmony_cidef select_or_reject( 1768e31aef6aSopenharmony_ci context: "Context", 1769e31aef6aSopenharmony_ci value: "t.Iterable[V]", 1770e31aef6aSopenharmony_ci args: t.Tuple, 1771e31aef6aSopenharmony_ci kwargs: t.Dict[str, t.Any], 1772e31aef6aSopenharmony_ci modfunc: t.Callable[[t.Any], t.Any], 1773e31aef6aSopenharmony_ci lookup_attr: bool, 1774e31aef6aSopenharmony_ci) -> "t.Iterator[V]": 1775e31aef6aSopenharmony_ci if value: 1776e31aef6aSopenharmony_ci func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr) 1777e31aef6aSopenharmony_ci 1778e31aef6aSopenharmony_ci for item in value: 1779e31aef6aSopenharmony_ci if func(item): 1780e31aef6aSopenharmony_ci yield item 1781e31aef6aSopenharmony_ci 1782e31aef6aSopenharmony_ci 1783e31aef6aSopenharmony_ciasync def async_select_or_reject( 1784e31aef6aSopenharmony_ci context: "Context", 1785e31aef6aSopenharmony_ci value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", 1786e31aef6aSopenharmony_ci args: t.Tuple, 1787e31aef6aSopenharmony_ci kwargs: t.Dict[str, t.Any], 1788e31aef6aSopenharmony_ci modfunc: t.Callable[[t.Any], t.Any], 1789e31aef6aSopenharmony_ci lookup_attr: bool, 1790e31aef6aSopenharmony_ci) -> "t.AsyncIterator[V]": 1791e31aef6aSopenharmony_ci if value: 1792e31aef6aSopenharmony_ci func = prepare_select_or_reject(context, args, kwargs, modfunc, lookup_attr) 1793e31aef6aSopenharmony_ci 1794e31aef6aSopenharmony_ci async for item in auto_aiter(value): 1795e31aef6aSopenharmony_ci if func(item): 1796e31aef6aSopenharmony_ci yield item 1797e31aef6aSopenharmony_ci 1798e31aef6aSopenharmony_ci 1799e31aef6aSopenharmony_ciFILTERS = { 1800e31aef6aSopenharmony_ci "abs": abs, 1801e31aef6aSopenharmony_ci "attr": do_attr, 1802e31aef6aSopenharmony_ci "batch": do_batch, 1803e31aef6aSopenharmony_ci "capitalize": do_capitalize, 1804e31aef6aSopenharmony_ci "center": do_center, 1805e31aef6aSopenharmony_ci "count": len, 1806e31aef6aSopenharmony_ci "d": do_default, 1807e31aef6aSopenharmony_ci "default": do_default, 1808e31aef6aSopenharmony_ci "dictsort": do_dictsort, 1809e31aef6aSopenharmony_ci "e": escape, 1810e31aef6aSopenharmony_ci "escape": escape, 1811e31aef6aSopenharmony_ci "filesizeformat": do_filesizeformat, 1812e31aef6aSopenharmony_ci "first": do_first, 1813e31aef6aSopenharmony_ci "float": do_float, 1814e31aef6aSopenharmony_ci "forceescape": do_forceescape, 1815e31aef6aSopenharmony_ci "format": do_format, 1816e31aef6aSopenharmony_ci "groupby": do_groupby, 1817e31aef6aSopenharmony_ci "indent": do_indent, 1818e31aef6aSopenharmony_ci "int": do_int, 1819e31aef6aSopenharmony_ci "join": do_join, 1820e31aef6aSopenharmony_ci "last": do_last, 1821e31aef6aSopenharmony_ci "length": len, 1822e31aef6aSopenharmony_ci "list": do_list, 1823e31aef6aSopenharmony_ci "lower": do_lower, 1824e31aef6aSopenharmony_ci "items": do_items, 1825e31aef6aSopenharmony_ci "map": do_map, 1826e31aef6aSopenharmony_ci "min": do_min, 1827e31aef6aSopenharmony_ci "max": do_max, 1828e31aef6aSopenharmony_ci "pprint": do_pprint, 1829e31aef6aSopenharmony_ci "random": do_random, 1830e31aef6aSopenharmony_ci "reject": do_reject, 1831e31aef6aSopenharmony_ci "rejectattr": do_rejectattr, 1832e31aef6aSopenharmony_ci "replace": do_replace, 1833e31aef6aSopenharmony_ci "reverse": do_reverse, 1834e31aef6aSopenharmony_ci "round": do_round, 1835e31aef6aSopenharmony_ci "safe": do_mark_safe, 1836e31aef6aSopenharmony_ci "select": do_select, 1837e31aef6aSopenharmony_ci "selectattr": do_selectattr, 1838e31aef6aSopenharmony_ci "slice": do_slice, 1839e31aef6aSopenharmony_ci "sort": do_sort, 1840e31aef6aSopenharmony_ci "string": soft_str, 1841e31aef6aSopenharmony_ci "striptags": do_striptags, 1842e31aef6aSopenharmony_ci "sum": do_sum, 1843e31aef6aSopenharmony_ci "title": do_title, 1844e31aef6aSopenharmony_ci "trim": do_trim, 1845e31aef6aSopenharmony_ci "truncate": do_truncate, 1846e31aef6aSopenharmony_ci "unique": do_unique, 1847e31aef6aSopenharmony_ci "upper": do_upper, 1848e31aef6aSopenharmony_ci "urlencode": do_urlencode, 1849e31aef6aSopenharmony_ci "urlize": do_urlize, 1850e31aef6aSopenharmony_ci "wordcount": do_wordcount, 1851e31aef6aSopenharmony_ci "wordwrap": do_wordwrap, 1852e31aef6aSopenharmony_ci "xmlattr": do_xmlattr, 1853e31aef6aSopenharmony_ci "tojson": do_tojson, 1854e31aef6aSopenharmony_ci} 1855