Skip to content

Commit

Permalink
StrictDefaultUndefined hot fix. See #62.
Browse files Browse the repository at this point in the history
  • Loading branch information
jg-rp committed Aug 10, 2022
1 parent ff6809a commit b758291
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 6 deletions.
12 changes: 11 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
Python Liquid Change Log
========================

Version 1.4.1
-------------

**Hot fix**

- Fixed a bug with the ``StrictDefaultFilter``. It was failing to be strict when
accessed by some filter decorators and helpers. Now the ``default`` filter will
immediately return its default value if its left value defines a
``force_liquid_default`` property and that property is truthy. See #62.

Version 1.4.0
--------------------------
-------------

**Features**

Expand Down
2 changes: 1 addition & 1 deletion liquid/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# flake8: noqa
# pylint: disable=useless-import-alias,missing-module-docstring

__version__ = "1.4.0"
__version__ = "1.4.1"

try:
from markupsafe import escape as escape
Expand Down
6 changes: 6 additions & 0 deletions liquid/builtin/filters/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ def size(obj: Any) -> int:
def default(obj: Any, default_: object = "", *, allow_false: bool = False) -> Any:
"""Return a default value if the input is nil, false, or empty."""
_obj = obj

# Return the default value immediately if the object defines a
# `force_liquid_default` property.
if hasattr(obj, "force_liquid_default") and obj.force_liquid_default:
return default_

if hasattr(obj, "__liquid__"):
_obj = obj.__liquid__()

Expand Down
7 changes: 5 additions & 2 deletions liquid/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,12 +353,15 @@ def __reversed__(self) -> Iterable[Any]:
class StrictDefaultUndefined(StrictUndefined):
"""An undefined that plays nicely with the `default` filter."""

# Force the `default` filter to return its default value
# without inspecting this class type.
force_liquid_default = True

# Properties that don't raise an UndefinedError.
allowed_properties = frozenset(
[
"__repr__",
"__liquid__",
"__class__",
"force_liquid_default",
"name",
"hint",
"obj",
Expand Down
20 changes: 18 additions & 2 deletions tests/test_undefined.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,8 @@ def test_debug_undefined_object(self):
def test_strict_default_undefined(self):
"""Test that we can use an undefined type with the default filter."""
env = Environment(undefined=StrictDefaultUndefined)
undef = DebugUndefined(name="nosuchthing", obj="foo")
template = env.from_string(r"{{ nosuchthing | default: 'hello' }}")
result = template.render(nosuchthing=undef)
result = template.render()
self.assertEqual(result, "hello")

template = env.from_string(r"{{ thing | default: 'hello' }}")
Expand All @@ -253,6 +252,23 @@ def test_strict_default_undefined(self):

self.assertEqual(str(raised.exception), "'nosuchthing' is undefined, on line 1")

def test_filter_strict_default_undefined(self):
"""Test that the default undefined type raises an exception when used as a
filter left value."""
env = Environment(undefined=StrictDefaultUndefined)
template = env.from_string(r"{{ nosuchthing | floor }}")
with self.assertRaises(UndefinedError) as raised:
template.render()

self.assertEqual(str(raised.exception), "'nosuchthing' is undefined, on line 1")

def test_isinstance_strict_default_filter(self):
"""Test that the default undefined type raises an exception when accessing
__class__."""
undef = StrictDefaultUndefined("nosuchthing")
with self.assertRaises(UndefinedError):
undef.__class__

def test_lax_filter(self):
"""Test that undefined filters can be silently ignored."""
tests = [
Expand Down

0 comments on commit b758291

Please sign in to comment.