Skip to content

Commit

Permalink
Merge pull request #33 from AllianceSoftware/fix/is_static_expression…
Browse files Browse the repository at this point in the history
…-handle-filters

fix: is_static_expression handle filters
  • Loading branch information
davecoates authored May 10, 2024
2 parents 2a21187 + e89e9eb commit 7be5a78
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ so make sure you follow the template
-->

## 4.x.x unreleased

### Fixed

* `is_static_expression` now handles expressions with filters correctly

## 4.0.0 2024-03-18

### Breaking Changes
Expand Down
16 changes: 12 additions & 4 deletions src/allianceutils/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,20 @@ def build_html_attrs(html_kwargs: Dict[str, str], prohibited_attrs: Optional[Lis
return output


def is_static_expression(expr: Optional[FilterExpression]) -> bool:
"""Check if a given expression is static"""
def is_static_expression(expr: Optional[Union[FilterExpression,str]]) -> bool:
"""Check if a given expression is static
This can be used when writing custom template tags to determine if the value passed in is a static value, and can
be resolved without ``context``.
"""
if expr is None or isinstance(expr, str):
return True
if not isinstance(expr, FilterExpression):
return False # type: ignore[unreachable] # unreachable because of type, but we want to return False rather than crash if not a FilterExpression
# the arg.var.lookups is how Variable internally determines if value is a literal. See its
# implementation of ``resolve``.
if not expr or not isinstance(expr.var, Variable) or expr.var.lookups is None:
return True
if not isinstance(expr.var, Variable) or expr.var.lookups is None:
# If it has filters then we assume it's not static
return not expr.filters
# There are 3 built-ins that look like vars but get resolved from a static list (see ``BaseContext``)
return expr.var.var in ["None", "True", "False"]
33 changes: 33 additions & 0 deletions src/test_allianceutils/tests/test_template_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from typing import cast

from django.template.base import Template
from django.test import SimpleTestCase

from allianceutils.template import is_static_expression
from allianceutils.templatetags.default_value import DefaultValueNode


class TestTemplateUtils(SimpleTestCase):

def _get_as_var(self, contents: str):
# Bit hacky, but use the default_value tag to parse the variable, which when then access from
# ``DefaultValueNode.assignments``.
template = Template(f'{{% load default_value %}}{{% default_value test_var={contents} %}}')
node: DefaultValueNode = cast(DefaultValueNode, template.compile_nodelist()[-1])
return node.assignments['test_var']

def test_is_static_expression_string(self):
self.assertTrue(is_static_expression(self._get_as_var('"foo"')))

def test_is_static_expression_bool(self):
self.assertTrue(is_static_expression(self._get_as_var("True")))
self.assertTrue(is_static_expression(self._get_as_var("False")))

def test_is_static_expression_with_filter(self):
self.assertFalse(is_static_expression(self._get_as_var("foo|add:''")))

def test_is_static_expression_raw_string(self):
self.assertTrue(is_static_expression("foo"))

def test_is_static_expression_other_value(self):
self.assertFalse(is_static_expression({})) # type: ignore[arg-type] # just testing runtime behaviour

0 comments on commit 7be5a78

Please sign in to comment.