This is a major release with several breaking changes. As well as API changes listed below, we:
- Drop support for Python version 3.7 and 3.8.
- Promote rendering behavior from `liquid.future.Environment` to be the default, so as to improve Shopify/liquid compatibility by default.
- Fix variable/identifier/path parsing described in [issue 39](https://github.com/jg-rp/liquid/issues/39).
- Improve Liquid syntax error messages and exposes source index, line numbers and column numbers through methods on Liquid exceptions. See [53](https://github.com/jg-rp/liquid/issues/53).
- Change comment tag parsing to better match Shopify/Liquid. See [133](https://github.com/jg-rp/liquid/issues/133).
- Remove `BoundTemplate.analyze_with_context()`. Shout if you need contextual analysis and we'll restore this feature.
- Remove the `cache_size` argument to `liquid.Environment` and `liquid.Template`. Template caching is now handled by template loaders.
- Remove the `expression_cache_size` argument to `liquid.Environment` and `liquid.Template`. Environment-level expression caching is no longer available as it does not play nicely with detailed error messages. If you need to cache parsing of Liquid expressions, it is now recommended to implement a cache per tag, where it makes sense to do so for your use case.
- Make [markupsafe](https://pypi.org/project/MarkupSafe/) a dependency. Previously markupsafe was an optional dependency. Version 3 of markupsafe brings some subtle changes to the `replace`, `replace_first` and `replace_last` filters when they receive a "safe" string wrapped in `Markup()`.
- Add new filters `reject`, `has`, `find` and `find_index`. See [Shopify/liquid 1869](https://github.com/Shopify/liquid/pull/1869).
- Add the new `doc` tag. See [Shopify/liquid 1895](https://github.com/Shopify/liquid/pull/1895).
API changes
Also see the [migration guide](https://jg-rp.github.io/liquid/migration/).
Miscellaneous
- Added `liquid.parse(source)`, `liquid.render(source, **data)` and `liquid.render_async(source, **data)`. These are shorthand functions that use `liquid.DEFAULT_ENVIRONMENT`.
- Renamed `liquid.Environment.parse` to `liquid.Environment._parse`, which returns a list of nodes, not a template.
- Aliased `liquid.Environment.from_string` as `liquid.Environment.parse`.
- Added `liquid.Environment.render(source, **data)` and `liquid.Environment.render_async(source, **data)`. These are convenience methods equivalent to `liquid.Environment.from_string(source).render(**data)`.
- Renamed `liquid.Context` to `liquid.RenderContext`.
- Change the `liquid.RenderContext` constructor (previously `liquid.Context`) to require an instance of `BoundTemplate` as its only positional argument instead of an instance of `Environment`. All other arguments are now keyword only.
- Renamed `liquid.exceptions.Error` to `liquid.exceptions.LiquidError`.
- Renamed `liquid.exceptions.TemplateNotFound` to `liquid.exceptions.TemplateNotFoundError`.
- Renamed `liquid.exceptions.NoSuchFilterFunc` to `liquid.exceptions.UnknownFilterError`.
Template loaders
- Changed `BaseLoader.get_source` and `BaseLoader.get_source_async` to accept and optional `context` keyword argument and arbitrary keyword arguments as "load context".
- Removed `BaseLoader.get_source_with_args` and `BaseLoader.get_source_with_context`, and their async equivalents. `BaseLoader.get_source` now accepts optional context and load context arguments.
- Changed `TemplateSource` (a named tuple) to be (text, name, uptodate, matter). It used to be (source, filename, uptodate, matter)
Builtin expressions
- Removed `liquid.expression.*`. Now built-in expressions live in `liquid.builtin.expressions`.
- Renamed `Identifier` to `Path`.
- Removed `IdentifierPathElement`. Path segments are now `list[str | int | Path]]`.
- Removed constant versions of `True`, `False`, `Nil`, `Empty` and `Blank`. Each of these primitive expressions now require a token, so they can't be constant.
Tag expression parsing
- Changed `liquid.token.Token` to be a named tuple of (kind, value, index, source). It used to be (linenum, type, value).
- Removed legacy expression parsing functions. If you're importing anything from `liquid.parse` for your custom tags, you'll need to use functions/methods from `liquid.builtin.expressions` instead.
- Removed `liquid.parse.expect()` and `liquid.parse.expect_peek()` in favour of `TokenStream.expect()` and `TokenStream.expect_peek()`.
- Removed `liquid.expressions.TokenStream`. Now there's only one `TokenStream` class, `liquid.stream.TokenStream`, reexported as `liquid.TokenStream`.
- All tokens are now named tuples. Previously functions in `liquid.expressions` would generate and use plain tuples internally.
- Added the `TOKEN_RANGE_LITERAL` token kind. The opening parenthesis of a range expression will use this kind to differentiate logical grouping parentheses from range expressions.
- Split tokens with kind `TOKEN_OUTPUT` in to two tokens, `TOKEN_OUTPUT` and `TOKEN_EXPRESSION`. Previously the value associated with `TOKEN_OUTPUT` would be the expression, now the expression follows in the next token, just like `TOKEN_TAG`.
Here's a summary mapping from old expression parsing functions to the recommended new parsing functions/methods.
| Old | New |
| ------------------------------------------ | ------------------------------------------------------------------ |
| `tokenize_common_expression(str, linenum)` | `liquid.builtin.expressions.tokenize(source, parent_token)` |
| `*.tokenize(source, linenum)` | `liquid.builtin.expressions.tokenize(source, parent_token)` |
| `parse_common_expression(stream)` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `parse_keyword_arguments(expr, linenum)` | `liquid.builtin.expressions.KeywordArgument.parse(env, stream)` |
| `parse_identifier(stream)` | `liquid.builtin.expressions.Path.parse(env, stream)` |
| `parse_unchained_identifier(stream)` | `liquid.builtin.expressions.parse_identifier(env, stream)` |
| `parse_string_or_identifier` | `liquid.builtin.expressions.parse_string_or_path(env, stream)` |
| `parse_unchained_identifier` | `liquid.builtin.expressions.parse_name(env, stream)` |
| `parse_boolean` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `parse_nil` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `parse_empty` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `parse_blank` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `parse_string_literal` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `parse_integer_literal` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `parse_float_literal` | `liquid.builtin.expressions.parse_primitive(env, stream)` |
| `Environment.parse_boolean_expression` | `liquid.builtin.expressions.BooleanExpression.parse(env, stream)` |
| `Environment.parse_filtered_expression` | `liquid.builtin.expressions.FilteredExpression.parse(env, stream)` |
| `Environment.parse_loop_expression` | `liquid.builtin.expressions.LoopExpression.parse(env, stream)` |
Template inheritance
- Added methods `variables()`, `variable_paths()`, `variable_segments()`, `global_variables()`, `global_variable_paths()`, `global_variable_segments()` and their async equivalents to `liquid.BoundTemplate`. These are convenience methods for reporting variables using static analysis.
- Added methods `filter_names()`, `tag_names()` and their async equivalents to `liquid.BoundTemplate`.
- Changed `liquid.ast.Node.children()` and added `expressions()`, `template_scope()`, `block_scope()` and `partial_scope()` methods. See docs.