python-liquid 1.12.0__tar.gz → 1.12.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. {python_liquid-1.12.0 → python_liquid-1.12.2}/.gitignore +3 -0
  2. {python_liquid-1.12.0 → python_liquid-1.12.2}/PKG-INFO +4 -3
  3. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/__init__.py +1 -1
  4. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/filters/array.py +21 -13
  5. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/filters/extra.py +1 -1
  6. python_liquid-1.12.2/liquid/builtin/filters/math.py +134 -0
  7. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/filters/misc.py +7 -4
  8. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/filters/string.py +36 -28
  9. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/loaders/base_loader.py +2 -1
  10. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/statement.py +3 -1
  11. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/assign_tag.py +7 -3
  12. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/decrement_tag.py +3 -1
  13. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/echo_tag.py +3 -3
  14. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/for_tag.py +4 -1
  15. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/if_tag.py +29 -2
  16. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/increment_tag.py +3 -1
  17. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/render_tag.py +4 -1
  18. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/tablerow_tag.py +42 -5
  19. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/unless_tag.py +28 -2
  20. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/context.py +1 -3
  21. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/environment.py +8 -7
  22. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expression.py +5 -10
  23. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/tags/if_expressions.py +13 -10
  24. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/tags/if_not.py +3 -1
  25. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/future/environment.py +11 -3
  26. python_liquid-1.12.2/liquid/future/tags/__init__.py +11 -0
  27. python_liquid-1.12.2/liquid/future/tags/_case_tag.py +182 -0
  28. python_liquid-1.12.2/liquid/future/tags/_if_tag.py +8 -0
  29. python_liquid-1.12.2/liquid/future/tags/_tablerow_tag.py +14 -0
  30. python_liquid-1.12.2/liquid/future/tags/_unless_tag.py +8 -0
  31. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/case_tag.py +46 -11
  32. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/if_tag.py +126 -0
  33. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/tablerow_tag.py +55 -0
  34. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/unless_tag.py +27 -0
  35. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/mode.py +1 -1
  36. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/parse.py +0 -1
  37. {python_liquid-1.12.0 → python_liquid-1.12.2}/pyproject.toml +60 -52
  38. python_liquid-1.12.0/liquid/builtin/filters/math.py +0 -141
  39. {python_liquid-1.12.0 → python_liquid-1.12.2}/LICENSE +0 -0
  40. {python_liquid-1.12.0 → python_liquid-1.12.2}/README.md +0 -0
  41. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/analyze_tags.py +0 -0
  42. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/ast.py +0 -0
  43. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/__init__.py +0 -0
  44. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/drops/__init__.py +0 -0
  45. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/drops/drops.py +0 -0
  46. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/filters/__init__.py +0 -0
  47. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/illegal.py +0 -0
  48. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/literal.py +0 -0
  49. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/loaders/__init__.py +0 -0
  50. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/loaders/caching_file_system_loader.py +0 -0
  51. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/loaders/choice_loader.py +0 -0
  52. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/loaders/file_system_loader.py +0 -0
  53. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/loaders/mixins.py +0 -0
  54. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/loaders/package_loader.py +0 -0
  55. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/__init__.py +0 -0
  56. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/capture_tag.py +0 -0
  57. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/case_tag.py +0 -0
  58. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/comment_tag.py +0 -0
  59. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/cycle_tag.py +0 -0
  60. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/ifchanged_tag.py +0 -0
  61. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/include_tag.py +0 -0
  62. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/inline_comment_tag.py +0 -0
  63. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/builtin/tags/liquid_tag.py +0 -0
  64. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/chain_map.py +0 -0
  65. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/exceptions.py +0 -0
  66. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/__init__.py +0 -0
  67. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/arguments/__init__.py +0 -0
  68. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/arguments/lex.py +0 -0
  69. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/arguments/parse.py +0 -0
  70. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/boolean/__init__.py +0 -0
  71. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/boolean/lex.py +0 -0
  72. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/boolean/parse.py +0 -0
  73. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/common.py +0 -0
  74. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/conditional/__init__.py +0 -0
  75. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/conditional/lex.py +0 -0
  76. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/conditional/parse.py +0 -0
  77. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/filtered/__init__.py +0 -0
  78. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/filtered/lex.py +0 -0
  79. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/filtered/parse.py +0 -0
  80. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/include/__init__.py +0 -0
  81. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/include/lex.py +0 -0
  82. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/loop/__init__.py +0 -0
  83. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/loop/lex.py +0 -0
  84. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/loop/parse.py +0 -0
  85. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/expressions/stream.py +0 -0
  86. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/__init__.py +0 -0
  87. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/filters/__init__.py +0 -0
  88. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/filters/_json.py +0 -0
  89. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/filters/array.py +0 -0
  90. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/filters/html.py +0 -0
  91. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/tags/__init__.py +0 -0
  92. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/tags/_with.py +0 -0
  93. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/tags/extends.py +0 -0
  94. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/extra/tags/macro.py +0 -0
  95. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/filter.py +0 -0
  96. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/future/__init__.py +0 -0
  97. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/future/filters/__init__.py +0 -0
  98. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/future/filters/_split.py +0 -0
  99. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/__init__.py +0 -0
  100. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/abs_filter.py +0 -0
  101. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/append_filter.py +0 -0
  102. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/assign_tag.py +0 -0
  103. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/at_least_filter.py +0 -0
  104. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/at_most_filter.py +0 -0
  105. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/base64_decode_filter.py +0 -0
  106. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/base64_encode_filter.py +0 -0
  107. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/base64_url_safe_decode_filter.py +0 -0
  108. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/base64_url_safe_encode_filter.py +0 -0
  109. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/capitalize_filter.py +0 -0
  110. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/capture_tag.py +0 -0
  111. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/case.py +0 -0
  112. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/ceil_filter.py +0 -0
  113. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/comment_tag.py +0 -0
  114. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/compact_filter.py +0 -0
  115. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/concat_filter.py +0 -0
  116. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/cycle_tag.py +0 -0
  117. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/date_filter.py +0 -0
  118. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/decrement_tag.py +0 -0
  119. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/default_filter.py +0 -0
  120. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/divided_by_filter.py +0 -0
  121. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/downcase_filter.py +0 -0
  122. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/echo_tag.py +0 -0
  123. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/escape_filter.py +0 -0
  124. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/escape_once_filter.py +0 -0
  125. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/first_filter.py +0 -0
  126. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/floor_filter.py +0 -0
  127. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/for_tag.py +0 -0
  128. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/identifiers.py +0 -0
  129. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/ifchanged_tag.py +0 -0
  130. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/illegal.py +0 -0
  131. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/include_tag.py +0 -0
  132. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/increment_tag.py +0 -0
  133. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/inline_comment_tag.py +0 -0
  134. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/join_filter.py +0 -0
  135. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/last_filter.py +0 -0
  136. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/liquid_tag.py +0 -0
  137. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/lstrip_filter.py +0 -0
  138. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/map_filter.py +0 -0
  139. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/minus_filter.py +0 -0
  140. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/modulo_filter.py +0 -0
  141. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/newline_to_br_filter.py +0 -0
  142. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/not_liquid.py +0 -0
  143. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/output_statement.py +0 -0
  144. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/plus_filter.py +0 -0
  145. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/prepend_filter.py +0 -0
  146. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/range_objects.py +0 -0
  147. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/raw_tag.py +0 -0
  148. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/remove_filter.py +0 -0
  149. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/remove_first_filter.py +0 -0
  150. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/remove_last_filter.py +0 -0
  151. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/render_tag.py +0 -0
  152. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/replace_filter.py +0 -0
  153. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/replace_first_filter.py +0 -0
  154. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/replace_last_filter.py +0 -0
  155. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/reverse_filter.py +0 -0
  156. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/round_filter.py +0 -0
  157. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/rstrip_filter.py +0 -0
  158. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/size_filter.py +0 -0
  159. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/slice_filter.py +0 -0
  160. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/sort_filter.py +0 -0
  161. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/sort_natural_filter.py +0 -0
  162. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/special.py +0 -0
  163. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/split_filter.py +0 -0
  164. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/strip_filter.py +0 -0
  165. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/strip_html_filter.py +0 -0
  166. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/strip_newlines_filter.py +0 -0
  167. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/sum_filter.py +0 -0
  168. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/times_filter.py +0 -0
  169. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/truncate_filter.py +0 -0
  170. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/truncatewords_filter.py +0 -0
  171. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/uniq_filter.py +0 -0
  172. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/upcase_filter.py +0 -0
  173. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/url_decode_filter.py +0 -0
  174. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/url_encode_filter.py +0 -0
  175. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/where_filter.py +0 -0
  176. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/golden/whitespace_control.py +0 -0
  177. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/lex.py +0 -0
  178. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/limits.py +0 -0
  179. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/loaders.py +0 -0
  180. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/output.py +0 -0
  181. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/py.typed +0 -0
  182. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/static_analysis.py +0 -0
  183. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/stream.py +0 -0
  184. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/stringify.py +0 -0
  185. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/tag.py +0 -0
  186. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/template.py +0 -0
  187. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/token.py +0 -0
  188. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/undefined.py +0 -0
  189. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/utils/__init__.py +0 -0
  190. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/utils/cache.py +0 -0
  191. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/utils/cache.pyi +0 -0
  192. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/utils/html.py +0 -0
  193. {python_liquid-1.12.0 → python_liquid-1.12.2}/liquid/utils/text.py +0 -0
@@ -76,6 +76,9 @@ ENV/
76
76
  # IDE
77
77
  .vscode/
78
78
 
79
+ # System
80
+ .DS_Store
81
+
79
82
  # Dev utils
80
83
  dev.py
81
84
  dump_golden.py
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-liquid
3
- Version: 1.12.0
3
+ Version: 1.12.2
4
4
  Summary: A Python engine for the Liquid template language.
5
5
  Project-URL: Change Log, https://github.com/jg-rp/liquid/blob/main/CHANGES.md
6
6
  Project-URL: Documentation, https://jg-rp.github.io/liquid/
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.9
20
20
  Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
23
24
  Classifier: Programming Language :: Python :: Implementation :: CPython
24
25
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
26
  Requires-Python: >=3.7
@@ -27,7 +28,7 @@ Requires-Dist: importlib-resources>=5.10.0
27
28
  Requires-Dist: python-dateutil>=2.8.1
28
29
  Requires-Dist: typing-extensions>=4.2.0
29
30
  Provides-Extra: autoescape
30
- Requires-Dist: markupsafe>=2.0.0; extra == 'autoescape'
31
+ Requires-Dist: markupsafe<3,>=2; extra == 'autoescape'
31
32
  Description-Content-Type: text/markdown
32
33
 
33
34
  <h1 align="center">Python Liquid</h1>
@@ -46,7 +46,7 @@ from .static_analysis import ContextualTemplateAnalysis
46
46
 
47
47
  from . import future
48
48
 
49
- __version__ = "1.12.0"
49
+ __version__ = "1.12.2"
50
50
 
51
51
  __all__ = (
52
52
  "AwareBoundTemplate",
@@ -33,6 +33,7 @@ if TYPE_CHECKING:
33
33
  from liquid import Environment
34
34
 
35
35
  ArrayT = Union[List[Any], Tuple[Any, ...]]
36
+ """Array-like objects."""
36
37
 
37
38
  # Send objects with missing keys to the end when sorting a list.
38
39
  MAX_CH = chr(0x10FFFF)
@@ -74,9 +75,12 @@ def _lower(obj: Any) -> str:
74
75
  @with_environment
75
76
  @sequence_filter
76
77
  def join(
77
- sequence: Iterable[object], separator: object = " ", *, environment: Environment
78
+ sequence: Iterable[object],
79
+ separator: object = " ",
80
+ *,
81
+ environment: Environment,
78
82
  ) -> str:
79
- """Concatenate an array of strings."""
83
+ """Return a string by joining items in _sequence_, separated by _separator_."""
80
84
  if not isinstance(separator, str):
81
85
  separator = str(separator)
82
86
 
@@ -88,7 +92,7 @@ def join(
88
92
 
89
93
  @liquid_filter
90
94
  def first(obj: Any) -> object:
91
- """Return the first item of an array."""
95
+ """Return the first item of collection _obj_."""
92
96
  if isinstance(obj, str):
93
97
  return None
94
98
 
@@ -103,7 +107,7 @@ def first(obj: Any) -> object:
103
107
 
104
108
  @liquid_filter
105
109
  def last(obj: Sequence[Any]) -> object:
106
- """Return the last item of an array."""
110
+ """Return the last item of array-like object _obj_."""
107
111
  if isinstance(obj, str):
108
112
  return None
109
113
 
@@ -115,7 +119,7 @@ def last(obj: Sequence[Any]) -> object:
115
119
 
116
120
  @sequence_filter
117
121
  def concat(sequence: ArrayT, second_array: ArrayT) -> ArrayT:
118
- """Return two arrays joined together."""
122
+ """Return the concatenation of _sequence_ and _second_array_."""
119
123
  if not isinstance(second_array, (list, tuple)):
120
124
  raise FilterArgumentError(
121
125
  f"concat expected an array, found {type(second_array).__name__}"
@@ -129,7 +133,7 @@ def concat(sequence: ArrayT, second_array: ArrayT) -> ArrayT:
129
133
 
130
134
  @sequence_filter
131
135
  def map_(sequence: ArrayT, key: object) -> List[object]:
132
- """Create an array of values from a map."""
136
+ """Return an array/list of items in _sequence_ selected by _key_."""
133
137
  try:
134
138
  return [_getitem(itm, str(key), default=NIL) for itm in sequence]
135
139
  except TypeError as err:
@@ -144,9 +148,9 @@ def reverse(array: ArrayT) -> List[object]:
144
148
 
145
149
  @array_filter
146
150
  def sort(sequence: ArrayT, key: object = None) -> List[object]:
147
- """Sorts items in an array in case-sensitive order.
151
+ """Return a copy of _sequence_ in ascending order.
148
152
 
149
- When a key string is provided, objects without the key property should
153
+ When a key string is provided, objects without the key property will
150
154
  be at the end of the output list/array.
151
155
  """
152
156
  if key:
@@ -161,7 +165,11 @@ def sort(sequence: ArrayT, key: object = None) -> List[object]:
161
165
 
162
166
  @array_filter
163
167
  def sort_natural(sequence: ArrayT, key: object = None) -> List[object]:
164
- """Sorts items in an array in case-insensitive order."""
168
+ """Return a copy of _sequence_ in ascending order, with case-insensitive comparison.
169
+
170
+ When a key string is provided, objects without the key property will
171
+ be at the end of the output list/array.
172
+ """
165
173
  if key:
166
174
  item_getter = partial(_getitem, key=str(key), default=MAX_CH)
167
175
  return sorted(sequence, key=lambda obj: _lower(item_getter(obj)))
@@ -171,7 +179,7 @@ def sort_natural(sequence: ArrayT, key: object = None) -> List[object]:
171
179
 
172
180
  @sequence_filter
173
181
  def where(sequence: ArrayT, attr: object, value: object = None) -> List[object]:
174
- """Create an array from a map where _attr_ equals _value_."""
182
+ """Return a list of items from _sequence_ where _attr_ equals _value_."""
175
183
  if value is not None and not is_undefined(value):
176
184
  return [itm for itm in sequence if _getitem(itm, attr) == value]
177
185
 
@@ -180,7 +188,7 @@ def where(sequence: ArrayT, attr: object, value: object = None) -> List[object]:
180
188
 
181
189
  @sequence_filter
182
190
  def uniq(sequence: ArrayT, key: object = None) -> List[object]:
183
- """Removes any duplicate elements in an array."""
191
+ """Return a copy of _sequence_ with duplicate elements removed."""
184
192
  # Note that we're not using a dict or set for deduplication because we need
185
193
  # to handle sequences containing unhashable objects, like dictionaries.
186
194
 
@@ -209,7 +217,7 @@ def uniq(sequence: ArrayT, key: object = None) -> List[object]:
209
217
 
210
218
  @sequence_filter
211
219
  def compact(sequence: ArrayT, key: object = None) -> List[object]:
212
- """Removes any nil values from an array."""
220
+ """Return a copy of _sequence_ with any nil values removed."""
213
221
  if key is not None:
214
222
  try:
215
223
  return [itm for itm in sequence if itm[key] is not None]
@@ -220,7 +228,7 @@ def compact(sequence: ArrayT, key: object = None) -> List[object]:
220
228
 
221
229
  @sequence_filter
222
230
  def sum_(sequence: ArrayT, key: object = None) -> Union[float, int, Decimal]:
223
- """Return the sum of all numeric elements in an array.
231
+ """Return the sum of all numeric elements in _sequence_.
224
232
 
225
233
  If _key_ is given, it is assumed that sequence items are mapping-like,
226
234
  and the values at _item[key]_ will be summed instead.
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
18
18
  @with_environment
19
19
  @string_filter
20
20
  def safe(val: str, *, environment: Environment) -> str:
21
- """Stringify and mark as safe."""
21
+ """Return a copy of _val_ that will not be automatically HTML escaped on output."""
22
22
  if environment.autoescape:
23
23
  return Markup(val)
24
24
  return val
@@ -0,0 +1,134 @@
1
+ """Maths related filter functions."""
2
+
3
+ import decimal
4
+ import math
5
+ from typing import Optional
6
+ from typing import Union
7
+
8
+ from liquid.context import is_undefined
9
+ from liquid.exceptions import FilterArgumentError
10
+ from liquid.filter import math_filter
11
+ from liquid.filter import num_arg
12
+
13
+ # TODO: Version 2 - Either handle all filter function argument type
14
+ # conversions in a decorator or all in the function itself. Having these type
15
+ # conversions split between decorators and calls to helper functions does not
16
+ # help with readability, or make it easy to write good doc strings.
17
+
18
+
19
+ @math_filter
20
+ def abs_(num: Union[float, int]) -> Union[float, int]:
21
+ """Return the absolute value of number _num_."""
22
+ return abs(num)
23
+
24
+
25
+ @math_filter
26
+ def at_most(num: Union[float, int], other: Union[float, int]) -> Union[float, int]:
27
+ """Return _val_ or _other_, whichever is smaller."""
28
+ other = num_arg(other, default=0)
29
+ return min(num, other)
30
+
31
+
32
+ @math_filter
33
+ def at_least(num: Union[float, int], other: Union[float, int]) -> Union[float, int]:
34
+ """Return _val_ or _other_, whichever is greater."""
35
+ other = num_arg(other, default=0)
36
+ return max(num, other)
37
+
38
+
39
+ @math_filter
40
+ def ceil(num: Union[float, int]) -> Union[float, int]:
41
+ """Return _num_ rounded up to the next integer."""
42
+ return math.ceil(num)
43
+
44
+
45
+ @math_filter
46
+ def divided_by(num: Union[float, int], other: object) -> Union[float, int]:
47
+ """Return the result of dividing _num_ by _other_.
48
+
49
+ If both _num_ and _other_ are integers, integer division is performed.
50
+ """
51
+ other = num_arg(other, default=0)
52
+
53
+ try:
54
+ if isinstance(other, int) and isinstance(num, int):
55
+ return num // other
56
+ return num / other
57
+ except ZeroDivisionError as err:
58
+ # TODO: [VERSION_2] move inclusion of filter name in error messages to
59
+ # FilteredExpression, where the filter is applied.
60
+ raise FilterArgumentError(f"divided_by: can't divide by {other}") from err
61
+
62
+
63
+ @math_filter
64
+ def floor(num: Union[float, int]) -> Union[float, int]:
65
+ """Return _num_ rounded down to the next integer."""
66
+ return math.floor(num)
67
+
68
+
69
+ @math_filter
70
+ def minus(num: Union[float, int], other: Union[float, int]) -> Union[float, int]:
71
+ """Return the result of subtracting _other_ from _num_."""
72
+ other = num_arg(other, default=0)
73
+
74
+ if isinstance(num, int) and isinstance(other, int):
75
+ return num - other
76
+ return float(decimal.Decimal(str(num)) - decimal.Decimal(str(other)))
77
+
78
+
79
+ @math_filter
80
+ def plus(num: Union[float, int], other: Union[float, int]) -> Union[float, int]:
81
+ """Return the result of adding _other_ to _num_."""
82
+ other = num_arg(other, default=0)
83
+
84
+ if isinstance(num, int) and isinstance(other, int):
85
+ return num + other
86
+ return float(decimal.Decimal(str(num)) + decimal.Decimal(str(other)))
87
+
88
+
89
+ @math_filter
90
+ def round_(num: Union[float, int], ndigits: Optional[int] = None) -> Union[float, int]:
91
+ """Returns the result of rounding _num_ to _ndigits_ decimal digits."""
92
+ if ndigits is None or is_undefined(ndigits):
93
+ return round(num)
94
+
95
+ try:
96
+ _ndigits = num_arg(ndigits)
97
+ except FilterArgumentError:
98
+ # Probably a string that can't be case to an int or float
99
+ return round(num)
100
+
101
+ if isinstance(_ndigits, float):
102
+ _ndigits = int(_ndigits)
103
+
104
+ if _ndigits < 0:
105
+ return 0
106
+ if _ndigits == 0:
107
+ return round(num)
108
+
109
+ return round(num, _ndigits)
110
+
111
+
112
+ @math_filter
113
+ def times(num: Union[float, int], other: Union[float, int]) -> Union[float, int]:
114
+ """Return the result of multiplying _num_ by _other_."""
115
+ other = num_arg(other, default=0)
116
+
117
+ if isinstance(num, int) and isinstance(other, int):
118
+ return num * other
119
+ return float(decimal.Decimal(str(num)) * decimal.Decimal(str(other)))
120
+
121
+
122
+ @math_filter
123
+ def modulo(num: Union[float, int], other: Union[float, int]) -> Union[float, int]:
124
+ """Return the remainder of dividing _num_ by _other_."""
125
+ other = num_arg(other, default=0)
126
+
127
+ try:
128
+ if isinstance(num, int) and isinstance(other, int):
129
+ return num % other
130
+ return float(decimal.Decimal(str(num)) % decimal.Decimal(str(other)))
131
+ except ZeroDivisionError as err:
132
+ # TODO: [VERSION_2] move inclusion of filter name in error messages to
133
+ # FilteredExpression, where the filter is applied.
134
+ raise FilterArgumentError(f"modulo: can't divide by {other}") from err
@@ -1,4 +1,4 @@
1
- """Miscellaneous filters."""
1
+ """Miscellaneous filter functions."""
2
2
  from __future__ import annotations
3
3
 
4
4
  import datetime
@@ -27,7 +27,10 @@ if TYPE_CHECKING:
27
27
 
28
28
  @liquid_filter
29
29
  def size(obj: Any) -> int:
30
- """Return the length of an array or string."""
30
+ """Return the length of _obj_.
31
+
32
+ _obj_ could be a dict, list, string or any class implementing _len_.
33
+ """
31
34
  try:
32
35
  return len(obj)
33
36
  except TypeError:
@@ -36,7 +39,7 @@ def size(obj: Any) -> int:
36
39
 
37
40
  @liquid_filter
38
41
  def default(obj: Any, default_: object = "", *, allow_false: bool = False) -> Any:
39
- """Return a default value if the input is nil, false, or empty."""
42
+ """Return _obj_, or _default_ if _obj_ is nil, false, or empty."""
40
43
  _obj = obj
41
44
 
42
45
  # Return the default value immediately if the object defines a
@@ -69,7 +72,7 @@ def date( # noqa: PLR0912 PLR0911
69
72
  *,
70
73
  environment: Environment,
71
74
  ) -> str:
72
- """Format a datetime according the the given format string."""
75
+ """Return a string representation of _dat_ using format string _fmt_."""
73
76
  if is_undefined(dat):
74
77
  return ""
75
78
 
@@ -1,4 +1,5 @@
1
1
  """Filter functions that operate on strings."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import base64
@@ -38,7 +39,10 @@ if TYPE_CHECKING:
38
39
 
39
40
  @string_filter
40
41
  def append(val: str, arg: object) -> str:
41
- """Concatenate two strings."""
42
+ """Return a copy of _val_ concatenated with _arg_.
43
+
44
+ If _arg_ is not a string, it will be converted to one before concatenation.
45
+ """
42
46
  if not isinstance(arg, str):
43
47
  arg = str(arg)
44
48
  return val + arg
@@ -46,20 +50,20 @@ def append(val: str, arg: object) -> str:
46
50
 
47
51
  @string_filter
48
52
  def capitalize(val: str) -> str:
49
- """Make sure the first character of _val_ is upper case and the rest lowercase."""
53
+ """Return _val_ with the first character in uppercase and the rest lowercase."""
50
54
  return val.capitalize()
51
55
 
52
56
 
53
57
  @string_filter
54
58
  def downcase(val: str) -> str:
55
- """Make all characters in a string lower case."""
59
+ """Return a copy of _val_ with all characters converted to lowercase."""
56
60
  return val.lower()
57
61
 
58
62
 
59
63
  @with_environment
60
64
  @string_filter
61
65
  def escape(val: str, *, environment: Environment) -> str:
62
- """Convert the characters &, < and > in string s to HTML-safe sequences."""
66
+ """Return _val_ with the characters &, < and > converted to HTML-safe sequences."""
63
67
  if environment.autoescape:
64
68
  return markupsafe_escape(str(val))
65
69
  return html.escape(val)
@@ -68,7 +72,11 @@ def escape(val: str, *, environment: Environment) -> str:
68
72
  @with_environment
69
73
  @string_filter
70
74
  def escape_once(val: str, *, environment: Environment) -> str:
71
- """Convert the characters &, < and > in string s to HTML-safe sequences."""
75
+ """Return _val_ with the characters &, < and > converted to HTML-safe sequences.
76
+
77
+ It is safe to use `escape_one` on string values that already contain HTML escape
78
+ sequences.
79
+ """
72
80
  if environment.autoescape:
73
81
  return Markup(val).unescape()
74
82
  return html.escape(html.unescape(val))
@@ -76,7 +84,7 @@ def escape_once(val: str, *, environment: Environment) -> str:
76
84
 
77
85
  @string_filter
78
86
  def lstrip(val: str) -> str:
79
- """Remove leading whitespace."""
87
+ """Return a copy of _val_ with leading whitespace removed."""
80
88
  return val.lstrip()
81
89
 
82
90
 
@@ -86,7 +94,7 @@ RE_LINETERM = re.compile(r"\r?\n")
86
94
  @with_environment
87
95
  @string_filter
88
96
  def newline_to_br(val: str, *, environment: Environment) -> str:
89
- """Convert LF or CRLF to `<br />`, plus a newline."""
97
+ """Return a copy of _val_ with LF or CRLF converted to `<br />`, plus a newline."""
90
98
  # The reference implementation was changed to replace "\r\n" as well as "\n",
91
99
  # but they don't seem to care about "\r" (Mac OS).
92
100
  if environment.autoescape:
@@ -97,25 +105,25 @@ def newline_to_br(val: str, *, environment: Environment) -> str:
97
105
 
98
106
  @string_filter
99
107
  def prepend(val: str, arg: str) -> str:
100
- """Concatenate string value to argument string."""
108
+ """Return a copy of _arg_ concatenated with _val_."""
101
109
  return soft_str(arg) + val
102
110
 
103
111
 
104
112
  @string_filter
105
113
  def remove(val: str, arg: str) -> str:
106
- """Remove all occurrences of argument string from value."""
114
+ """Return a copy of _val_ with all occurrences of _arg_ removed."""
107
115
  return val.replace(soft_str(arg), "")
108
116
 
109
117
 
110
118
  @string_filter
111
119
  def remove_first(val: str, arg: str) -> str:
112
- """Remove the first occurrence of the argument string from value."""
120
+ """Return a copy of _val_ with the first occurrence of _arg_ removed."""
113
121
  return val.replace(soft_str(arg), "", 1)
114
122
 
115
123
 
116
124
  @string_filter
117
125
  def remove_last(val: str, arg: str) -> str:
118
- """Remove the last occurrence of the argument string from value."""
126
+ """Return a copy of _val_ with last occurrence of _arg_ removed."""
119
127
  try:
120
128
  before, _, after = val.rpartition(soft_str(arg))
121
129
  except ValueError:
@@ -128,19 +136,19 @@ def remove_last(val: str, arg: str) -> str:
128
136
 
129
137
  @string_filter
130
138
  def replace(val: str, seq: str, sub: str = "") -> str:
131
- """Replace occurrences of _seq_ in _val_ with _sub_."""
139
+ """Return a copy of _val_ with each occurrence of _seq_ replaced with _sub_."""
132
140
  return val.replace(soft_str(seq), soft_str(sub))
133
141
 
134
142
 
135
143
  @string_filter
136
144
  def replace_first(val: str, seq: str, sub: str = "") -> str:
137
- """Replace the first occurrence of _seq_ in _val_ with _sub_."""
145
+ """Return a copy of _val_ with the first occurrence of _seq_ replaced with _sub_."""
138
146
  return val.replace(soft_str(seq), soft_str(sub), 1)
139
147
 
140
148
 
141
149
  @string_filter
142
150
  def replace_last(val: str, seq: str, sub: str) -> str:
143
- """Replace the last occurrence of _seq_ in _val_ with _sub_."""
151
+ """Return a copy of _val_ with the last occurrence of _seq_ replaced with _sub_."""
144
152
  try:
145
153
  before, _, after = val.rpartition(soft_str(seq))
146
154
  except ValueError:
@@ -153,7 +161,7 @@ def replace_last(val: str, seq: str, sub: str) -> str:
153
161
 
154
162
  @string_filter
155
163
  def upcase(val: str) -> str:
156
- """Make all characters in a string upper case."""
164
+ """Return a copy of _val_ with all characters converted to uppercase."""
157
165
  return val.upper()
158
166
 
159
167
 
@@ -211,7 +219,7 @@ def slice_(val: Any, start: Any, length: Any = 1) -> Union[str, List[object]]:
211
219
 
212
220
  @string_filter
213
221
  def split(val: str, seq: str) -> List[str]:
214
- """Split a string into a list of string, using the argument as a delimiter."""
222
+ """Return a list of strings from splitting _value_ on _seq_."""
215
223
  if not seq:
216
224
  return list(val)
217
225
 
@@ -220,20 +228,20 @@ def split(val: str, seq: str) -> List[str]:
220
228
 
221
229
  @string_filter
222
230
  def strip(val: str) -> str:
223
- """Remove leading and trailing whitespace."""
231
+ """Return a copy of _val_ with leading and trailing whitespace removed."""
224
232
  return val.strip()
225
233
 
226
234
 
227
235
  @string_filter
228
236
  def rstrip(val: str) -> str:
229
- """Remove trailing whitespace."""
237
+ """Return a copy of _val_ with trailing whitespace removed."""
230
238
  return val.rstrip()
231
239
 
232
240
 
233
241
  @with_environment
234
242
  @string_filter
235
243
  def strip_html(val: str, *, environment: Environment) -> str:
236
- """Return the given HTML with all HTML tags removed."""
244
+ """Return a copy of _val_ with all HTML tags removed."""
237
245
  stripped = strip_tags(val)
238
246
  if environment.autoescape and isinstance(val, Markup):
239
247
  return Markup(stripped)
@@ -243,7 +251,7 @@ def strip_html(val: str, *, environment: Environment) -> str:
243
251
  @with_environment
244
252
  @string_filter
245
253
  def strip_newlines(val: str, *, environment: Environment) -> str:
246
- """Return the given string with all newline characters removed."""
254
+ """Return ta copy of _val_ with all newline characters removed."""
247
255
  if environment.autoescape:
248
256
  val = markupsafe_escape(val)
249
257
  return Markup(RE_LINETERM.sub("", val))
@@ -252,7 +260,7 @@ def strip_newlines(val: str, *, environment: Environment) -> str:
252
260
 
253
261
  @string_filter
254
262
  def truncate(val: str, num: Any = 50, end: str = "...") -> str:
255
- """Truncate a string if it is longer than the specified number of characters."""
263
+ """Return a copy of _val_ truncated to _num_ characters."""
256
264
  if is_undefined(num):
257
265
  raise FilterArgumentError("truncate expected an integer, found Undefined")
258
266
 
@@ -273,7 +281,7 @@ MAX_TRUNC_WORDS = (1 << 31) - 1
273
281
 
274
282
  @string_filter
275
283
  def truncatewords(val: str, num: Any = 15, end: str = "...") -> str:
276
- """Shorten a string down to the given number of words."""
284
+ """Return a copy of _val_ truncated to at most _num_ words."""
277
285
  if is_undefined(num):
278
286
  raise FilterArgumentError("truncate expected an integer, found Undefined")
279
287
 
@@ -306,7 +314,7 @@ def truncatewords(val: str, num: Any = 15, end: str = "...") -> str:
306
314
  @with_environment
307
315
  @string_filter
308
316
  def url_encode(val: str, *, environment: Environment) -> str:
309
- """Percent encode a string so it is useable in a URL."""
317
+ """Return a percent-encoded copy of _val_ so it is useable in a URL."""
310
318
  if environment.autoescape:
311
319
  return Markup(urllib.parse.quote_plus(val))
312
320
  return urllib.parse.quote_plus(val)
@@ -314,20 +322,20 @@ def url_encode(val: str, *, environment: Environment) -> str:
314
322
 
315
323
  @string_filter
316
324
  def url_decode(val: str) -> str:
317
- """Decode a string that has been URL encoded."""
325
+ """Return a copy of _val_ after decoding percent-encoded sequences."""
318
326
  # Assuming URL decoded strings are all unsafe.
319
327
  return urllib.parse.unquote_plus(val)
320
328
 
321
329
 
322
330
  @string_filter
323
331
  def base64_encode(val: str) -> str:
324
- """Encode a string to base64."""
332
+ """Return _val_ encoded in base64."""
325
333
  return base64.b64encode(val.encode()).decode()
326
334
 
327
335
 
328
336
  @string_filter
329
337
  def base64_decode(val: str) -> str:
330
- """Decode a string from base64.
338
+ """Return _val_ decoded as base64.
331
339
 
332
340
  The decoded value is assumed to be UTF-8 and will be decoded as UTF-8.
333
341
  """
@@ -339,13 +347,13 @@ def base64_decode(val: str) -> str:
339
347
 
340
348
  @string_filter
341
349
  def base64_url_safe_encode(val: str) -> str:
342
- """Encode a string to URL safe base64."""
350
+ """Return _val_ encoded in URL-safe base64."""
343
351
  return base64.urlsafe_b64encode(val.encode()).decode()
344
352
 
345
353
 
346
354
  @string_filter
347
355
  def base64_url_safe_decode(val: str) -> str:
348
- """Decode a URL safe string from base64.
356
+ """Return _val_ decoded as URL-safe base64.
349
357
 
350
358
  The decoded value is assumed to be UTF-8 and will be decoded as UTF-8.
351
359
  """
@@ -1,4 +1,5 @@
1
1
  """Base template loader."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from abc import ABC
@@ -48,7 +49,7 @@ class BaseLoader(ABC): # noqa: B024
48
49
 
49
50
  Attributes:
50
51
  caching_loader (bool): Indicates if this loader implements its own cache.
51
- Setting this sto `True` will cause the `Environment` to disable its cache
52
+ Setting this to `True` will cause the `Environment` to disable its cache
52
53
  when initialized with a caching loader.
53
54
  """
54
55
 
@@ -60,4 +60,6 @@ class Statement(Tag):
60
60
  def parse(self, stream: TokenStream) -> StatementNode:
61
61
  tok = stream.current
62
62
  expect(stream, TOKEN_STATEMENT)
63
- return self.node_class(tok, self.env.parse_filtered_expression_value(tok.value))
63
+ return self.node_class(
64
+ tok, self.env.parse_filtered_expression_value(tok.value, tok.linenum)
65
+ )
@@ -65,8 +65,8 @@ class AssignTag(Tag):
65
65
  block = False
66
66
  node_class = AssignNode
67
67
 
68
- def _parse_expression(self, value: str) -> Expression:
69
- return self.env.parse_filtered_expression_value(value)
68
+ def _parse_expression(self, value: str, linenum: int) -> Expression:
69
+ return self.env.parse_filtered_expression_value(value, linenum)
70
70
 
71
71
  def parse(self, stream: TokenStream) -> AssignNode:
72
72
  expect(stream, TOKEN_TAG, value=TAG_ASSIGN)
@@ -83,5 +83,9 @@ class AssignTag(Tag):
83
83
  )
84
84
 
85
85
  return self.node_class(
86
- tok, AssignmentExpression(name, self._parse_expression(right))
86
+ tok,
87
+ AssignmentExpression(
88
+ name,
89
+ self._parse_expression(right, stream.current.linenum),
90
+ ),
87
91
  )
@@ -63,7 +63,9 @@ class DecrementTag(Tag):
63
63
  tok=tok,
64
64
  identifier=str(
65
65
  parse_unchained_identifier(
66
- ExprTokenStream(tokenize(stream.current.value))
66
+ ExprTokenStream(
67
+ tokenize(stream.current.value, stream.current.linenum)
68
+ )
67
69
  )
68
70
  ),
69
71
  )
@@ -30,8 +30,8 @@ class EchoTag(Tag):
30
30
  block = False
31
31
  node_class = EchoNode
32
32
 
33
- def _parse_expression(self, value: str) -> Expression:
34
- return self.env.parse_filtered_expression_value(value)
33
+ def _parse_expression(self, value: str, linenum: int) -> Expression:
34
+ return self.env.parse_filtered_expression_value(value, linenum)
35
35
 
36
36
  def parse(self, stream: TokenStream) -> Node: # noqa: D102
37
37
  expect(stream, TOKEN_TAG, value=TAG_ECHO)
@@ -42,5 +42,5 @@ class EchoTag(Tag):
42
42
  expr: Expression = NIL
43
43
  else:
44
44
  expect(stream, TOKEN_EXPRESSION)
45
- expr = self._parse_expression(stream.current.value)
45
+ expr = self._parse_expression(stream.current.value, tok.linenum)
46
46
  return self.node_class(tok, expression=expr)