Skip to content

Commit

Permalink
Merge pull request #16 from jg-rp/lambda-expressions
Browse files Browse the repository at this point in the history
Support lambda expressions as filter arguments.
  • Loading branch information
jg-rp authored Jan 22, 2025
2 parents beaadde + 54ce52f commit 6666c72
Show file tree
Hide file tree
Showing 57 changed files with 3,169 additions and 244 deletions.
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

0 comments on commit 6666c72

Please sign in to comment.