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