1"""Built-in template tests used with the ``is`` operator."""
2import operator
3import re
4from collections import abc
5from numbers import Number
6
7from .runtime import Undefined
8
9number_re = re.compile(r"^-?\d+(\.\d+)?$")
10regex_type = type(number_re)
11test_callable = callable
12
13
14def test_odd(value):
15    """Return true if the variable is odd."""
16    return value % 2 == 1
17
18
19def test_even(value):
20    """Return true if the variable is even."""
21    return value % 2 == 0
22
23
24def test_divisibleby(value, num):
25    """Check if a variable is divisible by a number."""
26    return value % num == 0
27
28
29def test_defined(value):
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):
47    """Like :func:`defined` but the other way round."""
48    return isinstance(value, Undefined)
49
50
51def test_none(value):
52    """Return true if the variable is none."""
53    return value is None
54
55
56def test_boolean(value):
57    """Return true if the object is a boolean value.
58
59    .. versionadded:: 2.11
60    """
61    return value is True or value is False
62
63
64def test_false(value):
65    """Return true if the object is False.
66
67    .. versionadded:: 2.11
68    """
69    return value is False
70
71
72def test_true(value):
73    """Return true if the object is True.
74
75    .. versionadded:: 2.11
76    """
77    return value is True
78
79
80# NOTE: The existing 'number' test matches booleans and floats
81def test_integer(value):
82    """Return true if the object is an integer.
83
84    .. versionadded:: 2.11
85    """
86    return isinstance(value, int) and value is not True and value is not False
87
88
89# NOTE: The existing 'number' test matches booleans and integers
90def test_float(value):
91    """Return true if the object is a float.
92
93    .. versionadded:: 2.11
94    """
95    return isinstance(value, float)
96
97
98def test_lower(value):
99    """Return true if the variable is lowercased."""
100    return str(value).islower()
101
102
103def test_upper(value):
104    """Return true if the variable is uppercased."""
105    return str(value).isupper()
106
107
108def test_string(value):
109    """Return true if the object is a string."""
110    return isinstance(value, str)
111
112
113def test_mapping(value):
114    """Return true if the object is a mapping (dict etc.).
115
116    .. versionadded:: 2.6
117    """
118    return isinstance(value, abc.Mapping)
119
120
121def test_number(value):
122    """Return true if the variable is a number."""
123    return isinstance(value, Number)
124
125
126def test_sequence(value):
127    """Return true if the variable is a sequence. Sequences are variables
128    that are iterable.
129    """
130    try:
131        len(value)
132        value.__getitem__
133    except Exception:
134        return False
135    return True
136
137
138def test_sameas(value, other):
139    """Check if an object points to the same memory address than another
140    object:
141
142    .. sourcecode:: jinja
143
144        {% if foo.attribute is sameas false %}
145            the foo attribute really is the `False` singleton
146        {% endif %}
147    """
148    return value is other
149
150
151def test_iterable(value):
152    """Check if it's possible to iterate over an object."""
153    try:
154        iter(value)
155    except TypeError:
156        return False
157    return True
158
159
160def test_escaped(value):
161    """Check if the value is escaped."""
162    return hasattr(value, "__html__")
163
164
165def test_in(value, seq):
166    """Check if value is in seq.
167
168    .. versionadded:: 2.10
169    """
170    return value in seq
171
172
173TESTS = {
174    "odd": test_odd,
175    "even": test_even,
176    "divisibleby": test_divisibleby,
177    "defined": test_defined,
178    "undefined": test_undefined,
179    "none": test_none,
180    "boolean": test_boolean,
181    "false": test_false,
182    "true": test_true,
183    "integer": test_integer,
184    "float": test_float,
185    "lower": test_lower,
186    "upper": test_upper,
187    "string": test_string,
188    "mapping": test_mapping,
189    "number": test_number,
190    "sequence": test_sequence,
191    "iterable": test_iterable,
192    "callable": test_callable,
193    "sameas": test_sameas,
194    "escaped": test_escaped,
195    "in": test_in,
196    "==": operator.eq,
197    "eq": operator.eq,
198    "equalto": operator.eq,
199    "!=": operator.ne,
200    "ne": operator.ne,
201    ">": operator.gt,
202    "gt": operator.gt,
203    "greaterthan": operator.gt,
204    "ge": operator.ge,
205    ">=": operator.ge,
206    "<": operator.lt,
207    "lt": operator.lt,
208    "lessthan": operator.lt,
209    "<=": operator.le,
210    "le": operator.le,
211}
212