Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support lambda expressions as filter arguments. #16

Merged
merged 14 commits into from
Jan 22, 2025
Merged
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
# Python Liquid2 Change Log

## Version 0.2.1 (unreleased)
## Version 0.3.0 (unreleased)

**Breaking changes**

- Most built-in expression parsing functions/methods now expect the current `Environment` instance to be passed as the first argument.

**Fixes**

- Fixed `{% for %}` tag expressions with a comma between the iterable and `limit`, `offset` or `reversed`. Previously we were assuming a comma immediately following the iterable would mean we are iterating an array literal. We're also explicitly disallowing `limit`, `offset` and `reversed` arguments after an array literal.

**Features**

- Added optional filter argument validation at template parse time. ([docs](https://jg-rp.github.io/python-liquid2/custom_filters/#filter-argument-validation))
- Added lambda expressions as filter arguments. Both custom and built-in filters can accept arguments of the form `<identifier> => <boolean expression>` or `(<identifier>, <identifier>) => <boolean expression>`.
- Updated filters `map`, `where`, `sort`, `sort_natural`, `uniq`, `compact` and `sum` to accept lambda expression or string arguments.
- Added filters `reject`, `has`, `find` and `find_index`.

## Version 0.2.0

**Features**
Expand Down
1 change: 1 addition & 0 deletions docs/api/expression.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: liquid2.expression.Expression
48 changes: 48 additions & 0 deletions docs/custom_filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,51 @@ del env.filters["safe"]
!!! tip

You can add, remove and replace filters on `liquid2.DEFAULT_ENVIRONMENT` too. Convenience functions [`parse()`](api/convenience.md#liquid2.parse) and [`render()`](api/convenience.md#liquid2.render) use `DEFAULT_ENVIRONMENT`

## Filter argument validation

When implementing a filter as a class, you have the option of implementing a `validate()` method. If present, `validate` will be called when parsing the template, giving you the opportunity to raise an exception if the filter's arguments are not acceptable.

Here's an example of the built-in [`map`](filter_reference.md#map) filter. It uses `validate` to check that if a lambda expression is passed as an argument, that expression is a path to a variable, not a logical expression.

!!! tip

See [map_filter.py](https://github.com/jg-rp/python-liquid2/tree/main/liquid2/builtin/filters/map_filter.py) for the full example.

```python
class MapFilter:
"""An implementation of the `map` filter that accepts lambda expressions."""

with_context = True

def validate(
self,
_env: Environment,
token: TokenT,
name: str,
args: list[KeywordArgument | PositionalArgument],
) -> None:
"""Raise a `LiquidTypeError` if _args_ are not valid."""
if len(args) != 1:
raise LiquidTypeError(
f"{name!r} expects exactly one argument, got {len(args)}",
token=token,
)

if not isinstance(args[0], PositionalArgument):
raise LiquidTypeError(
f"{name!r} takes no keyword arguments",
token=token,
)

arg = args[0].value

if isinstance(arg, LambdaExpression) and not isinstance(arg.expression, Path):
raise LiquidTypeError(
f"{name!r} expects a path to a variable, "
f"got {arg.expression.__class__.__name__}",
token=arg.expression.token,
)

# ...
```
Loading
Loading