Merge pull request #922 from CleoQc/feature/support_scientific_notation

Feature/support scientific notation
This commit is contained in:
David Lord 2019-07-23 08:35:15 -07:00 committed by GitHub
commit 278a0574f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 12 deletions

View File

@ -21,6 +21,8 @@ unreleased
- Added a ``default`` parameter for the ``map`` filter. (`#557`_)
- Exclude environment globals from
:func:`meta.find_undeclared_variables`. #931
- Float literals can be written with scientific notation, like
``{{ 2.56e-3 }}``. #912, #922
.. _#557: https://github.com/pallets/jinja/issues/557
.. _#765: https://github.com/pallets/jinja/issues/765

View File

@ -1177,11 +1177,13 @@ 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 / 42.23:
Integers and floating point numbers are created by just writing the
number down. If a dot is present, the number is a float, otherwise an
integer. Keep in mind that, in Python, ``42`` and ``42.0``
are different (``int`` and ``float``, respectively).
42:
Integers are whole numbers without a decimal part.
42.23 / 42.1e2:
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.
['list', 'of', 'objects']:
Everything between two brackets is a list. Lists are useful for storing

View File

@ -15,6 +15,7 @@
:license: BSD, see LICENSE for more details.
"""
import re
from ast import literal_eval
from collections import deque
from operator import itemgetter
@ -52,7 +53,7 @@ else:
del jinja2._identifier
del _identifier
float_re = re.compile(r'(?<!\.)\d+\.\d+')
float_re = re.compile(r'(?<!\.)\d+(?:\.\d+)?(?:e[+\-]?\d+)?', re.IGNORECASE)
newline_re = re.compile(r'(\r\n|\r|\n)')
# internal the tokens and keep references to them
@ -601,7 +602,7 @@ class Lexer(object):
elif token == 'integer':
value = int(value)
elif token == 'float':
value = float(value)
value = literal_eval(value)
elif token == 'operator':
token = operators[value]
yield Token(lineno, token, value)

View File

@ -136,9 +136,11 @@ class TestFilter(object):
def test_float(self, env):
tmpl = env.from_string('{{ "42"|float }}|'
'{{ "ajsghasjgd"|float }}|'
'{{ "10e1"|float }}|'
'{{ "10.5e-10"|float }}|'
'{{ "32.32"|float }}')
out = tmpl.render()
assert out == '42.0|0.0|32.32'
assert out == '42.0|0.0|100.0|1.05e-09|32.32'
def test_format(self, env):
tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''')

View File

@ -336,9 +336,36 @@ class TestSyntax(object):
tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}')
assert tmpl.render() == 'True|False'
def test_literals(self, env):
tmpl = env.from_string('{{ [] }}|{{ {} }}|{{ () }}')
assert tmpl.render().lower() == '[]|{}|()'
@pytest.mark.parametrize("value", ("[]", "{}", "()"))
def test_collection_literal(self, env, value):
t = env.from_string("{{ %s }}" % value)
assert t.render() == value
@pytest.mark.parametrize("value", ("1", "123"))
def test_int_literal(self, env, value):
t = env.from_string("{{ %s }}" % value)
assert t.render() == value
@pytest.mark.parametrize(
"value",
(
"1.2",
"34.56",
("1e0", "1.0"),
("10e1", "100.0"),
("2.5e100", "2.5e+100"),
"2.5e+100",
("25.6e-10", "2.56e-09"),
),
)
def test_float_literal(self, env, value):
if isinstance(value, tuple):
value, expect = value
else:
expect = value
t = env.from_string("{{ %s }}" % value)
assert t.render() == expect
def test_bool(self, env):
tmpl = env.from_string('{{ true and false }}|{{ false '