python-liquid 2.3.5__py3-none-any.whl → 3.0.0__py3-none-any.whl

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 (213) hide show
  1. liquid/__about__.py +1 -0
  2. liquid/__init__.py +161 -187
  3. liquid/_base_tag.py +66 -0
  4. liquid/{utils/chain_map.py → _chain_map.py} +18 -19
  5. liquid/_context.py +465 -0
  6. liquid/_drop.py +108 -0
  7. liquid/_filter.py +174 -0
  8. liquid/_io.py +18 -0
  9. liquid/_lax_parser_mixin.py +100 -0
  10. liquid/_lexer.py +93 -0
  11. liquid/_lexer_extended.py +623 -0
  12. liquid/_lexer_legacy.py +536 -0
  13. liquid/_loader.py +84 -0
  14. liquid/{utils/lru_cache.py → _lru_cache.py} +28 -36
  15. liquid/_markup.py +253 -0
  16. liquid/_messages.py +292 -0
  17. liquid/_missing.py +3 -0
  18. liquid/_nothing.py +28 -0
  19. liquid/_parser.py +460 -0
  20. liquid/_parser_extended.py +293 -0
  21. liquid/_parser_legacy.py +819 -0
  22. liquid/_resource_limits.py +50 -0
  23. liquid/_slice.py +41 -0
  24. liquid/_static_analysis.py +503 -0
  25. liquid/_strftime.py +186 -0
  26. liquid/_template.py +536 -0
  27. liquid/drops/__init__.py +20 -0
  28. liquid/drops/forloop.py +78 -0
  29. liquid/drops/iterable.py +25 -0
  30. liquid/drops/range.py +76 -0
  31. liquid/drops/self_.py +45 -0
  32. liquid/drops/tablerowloop.py +142 -0
  33. liquid/drops/undefined.py +228 -0
  34. liquid/environment.py +717 -551
  35. liquid/exceptions.py +67 -214
  36. liquid/expressions.py +1184 -0
  37. liquid/filters/__init__.py +134 -0
  38. liquid/filters/abs.py +7 -0
  39. liquid/filters/append.py +5 -0
  40. liquid/filters/at_least.py +9 -0
  41. liquid/filters/at_most.py +9 -0
  42. liquid/filters/babel.py +560 -0
  43. liquid/filters/base64.py +26 -0
  44. liquid/filters/capitalize.py +5 -0
  45. liquid/filters/ceil.py +8 -0
  46. liquid/filters/compact.py +25 -0
  47. liquid/filters/concat.py +11 -0
  48. liquid/filters/date.py +27 -0
  49. liquid/filters/default.py +25 -0
  50. liquid/filters/divided_by.py +21 -0
  51. liquid/filters/downcase.py +5 -0
  52. liquid/filters/escape.py +56 -0
  53. liquid/filters/find.py +56 -0
  54. liquid/filters/first.py +19 -0
  55. liquid/filters/floor.py +8 -0
  56. liquid/filters/html.py +23 -0
  57. liquid/filters/index.py +12 -0
  58. liquid/filters/join.py +13 -0
  59. liquid/filters/json_.py +30 -0
  60. liquid/filters/last.py +17 -0
  61. liquid/filters/map.py +5 -0
  62. liquid/filters/minus.py +13 -0
  63. liquid/filters/modulo.py +18 -0
  64. liquid/filters/newline_to_br.py +16 -0
  65. liquid/filters/plus.py +13 -0
  66. liquid/filters/prepend.py +5 -0
  67. liquid/filters/reject.py +19 -0
  68. liquid/filters/remove.py +36 -0
  69. liquid/filters/replace.py +32 -0
  70. liquid/filters/reverse.py +5 -0
  71. liquid/filters/round.py +35 -0
  72. liquid/filters/size.py +25 -0
  73. liquid/filters/slice.py +39 -0
  74. liquid/filters/sort.py +148 -0
  75. liquid/filters/split.py +34 -0
  76. liquid/filters/squish.py +9 -0
  77. liquid/filters/strip.py +13 -0
  78. liquid/filters/strip_html.py +19 -0
  79. liquid/filters/strip_newlines.py +17 -0
  80. liquid/filters/sum.py +21 -0
  81. liquid/filters/times.py +13 -0
  82. liquid/filters/translate.py +380 -0
  83. liquid/filters/truncate.py +39 -0
  84. liquid/filters/uniq.py +11 -0
  85. liquid/filters/upcase.py +5 -0
  86. liquid/filters/url.py +18 -0
  87. liquid/filters/where.py +18 -0
  88. liquid/loaders/__init__.py +17 -0
  89. liquid/loaders/caching_file_system_loader.py +64 -0
  90. liquid/loaders/choice_loader.py +93 -0
  91. liquid/loaders/dict_loader.py +56 -0
  92. liquid/loaders/file_system_loader.py +143 -0
  93. liquid/{builtin/loaders → loaders}/mixins.py +40 -52
  94. liquid/loaders/package_loader.py +110 -0
  95. liquid/tags/__init__.py +61 -0
  96. liquid/tags/assign.py +68 -0
  97. liquid/tags/capture.py +83 -0
  98. liquid/tags/case.py +199 -0
  99. liquid/tags/comment.py +56 -0
  100. liquid/tags/cycle.py +131 -0
  101. liquid/tags/decrement.py +55 -0
  102. liquid/tags/doc.py +56 -0
  103. liquid/tags/echo.py +65 -0
  104. liquid/tags/else_.py +53 -0
  105. liquid/tags/error.py +51 -0
  106. liquid/tags/extends.py +448 -0
  107. liquid/tags/for_.py +421 -0
  108. liquid/tags/if_.py +156 -0
  109. liquid/tags/ifchanged.py +69 -0
  110. liquid/tags/include.py +223 -0
  111. liquid/tags/increment.py +55 -0
  112. liquid/tags/inline_comment.py +70 -0
  113. liquid/tags/liquid.py +56 -0
  114. liquid/tags/macro.py +281 -0
  115. liquid/tags/output.py +58 -0
  116. liquid/tags/raw.py +60 -0
  117. liquid/tags/render.py +245 -0
  118. liquid/tags/tablerow.py +254 -0
  119. liquid/tags/template_comment.py +56 -0
  120. liquid/tags/translate.py +316 -0
  121. liquid/tags/unless.py +121 -0
  122. liquid/tags/with_.py +83 -0
  123. liquid/tokens.py +370 -0
  124. {python_liquid-2.3.5.dist-info → python_liquid-3.0.0.dist-info}/METADATA +32 -39
  125. python_liquid-3.0.0.dist-info/RECORD +128 -0
  126. {python_liquid-2.3.5.dist-info → python_liquid-3.0.0.dist-info}/WHEEL +1 -1
  127. {python_liquid-2.3.5.dist-info → python_liquid-3.0.0.dist-info}/licenses/LICENSE +1 -1
  128. liquid/analyze_tags.py +0 -214
  129. liquid/ast.py +0 -252
  130. liquid/builtin/__init__.py +0 -203
  131. liquid/builtin/content.py +0 -41
  132. liquid/builtin/drops/__init__.py +0 -3
  133. liquid/builtin/drops/drops.py +0 -13
  134. liquid/builtin/expressions/__init__.py +0 -47
  135. liquid/builtin/expressions/_tokenize.py +0 -186
  136. liquid/builtin/expressions/arguments.py +0 -207
  137. liquid/builtin/expressions/filtered.py +0 -387
  138. liquid/builtin/expressions/logical.py +0 -672
  139. liquid/builtin/expressions/loop.py +0 -275
  140. liquid/builtin/expressions/path.py +0 -163
  141. liquid/builtin/expressions/primitive.py +0 -417
  142. liquid/builtin/filters/__init__.py +0 -0
  143. liquid/builtin/filters/array.py +0 -329
  144. liquid/builtin/filters/extra.py +0 -83
  145. liquid/builtin/filters/math.py +0 -129
  146. liquid/builtin/filters/misc.py +0 -137
  147. liquid/builtin/filters/string.py +0 -416
  148. liquid/builtin/illegal.py +0 -28
  149. liquid/builtin/loaders/__init__.py +0 -0
  150. liquid/builtin/loaders/caching_file_system_loader.py +0 -61
  151. liquid/builtin/loaders/choice_loader.py +0 -94
  152. liquid/builtin/loaders/dict_loader.py +0 -64
  153. liquid/builtin/loaders/file_system_loader.py +0 -146
  154. liquid/builtin/loaders/package_loader.py +0 -132
  155. liquid/builtin/output.py +0 -74
  156. liquid/builtin/tags/__init__.py +0 -0
  157. liquid/builtin/tags/assign_tag.py +0 -75
  158. liquid/builtin/tags/capture_tag.py +0 -98
  159. liquid/builtin/tags/case_tag.py +0 -287
  160. liquid/builtin/tags/comment_tag.py +0 -77
  161. liquid/builtin/tags/cycle_tag.py +0 -132
  162. liquid/builtin/tags/decrement_tag.py +0 -59
  163. liquid/builtin/tags/doc_tag.py +0 -68
  164. liquid/builtin/tags/echo_tag.py +0 -45
  165. liquid/builtin/tags/for_tag.py +0 -337
  166. liquid/builtin/tags/if_tag.py +0 -209
  167. liquid/builtin/tags/ifchanged_tag.py +0 -94
  168. liquid/builtin/tags/include_tag.py +0 -251
  169. liquid/builtin/tags/increment_tag.py +0 -60
  170. liquid/builtin/tags/inline_comment_tag.py +0 -47
  171. liquid/builtin/tags/liquid_tag.py +0 -183
  172. liquid/builtin/tags/render_tag.py +0 -371
  173. liquid/builtin/tags/tablerow_tag.py +0 -320
  174. liquid/builtin/tags/unless_tag.py +0 -218
  175. liquid/context.py +0 -615
  176. liquid/expression.py +0 -41
  177. liquid/extra/__init__.py +0 -98
  178. liquid/extra/filters/__init__.py +0 -33
  179. liquid/extra/filters/_json.py +0 -33
  180. liquid/extra/filters/array.py +0 -60
  181. liquid/extra/filters/babel.py +0 -552
  182. liquid/extra/filters/html.py +0 -32
  183. liquid/extra/filters/translate.py +0 -448
  184. liquid/extra/tags/__init__.py +0 -17
  185. liquid/extra/tags/_with.py +0 -80
  186. liquid/extra/tags/extends_tag.py +0 -576
  187. liquid/extra/tags/macro_tag.py +0 -270
  188. liquid/extra/tags/snippet_tag.py +0 -107
  189. liquid/extra/tags/translate_tag.py +0 -388
  190. liquid/filter.py +0 -230
  191. liquid/future/__init__.py +0 -3
  192. liquid/future/environment.py +0 -30
  193. liquid/future/filters/__init__.py +0 -1
  194. liquid/future/tags/__init__.py +0 -1
  195. liquid/lex.py +0 -260
  196. liquid/limits.py +0 -66
  197. liquid/loader.py +0 -141
  198. liquid/messages.py +0 -357
  199. liquid/mode.py +0 -12
  200. liquid/output.py +0 -35
  201. liquid/parser.py +0 -119
  202. liquid/span.py +0 -35
  203. liquid/static_analysis.py +0 -443
  204. liquid/stream.py +0 -148
  205. liquid/stringify.py +0 -31
  206. liquid/tag.py +0 -47
  207. liquid/template.py +0 -658
  208. liquid/token.py +0 -145
  209. liquid/undefined.py +0 -218
  210. liquid/utils/__init__.py +0 -15
  211. liquid/utils/html.py +0 -57
  212. liquid/utils/text.py +0 -28
  213. python_liquid-2.3.5.dist-info/RECORD +0 -96
@@ -0,0 +1,293 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from . import expressions
6
+ from ._parser import (
7
+ PRECEDENCE_ADD,
8
+ PRECEDENCE_COMPARISON,
9
+ PRECEDENCE_LOGICAL_AND,
10
+ PRECEDENCE_LOGICAL_NOT,
11
+ PRECEDENCE_LOGICAL_OR,
12
+ PRECEDENCE_LOGICAL_RIGHT,
13
+ PRECEDENCE_LOWEST,
14
+ PRECEDENCE_MEMBERSHIP,
15
+ PRECEDENCE_MULL,
16
+ PRECEDENCE_NEG,
17
+ PRECEDENCE_PIPE,
18
+ TERMINATE_EXPRESSION,
19
+ Precedence,
20
+ )
21
+ from ._parser_legacy import LegacyParser
22
+ from .exceptions import LiquidSyntaxError
23
+ from .tokens import *
24
+ from .tokens import TokenKind, token_value
25
+
26
+ if TYPE_CHECKING:
27
+ from ._markup import Expression
28
+ from .environment import Environment
29
+ from .tokens import Token
30
+
31
+ CONVENTIONAL_PRECEDENCES: dict[TokenKind, Precedence] = {
32
+ TOKEN_AND: PRECEDENCE_LOGICAL_AND,
33
+ TOKEN_OR: PRECEDENCE_LOGICAL_OR,
34
+ TOKEN_NOT: PRECEDENCE_LOGICAL_NOT,
35
+ TOKEN_CONTAINS: PRECEDENCE_MEMBERSHIP,
36
+ TOKEN_EQ: PRECEDENCE_COMPARISON,
37
+ TOKEN_NE: PRECEDENCE_COMPARISON,
38
+ TOKEN_LT: PRECEDENCE_COMPARISON,
39
+ TOKEN_LE: PRECEDENCE_COMPARISON,
40
+ TOKEN_GT: PRECEDENCE_COMPARISON,
41
+ TOKEN_GE: PRECEDENCE_COMPARISON,
42
+ TOKEN_PIPE: PRECEDENCE_PIPE,
43
+ TOKEN_ADD: PRECEDENCE_ADD,
44
+ TOKEN_SUB: PRECEDENCE_ADD,
45
+ TOKEN_MUL: PRECEDENCE_MULL,
46
+ TOKEN_DIV: PRECEDENCE_MULL,
47
+ TOKEN_MOD: PRECEDENCE_MULL,
48
+ }
49
+
50
+ LEGACY_PRECEDENCES: dict[TokenKind, Precedence] = {
51
+ TOKEN_AND: PRECEDENCE_LOGICAL_RIGHT,
52
+ TOKEN_OR: PRECEDENCE_LOGICAL_RIGHT,
53
+ TOKEN_NOT: PRECEDENCE_LOGICAL_NOT,
54
+ TOKEN_CONTAINS: PRECEDENCE_MEMBERSHIP,
55
+ TOKEN_EQ: PRECEDENCE_COMPARISON,
56
+ TOKEN_NE: PRECEDENCE_COMPARISON,
57
+ TOKEN_LT: PRECEDENCE_COMPARISON,
58
+ TOKEN_LE: PRECEDENCE_COMPARISON,
59
+ TOKEN_GT: PRECEDENCE_COMPARISON,
60
+ TOKEN_GE: PRECEDENCE_COMPARISON,
61
+ TOKEN_PIPE: PRECEDENCE_PIPE,
62
+ TOKEN_ADD: PRECEDENCE_ADD,
63
+ TOKEN_SUB: PRECEDENCE_ADD,
64
+ TOKEN_MUL: PRECEDENCE_MULL,
65
+ TOKEN_DIV: PRECEDENCE_MULL,
66
+ TOKEN_MOD: PRECEDENCE_MULL,
67
+ }
68
+
69
+
70
+ class ExtendedParser(LegacyParser):
71
+ """A parser that extends Shopify/liquid syntax with composable expressions."""
72
+
73
+ INFIX_OPERATORS: frozenset[TokenKind] = frozenset(
74
+ [
75
+ TOKEN_AND,
76
+ TOKEN_OR,
77
+ TOKEN_CONTAINS,
78
+ TOKEN_EQ,
79
+ TOKEN_NE,
80
+ TOKEN_LT,
81
+ TOKEN_LE,
82
+ TOKEN_GT,
83
+ TOKEN_GE,
84
+ TOKEN_PIPE,
85
+ TOKEN_ADD,
86
+ TOKEN_SUB,
87
+ TOKEN_MUL,
88
+ TOKEN_DIV,
89
+ TOKEN_MOD,
90
+ ]
91
+ )
92
+
93
+ TERMINATE_FILTER: frozenset[TokenKind] = frozenset(
94
+ [
95
+ TOKEN_WC,
96
+ TOKEN_OUT_END,
97
+ TOKEN_TAG_END,
98
+ TOKEN_TEXT,
99
+ TOKEN_RPAREN,
100
+ TOKEN_EOI,
101
+ TOKEN_IF,
102
+ TOKEN_ELSE,
103
+ TOKEN_RBRACKET,
104
+ TOKEN_RBRACE,
105
+ TOKEN_PIPE,
106
+ TOKEN_AND,
107
+ TOKEN_OR,
108
+ TOKEN_CONTAINS,
109
+ TOKEN_EQ,
110
+ TOKEN_NE,
111
+ TOKEN_LT,
112
+ TOKEN_LE,
113
+ TOKEN_GT,
114
+ TOKEN_GE,
115
+ ]
116
+ )
117
+
118
+ KEYWORD_ARGUMENT_DELIMITERS: frozenset[TokenKind] = frozenset(
119
+ [
120
+ TOKEN_COLON,
121
+ TOKEN_ASSIGN,
122
+ ]
123
+ )
124
+
125
+ __slots__ = ()
126
+
127
+ def __init__(
128
+ self,
129
+ env: Environment,
130
+ source: str,
131
+ template_name: str,
132
+ tokens: list[Token],
133
+ ) -> None:
134
+ super().__init__(env, source, template_name, tokens)
135
+ if env.conventional_operator_precedence:
136
+ self.precedences = CONVENTIONAL_PRECEDENCES
137
+ else:
138
+ self.precedences = LEGACY_PRECEDENCES
139
+
140
+ def parse_expression(
141
+ self,
142
+ precedence: Precedence = PRECEDENCE_LOWEST,
143
+ *,
144
+ infix: bool = True,
145
+ expect_end_of_expression: bool = False,
146
+ ) -> Expression:
147
+ # Ignore `infix`. Infix operators are valid in all contexts in the extended
148
+ # parser.
149
+ expr = self.parse_primary_expression(precedence, infix=True)
150
+ expr = self.parse_ternary_expression(expr) if self.kind() == TOKEN_IF else expr
151
+
152
+ if expect_end_of_expression and self.kind() not in TERMINATE_EXPRESSION:
153
+ raise LiquidSyntaxError(
154
+ "unknown or missing operator",
155
+ token=self.current(),
156
+ source=self.source,
157
+ name=self.template_name,
158
+ )
159
+
160
+ return expr
161
+
162
+ def parse_filtered_expression(
163
+ self, precedence: Precedence = PRECEDENCE_LOWEST, *, infix: bool = False
164
+ ) -> Expression:
165
+ # Delegate to `parse_expression`. Filters are valid in all contexts in the
166
+ # extended parser.
167
+
168
+ # Ignore `infix`. Infix operators are valid in all contexts in the extended
169
+ # parser.
170
+ return self.parse_expression(precedence, infix=True)
171
+
172
+ def parse_ternary_expression(self, consequence: Expression) -> Expression:
173
+ self.eat(TOKEN_IF)
174
+
175
+ condition = self.parse_primary_expression(infix=True)
176
+ alternative: Expression | None = None
177
+
178
+ if self.kind() == TOKEN_ELSE:
179
+ self.pos += 1
180
+ alternative = self.parse_primary_expression(infix=True)
181
+
182
+ return expressions.Ternary(
183
+ consequence.token, consequence, condition, alternative
184
+ )
185
+
186
+ def parse_basic_expression(self) -> Expression:
187
+ kind = self.kind()
188
+
189
+ if kind == TOKEN_LPAREN:
190
+ return self.parse_range_or_group()
191
+
192
+ if kind == TOKEN_NOT:
193
+ token = self.next()
194
+ return expressions.LogicalNot(
195
+ token, self.parse_expression(PRECEDENCE_LOGICAL_NOT)
196
+ )
197
+
198
+ if kind == TOKEN_ADD:
199
+ token = self.next()
200
+ return expressions.Pos(token, self.parse_expression(PRECEDENCE_NEG))
201
+
202
+ if kind == TOKEN_SUB:
203
+ token = self.next()
204
+ return expressions.Neg(token, self.parse_expression(PRECEDENCE_NEG))
205
+
206
+ return super().parse_basic_expression()
207
+
208
+ def parse_range_or_group(self) -> Expression:
209
+ token = self.eat(TOKEN_LPAREN)
210
+ expr = self.parse_expression()
211
+
212
+ if self.kind() == TOKEN_DOUBLE_DOT:
213
+ self.pos += 1
214
+
215
+ if self.kind() == TOKEN_DOT:
216
+ raise LiquidSyntaxError(
217
+ "too many dots",
218
+ token=self.current(),
219
+ source=self.source,
220
+ name=self.template_name,
221
+ )
222
+
223
+ stop = self.parse_expression(PRECEDENCE_LOWEST, infix=False)
224
+ self.eat(TOKEN_RPAREN, message="missing bracket")
225
+ return expressions.RangeLiteral(token, expr, stop)
226
+
227
+ closing_token = self.eat(TOKEN_RPAREN, message="unbalanced brackets")
228
+ segments: list[expressions.PathSegment] | None = None
229
+
230
+ if self.kind() in self.PATH_PUNCTUATION:
231
+ segments = self.parse_path_selectors(depth=1)
232
+
233
+ return expressions.Group(token, expr, segments, closing_token)
234
+
235
+ def parse_infix(self, left: Expression) -> Expression:
236
+ op_token = self.next()
237
+ kind = op_token[0]
238
+
239
+ if kind == TOKEN_PIPE:
240
+ return self.parse_filter(op_token, left)
241
+
242
+ right = self.parse_expression(
243
+ self.precedences.get(kind, PRECEDENCE_LOWEST), infix=True
244
+ )
245
+
246
+ if kind == TOKEN_OR:
247
+ return expressions.LogicalOr(op_token, left, right)
248
+
249
+ if kind == TOKEN_AND:
250
+ return expressions.LogicalAnd(op_token, left, right)
251
+
252
+ if kind == TOKEN_EQ:
253
+ return expressions.Eq(op_token, left, right)
254
+
255
+ if kind == TOKEN_NE:
256
+ return expressions.Ne(op_token, left, right)
257
+
258
+ if kind == TOKEN_LT:
259
+ return expressions.Lt(op_token, left, right)
260
+
261
+ if kind == TOKEN_LE:
262
+ return expressions.Le(op_token, left, right)
263
+
264
+ if kind == TOKEN_GT:
265
+ return expressions.Gt(op_token, left, right)
266
+
267
+ if kind == TOKEN_GE:
268
+ return expressions.Ge(op_token, left, right)
269
+
270
+ if kind == TOKEN_CONTAINS:
271
+ return expressions.Contains(op_token, left, right)
272
+
273
+ if kind == TOKEN_ADD:
274
+ return expressions.Add(op_token, left, right)
275
+
276
+ if kind == TOKEN_SUB:
277
+ return expressions.Sub(op_token, left, right)
278
+
279
+ if kind == TOKEN_MUL:
280
+ return expressions.Mul(op_token, left, right)
281
+
282
+ if kind == TOKEN_DIV:
283
+ return expressions.Div(op_token, left, right)
284
+
285
+ if kind == TOKEN_MOD:
286
+ return expressions.Mod(op_token, left, right)
287
+
288
+ raise LiquidSyntaxError(
289
+ f"unknown infix operator {token_value(op_token, self.source)!r}",
290
+ token=op_token,
291
+ source=self.source,
292
+ name=self.template_name,
293
+ )