256 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Built-in template tests used with the ``is`` operator."""
 | 
						|
import operator
 | 
						|
import typing as t
 | 
						|
from collections import abc
 | 
						|
from numbers import Number
 | 
						|
 | 
						|
from .runtime import Undefined
 | 
						|
from .utils import pass_environment
 | 
						|
 | 
						|
if t.TYPE_CHECKING:
 | 
						|
    from .environment import Environment
 | 
						|
 | 
						|
 | 
						|
def test_odd(value: int) -> bool:
 | 
						|
    """Return true if the variable is odd."""
 | 
						|
    return value % 2 == 1
 | 
						|
 | 
						|
 | 
						|
def test_even(value: int) -> bool:
 | 
						|
    """Return true if the variable is even."""
 | 
						|
    return value % 2 == 0
 | 
						|
 | 
						|
 | 
						|
def test_divisibleby(value: int, num: int) -> bool:
 | 
						|
    """Check if a variable is divisible by a number."""
 | 
						|
    return value % num == 0
 | 
						|
 | 
						|
 | 
						|
def test_defined(value: t.Any) -> bool:
 | 
						|
    """Return true if the variable is defined:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {% if variable is defined %}
 | 
						|
            value of variable: {{ variable }}
 | 
						|
        {% else %}
 | 
						|
            variable is not defined
 | 
						|
        {% endif %}
 | 
						|
 | 
						|
    See the :func:`default` filter for a simple way to set undefined
 | 
						|
    variables.
 | 
						|
    """
 | 
						|
    return not isinstance(value, Undefined)
 | 
						|
 | 
						|
 | 
						|
def test_undefined(value: t.Any) -> bool:
 | 
						|
    """Like :func:`defined` but the other way round."""
 | 
						|
    return isinstance(value, Undefined)
 | 
						|
 | 
						|
 | 
						|
@pass_environment
 | 
						|
def test_filter(env: "Environment", value: str) -> bool:
 | 
						|
    """Check if a filter exists by name. Useful if a filter may be
 | 
						|
    optionally available.
 | 
						|
 | 
						|
    .. code-block:: jinja
 | 
						|
 | 
						|
        {% if 'markdown' is filter %}
 | 
						|
            {{ value | markdown }}
 | 
						|
        {% else %}
 | 
						|
            {{ value }}
 | 
						|
        {% endif %}
 | 
						|
 | 
						|
    .. versionadded:: 3.0
 | 
						|
    """
 | 
						|
    return value in env.filters
 | 
						|
 | 
						|
 | 
						|
@pass_environment
 | 
						|
def test_test(env: "Environment", value: str) -> bool:
 | 
						|
    """Check if a test exists by name. Useful if a test may be
 | 
						|
    optionally available.
 | 
						|
 | 
						|
    .. code-block:: jinja
 | 
						|
 | 
						|
        {% if 'loud' is test %}
 | 
						|
            {% if value is loud %}
 | 
						|
                {{ value|upper }}
 | 
						|
            {% else %}
 | 
						|
                {{ value|lower }}
 | 
						|
            {% endif %}
 | 
						|
        {% else %}
 | 
						|
            {{ value }}
 | 
						|
        {% endif %}
 | 
						|
 | 
						|
    .. versionadded:: 3.0
 | 
						|
    """
 | 
						|
    return value in env.tests
 | 
						|
 | 
						|
 | 
						|
def test_none(value: t.Any) -> bool:
 | 
						|
    """Return true if the variable is none."""
 | 
						|
    return value is None
 | 
						|
 | 
						|
 | 
						|
def test_boolean(value: t.Any) -> bool:
 | 
						|
    """Return true if the object is a boolean value.
 | 
						|
 | 
						|
    .. versionadded:: 2.11
 | 
						|
    """
 | 
						|
    return value is True or value is False
 | 
						|
 | 
						|
 | 
						|
def test_false(value: t.Any) -> bool:
 | 
						|
    """Return true if the object is False.
 | 
						|
 | 
						|
    .. versionadded:: 2.11
 | 
						|
    """
 | 
						|
    return value is False
 | 
						|
 | 
						|
 | 
						|
def test_true(value: t.Any) -> bool:
 | 
						|
    """Return true if the object is True.
 | 
						|
 | 
						|
    .. versionadded:: 2.11
 | 
						|
    """
 | 
						|
    return value is True
 | 
						|
 | 
						|
 | 
						|
# NOTE: The existing 'number' test matches booleans and floats
 | 
						|
def test_integer(value: t.Any) -> bool:
 | 
						|
    """Return true if the object is an integer.
 | 
						|
 | 
						|
    .. versionadded:: 2.11
 | 
						|
    """
 | 
						|
    return isinstance(value, int) and value is not True and value is not False
 | 
						|
 | 
						|
 | 
						|
# NOTE: The existing 'number' test matches booleans and integers
 | 
						|
def test_float(value: t.Any) -> bool:
 | 
						|
    """Return true if the object is a float.
 | 
						|
 | 
						|
    .. versionadded:: 2.11
 | 
						|
    """
 | 
						|
    return isinstance(value, float)
 | 
						|
 | 
						|
 | 
						|
def test_lower(value: str) -> bool:
 | 
						|
    """Return true if the variable is lowercased."""
 | 
						|
    return str(value).islower()
 | 
						|
 | 
						|
 | 
						|
def test_upper(value: str) -> bool:
 | 
						|
    """Return true if the variable is uppercased."""
 | 
						|
    return str(value).isupper()
 | 
						|
 | 
						|
 | 
						|
def test_string(value: t.Any) -> bool:
 | 
						|
    """Return true if the object is a string."""
 | 
						|
    return isinstance(value, str)
 | 
						|
 | 
						|
 | 
						|
def test_mapping(value: t.Any) -> bool:
 | 
						|
    """Return true if the object is a mapping (dict etc.).
 | 
						|
 | 
						|
    .. versionadded:: 2.6
 | 
						|
    """
 | 
						|
    return isinstance(value, abc.Mapping)
 | 
						|
 | 
						|
 | 
						|
def test_number(value: t.Any) -> bool:
 | 
						|
    """Return true if the variable is a number."""
 | 
						|
    return isinstance(value, Number)
 | 
						|
 | 
						|
 | 
						|
def test_sequence(value: t.Any) -> bool:
 | 
						|
    """Return true if the variable is a sequence. Sequences are variables
 | 
						|
    that are iterable.
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        len(value)
 | 
						|
        value.__getitem__
 | 
						|
    except Exception:
 | 
						|
        return False
 | 
						|
 | 
						|
    return True
 | 
						|
 | 
						|
 | 
						|
def test_sameas(value: t.Any, other: t.Any) -> bool:
 | 
						|
    """Check if an object points to the same memory address than another
 | 
						|
    object:
 | 
						|
 | 
						|
    .. sourcecode:: jinja
 | 
						|
 | 
						|
        {% if foo.attribute is sameas false %}
 | 
						|
            the foo attribute really is the `False` singleton
 | 
						|
        {% endif %}
 | 
						|
    """
 | 
						|
    return value is other
 | 
						|
 | 
						|
 | 
						|
def test_iterable(value: t.Any) -> bool:
 | 
						|
    """Check if it's possible to iterate over an object."""
 | 
						|
    try:
 | 
						|
        iter(value)
 | 
						|
    except TypeError:
 | 
						|
        return False
 | 
						|
 | 
						|
    return True
 | 
						|
 | 
						|
 | 
						|
def test_escaped(value: t.Any) -> bool:
 | 
						|
    """Check if the value is escaped."""
 | 
						|
    return hasattr(value, "__html__")
 | 
						|
 | 
						|
 | 
						|
def test_in(value: t.Any, seq: t.Container) -> bool:
 | 
						|
    """Check if value is in seq.
 | 
						|
 | 
						|
    .. versionadded:: 2.10
 | 
						|
    """
 | 
						|
    return value in seq
 | 
						|
 | 
						|
 | 
						|
TESTS = {
 | 
						|
    "odd": test_odd,
 | 
						|
    "even": test_even,
 | 
						|
    "divisibleby": test_divisibleby,
 | 
						|
    "defined": test_defined,
 | 
						|
    "undefined": test_undefined,
 | 
						|
    "filter": test_filter,
 | 
						|
    "test": test_test,
 | 
						|
    "none": test_none,
 | 
						|
    "boolean": test_boolean,
 | 
						|
    "false": test_false,
 | 
						|
    "true": test_true,
 | 
						|
    "integer": test_integer,
 | 
						|
    "float": test_float,
 | 
						|
    "lower": test_lower,
 | 
						|
    "upper": test_upper,
 | 
						|
    "string": test_string,
 | 
						|
    "mapping": test_mapping,
 | 
						|
    "number": test_number,
 | 
						|
    "sequence": test_sequence,
 | 
						|
    "iterable": test_iterable,
 | 
						|
    "callable": callable,
 | 
						|
    "sameas": test_sameas,
 | 
						|
    "escaped": test_escaped,
 | 
						|
    "in": test_in,
 | 
						|
    "==": operator.eq,
 | 
						|
    "eq": operator.eq,
 | 
						|
    "equalto": operator.eq,
 | 
						|
    "!=": operator.ne,
 | 
						|
    "ne": operator.ne,
 | 
						|
    ">": operator.gt,
 | 
						|
    "gt": operator.gt,
 | 
						|
    "greaterthan": operator.gt,
 | 
						|
    "ge": operator.ge,
 | 
						|
    ">=": operator.ge,
 | 
						|
    "<": operator.lt,
 | 
						|
    "lt": operator.lt,
 | 
						|
    "lessthan": operator.lt,
 | 
						|
    "<=": operator.le,
 | 
						|
    "le": operator.le,
 | 
						|
}
 |