arguments to function calls and filters, or just to extend or include a
template).
-42:
- Integers are whole numbers without a decimal part.
+42 / 123_456:
+ Integers are whole numbers without a decimal part. The '_' character
+ can be used to separate groups for legibility.
-42.23 / 42.1e2:
+42.23 / 42.1e2 / 123_456.789:
Floating point numbers can be written using a '.' as a decimal mark.
They can also be written in scientific notation with an upper or
- lower case 'e' to indicate the exponent part.
+ lower case 'e' to indicate the exponent part. The '_' character can
+ be used to separate groups for legibility, but cannot be used in the
+ exponent part.
['list', 'of', 'objects']:
Everything between two brackets is a list. Lists are useful for storing
from jinja2.exceptions import TemplateSyntaxError
from jinja2.utils import LRUCache
+from ast import literal_eval # to support scientific notation
+
# cache for the lexers. Exists in order to be able to have multiple
# environments with the same lexer
_lexer_cache = LRUCache(50)
whitespace_re = re.compile(r'\s+', re.U)
string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
-integer_re = re.compile(r'\d+')
+integer_re = re.compile(r'(\d+_)*(\d+)')
try:
# check if this Python supports Unicode identifiers
del jinja2._identifier
del _identifier
-float_re = re.compile(r'(?<!\.)\d+(?:\.\d+)?(?:e[+\-]?\d+)?', re.IGNORECASE)
+float_re = re.compile(r'(?<!\.)((((\d+_)*)\d+)(((\.\d+)?e-?\d+)|(\.((\d+_)*\d+))+))', re.IGNORECASE)
newline_re = re.compile(r'(\r\n|\r|\n)')
# internal the tokens and keep references to them
msg = str(e).split(':')[-1].strip()
raise TemplateSyntaxError(msg, lineno, name, filename)
elif token == 'integer':
- value = int(value)
+ value = int(value.replace("_", ""))
elif token == 'float':
- value = literal_eval(value)
+ # remove all "_" first to support more Python versions
+ value = literal_eval(value.replace("_", ""))
elif token == 'operator':
token = operators[value]
yield Token(lineno, token, value)
"""
import random
import pytest
-from jinja2 import Markup, Environment
+from jinja2 import Markup, Environment, Template
from jinja2._compat import text_type, implements_to_string
def test_float(self, env):
tmpl = env.from_string('{{ "42"|float }}|'
'{{ "ajsghasjgd"|float }}|'
+ '{{ "1e2"|float }}|'
'{{ "10e1"|float }}|'
'{{ "10.5e-10"|float }}|'
'{{ "32.32"|float }}')
out = tmpl.render()
- assert out == '42.0|0.0|100.0|1.05e-09|32.32'
+ assert out == '42.0|0.0|100.0|100.0|1.05e-09|32.32'
+
+ out = Template("{{12_3_4.5_6}}|{{12_34e0}}").render()
+ assert out == '1234.56|1234.0'
def test_format(self, env):
tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''')
tmpl = env.from_string('{{ "42"|int }}|{{ "ajsghasjgd"|int }}|'
'{{ "32.32"|int }}|{{ "0x4d32"|int(0, 16) }}|'
+ '{{ "1234567890"|int }}|'
'{{ "011"|int(0, 8)}}|{{ "0x33FU"|int(0, 16) }}|'
'{{ obj|int }}')
out = tmpl.render(obj=IntIsh())
- assert out == '42|0|32|19762|9|0|42'
+ assert out == '42|0|32|19762|1234567890|9|0|42'
+ out = Template('{{ 5_555 }}|{{ 123_456_789 }}|{{ 12_34_56_78 }}').render()
+ assert out == '5555|123456789|12345678'
def test_join(self, env):
tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}')