Fixes
- Fixed
{% case %}
/{% when %}
behavior. When usingliquid.future.Environment
, we now render any number of{% else %}
blocks and allow{% when %}
tags to appear after{% else %}
tags. The defaultEnvironment
continues to raise aLiquidSyntaxError
in such cases.
Fixes
- Fixed handling of
{% else %}
tags that include text betweenelse
and the closing tag delimiter (%}
). Previously we were treating such text as part of the{% else %}
block. Now the default behavior is to raise aLiquidSyntaxError
. When usingliquid.future.Environment
, we follow Shopify/Liquid behavior of silently ignoring{% else %}
tag expressions, even in strict mode. See #150. liquid.future.Environment
now silently ignores superfluous{% else %}
and{% elsif %}
blocks. The default environment continues to raise aLiquidSyntaxError
if{% else %}
or{% elsif %}
appear after the first{% else %}
tag. See #151.
Fixes
- Fixed a bug with the LRU cache. We now raise a
ValueError
at construction time if a caching template loader is given a cache size less than 1. Previously, in such cases, anIndexError
would have been raised when attempting to write to the cache. See #148.
Features
- Added
make_choice_loader()
, a factory function that returns aChoiceLoader
orCachingChoiceLoader
depending on its arguments. (docs, source) - Added
make_file_system_loader()
, a factory function that returns aFileSystemLoader
,FileExtensionLoader
orCachingFileSystemLoader
depending on its arguments. (docs, source)
Fixes
- Fixed comparing strings with
<
,<=
,>
and>=
in boolean expressions ({% if %}
and{% unless %}
). Previously we were raising aLiquidTypeError
, now we return the result of comparing two string by their lexicographical order. See #141.
Features
- Added
CachingChoiceLoader
, a template loader that chooses between a list of template loaders and caches parsed templates in memory. (docs, source) - Added
PackageLoader
, a template loader that reads templates from Python packages. (docs, source)
Dependencies
- Python Liquid now depends on importlib-resources >= 5.10. This is that backport of importlib.resources from Python's standard library. We use it in
PackageLoader
.
Fixes
- Added an additional implementation of the
split
filter, which resolves some compatibility issues between Python Liquid and the reference implementation. Previously, when given an empty string to split or when the string and the delimiter were equal, we used Python'sstr.split()
behavior of producing one or two element lists containing empty strings. We now match Shopify/Liquid in returning an empty list for such cases. The newsplit
filter will be enabled by default when usingliquid.future.Environment
, and can optionally be registered withliquid.Environment
for those that don't mind the behavioral change. See #135. - Fixed unexpected errors from the
date
filter when it's given an invalid format string. Previously we were raising aliquid.exceptions.Error
in response to aValueError
from strftime. We now raise aFilterArgumentError
with its__cause__
set to theValueError
. - Fixed handling of
"%s"
date filter format strings. We now fall back to a string representation ofdatetime.timestamp()
for platforms that don't support%s
. Note that this is for"%s"
exactly. We don't handle the more general case of%s
appearing in a longer format string.
Version 1.10.1 was accidentally skipped and not released.
Features
- Optionally disable automatic suppression of whitespace only blocks with the
Environment
class attributerender_whitespace_only_blocks
. (docs). - All built-in and included "extra" tags now have a
node_class
class attribute specifying theNode
type the tag contributes to a templates AST. This is done for easier customization throughTag
andNode
subclassing.
Fixes
- Fixed async loading of templates with the
{% extends %}
tag. Previously templates were being loaded synchronously, even when usingrender_async()
. See #124. - Fixed handling of recursive
{% extends %}
tags during async static analysis. See #125.
Fixes
- Removed
is_up_to_date
fromliquid.BoundTemplate.__repr__
. It was causingRuntimeWarning
s with Python 3.11 when using an async template loader. Specifically warnings about coroutines that were never awaited. - Fixed the
map
filter. If given a nested array-like input, it now flattens it automatically. See #119. - Fixed the behavior of the
liquid
tag when other liquid tags appear within it. See #123.
Features
- Added the
sum
filter, which will return the sum of all numeric values in its input sequence. (docs, source). - Added
Environment.setup_tags_and_filters()
for easier tag and filter registration when subclassingEnvironment
. See #122.
Fixes
- Fixed a bug where a class-based filter defining
filter_async
and settingwith_context
orwith_environment
toTrue
would not be awaited. See #117.
Build
- Fixed some package build issues since moving to hatch. Both version 1.9.0 and 1.9.1 inadvertently included
.mypy_cache
folders, making the distribution files significantly larger.
Fixes
- Force the "wheel" build target to include
py.typed
. - Restore
liquid.__version__
.
Fixes
- Changed
FileSystemLoader
so that loaded templates can be pickled. See #107. - Fixed the
liquid.Environment
template cache. Now, when given acache_size
of0
, the cache is disabled. See #108.
Features
- Added context-aware template loader bootstrapping methods. See #109.
- Added support for async class-based filters. See #115.
- Added
CachingFileSystemLoader
, a template loader that handles its own cache rather than relying on theEnvironment
template cache, which can't handle context-aware loaders. See #116.
Fixes
- Removed unnecessary wrapping of inline conditional expressions in
BooleanExpression
. - Fixed async evaluation of macro arguments. Previously they were always being evaluated synchronously.
- Allow
macro
names to be quoted or unquoted. Quoted and unquoted macro names are now equivalent when defining and/or calling a macro.
Compatibility
- Support bracketed variables without a leading identifier. See Shopify/liquid #1680.
- Allow whitespace control from
raw
tags. See Shopify/liquid #1683.
Features
- Added
{% extends %}
and{% block %}
tags for template inheritance. These are extra tags that need to be registered with aliquid.Environment
explicitly. (docs, source) - Added a new
sort_numeric
filter.sort_numeric
returns a new list with items from the input sequence sorted by any integers and/or floats found in the string representation of each item. (docs, source)
Fixes
- Fixed a bug with the
cycle
tag when usingliquid.future.Environment
. We were misinterpreting unquoted cycle group names as strings rather than variables to be resolved, and not Liquid stringifying some cycled items before output. We've also rolled back changes toCycleNode.children()
from version 1.7.0. - Fixed a regression bug that lead to some erroneous filtered expressions tokens to be silently ignored. Specifically any tokens that appear after a valid left value and the first filter or end of expression. We now raise a
LiquidSyntaxError
in such cases. See #103. - Fixed parenthesized conditional expression syntax error reporting. We now raise a
LiquidSyntaxError
when given unbalanced parentheses. See #101.
Compatibility
- The
{% for %}
tag now accepts a string literal as its iterable. Unlike Shopify/liquid, whether a string literal or a variable resolving to a string, the defaultEnvironment
will iterate over characters in the string.liquid.future.Environment
is now consistent with Shopify/liquid, in that it iterates over an "array" where the first an only item is the string. See #102. - The
round
filter is now consistent with Shopify/liquid and Ruby 3 when given non-integer arguments. See Shopify/liquid#1590.
Fixes
- Fixed counting of "local" variables (those created with
assign
,capture
, etc.) in templates rendered with the{% render %}
tag during contextual analysis. Previously these variables were not reported in the results ofBoundTemplate.analyze_with_context()
. See #92. - Both #43 and #90 have been fixed with the introduction of
liquid.future.Environment
, an environment that aims for maximum compatibility with Ruby Liquid, without concern for backwards incompatible changes to template rendering behavior.liquid.Environment
should be considered the most stable "standard" configuration,liquid.future.Environment
sacrifices stability for improved compatibility with Ruby Liquid. See the known issues page. - Fixed a bug with AST traversal of
cycle
nodes. Previously,CycleNode.children()
erroneously included acycle group name
expression, if available.
Features
- Report template filter usage as well as variable usage with
BoundTemplate.analyze()
andBoundTemplate.analyze_with_context()
. See #91 and docs. - Report template tag usage when statically analyzing a template with
BoundTemplate.analyze()
. See #97 and docs. - Analyze template tags using
Environment.analyze_tags()
. This form of tag analysis happens before a template is fully parsed, giving us the opportunity to find unknown, unexpected and unbalanced tags that might cause the parser to raise an exception or skip template blocks. See #98 and docs.
Fixes
- Fixed static template analysis fails with
{% break %}
and{% continue %}
. See #89.
Fixes
- Fixed the string representation of
liquid.expression.Identifier
, which is exposed in the results ofliquid.BoundTemplate.analyze()
. We now represent variable path elements containing a.
as quoted strings inside square brackets. See #87.
Features
- The dictionaries returned by
liquid.BoundTemplate.analyze()
now use instances ofReferencedVariable
for their keys.ReferencedVariable
is astr
subclass that adds aparts
property, being a tuple representation of the variable. See #87.
Fixes
- Fixed
case
andwhen
tag expression parsing.when
expressions no longer fail when presented with a string containing a comma. Handling of comma andor
separated "sub-expressions" is now consistent with the reference implementation. See #86.
Features
The following non-standard tags and filters are reimplementations of those found in the Python Liquid Extra project, which is now receiving bugfix updates only. Unlike standard tags and filters, which are registered for you automatically, extra tags and filters must be explicitly registered with an Environment`. See https://jg-rp.github.io/liquid/extra/introduction.
-
Added an
if
tag that supports a logicalnot
operator and grouping terms with parentheses. (docs, source) -
Added drop-in replacements for the standard output statement,
assign
tag andecho
tag that support inline conditional expressions. (docs, source) -
Added
macro
andcall
tags that define parameterized Liquid snippets for reuse. (docs, source) -
Added the
with
tag that extends the local namespace with block scoped variables. (docs, source) -
Added the
json
,index
,script_tag
andstylesheet_tag
filters. (docs, source)
Compatibility
for
tag arguments can now be separated by commas as well as whitespace. See Shopify/liquid#1658
Hot fix
- Fixed a bug where use of a local namespace limit would raise a
TypeError
when unhashable types were found in a render context's local namespace. See #79.
Fixes
- The
tablerowloop
drop now exposes itsrow
property. See #77. for
andtablerow
tag arguments can now be string representations of integers as well as integer literals and variables that resolve to integers. See #78.
Compatibility
- The
truncatewords
filter no longer raises aFilterArgumentError
if its argument is greater than2147483648
and the number of words in the input string is less than the target number of words. This is inline with recent changes committed to the reference implementation of Liquid. - The
slice
filter now clamps its arguments to between-9223372036854775808
and9223372036854775807
, as does the reference implementation of Liquid.
Hot fix
- Fixed a bug where boolean expressions and the default filter would treat
0.0
anddecimal.Decimal("0")
asFalse
. Python considers these values to be falsy, Liquid does not. See #74. - Future-proof str to int conversion limit. We will now use
sys.get_int_max_str_digits
if it is available andLIQUIDINTMAXSTRDIGITS
is not set. Note thatsys.get_int_max_str_digits
is called once at startup, so Liquid's limit will change withsys.set_int_max_str_digits
.
Fixes
- Keep comment text for later static analysis when parsing
{% comment %}
block tags. See #70. - Guard against DoS by very large str to int conversion. See python/cpython#95778. (docs)
Fixes
- Updated the built-in
date
filter to support parsing UNIX timestamps from integers and string representations of integers. For consistency with the reference implementation of Liquid,date
now returns the input string unchanged if it can not be parsed. See #67. - Fixed an issue with the "loop iteration" resource limit. It was failing to carry an existing loop counter more than one level deep when using
Context.copy()
. See #68.
Fixes
- Fixed a potential memory leak from using
functools.lru_cache
on a class method. See #63. - Fixed a bug with the
default
filter. Liquid zero should not be equal toFalse
. Thedefault
filter now returns0
if its left value is zero. Before it would have return its default value. See #62. - Fixed a bug where boolean expressions would consider Liquid
0
andfalse
to be equal and0
to be falsy. Python Liquid is now consistent with the reference implementation when comparing integers to booleans. See #65.
Hot fix
- Fixed a bug with the
StrictDefaultFilter
. It was failing to be strict when accessed by some filter decorators and helpers. Now thedefault
filter will immediately return its default value if its left value defines aforce_liquid_default
property and that property is truthy. See #62.
Features
StrictDefaultUndefined
, an undefined type that plays nicely with thedefault
filter, is now built in. (docs)- Configure resource limits with class attributes set on a Liquid
Environment
. Those class attributes arecontext_depth_limit
,loop_iteration_limit
,local_namespace_limit
andoutput_stream_limit
. (docs)
Fixes
- Fixed a bug in
StrictUndefined
that, when extended, stopped if from looking at its ownmsg
property. See #57.
Features
- Allow render context customization by subclassing
Context
andBoundTemplate
. - Contextual template analysis with
BoundTemplate.analyze_with_context()
. Complementing static template analysis, released in version 1.2.0, contextual template analysis performs a templaterender
, capturing information about template variable usage as it goes. (docs)
- Add
typing-extensions
dependency.
Features
- New inline comment tag
{% # .. %}
. See Shopify Liquid PR #1498 - Template static analysis.
BoundTemplate.analyze()
andBoundTemplate.analyze_async()
traverse a template's abstract syntax tree and report template variable usage. Static tree traversal (without rendering or evaluating expressions) is supported by the new, optionalchildren()
methods ofliquid.expression.Expression
andliquid.ast.Node
. (docs)
Fixes
- Fixed a bug where the lexer would incorrectly calculate an expression's line number if there were one or more newlines between a tag name and it's expression. Most notable with
liquid
tags where it is common to put a newline immediately after "liquid". - More robust syntax error handling when parsing Liquid expression filters. The refactored expression lexers from version 1.1.3 failed to account for some classes of syntax error.
- Fixed a bug where double pipe characters (
||
) in a filtered expression would cause anIndexError
. ALiquidSyntaxError
is now raised in such cases, including the line number of the offending error. - Changed
Environment.fromString
to catch unexpected parsing errors. A LiquidError
will now be raised with a message of "unexpected liquid parsing error" and its__cause__
set to the offending exception.
- Fixed a bug where the
where
filter would incorrectly ignore an explicitfalse
given as the target value. See #51.
- Prioritise object properties and keys named
size
,first
andlast
over the special built-in properties of the same names. See #46. - Fixed a bug with the
uniq
filter. It no longer raises an exception when given a key argument and a sequence containing objects that don't have that key/property. See #47. - The
strip_html
filter now removesstyle
andscript
tag blocks in their entirety, including everything in between. See #45. - Added
remove_last
andreplace_last
filters.
- Lazy
forloop
helper variables. Don't calculateindex
,rindex
etc. unless accessed. - Implemented
forloop.name
, as per the reference implementation.forloop.name
is the concatenation of the loop variable identifier and the target iterable identifier, or a string representation of a range literal, separated by a hyphen. - Fixed a bug with the
divided_by
filter. Given a float value and integer argument, it was incorrectly doing integer division. - Simplified
tablerowloop
andtablerow
HTML generation.
- Refactored expression lexers. New, subtly different, tag expression tokenizers are now in
liquid.expressions
. Built-in tags use these lexers indirectly via new specialized expression parsers. Older expression lexers and parsers will be maintained until at least Python Liquid version 2.0 for those that use them in custom tags. See #42. - Specialized expression parsers. Each of the three built-in expression types now have a dedicated parser defined in
liquid.expressions
, whereas before all expression parsing went throughliquid.parse.ExpressionParser.parse_expression()
. Built-in tags now use these new parsers. The more general parser will be maintained until at least Python Liquid Version 2.0. See #42. liquid.parse.Parser.parse_block()
now accepts any container as itsend
argument. Benchmarks show that using afrozenset
forend
instead of a tuple gives a small performance improvement.- Fixed an incompatibility with the reference implementation where Python Liquid would not recognize identifiers with a trailing question mark. This seems to be a common idiom in Ruby to indicate something returns a Boolean value.
- Added
get_source_with_context()
andget_source_with_context_async()
toliquid.loaders.BaseLoader
. Custom loaders can now use the active render context to dynamically modify their search space when used frominclude
orrender
, or any custom tag usingContext.get_template_with_context()
.Context.get_template_with_context()
also accepts arbitrary keyword arguments that are passed along toget_source_with_context()
. The build-ininclude
andrender
tags add atag
argument with their tag name, so custom loaders can modify their search space depending on which tag was used. See the Custom Loaders documentation for examples.
- Fixed a bug where a for loop's limit would be incorrect when using
offset: continue
multiple times (three or morefor
tags looping over the same sequence). See #41.
- Fixed a bug where blocks that contain whitespace only were being suppressed when the whitespace was explicitly output. Automatic whitespace suppression now only occurs in
if
,unlesss
andfor
blocks that don't contain an output statement orecho
tag, even if the output itself is whitespace. See #38. - Fixed a bug where the behavior of the special
.first
and.last
properties did not match that of thefirst
andlast
filters. Now, if given a string,.first
and.last
will return an undefined, and thefirst
andlast
filters will returnNone
. See #34.
- Added new comment syntax. Disabled by default, enable shorthand comments with the
template_comments
argument toliquid.Template
orliquid.Environment
. WhenTrue
, anything between{#
and#}
will be considered a comment. - New expression cache. Distinct from the existing template cache, optionally cache common Liquid expression types (conditions, loops and filtered expressions) to avoid lexing and parsing the same expression multiple times.
- Fixed a bug where, in some cases,
forloop.length
would be incorrect when usingoffsset: continue
in a loop expression.
- A range literal will now use a default of
0
rather than raising aLiquidTypeError
if either its start or stop values can't be cast to an integer. - Gracefully handle
liquid
tags that are empty or only contain whitespace. - Gracefully handle empty
echo
tags.
- Explicit re-export
- Changed
Context._tag_namespace
toContext.tag_namespace
.
- Fixed manifest error.
- Added
py.typed
Version bump. First stable release.
- Template loaders can now include additional template meta data using the
matter
argument ofliquid.loaders.TemplateSource
. See the exampleFrontMatterFileSystemLoader
in the README. See #32. - Implemented
ChoiceLoader
, a loader that tries to load a template from a list of loaders. - Added a
FileExtensionLoader
, a template loader that inherits fromFileSystemLoader
and automatically appends a file extension if one is missing. - The built-in
date
filter now accepts the special input value of "today" as well as "now". - The built-in
truncate
filter now has a default length of 50. - The built-in
truncatewords
filter now has a default number of words of 15. - Fixed a bug with the
slice
filter where it would return an empty string when presented with a negative start index and length that would have exceeded the length of the sequence. See #35. - Drops can now define safe HTML string representations using a
__html__
method. - Removed
liquid.mode.error()
in favour ofliquid.Environment.error()
The following behavioral changes are the result of feedback gained from exporting Python
Liquid's "golden" test cases, and running them against Ruby Liquid (the reference
implementation). Both Python Liquid version 0.11.0 and Ruby Liquid version 5.1.0 pass
all tests currently defined in liquid/golden/
.
- Added support for comma separated
when
expressions. See #31. - The built-in
join
,concat
,where
,uniq
andcompact
filters now use the newsequence_filter
decorator.sequence_filter
coerces filter left values to array-like objects.sequence_filter
will also flatten nested array-like objects, just like the reference implementation. - The built-in
first
,last
andmap
filters now operate on any array-like objects. Previously they were limited to lists and tuples. Strings still don't work. - The built-in
uniq
andcompact
filters now accept an optional argument. If an argument is provided, it should be the name of a property and the left value should be a sequence of objects. - The
size
filter now returns a default of0
if its left value does not have a__len__
method. - The
replace
andreplace_first
filters now treat undefined arguments as an empty string. - The
slice
filter now works on lists, tuples and ranges, as well as strings. - Fixed a bug where the
math_filter
decorator would cast strings representations of negative integers to a float rather than an int. - Added golden test cases for all filters.
- Moved and organized "render" test cases into importable "golden" test cases.
- Change
RangeLiteral
string representation to match the reference implementation. - Add newlines to
tablerow
output. As per the reference implementation.
- Range literals can now be assigned, compared and passed as arguments to
include
orrender
tags. They can also be filtered as if they were an array. - Range literals will accept a float value for start and/or stop values. If a float is given for start or stop, it will be cast to an integer.
- Fixed a bug where the token stream would return the wrong token when peeking immediately after a push.
- Changed named counter (
increment
anddecrement
) scoping. Unless a named counter is shadowed by anassign
orcapture
, the counter will be in scope for all subsequent Liquid expressions. - Changed
{% increment %}
to be a post-increment operation.{% decrement %}
remains a pre-decrement operation. - Added
forloop.parentloop
. Access parentforloop
objects from nested loops.
unless
blocks can now containelse
andelsif
blocks.- Added support for array item access with negative indices. Closes #27.
- Improved error messages for context lookups that resulting an
IndexError
orKeyError
.
- Fixed a bug where arguments to
Template()
where not being passed to the implicit environment properly (again). - Fixed a bug where some errors from the
sort
andmap
filters were being ignored. Those filters can now raise aFilterError
. - Removed depreciated class-based filters.
- Removed
@abstractmethod
fromliquid.loaders.BaseLoader.get_source
. Custom loaders are now free to implement eitherget_source
orget_source_async
or both. TheBaseLoader
implementation ofget_source
simply raises aNotImplementedError
. liquid.loaders.TemplateSource.uptodate
(as returned byget_source
andget_source_async
) can now be a coroutine function. This means async loaders can check a template's source for changes asynchronously.- Added the
cache_size
argument toEnvironment
andTemplate
for controlling the capacity of the default template cache. - Easier subclassing of
liquid.parser.ExpressionParser
withEND_EXPRESSION
.
Version bump. Last release before removing depreciated class-based filters.
- The
default
filter now uses__liquid__
, if available, when testing an object for truthy-ness.
- Recursive use of the "render" tag now raises a
ContextDepthError
ifMAX_CONTEXT_DEPTH
is exceeded. This is now consistent with recursive "include". - Drops (custom classes in a render context) can now mimic primitive Liquid values when used as array indexes or hash keys, or in conditional expressions (including
case
/when
andunless
). If defined, the result of calling a drop's__liquid__
method will be used in those scenarios. - Added
base64_encode
,base64_decode
,base64_url_safe_encode
andbase64_url_safe_decode
filters. - Added asynchronous template loading and rendering. When
Template.render_async
is awaited,render
andinclude
tags will load templates asynchronously. Custom loaders should implementget_source_async
. - Added support for asynchronous drops. If a class implements
__getitem_async__
, which is assumed to be an async version of__getitem__
, it will be awaited instead of calling__getitem__
.
- Class-based filters are now depreciated in favour of decorated filter functions. Abstract filter classes (such as
liquid.builtin.filters.string.StringFilter
) will be removed in Liquid 0.9. - All built-in filters are now implemented as decorated functions. Legacy, class-based filters are no longer registered automatically and will be removed in Liquid 0.9.
- Legacy filter "helpers" are now depreciated and will be removed in Liquid 0.9. Use the new decorators, like
liquid.filter.string_filter
andliquid.filter.with_context
, instead. - The
block
argument to theliquid.ast.ConditionalBlockNode
constructor is no longer optional.
- Auto reload. Disable automatic reloading of templates by passing
auto_reload=False
toEnvironment
orTemplate
. For deployments where template sources don't change between service reloads, setting auto_reload toFalse
can yield an increase in performance by avoiding calls touptodate
. - Fixed a bug where, when using the
Template
API,undefined
,strict_filters
, andautoescape
where not always passed through to the implicit environment correctly. - Added support for continuing a for loop using
offset: continue
. See #14.
.. _MarkupSafe: https://github.com/pallets/markupsafe
- HTML auto-escaping. If
MarkupSafe
_ is installed and theautoescape
argument toEnvironment
orTemplate
isTrue
, context variables will be HTML escaped automatically. LiquidSyntaxError
exceptions now include the offending template source in thesource
property.
- The built-in
FileSystemLoader
now accepts a list of paths to search, in order, returning the first template source found. Thesearch_path
argument can be a string or path-like, or an iterable of strings or path-like objects. - Added the
encoding
argument toFileSystemLoader
. Files will be opened with the given encoding. Defaults to"utf-8"
. FileSystemLoader
will raise aTemplateNotFound
exception if..
appears in a template name.
- Undefined variables are now represented by the
Undefined
type, or a subclass ofUndefined
.Undefined
behaves likenil
, but can also be iterated over and indexed without error. - Attempting to loop over an undefined variable no longer raises a
LiquidTypeError
. - Optionally pass
liquid.StrictUndefined
as theundefined
argument toTemplate()
orEnvironment()
to render instrict variables
mode. All operations on an instance ofStrictUndefined
raise anUndefinedError
. - Filters can now raise
FilterValueError
in addition toFilterArgumentError
. Where aFilterValueError
refers to an issue with the left value a filter is applied to. - Applying a built-in filter to an undefined variable no longer raises a
FilterArgumentError
in most cases. - Added the
strict_filters
argument to theEnvironment
andTemplate
constructors. WhenTrue
, the default, undefined filters raise aNoSuchFilterFunc
exception at render time. WhenFalse
, undefined filters are silently ignored. - The
join
filter now forces items in its left value to strings before joining them. - The
join
filter's argument is now optional, defaulting to a string containing a single space.
- New
Template
API. Create templates from strings without anEnvironment
. - The
template
object is no longer included in every render context automatically.
- Implemented the
blank
keyword. Empty string and strings containing only whitespace are equal toblank
. - Implemented the
null
keyword.null
is an alias fornil
. - Implemented the
ifchanged
tag.
- Refactored the standard expression parser. It's now possible to extend
ExpressionParser
for use with custom tags. - Decoupled boolean expression parsing for easier
if
,unless
andcase
tag subclassing.
- Added support for Python 3.7 and PyPy3.7.
- Added support for named filter parameters.
- The
default
filter now accepts the named parameterallow_false
. - The
truncate_words
filter now forces a minimum number of words to 1. - The
newline_to_br
filter now replaces\n
and\r\n
with<br />\n
. - The
strip_newlines
filter strips\r\n
.