]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
support underscore in int and float
authorCleoQc <cleoqc1124@gmail.com>
Tue, 13 Nov 2018 00:43:13 +0000 (19:43 -0500)
committerDavid Lord <davidism@gmail.com>
Tue, 23 Jul 2019 16:25:16 +0000 (09:25 -0700)
docs/templates.rst
jinja2/lexer.py
tests/test_filters.py

index 88df916745a3c40c2c81b228edf10bf18bae155d..0e6781cfa0ddd1d2ec6973b697996a2a565db68d 100644 (file)
@@ -1177,13 +1177,16 @@ for Python objects such as strings and numbers.  The following literals exist:
     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
index d8812c9b9128e0fcc97b4a90b08ba5764fd6e91f..a387cf92d5154721917ace864ae5a35c81c4b17e 100644 (file)
@@ -23,6 +23,8 @@ from jinja2._compat import implements_iterator, intern, iteritems, text_type
 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)
@@ -31,7 +33,7 @@ _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
@@ -53,7 +55,7 @@ else:
     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
@@ -600,9 +602,10 @@ class Lexer(object):
                     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)
index 28d1f302f332bab72a3930e61c17614ad7807177..199c696d24e8ff954a5908a8c170f47b71293524 100644 (file)
@@ -10,7 +10,7 @@
 """
 import random
 import pytest
-from jinja2 import Markup, Environment
+from jinja2 import Markup, Environment, Template
 from jinja2._compat import text_type, implements_to_string
 
 
@@ -136,11 +136,15 @@ class TestFilter(object):
     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") }}''')
@@ -187,10 +191,13 @@ class TestFilter(object):
 
         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("|") }}')