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