1"""Built-in template tests used with the ``is`` operator.""" 2import operator 3import typing as t 4from collections import abc 5from numbers import Number 6 7from .runtime import Undefined 8from .utils import pass_environment 9 10if t.TYPE_CHECKING: 11 from .environment import Environment 12 13 14def test_odd(value: int) -> bool: 15 """Return true if the variable is odd.""" 16 return value % 2 == 1 17 18 19def test_even(value: int) -> bool: 20 """Return true if the variable is even.""" 21 return value % 2 == 0 22 23 24def test_divisibleby(value: int, num: int) -> bool: 25 """Check if a variable is divisible by a number.""" 26 return value % num == 0 27 28 29def test_defined(value: t.Any) -> bool: 30 """Return true if the variable is defined: 31 32 .. sourcecode:: jinja 33 34 {% if variable is defined %} 35 value of variable: {{ variable }} 36 {% else %} 37 variable is not defined 38 {% endif %} 39 40 See the :func:`default` filter for a simple way to set undefined 41 variables. 42 """ 43 return not isinstance(value, Undefined) 44 45 46def test_undefined(value: t.Any) -> bool: 47 """Like :func:`defined` but the other way round.""" 48 return isinstance(value, Undefined) 49 50 51@pass_environment 52def test_filter(env: "Environment", value: str) -> bool: 53 """Check if a filter exists by name. Useful if a filter may be 54 optionally available. 55 56 .. code-block:: jinja 57 58 {% if 'markdown' is filter %} 59 {{ value | markdown }} 60 {% else %} 61 {{ value }} 62 {% endif %} 63 64 .. versionadded:: 3.0 65 """ 66 return value in env.filters 67 68 69@pass_environment 70def test_test(env: "Environment", value: str) -> bool: 71 """Check if a test exists by name. Useful if a test may be 72 optionally available. 73 74 .. code-block:: jinja 75 76 {% if 'loud' is test %} 77 {% if value is loud %} 78 {{ value|upper }} 79 {% else %} 80 {{ value|lower }} 81 {% endif %} 82 {% else %} 83 {{ value }} 84 {% endif %} 85 86 .. versionadded:: 3.0 87 """ 88 return value in env.tests 89 90 91def test_none(value: t.Any) -> bool: 92 """Return true if the variable is none.""" 93 return value is None 94 95 96def test_boolean(value: t.Any) -> bool: 97 """Return true if the object is a boolean value. 98 99 .. versionadded:: 2.11 100 """ 101 return value is True or value is False 102 103 104def test_false(value: t.Any) -> bool: 105 """Return true if the object is False. 106 107 .. versionadded:: 2.11 108 """ 109 return value is False 110 111 112def test_true(value: t.Any) -> bool: 113 """Return true if the object is True. 114 115 .. versionadded:: 2.11 116 """ 117 return value is True 118 119 120# NOTE: The existing 'number' test matches booleans and floats 121def test_integer(value: t.Any) -> bool: 122 """Return true if the object is an integer. 123 124 .. versionadded:: 2.11 125 """ 126 return isinstance(value, int) and value is not True and value is not False 127 128 129# NOTE: The existing 'number' test matches booleans and integers 130def test_float(value: t.Any) -> bool: 131 """Return true if the object is a float. 132 133 .. versionadded:: 2.11 134 """ 135 return isinstance(value, float) 136 137 138def test_lower(value: str) -> bool: 139 """Return true if the variable is lowercased.""" 140 return str(value).islower() 141 142 143def test_upper(value: str) -> bool: 144 """Return true if the variable is uppercased.""" 145 return str(value).isupper() 146 147 148def test_string(value: t.Any) -> bool: 149 """Return true if the object is a string.""" 150 return isinstance(value, str) 151 152 153def test_mapping(value: t.Any) -> bool: 154 """Return true if the object is a mapping (dict etc.). 155 156 .. versionadded:: 2.6 157 """ 158 return isinstance(value, abc.Mapping) 159 160 161def test_number(value: t.Any) -> bool: 162 """Return true if the variable is a number.""" 163 return isinstance(value, Number) 164 165 166def test_sequence(value: t.Any) -> bool: 167 """Return true if the variable is a sequence. Sequences are variables 168 that are iterable. 169 """ 170 try: 171 len(value) 172 value.__getitem__ 173 except Exception: 174 return False 175 176 return True 177 178 179def test_sameas(value: t.Any, other: t.Any) -> bool: 180 """Check if an object points to the same memory address than another 181 object: 182 183 .. sourcecode:: jinja 184 185 {% if foo.attribute is sameas false %} 186 the foo attribute really is the `False` singleton 187 {% endif %} 188 """ 189 return value is other 190 191 192def test_iterable(value: t.Any) -> bool: 193 """Check if it's possible to iterate over an object.""" 194 try: 195 iter(value) 196 except TypeError: 197 return False 198 199 return True 200 201 202def test_escaped(value: t.Any) -> bool: 203 """Check if the value is escaped.""" 204 return hasattr(value, "__html__") 205 206 207def test_in(value: t.Any, seq: t.Container) -> bool: 208 """Check if value is in seq. 209 210 .. versionadded:: 2.10 211 """ 212 return value in seq 213 214 215TESTS = { 216 "odd": test_odd, 217 "even": test_even, 218 "divisibleby": test_divisibleby, 219 "defined": test_defined, 220 "undefined": test_undefined, 221 "filter": test_filter, 222 "test": test_test, 223 "none": test_none, 224 "boolean": test_boolean, 225 "false": test_false, 226 "true": test_true, 227 "integer": test_integer, 228 "float": test_float, 229 "lower": test_lower, 230 "upper": test_upper, 231 "string": test_string, 232 "mapping": test_mapping, 233 "number": test_number, 234 "sequence": test_sequence, 235 "iterable": test_iterable, 236 "callable": callable, 237 "sameas": test_sameas, 238 "escaped": test_escaped, 239 "in": test_in, 240 "==": operator.eq, 241 "eq": operator.eq, 242 "equalto": operator.eq, 243 "!=": operator.ne, 244 "ne": operator.ne, 245 ">": operator.gt, 246 "gt": operator.gt, 247 "greaterthan": operator.gt, 248 "ge": operator.ge, 249 ">=": operator.ge, 250 "<": operator.lt, 251 "lt": operator.lt, 252 "lessthan": operator.lt, 253 "<=": operator.le, 254 "le": operator.le, 255} 256