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