This release brings pongo2 significantly closer to Django template behavior.
timesince/timeuntil: Rewritten to use calendar-based month/year arithmetic and Django's adjacency rule (only adjacent time units shown). Future dates return "0 minutes" fortimesince, past dates fortimeuntil.linebreaks: Rewritten paragraph algorithm to match Django (normalize newlines, strip trailing whitespace, correct<p>/<br />wrapping).title: Rewritten to match Django titlecase behavior (apostrophes don't break words, non-letter characters act as word boundaries).filesizeformat: Uses singular "byte", non-breaking spaces, and supports negative values.unordered_list: Tab indentation and newlines to match Django format.linenumbers: Zero-padded line numbers and normalized newlines.center: Corrected padding direction to match Django/Pythonstr.center().wordwrap: Wraps at character width instead of word count; normalize\r\nand\rto\nbefore wrapping.truncatewords/truncatewords_html: Use unicode ellipsis (\u2026) instead of three dots.
widthratio: Handle division by zero (return "0") and use banker's rounding (math.RoundToEven).truncatechars_html: Use rune count instead of byte length for multi-byte character support.slugify: Apply NFKD normalization for accented characters; preserve underscores.dictsort: Use numeric comparison for numeric fields instead of string comparison.get_digit: Handle non-digit characters correctly (return original value for non-integer strings).pluralize: Use float comparison to avoid truncating decimals (e.g., 1.5 is now plural).urlizetrunc: Use rune-based truncation for multi-byte URLs.yesno: Mapnilto "no" value when only 2 custom args provided.linebreaksbr: Normalize\r\nand\rbefore processing.linebreaks: Normalize\r\nand\rbefore processing.json_script: Apply full HTML escaping to element ID (not just double quotes).urlencode: Document Django difference (spaces as+viaurl.QueryEscapevs Django's%20;/encoded vs Django preserving it).iriencode: Document Django difference (spaces as+viaurl.QueryEscapevs Django's%20). Switch frombytes.Buffertostrings.Builder.spaceless: Strip leading/trailing whitespace to match Django.lorem: Use double newline between paragraphs to match Django.cycle: Apply autoescape to output values like Django.filters: Mark HTML-producing filters (escape,escapejs,linebreaks,linebreaksbr,urlize,urlizetrunc) as safe to prevent double-escaping with autoescape.include: Use start token for lazy parse errors (fixes panic); support{% include "file" only %}withoutwithkeyword.macro: Store default values consistently with positional arguments.template: Usesync.Oncefor TrimBlocks/LStripBlocks token trimming (fixes race condition).template_sets: Closeio.ReadCloserinFromFile(fixes resource leak with file-based loaders).template_sets: Detect recursive includes at parse time (falls back to lazy evaluation instead of infinite recursion).value: Add bounds checking toIndex()andSlice()public API; return nil for unsupported types.value: Dereference pointers inContains()andGetItem()type checks.value: Use numeric comparison for mixed int/float sorting.- Fix operator precedence for
not X in Yexpressions. - Fix float and int comparison by numeric value.
- Fix
ifchangedto render else block for content-based comparison. - Fix rune indexing for multi-byte UTF-8 strings in variable resolution.
- Fix format verb in
ljust/rjusterror messages. - Fix autoescape state restoration on error.
- Fix lexer column tracking by character count instead of byte width.
- Fix
Negate()return value andIndex()log message. - Fix
widthratioto usemath.Roundfor correct ratio calculation. - Fix
ifnotequalerror message to say "ifnotequal" instead of "ifequal". - Fix
cycleandifchangedtags to use per-execution state instead of shared AST node state (fixes race conditions).
- Extract
normalizeNewlineshelper to reduce duplication across newline-normalizing filters.
- Add comprehensive wordwrap tests covering word boundaries, mid-word lengths, newline handling, and Unicode.
- Fix inaccurate filter examples in code comments and docs (ellipsis characters, float rendering format, boolean capitalization,
floatformatdefault,json_scriptspacing,urlizetruncoutput). - Fix API signatures in extension guides:
*Error→errorreturn types,TagExists→BuiltinTagExists,INodeTag.Executereturn type, TokenType enum ordering. - Update version to 7.0.0-alpha.2, move built-in filters out of addons list, update godoc links to
/v7.
- New filters:
json_script,safeseq,escapeseq,dictsort,unordered_list,slugify,filesizeformat,timesince,timeuntil. - [Backwards-Incompatible] Improved
escapejsfilter to match Django behavior. - [Backwards-Incompatible] Improved
striptagsandremovetagsfilters with better security (recursive stripping, iteration limits). - [Backwards-Incompatible] Use proper ellipsis character (…) in
truncatechars,truncatechars_html, andurlizetruncfilters. - [Backwards-Incompatible] Empty
{% filter %}tag now returns a parse error (Django compatibility). - [Backwards-Incompatible] Empty
{% firstof %}tag now returns a parse error (Django compatibility). - Support for negative number literals in arguments.
- Support for escape sequences in string literals (
\n,\t, etc.). - Support for sorted and reversed iteration over strings.
- Inline variable definitions (
{% set foo = "bar" %}). - Expand
urlizefilter to support more TLDs.
- [Backwards-Incompatible] Fix
and/oroperators to return actual values instead of booleans (#362). - Fix panic in
cycletag with no arguments. - Fix integer overflow when converting
uint64toint. - Fix panic on uncomparable types in comparisons.
- Fix nil subscript panic.
- Fix
ifchangedtag to only evaluate else block if it exists. - Fix
inoperator type compatibility for maps. - Fix
Containsmethod to support all map key types (float64, bool, etc.). - Fix array parser panic introduced by subscript feature.
- Support virtual filesystems in
ssitag plaintext mode andError.RawLine(). - Prevent memory exhaustion by limiting
loremtag generation. - Prevent infinite loop in unclosed parameterized tag situations.
- Prevent stack overflow by limiting macro call depth.
- Prevent DoS via huge parameters in certain filters.
- Prevent panic from integer divide by zero.
- Fix string indexing to return character instead of byte (Django compatibility).
- Fix
NewSetto validate that loaders are not nil.
- Optimize lexer with keyword map and pre-compiled string replacer.
- Use pre-compiled
strings.Replacerfor HTML escaping filters. - Cache
getResolvedValue()result in Value methods. - Optimize context valid identifier check (#340).
- Speed optimizations for
joinfilter on long strings.
ifequalandifnotequaltags now emit deprecation warnings (use{% if %}instead).ssitag is deprecated.
- [Backwards-Incompatible] Remove
HttpFilesystemLoader. UseFSLoaderwithNewFSLoader()instead, which supports Go'sfs.FSinterface includingos.DirFS()andembed.FS. - [Backwards-Incompatible] Remove incomplete
SandboxedFilesystemLoader. For sandboxing, useBanTag()to restrictinclude/import/ssi/extendstags.
- Go 1.25 is now the minimum required Go version.
- Added comprehensive fuzz testing infrastructure.
- Comprehensive documentation overhaul with new guides for getting started, template syntax, security, and custom extensions.
Thanks to all contributors.
- Go 1.18 is now the minimum required Go version.
- Improved block performance (#293).
- Support for variable subscript syntax (for maps/arrays/slices), such as
mymap["foo"]ormyarray[0](#281). - Backwards-incompatible change:
block.Superwon't be escaped anymore by default (#301). nilis now supported in function calls (#277).
Thanks to all contributors.