Skip to content

Commit

Permalink
Fix/support for floating point (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
cloutierMat authored Nov 12, 2024
1 parent ee2e692 commit 4efbbd2
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ var/
.installed.cfg
*.egg

# Python environment
.venv

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This is a fork of the original [`airspeed`](https://github.com/purcell/airspeed)
⚠️ Note: This fork of `airspeed` focuses on providing maximum parity with AWS' implementation of Velocity templates (used in, e.g., API Gateway or AppSync). In some cases, the behavior may diverge from the VTL spec, or from the Velocity [reference implementation](https://velocity.apache.org/download.cgi).

## Change Log:
* v0.6.7: fix support for floating point starting with a decimal; Implement `REPLACE_FORMAL_TEXT` to allow bypassing silent behavior of `FormalReference` element.
* v0.6.6: add support for `$string.matches( $pattern )`; fix bug where some escaped character would prevent string matching
* v0.6.5: handle `$map.put('key', null)` correctly
* v0.6.4: add support for string.indexOf, string.substring and array.isEmpty
Expand Down
10 changes: 8 additions & 2 deletions airspeed/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
LOG = logging.getLogger(__name__)


# This can be used by a function to bypass the silent behavior of a FormalReference.
# If a function returns this value instead of None, the origial text of the reference will be returned instead.
REPLACE_FORMAL_TEXT = "__FORMAL_REFERENCE__REPLACE_TEXT__"

# A dict that maps classes to dicts of additional methods.
# This allows support for methods that are available in Java-based Velocity
# implementations, e.g., .size() of a list or .length() of a string.
Expand Down Expand Up @@ -434,7 +438,7 @@ def calculate(self, namespace, loader):


class FloatingPointLiteral(_Element):
FLOAT = re.compile(r"(-?\d+\.\d+)(.*)", re.S)
FLOAT = re.compile(r"(-?\d*\.\d+)(.*)", re.S)

def parse(self):
(self.value,) = self.identity_match(self.FLOAT)
Expand Down Expand Up @@ -795,7 +799,9 @@ def evaluate_raw(self, stream, namespace, loader):
value = None
if self.expression is not None:
value = self.expression.calculate(namespace, loader)
if value is None:
if value == REPLACE_FORMAL_TEXT:
value = self.my_text()
elif value is None:
if self.alternate is not None:
value = self.alternate.calculate(namespace, loader) or ""
elif self.silent and self.expression is not None:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="airspeed-ext",
version="0.6.6",
version="0.6.7",
description=(
"Airspeed is a powerful and easy-to-use templating engine "
"for Python that aims for a high level of compatibility "
Expand Down
36 changes: 31 additions & 5 deletions tests/test_templating.py
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,10 @@ def test_string_with_escaped_char(self, test_render):
template = '"\{\n\t\r\%\5"' # noqa
test_render(template)

def test_floating_point_starting_with_decimal(self, test_render):
template = """#set($x = .5*.1)$x"""
test_render(template)


class TestInternals:
"""
Expand Down Expand Up @@ -1206,15 +1210,37 @@ def test_template_cannot_modify_its_args(self, test_render):

def test_doesnt_blow_stack(self, test_render):
template = airspeed.Template(
"""
#foreach($i in [1..$end])
$assembly##
#end
"""
textwrap.dedent(
"""
#foreach($i in [1..$end])
$assembly##
#end
"""
)
)
ns = {"end": 400}
template.merge(ns)

def test_formal_reference_test_bypass(self, test_render_locally):
test_render = test_render_locally(
f'#set($bypass = "{airspeed.operators.REPLACE_FORMAL_TEXT}")$bypass'
)
assert test_render == "$bypass"

test_render_map = test_render_locally(
textwrap.dedent(
f"""
#set( $map = {{}})
#set($ignore = $map.put('bypass', "{airspeed.operators.REPLACE_FORMAL_TEXT}"))
#set($ignore = $map.put('no-bypass', "value"))
$map.bypass
$map.no-bypass
"""
)
)

assert test_render_map == "\n$map.bypass\nvalue\n"


class TestMacros:
"""
Expand Down
7 changes: 7 additions & 0 deletions tests/test_templating.snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -1104,5 +1104,12 @@
"render-result-2-cli": "true",
"render-result-2": "true"
}
},
"tests/test_templating.py::TestTemplating::test_floating_point_starting_with_decimal": {
"recorded-date": "10-11-2024, 04:53:47",
"recorded-content": {
"render-result-1-cli": "0.05",
"render-result-1": "0.05"
}
}
}

0 comments on commit 4efbbd2

Please sign in to comment.