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
@@ -1,287 +0,0 @@
1
- """The built-in _case_ tag."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from typing import TYPE_CHECKING
7
- from typing import Iterable
8
- from typing import TextIO
9
- from typing import Union
10
-
11
- from liquid.ast import BlockNode
12
- from liquid.ast import Node
13
- from liquid.builtin.expressions import parse_primitive
14
- from liquid.builtin.expressions.logical import _eq # type: ignore
15
- from liquid.exceptions import LiquidSyntaxError
16
- from liquid.expression import Expression
17
- from liquid.parser import get_parser
18
- from liquid.tag import Tag
19
- from liquid.token import TOKEN_COMMA
20
- from liquid.token import TOKEN_EOF
21
- from liquid.token import TOKEN_OR
22
- from liquid.token import TOKEN_TAG
23
- from liquid.token import Token
24
-
25
- if TYPE_CHECKING:
26
- from liquid import Environment
27
- from liquid import TokenStream
28
- from liquid.context import RenderContext
29
-
30
-
31
- TAG_CASE = sys.intern("case")
32
- TAG_ENDCASE = sys.intern("endcase")
33
- TAG_WHEN = sys.intern("when")
34
- TAG_ELSE = sys.intern("else")
35
-
36
- ENDWHENBLOCK = frozenset((TAG_ENDCASE, TAG_WHEN, TAG_ELSE, TOKEN_EOF))
37
- ENDCASEBLOCK = frozenset((TAG_ENDCASE, TAG_ELSE))
38
-
39
-
40
- class CaseNode(Node):
41
- """The built-in _case_ tag."""
42
-
43
- __slots__ = ("blocks", "expression")
44
-
45
- def __init__(
46
- self,
47
- token: Token,
48
- expression: Expression,
49
- blocks: list[Union[MultiExpressionBlockNode, BlockNode]],
50
- ):
51
- super().__init__(token)
52
- self.expression = expression
53
- self.blocks = blocks
54
-
55
- self.blank = all(node.blank for node in self.blocks)
56
-
57
- for node in self.blocks:
58
- node.blank = self.blank
59
- if isinstance(node, MultiExpressionBlockNode):
60
- node.block.blank = self.blank
61
-
62
- def __str__(self) -> str:
63
- blocks: list[str] = []
64
- for block in self.blocks:
65
- if isinstance(block, BlockNode):
66
- blocks.append(f"{{% else %}}{block}")
67
- else:
68
- blocks.append(str(block))
69
-
70
- blocks_ = "".join(blocks)
71
-
72
- return f"{{% case {self.expression} %}}\n{blocks_}{{% endcase %}}"
73
-
74
- def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
75
- """Render the node to the output buffer."""
76
- count = 0
77
- default = True
78
-
79
- for block in self.blocks:
80
- # Always render truthy `when` blocks, no matter the order.
81
- if isinstance(block, MultiExpressionBlockNode):
82
- count_ = block.render(context, buffer)
83
- if count_ >= 0:
84
- default = False
85
- count += count_
86
- # Only render `else` blocks if all preceding `when` blocks are falsy.
87
- # Multiple `else` blocks are OK.
88
- elif default:
89
- count += block.render(context, buffer)
90
-
91
- return count
92
-
93
- async def render_to_output_async(
94
- self, context: RenderContext, buffer: TextIO
95
- ) -> int:
96
- """Render the node to the output buffer."""
97
- count = 0
98
- default = True
99
-
100
- for block in self.blocks:
101
- # Always render truthy `when` blocks, no matter the order.
102
- if isinstance(block, MultiExpressionBlockNode):
103
- count_ = await block.render_async(context, buffer)
104
- if count_ >= 0:
105
- default = False
106
- count += count_
107
- # Only render `else` blocks if all preceding `when` blocks are falsy.
108
- # Multiple `else` blocks are OK.
109
- elif default:
110
- count += await block.render_async(context, buffer)
111
-
112
- return count
113
-
114
- def children(
115
- self,
116
- static_context: RenderContext, # noqa: ARG002
117
- *,
118
- include_partials: bool = True, # noqa: ARG002
119
- ) -> Iterable[Node]:
120
- """Return this node's children."""
121
- yield from self.blocks
122
-
123
- def expressions(self) -> Iterable[Expression]:
124
- """Return this node's expressions."""
125
- yield self.expression
126
-
127
-
128
- class CaseTag(Tag):
129
- """The built-in _case_ tag."""
130
-
131
- name = TAG_CASE
132
- end = TAG_ENDCASE
133
- node_class = CaseNode
134
-
135
- def __init__(self, env: Environment):
136
- super().__init__(env)
137
- self.parser = get_parser(self.env)
138
-
139
- def parse(self, stream: TokenStream) -> Node:
140
- """Parse tokens from _stream_ into an AST node."""
141
- token = stream.eat(TOKEN_TAG)
142
- tokens = stream.into_inner(tag=token)
143
- left = parse_primitive(self.env, tokens)
144
- tokens.expect_eos()
145
-
146
- # Eat whitespace or junk between `case` and when/else/endcase
147
- while (
148
- stream.current.kind not in (TOKEN_TAG, TOKEN_EOF)
149
- and stream.current.value not in ENDWHENBLOCK
150
- ):
151
- next(stream)
152
-
153
- parse_block = get_parser(self.env).parse_block
154
- blocks: list[Union[MultiExpressionBlockNode, BlockNode]] = []
155
-
156
- while not stream.current.is_tag(TAG_ENDCASE):
157
- if stream.current.is_tag(TAG_ELSE):
158
- next(stream)
159
- blocks.append(parse_block(stream, ENDWHENBLOCK))
160
- elif stream.current.is_tag(TAG_WHEN):
161
- alternative_token = next(stream)
162
- expressions = self._parse_when_expression(
163
- stream.into_inner(tag=alternative_token)
164
- )
165
- alternative_block = parse_block(stream, ENDWHENBLOCK)
166
-
167
- blocks.append(
168
- MultiExpressionBlockNode(
169
- alternative_token,
170
- alternative_block,
171
- _AnyExpression(alternative_token, left, expressions),
172
- )
173
- )
174
- else:
175
- raise LiquidSyntaxError(
176
- f"unexpected tag {stream.current.value!r}", token=stream.current
177
- )
178
-
179
- stream.expect(TOKEN_TAG, TAG_ENDCASE)
180
-
181
- return self.node_class(
182
- token,
183
- left,
184
- blocks,
185
- )
186
-
187
- def _parse_when_expression(self, stream: TokenStream) -> list[Expression]:
188
- expressions: list[Expression] = [parse_primitive(self.env, stream)]
189
- while stream.current.kind in (TOKEN_COMMA, TOKEN_OR):
190
- next(stream)
191
- try:
192
- expressions.append(parse_primitive(self.env, stream))
193
- except LiquidSyntaxError:
194
- # Use expressions we have so far an discard the rest.
195
- return expressions
196
-
197
- return expressions
198
-
199
-
200
- class _AnyExpression(Expression):
201
- __slots__ = (
202
- "left",
203
- "expressions",
204
- )
205
-
206
- def __init__(
207
- self, token: Token, left: Expression, expressions: list[Expression]
208
- ) -> None:
209
- super().__init__(token)
210
- self.left = left
211
- self.expressions = expressions
212
-
213
- def __str__(self) -> str:
214
- return ", ".join(str(expr) for expr in self.expressions)
215
-
216
- def evaluate(self, context: RenderContext) -> list[bool]:
217
- left = self.left.evaluate(context)
218
- return [_eq(left, right.evaluate(context)) for right in self.expressions]
219
-
220
- async def evaluate_async(self, context: RenderContext) -> list[bool]:
221
- left = await self.left.evaluate_async(context)
222
- return [
223
- _eq(left, await right.evaluate_async(context)) for right in self.expressions
224
- ]
225
-
226
- def children(self) -> list[Expression]:
227
- return self.expressions
228
-
229
-
230
- class MultiExpressionBlockNode(Node):
231
- """A node containing a sequence of nodes guarded by a choice of expressions."""
232
-
233
- __slots__ = ("block", "expression")
234
-
235
- def __init__(
236
- self,
237
- token: Token,
238
- block: BlockNode,
239
- expression: _AnyExpression,
240
- ) -> None:
241
- super().__init__(token)
242
- self.block = block
243
- self.expression = expression
244
- self.blank = self.block.blank
245
-
246
- def __str__(self) -> str:
247
- return f"{{% when {self.expression} %}}{self.block}"
248
-
249
- def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
250
- """Render the node to the output buffer."""
251
- matches = self.expression.evaluate(context)
252
-
253
- if any(matches):
254
- return sum(
255
- [self.block.render(context, buffer) for _ in range(matches.count(True))]
256
- )
257
-
258
- return -1
259
-
260
- async def render_to_output_async(
261
- self, context: RenderContext, buffer: TextIO
262
- ) -> int:
263
- """Render the node to the output buffer."""
264
- matches = await self.expression.evaluate_async(context)
265
-
266
- if any(matches):
267
- return sum(
268
- [
269
- await self.block.render_async(context, buffer)
270
- for _ in range(matches.count(True))
271
- ]
272
- )
273
-
274
- return -1
275
-
276
- def children(
277
- self,
278
- static_context: RenderContext, # noqa: ARG002
279
- *,
280
- include_partials: bool = True, # noqa: ARG002
281
- ) -> Iterable[Node]:
282
- """Return this node's children."""
283
- yield self.block
284
-
285
- def expressions(self) -> Iterable[Expression]:
286
- """Return this node's expressions."""
287
- yield self.expression
@@ -1,77 +0,0 @@
1
- """The built-in _comment_ tag."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from typing import TYPE_CHECKING
7
- from typing import Optional
8
- from typing import TextIO
9
-
10
- from liquid import ast
11
- from liquid.exceptions import LiquidSyntaxError
12
- from liquid.tag import Tag
13
- from liquid.token import TOKEN_EOF
14
- from liquid.token import TOKEN_TAG
15
- from liquid.token import Token
16
-
17
- if TYPE_CHECKING:
18
- from liquid.context import RenderContext
19
- from liquid.stream import TokenStream
20
-
21
-
22
- TAG_COMMENT = sys.intern("comment")
23
- TAG_ENDCOMMENT = sys.intern("endcomment")
24
-
25
- END_COMMENTBLOCK = frozenset((TAG_ENDCOMMENT,))
26
-
27
-
28
- class CommentNode(ast.Node):
29
- """The built-in _comment_ tag."""
30
-
31
- __slots__ = ("text",)
32
-
33
- def __init__(self, token: Token, text: Optional[str] = None):
34
- super().__init__(token)
35
- self.text = text
36
-
37
- def __str__(self) -> str:
38
- return f"{{% comment %}}{self.text}{{% endcomment %}}"
39
-
40
- def render_to_output(self, context: RenderContext, buffer: TextIO) -> int: # noqa: ARG002
41
- """Render the node to the output buffer."""
42
- return 0
43
-
44
-
45
- class CommentTag(Tag):
46
- """An implementation of the built-in _comment_ tag that retains comment text.
47
-
48
- Some Liquid markup might be stripped out by the lexer, so comment text is not
49
- guaranteed to be identical to that in the source document.
50
- """
51
-
52
- name = TAG_COMMENT
53
- end = TAG_ENDCOMMENT
54
- node_class = CommentNode
55
-
56
- def parse(self, stream: TokenStream) -> CommentNode:
57
- """Parse tokens from _stream_ into an AST node."""
58
- if stream.current.kind == "COMMENT":
59
- # A `{# #}` style comment
60
- return self.node_class(stream.current, text=stream.current.value)
61
-
62
- # A block `{% comment %}`
63
- token = stream.eat(TOKEN_TAG)
64
- text: list[str] = []
65
-
66
- while True:
67
- if stream.current.kind == TOKEN_EOF:
68
- raise LiquidSyntaxError("comment tag was never closed", token=token)
69
- if (
70
- stream.current.kind == TOKEN_TAG
71
- and stream.current.value == TAG_ENDCOMMENT
72
- ):
73
- break
74
- text.append(stream.current.value)
75
- next(stream)
76
-
77
- return self.node_class(token, text="".join(text))
@@ -1,132 +0,0 @@
1
- """The built-in _cycle_ tag."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from typing import TYPE_CHECKING
7
- from typing import Iterable
8
- from typing import Optional
9
- from typing import TextIO
10
-
11
- from liquid.ast import Node
12
- from liquid.builtin.expressions import PositionalArgument
13
- from liquid.builtin.expressions import parse_primitive
14
- from liquid.exceptions import LiquidSyntaxError
15
- from liquid.stringify import to_liquid_string
16
- from liquid.tag import Tag
17
- from liquid.token import TOKEN_COLON
18
- from liquid.token import TOKEN_TAG
19
- from liquid.token import Token
20
- from liquid.undefined import is_undefined
21
-
22
- if TYPE_CHECKING:
23
- from liquid.context import RenderContext
24
- from liquid.expression import Expression
25
- from liquid.stream import TokenStream
26
-
27
-
28
- TAG_CYCLE = sys.intern("cycle")
29
-
30
-
31
- class CycleNode(Node):
32
- """The built-in _cycle_ tag."""
33
-
34
- __slots__ = ("group", "args")
35
-
36
- group_by_args: bool = False
37
- """If `True`, use cycle arguments as well as group name when building cycle
38
- context keys."""
39
-
40
- def __init__(
41
- self, token: Token, group: Optional[Expression], args: list[Expression]
42
- ):
43
- super().__init__(token)
44
- self.group = group
45
- self.args = args
46
- self.blank = False
47
-
48
- def __str__(self) -> str:
49
- name = f"{self.group.token.value}: " if self.group else ""
50
- items = ", ".join(str(i) for i in self.args)
51
- return f"{{% cycle {name}{items} %}}"
52
-
53
- def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
54
- """Render the node to the output buffer."""
55
- if self.group:
56
- _group = self.group.evaluate(context)
57
- group_name = "__UNDEFINED" if is_undefined(_group) else str(_group)
58
- else:
59
- group_name = ""
60
-
61
- args = [arg.evaluate(context) for arg in self.args]
62
-
63
- if self.group_by_args:
64
- key: object = (group_name, tuple(args))
65
- else:
66
- key = group_name if group_name else str(args)
67
-
68
- index = context.cycle(key, len(args))
69
-
70
- if index >= len(args):
71
- return 0
72
-
73
- return buffer.write(
74
- to_liquid_string(args[index], autoescape=context.autoescape)
75
- )
76
-
77
- async def render_to_output_async(
78
- self, context: RenderContext, buffer: TextIO
79
- ) -> int:
80
- """Render the node to the output buffer."""
81
- if self.group:
82
- _group = await self.group.evaluate_async(context)
83
- group_name = "__UNDEFINED" if is_undefined(_group) else str(_group)
84
- else:
85
- group_name = ""
86
-
87
- args = [await arg.evaluate_async(context) for arg in self.args]
88
-
89
- if self.group_by_args:
90
- key: object = (group_name, tuple(args))
91
- else:
92
- key = group_name if group_name else str(args)
93
-
94
- index = context.cycle(key, len(args))
95
-
96
- if index >= len(args):
97
- return 0
98
-
99
- return buffer.write(
100
- to_liquid_string(args[index], autoescape=context.autoescape)
101
- )
102
-
103
- def expressions(self) -> Iterable[Expression]:
104
- """Return this node's expressions."""
105
- if self.group:
106
- yield self.group
107
- yield from self.args
108
-
109
-
110
- class CycleTag(Tag):
111
- """The built-in _cycle_ tag."""
112
-
113
- name = TAG_CYCLE
114
- block = False
115
- node_class = CycleNode
116
-
117
- def parse(self, stream: TokenStream) -> CycleNode:
118
- """Parse tokens from _stream_ into an AST node."""
119
- token = stream.eat(TOKEN_TAG)
120
- tokens = stream.into_inner(tag=token, eat=False)
121
-
122
- group_name: Optional[Expression] = None
123
- if tokens.peek.kind == TOKEN_COLON:
124
- group_name = parse_primitive(self.env, tokens)
125
- tokens.eat(TOKEN_COLON)
126
-
127
- args = PositionalArgument.parse(self.env, tokens)
128
-
129
- if not args:
130
- raise LiquidSyntaxError("expected at least one argument", token=token)
131
-
132
- return self.node_class(token, group_name, [arg.value for arg in args])
@@ -1,59 +0,0 @@
1
- """The built-in _decrement_ tag."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from typing import TYPE_CHECKING
7
- from typing import Iterable
8
- from typing import TextIO
9
-
10
- from liquid import ast
11
- from liquid.builtin.expressions import parse_identifier
12
- from liquid.tag import Tag
13
- from liquid.token import TOKEN_TAG
14
- from liquid.token import Token
15
-
16
- if TYPE_CHECKING:
17
- from liquid.builtin.expressions import Identifier
18
- from liquid.context import RenderContext
19
- from liquid.stream import TokenStream
20
-
21
- TAG_DECREMENT = sys.intern("decrement")
22
-
23
-
24
- class DecrementNode(ast.Node):
25
- """The built-in _decrement_ tag."""
26
-
27
- __slots__ = ("name",)
28
-
29
- def __init__(self, token: Token, name: Identifier):
30
- super().__init__(token)
31
- self.name = name
32
- self.blank = False
33
-
34
- def __str__(self) -> str:
35
- return f"{{% decrement {self.name} %}}"
36
-
37
- def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
38
- """Render the node to the output buffer."""
39
- return buffer.write(str(context.decrement(self.name)))
40
-
41
- def template_scope(self) -> Iterable[Identifier]:
42
- """Return variables this node adds to the template local scope."""
43
- yield self.name
44
-
45
-
46
- class DecrementTag(Tag):
47
- """The built-in _decrement_ tag."""
48
-
49
- name = TAG_DECREMENT
50
- block = False
51
- node_class = DecrementNode
52
-
53
- def parse(self, stream: TokenStream) -> DecrementNode:
54
- """Parse tokens from _stream_ into an AST node."""
55
- token = stream.eat(TOKEN_TAG)
56
- return self.node_class(
57
- token,
58
- name=parse_identifier(self.env, stream.into_inner(tag=token, eat=False)),
59
- )
@@ -1,68 +0,0 @@
1
- """The built-in _doc_ tag."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import TYPE_CHECKING
6
- from typing import Optional
7
- from typing import TextIO
8
-
9
- from liquid.ast import Node
10
- from liquid.exceptions import LiquidSyntaxError
11
- from liquid.tag import Tag
12
- from liquid.token import TOKEN_DOC
13
- from liquid.token import TOKEN_EOF
14
- from liquid.token import TOKEN_EXPRESSION
15
- from liquid.token import TOKEN_TAG
16
-
17
- if TYPE_CHECKING:
18
- from liquid import RenderContext
19
- from liquid import TokenStream
20
- from liquid.token import Token
21
-
22
-
23
- class DocNode(Node):
24
- """The built-in _doc_ tag."""
25
-
26
- def __init__(self, token: Token, text: Optional[str] = None):
27
- super().__init__(token)
28
- self.text = text
29
-
30
- def __str__(self) -> str:
31
- return f"{{% doc %}}{self.text}{{% enddoc %}}"
32
-
33
- def render_to_output(self, _: RenderContext, __: TextIO) -> int:
34
- """Render the node to the output buffer."""
35
- return 0
36
-
37
-
38
- class DocTag(Tag):
39
- """The built-in _doc_ tag."""
40
-
41
- block = True
42
- name = "doc"
43
- end = "enddoc"
44
- node_class = DocNode
45
-
46
- def parse(self, stream: TokenStream) -> DocNode:
47
- """Parse tokens from _stream_ into an AST node."""
48
- if stream.current.kind == TOKEN_DOC:
49
- return self.node_class(stream.current, text=stream.current.value)
50
-
51
- # This only happens if the doc tag is malformed
52
- token = stream.eat(TOKEN_TAG)
53
- text: list[str] = []
54
-
55
- if stream.current.kind == TOKEN_EXPRESSION:
56
- raise LiquidSyntaxError("unexpected expression", token=stream.current)
57
-
58
- while True:
59
- if stream.current.is_tag(self.name):
60
- raise LiquidSyntaxError("nested doc tags are not allowed", token=token)
61
- if stream.current.is_tag(self.end):
62
- break
63
- if stream.current.kind == TOKEN_EOF:
64
- raise LiquidSyntaxError("doc tag wad never closed", token=token)
65
- text.append(stream.current.value)
66
- next(stream)
67
-
68
- return self.node_class(token, text="".join(text))
@@ -1,45 +0,0 @@
1
- """The built-in _echo_ tag."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- from typing import TYPE_CHECKING
7
-
8
- from liquid.builtin.expressions import FilteredExpression
9
- from liquid.builtin.expressions import Nil
10
- from liquid.builtin.output import OutputNode
11
- from liquid.tag import Tag
12
- from liquid.token import TOKEN_EOF
13
- from liquid.token import TOKEN_TAG
14
-
15
- if TYPE_CHECKING:
16
- from liquid.ast import Node
17
- from liquid.expression import Expression
18
- from liquid.stream import TokenStream
19
-
20
- TAG_ECHO = sys.intern("echo")
21
-
22
-
23
- class EchoNode(OutputNode):
24
- """The built-in _echo_ tag."""
25
-
26
- def __str__(self) -> str:
27
- return f"{{% echo {self.expression} %}}"
28
-
29
-
30
- class EchoTag(Tag):
31
- """The built-in _echo_ tag."""
32
-
33
- name = TAG_ECHO
34
- block = False
35
- node_class = EchoNode
36
-
37
- def parse(self, stream: TokenStream) -> Node: # noqa: D102
38
- token = stream.eat(TOKEN_TAG)
39
- if stream.current.kind == TOKEN_EOF:
40
- expr: Expression = Nil(stream.current)
41
- else:
42
- expr = FilteredExpression.parse(
43
- self.env, stream.into_inner(tag=token, eat=False)
44
- )
45
- return self.node_class(token, expr)