Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Version 0.4.0 (unreleased)

**Fixes**

- Fixed some corner cases with `find`, `find_index` and `has` filters.

**Features**

- Added the `shorthand_indexes` class variable to `liquid2.Environment`. When `shorthand_indexes` is set to `True` (the default is `False`), array indexes in variable paths need not be surrounded by square brackets.
Expand Down
1 change: 0 additions & 1 deletion liquid2/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class Node(ABC):
__slots__ = ("token", "blank")

def __init__(self, token: TokenT) -> None:
super().__init__()
self.token = token

self.blank = True
Expand Down
57 changes: 33 additions & 24 deletions liquid2/builtin/filters/find_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@
from liquid2.builtin import KeywordArgument


def _getitem(obj: Any, key: object, default: object = None) -> Any:
def _getitem(sequence: Any, key: object, default: object = None) -> Any:
"""Helper for the `find` filter.

Same as obj[key], but returns a default value if key does not exist
in obj.
Same as sequence[key], but returns a default value if key does not exist
in sequence, and handles some corner cases so as to mimic Shopify/Liquid
behavior.
"""
try:
return getitem(obj, key)
return getitem(sequence, key)
except (KeyError, IndexError):
return default
except TypeError:
if not hasattr(obj, "__getitem__"):
raise
if isinstance(sequence, str) and isinstance(key, str) and key in sequence:
return key
if isinstance(sequence, int) and isinstance(key, int):
return sequence == key
return default


Expand Down Expand Up @@ -81,14 +84,13 @@ def __call__(
return item

elif value is not None and not is_undefined(value):
for item in left:
if _getitem(item, key) == value:
return item
return next((itm for itm in left if _getitem(itm, key) == value), None)

else:
for item in left:
if item not in (False, None):
return item
return next(
(itm for itm in left if _getitem(itm, key) not in (False, None)),
None,
)

return None

Expand All @@ -108,20 +110,25 @@ def __call__(
left = sequence_arg(left)

if isinstance(key, LambdaExpression):
for i, pair in enumerate(zip(left, key.map(context, left), strict=True)):
item, rv = pair
for i, rv in enumerate(key.map(context, left)):
if not is_undefined(rv) and is_truthy(rv):
return i

elif value is not None and not is_undefined(value):
for i, item in enumerate(left):
if _getitem(item, key) == value:
return i
return next(
(i for i, itm in enumerate(left) if _getitem(itm, key) == value),
None,
)

else:
for i, item in enumerate(left):
if item not in (False, None):
return i
return next(
(
i
for i, itm in enumerate(left)
if _getitem(itm, key) not in (False, None)
),
None,
)

return None

Expand All @@ -146,11 +153,13 @@ def __call__(
return True

elif value is not None and not is_undefined(value):
for item in left:
if _getitem(item, key) == value:
return True
return any(
(itm for itm in left if _getitem(itm, key) == value),
)

else:
return any(item not in (False, None) for item in left)
return any(
(itm for itm in left if _getitem(itm, key) not in (False, None)),
)

return False
2 changes: 1 addition & 1 deletion liquid2/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ class DisabledTagError(LiquidError):
"""Exception raised when an attempt is made to render a disabled tag."""


class UnknownFilterError(LiquidError): # noqa: N818
class UnknownFilterError(LiquidError):
"""Exception raised when a filter lookup fails."""


Expand Down
Loading