python-liquid 2.3.6__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.
- liquid/__about__.py +1 -0
- liquid/__init__.py +161 -187
- liquid/_base_tag.py +66 -0
- liquid/{utils/chain_map.py → _chain_map.py} +18 -19
- liquid/_context.py +465 -0
- liquid/_drop.py +108 -0
- liquid/_filter.py +174 -0
- liquid/_io.py +18 -0
- liquid/_lax_parser_mixin.py +100 -0
- liquid/_lexer.py +93 -0
- liquid/_lexer_extended.py +623 -0
- liquid/_lexer_legacy.py +536 -0
- liquid/_loader.py +84 -0
- liquid/{utils/lru_cache.py → _lru_cache.py} +28 -36
- liquid/_markup.py +253 -0
- liquid/_messages.py +292 -0
- liquid/_missing.py +3 -0
- liquid/_nothing.py +28 -0
- liquid/_parser.py +460 -0
- liquid/_parser_extended.py +293 -0
- liquid/_parser_legacy.py +819 -0
- liquid/_resource_limits.py +50 -0
- liquid/_slice.py +41 -0
- liquid/_static_analysis.py +503 -0
- liquid/_strftime.py +186 -0
- liquid/_template.py +536 -0
- liquid/drops/__init__.py +20 -0
- liquid/drops/forloop.py +78 -0
- liquid/drops/iterable.py +25 -0
- liquid/drops/range.py +76 -0
- liquid/drops/self_.py +45 -0
- liquid/drops/tablerowloop.py +142 -0
- liquid/drops/undefined.py +228 -0
- liquid/environment.py +717 -551
- liquid/exceptions.py +67 -214
- liquid/expressions.py +1184 -0
- liquid/filters/__init__.py +134 -0
- liquid/filters/abs.py +7 -0
- liquid/filters/append.py +5 -0
- liquid/filters/at_least.py +9 -0
- liquid/filters/at_most.py +9 -0
- liquid/filters/babel.py +560 -0
- liquid/filters/base64.py +26 -0
- liquid/filters/capitalize.py +5 -0
- liquid/filters/ceil.py +8 -0
- liquid/filters/compact.py +25 -0
- liquid/filters/concat.py +11 -0
- liquid/filters/date.py +27 -0
- liquid/filters/default.py +25 -0
- liquid/filters/divided_by.py +21 -0
- liquid/filters/downcase.py +5 -0
- liquid/filters/escape.py +56 -0
- liquid/filters/find.py +56 -0
- liquid/filters/first.py +19 -0
- liquid/filters/floor.py +8 -0
- liquid/filters/html.py +23 -0
- liquid/filters/index.py +12 -0
- liquid/filters/join.py +13 -0
- liquid/filters/json_.py +30 -0
- liquid/filters/last.py +17 -0
- liquid/filters/map.py +5 -0
- liquid/filters/minus.py +13 -0
- liquid/filters/modulo.py +18 -0
- liquid/filters/newline_to_br.py +16 -0
- liquid/filters/plus.py +13 -0
- liquid/filters/prepend.py +5 -0
- liquid/filters/reject.py +19 -0
- liquid/filters/remove.py +36 -0
- liquid/filters/replace.py +32 -0
- liquid/filters/reverse.py +5 -0
- liquid/filters/round.py +35 -0
- liquid/filters/size.py +25 -0
- liquid/filters/slice.py +39 -0
- liquid/filters/sort.py +148 -0
- liquid/filters/split.py +34 -0
- liquid/filters/squish.py +9 -0
- liquid/filters/strip.py +13 -0
- liquid/filters/strip_html.py +19 -0
- liquid/filters/strip_newlines.py +17 -0
- liquid/filters/sum.py +21 -0
- liquid/filters/times.py +13 -0
- liquid/filters/translate.py +380 -0
- liquid/filters/truncate.py +39 -0
- liquid/filters/uniq.py +11 -0
- liquid/filters/upcase.py +5 -0
- liquid/filters/url.py +18 -0
- liquid/filters/where.py +18 -0
- liquid/loaders/__init__.py +17 -0
- liquid/loaders/caching_file_system_loader.py +64 -0
- liquid/loaders/choice_loader.py +93 -0
- liquid/loaders/dict_loader.py +56 -0
- liquid/{builtin/loaders → loaders}/file_system_loader.py +51 -55
- liquid/{builtin/loaders → loaders}/mixins.py +39 -51
- liquid/loaders/package_loader.py +110 -0
- liquid/tags/__init__.py +61 -0
- liquid/tags/assign.py +68 -0
- liquid/tags/capture.py +83 -0
- liquid/tags/case.py +199 -0
- liquid/tags/comment.py +56 -0
- liquid/tags/cycle.py +131 -0
- liquid/tags/decrement.py +55 -0
- liquid/tags/doc.py +56 -0
- liquid/tags/echo.py +65 -0
- liquid/tags/else_.py +53 -0
- liquid/tags/error.py +51 -0
- liquid/tags/extends.py +448 -0
- liquid/tags/for_.py +421 -0
- liquid/tags/if_.py +156 -0
- liquid/tags/ifchanged.py +69 -0
- liquid/tags/include.py +223 -0
- liquid/tags/increment.py +55 -0
- liquid/tags/inline_comment.py +70 -0
- liquid/tags/liquid.py +56 -0
- liquid/tags/macro.py +281 -0
- liquid/tags/output.py +58 -0
- liquid/tags/raw.py +60 -0
- liquid/tags/render.py +245 -0
- liquid/tags/tablerow.py +254 -0
- liquid/tags/template_comment.py +56 -0
- liquid/tags/translate.py +316 -0
- liquid/tags/unless.py +121 -0
- liquid/tags/with_.py +83 -0
- liquid/tokens.py +370 -0
- {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/METADATA +32 -39
- python_liquid-3.0.0.dist-info/RECORD +128 -0
- {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/WHEEL +1 -1
- {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/licenses/LICENSE +1 -1
- liquid/analyze_tags.py +0 -214
- liquid/ast.py +0 -252
- liquid/builtin/__init__.py +0 -203
- liquid/builtin/content.py +0 -41
- liquid/builtin/drops/__init__.py +0 -3
- liquid/builtin/drops/drops.py +0 -13
- liquid/builtin/expressions/__init__.py +0 -47
- liquid/builtin/expressions/_tokenize.py +0 -186
- liquid/builtin/expressions/arguments.py +0 -207
- liquid/builtin/expressions/filtered.py +0 -387
- liquid/builtin/expressions/logical.py +0 -672
- liquid/builtin/expressions/loop.py +0 -275
- liquid/builtin/expressions/path.py +0 -163
- liquid/builtin/expressions/primitive.py +0 -417
- liquid/builtin/filters/__init__.py +0 -0
- liquid/builtin/filters/array.py +0 -329
- liquid/builtin/filters/extra.py +0 -83
- liquid/builtin/filters/math.py +0 -129
- liquid/builtin/filters/misc.py +0 -137
- liquid/builtin/filters/string.py +0 -416
- liquid/builtin/illegal.py +0 -28
- liquid/builtin/loaders/__init__.py +0 -0
- liquid/builtin/loaders/caching_file_system_loader.py +0 -61
- liquid/builtin/loaders/choice_loader.py +0 -94
- liquid/builtin/loaders/dict_loader.py +0 -64
- liquid/builtin/loaders/package_loader.py +0 -132
- liquid/builtin/output.py +0 -74
- liquid/builtin/tags/__init__.py +0 -0
- liquid/builtin/tags/assign_tag.py +0 -75
- liquid/builtin/tags/capture_tag.py +0 -98
- liquid/builtin/tags/case_tag.py +0 -287
- liquid/builtin/tags/comment_tag.py +0 -77
- liquid/builtin/tags/cycle_tag.py +0 -132
- liquid/builtin/tags/decrement_tag.py +0 -59
- liquid/builtin/tags/doc_tag.py +0 -68
- liquid/builtin/tags/echo_tag.py +0 -45
- liquid/builtin/tags/for_tag.py +0 -337
- liquid/builtin/tags/if_tag.py +0 -209
- liquid/builtin/tags/ifchanged_tag.py +0 -94
- liquid/builtin/tags/include_tag.py +0 -251
- liquid/builtin/tags/increment_tag.py +0 -60
- liquid/builtin/tags/inline_comment_tag.py +0 -47
- liquid/builtin/tags/liquid_tag.py +0 -183
- liquid/builtin/tags/render_tag.py +0 -371
- liquid/builtin/tags/tablerow_tag.py +0 -320
- liquid/builtin/tags/unless_tag.py +0 -218
- liquid/context.py +0 -615
- liquid/expression.py +0 -41
- liquid/extra/__init__.py +0 -98
- liquid/extra/filters/__init__.py +0 -33
- liquid/extra/filters/_json.py +0 -33
- liquid/extra/filters/array.py +0 -60
- liquid/extra/filters/babel.py +0 -552
- liquid/extra/filters/html.py +0 -32
- liquid/extra/filters/translate.py +0 -448
- liquid/extra/tags/__init__.py +0 -17
- liquid/extra/tags/_with.py +0 -80
- liquid/extra/tags/extends_tag.py +0 -576
- liquid/extra/tags/macro_tag.py +0 -270
- liquid/extra/tags/snippet_tag.py +0 -107
- liquid/extra/tags/translate_tag.py +0 -388
- liquid/filter.py +0 -230
- liquid/future/__init__.py +0 -3
- liquid/future/environment.py +0 -30
- liquid/future/filters/__init__.py +0 -1
- liquid/future/tags/__init__.py +0 -1
- liquid/lex.py +0 -260
- liquid/limits.py +0 -66
- liquid/loader.py +0 -141
- liquid/messages.py +0 -357
- liquid/mode.py +0 -12
- liquid/output.py +0 -35
- liquid/parser.py +0 -119
- liquid/span.py +0 -35
- liquid/static_analysis.py +0 -443
- liquid/stream.py +0 -148
- liquid/stringify.py +0 -31
- liquid/tag.py +0 -47
- liquid/template.py +0 -658
- liquid/token.py +0 -145
- liquid/undefined.py +0 -218
- liquid/utils/__init__.py +0 -15
- liquid/utils/html.py +0 -57
- liquid/utils/text.py +0 -28
- python_liquid-2.3.6.dist-info/RECORD +0 -96
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
"""The built-in _include_ 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.ast import Partial
|
|
13
|
-
from liquid.ast import PartialScope
|
|
14
|
-
from liquid.builtin.drops import IterableDrop
|
|
15
|
-
from liquid.builtin.expressions import Identifier
|
|
16
|
-
from liquid.builtin.expressions import KeywordArgument
|
|
17
|
-
from liquid.builtin.expressions import Literal
|
|
18
|
-
from liquid.builtin.expressions import Path
|
|
19
|
-
from liquid.builtin.expressions import parse_identifier
|
|
20
|
-
from liquid.builtin.expressions import parse_string_or_path
|
|
21
|
-
from liquid.exceptions import TemplateNotFoundError
|
|
22
|
-
from liquid.tag import Tag
|
|
23
|
-
from liquid.token import TOKEN_AS
|
|
24
|
-
from liquid.token import TOKEN_FOR
|
|
25
|
-
from liquid.token import TOKEN_TAG
|
|
26
|
-
from liquid.token import TOKEN_WITH
|
|
27
|
-
from liquid.token import TOKEN_WORD
|
|
28
|
-
from liquid.token import Token
|
|
29
|
-
|
|
30
|
-
if TYPE_CHECKING:
|
|
31
|
-
from liquid.context import RenderContext
|
|
32
|
-
from liquid.expression import Expression
|
|
33
|
-
from liquid.stream import TokenStream
|
|
34
|
-
|
|
35
|
-
TAG_INCLUDE = sys.intern("include")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class IncludeNode(Node):
|
|
39
|
-
"""The built-in _include_ tag."""
|
|
40
|
-
|
|
41
|
-
__slots__ = ("name", "var", "alias", "args")
|
|
42
|
-
tag = TAG_INCLUDE
|
|
43
|
-
|
|
44
|
-
def __init__(
|
|
45
|
-
self,
|
|
46
|
-
token: Token,
|
|
47
|
-
name: Expression,
|
|
48
|
-
var: Optional[Expression] = None,
|
|
49
|
-
alias: Optional[Identifier] = None,
|
|
50
|
-
args: Optional[list[KeywordArgument]] = None,
|
|
51
|
-
):
|
|
52
|
-
super().__init__(token)
|
|
53
|
-
self.name = name
|
|
54
|
-
self.var = var
|
|
55
|
-
self.alias = alias
|
|
56
|
-
self.args = args or []
|
|
57
|
-
self.blank = False
|
|
58
|
-
|
|
59
|
-
def __str__(self) -> str:
|
|
60
|
-
var = f" with {self.var}" if self.var else ""
|
|
61
|
-
if self.alias:
|
|
62
|
-
var += f" as {self.alias}"
|
|
63
|
-
if self.args:
|
|
64
|
-
var += ","
|
|
65
|
-
args = " " + ", ".join(str(arg) for arg in self.args) if self.args else ""
|
|
66
|
-
return f"{{% include {self.name}{var}{args} %}}"
|
|
67
|
-
|
|
68
|
-
def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
|
|
69
|
-
"""Render the node to the output buffer."""
|
|
70
|
-
name = self.name.evaluate(context)
|
|
71
|
-
|
|
72
|
-
try:
|
|
73
|
-
template = context.env.get_template(
|
|
74
|
-
str(name), context=context, tag=self.tag
|
|
75
|
-
)
|
|
76
|
-
except TemplateNotFoundError as err:
|
|
77
|
-
err.token = self.name.token
|
|
78
|
-
err.template_name = context.template.full_name()
|
|
79
|
-
raise
|
|
80
|
-
|
|
81
|
-
namespace: dict[str, object] = {
|
|
82
|
-
arg.name: arg.value.evaluate(context) for arg in self.args
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
with context.extend(namespace, template=template):
|
|
86
|
-
# Bind a variable to the included template.
|
|
87
|
-
if self.var is not None:
|
|
88
|
-
val = self.var.evaluate(context)
|
|
89
|
-
key = self.alias or template.name.split(".")[0]
|
|
90
|
-
|
|
91
|
-
# If the variable is array-like, render the template once for each
|
|
92
|
-
# item in the array.
|
|
93
|
-
#
|
|
94
|
-
# The reference implementation does not seem to distinguish between
|
|
95
|
-
# "for" and "with". Just checks for array-like-ness.
|
|
96
|
-
if isinstance(val, (tuple, list, IterableDrop)):
|
|
97
|
-
# NOTE: What if an included template with a bound array updates
|
|
98
|
-
# a keyword argument value? Do we need to update namespace
|
|
99
|
-
# arguments after each loop?
|
|
100
|
-
#
|
|
101
|
-
# The reference implementation seems to evaluate arguments once,
|
|
102
|
-
# before the loop.
|
|
103
|
-
context.raise_for_loop_limit(len(val))
|
|
104
|
-
for itm in val:
|
|
105
|
-
namespace[key] = itm
|
|
106
|
-
template.render_with_context(context, buffer, partial=True)
|
|
107
|
-
else:
|
|
108
|
-
namespace[key] = val
|
|
109
|
-
template.render_with_context(context, buffer, partial=True)
|
|
110
|
-
else:
|
|
111
|
-
template.render_with_context(context, buffer, partial=True)
|
|
112
|
-
|
|
113
|
-
return True
|
|
114
|
-
|
|
115
|
-
async def render_to_output_async(
|
|
116
|
-
self, context: RenderContext, buffer: TextIO
|
|
117
|
-
) -> int:
|
|
118
|
-
"""Render the node to the output buffer."""
|
|
119
|
-
name = await self.name.evaluate_async(context)
|
|
120
|
-
|
|
121
|
-
try:
|
|
122
|
-
template = await context.env.get_template_async(
|
|
123
|
-
str(name), context=context, tag=self.tag
|
|
124
|
-
)
|
|
125
|
-
except TemplateNotFoundError as err:
|
|
126
|
-
err.token = self.name.token
|
|
127
|
-
err.template_name = context.template.full_name()
|
|
128
|
-
raise
|
|
129
|
-
|
|
130
|
-
namespace: dict[str, object] = {
|
|
131
|
-
arg.name: await arg.value.evaluate_async(context) for arg in self.args
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
with context.extend(namespace, template=template):
|
|
135
|
-
if self.var is not None:
|
|
136
|
-
val = await self.var.evaluate_async(context)
|
|
137
|
-
key = self.alias or template.name.split(".")[0]
|
|
138
|
-
|
|
139
|
-
if isinstance(val, (tuple, list, IterableDrop)):
|
|
140
|
-
context.raise_for_loop_limit(len(val))
|
|
141
|
-
for itm in val:
|
|
142
|
-
namespace[key] = itm
|
|
143
|
-
await template.render_with_context_async(
|
|
144
|
-
context, buffer, partial=True
|
|
145
|
-
)
|
|
146
|
-
else:
|
|
147
|
-
namespace[key] = val
|
|
148
|
-
await template.render_with_context_async(
|
|
149
|
-
context, buffer, partial=True
|
|
150
|
-
)
|
|
151
|
-
else:
|
|
152
|
-
await template.render_with_context_async(context, buffer, partial=True)
|
|
153
|
-
|
|
154
|
-
return True
|
|
155
|
-
|
|
156
|
-
def children(
|
|
157
|
-
self, static_context: RenderContext, *, include_partials: bool = True
|
|
158
|
-
) -> Iterable[Node]:
|
|
159
|
-
"""Return this node's children."""
|
|
160
|
-
if include_partials:
|
|
161
|
-
name = self.name.evaluate(static_context)
|
|
162
|
-
try:
|
|
163
|
-
template = static_context.env.get_template(
|
|
164
|
-
str(name), context=static_context, tag=self.tag
|
|
165
|
-
)
|
|
166
|
-
yield from template.nodes
|
|
167
|
-
except TemplateNotFoundError as err:
|
|
168
|
-
err.token = self.name.token
|
|
169
|
-
err.template_name = static_context.template.full_name()
|
|
170
|
-
raise
|
|
171
|
-
|
|
172
|
-
async def children_async(
|
|
173
|
-
self, static_context: RenderContext, *, include_partials: bool = True
|
|
174
|
-
) -> Iterable[Node]:
|
|
175
|
-
"""Return this node's children."""
|
|
176
|
-
if include_partials:
|
|
177
|
-
name = await self.name.evaluate_async(static_context)
|
|
178
|
-
try:
|
|
179
|
-
template = await static_context.env.get_template_async(
|
|
180
|
-
str(name), context=static_context, tag=self.tag
|
|
181
|
-
)
|
|
182
|
-
return template.nodes
|
|
183
|
-
except TemplateNotFoundError as err:
|
|
184
|
-
err.token = self.name.token
|
|
185
|
-
err.template_name = static_context.template.full_name()
|
|
186
|
-
raise
|
|
187
|
-
return []
|
|
188
|
-
|
|
189
|
-
def expressions(self) -> Iterable[Expression]:
|
|
190
|
-
"""Return this node's expressions."""
|
|
191
|
-
yield self.name
|
|
192
|
-
if self.var:
|
|
193
|
-
yield self.var
|
|
194
|
-
yield from (arg.value for arg in self.args)
|
|
195
|
-
|
|
196
|
-
def partial_scope(self) -> Partial | None:
|
|
197
|
-
"""Return information about a partial template loaded by this node."""
|
|
198
|
-
scope: list[Identifier] = [
|
|
199
|
-
Identifier(arg.name, token=arg.token) for arg in self.args
|
|
200
|
-
]
|
|
201
|
-
|
|
202
|
-
if self.var:
|
|
203
|
-
if self.alias:
|
|
204
|
-
scope.append(self.alias)
|
|
205
|
-
elif isinstance(self.name, Literal):
|
|
206
|
-
scope.append(
|
|
207
|
-
Identifier(
|
|
208
|
-
str(self.name.value).split(".", 1)[0], token=self.name.token
|
|
209
|
-
)
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
return Partial(name=self.name, scope=PartialScope.SHARED, in_scope=scope)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
BIND_TOKENS = frozenset((TOKEN_WITH, TOKEN_FOR))
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
class IncludeTag(Tag):
|
|
219
|
-
"""The built-in _include_ tag."""
|
|
220
|
-
|
|
221
|
-
name = TAG_INCLUDE
|
|
222
|
-
block = False
|
|
223
|
-
node_class = IncludeNode
|
|
224
|
-
|
|
225
|
-
def parse(self, stream: TokenStream) -> Node:
|
|
226
|
-
"""Read an IncludeNode from the given stream of tokens."""
|
|
227
|
-
token = stream.eat(TOKEN_TAG)
|
|
228
|
-
tokens = stream.into_inner(tag=token, eat=False)
|
|
229
|
-
|
|
230
|
-
# Need a string or identifier that resolves to a string. This is the name
|
|
231
|
-
# of the template to be included.
|
|
232
|
-
name = parse_string_or_path(self.env, tokens)
|
|
233
|
-
var: Optional[Path] = None
|
|
234
|
-
alias: Optional[Identifier] = None
|
|
235
|
-
|
|
236
|
-
# Optionally bind a variable to the included template context
|
|
237
|
-
if tokens.current.kind in BIND_TOKENS:
|
|
238
|
-
next(tokens) # Eat 'with' or 'for'
|
|
239
|
-
tokens.expect(TOKEN_WORD)
|
|
240
|
-
var = Path.parse(self.env, tokens)
|
|
241
|
-
|
|
242
|
-
# The bound variable will take the name of the template by default,
|
|
243
|
-
# or an alias if an identifier follows the "as" keyword.
|
|
244
|
-
if tokens.current.kind == TOKEN_AS:
|
|
245
|
-
next(tokens) # Eat 'as'
|
|
246
|
-
tokens.expect(TOKEN_WORD)
|
|
247
|
-
alias = parse_identifier(self.env, tokens)
|
|
248
|
-
|
|
249
|
-
# Zero or more keyword arguments
|
|
250
|
-
args = KeywordArgument.parse(self.env, tokens)
|
|
251
|
-
return self.node_class(token, name, var, alias, args)
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"""The built-in _increment_ 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.ast import Node
|
|
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
|
-
|
|
22
|
-
TAG_INCREMENT = sys.intern("increment")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class IncrementNode(Node):
|
|
26
|
-
"""The built-in _increment_ tag."""
|
|
27
|
-
|
|
28
|
-
__slots__ = ("name",)
|
|
29
|
-
|
|
30
|
-
def __init__(self, token: Token, name: Identifier):
|
|
31
|
-
super().__init__(token)
|
|
32
|
-
self.name = name
|
|
33
|
-
self.blank = False
|
|
34
|
-
|
|
35
|
-
def __str__(self) -> str:
|
|
36
|
-
return f"{{% increment {self.name} %}}"
|
|
37
|
-
|
|
38
|
-
def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
|
|
39
|
-
"""Render the node to the output buffer."""
|
|
40
|
-
return buffer.write(str(context.increment(self.name)))
|
|
41
|
-
|
|
42
|
-
def template_scope(self) -> Iterable[Identifier]:
|
|
43
|
-
"""Return variables this node adds to the template local scope."""
|
|
44
|
-
yield self.name
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class IncrementTag(Tag):
|
|
48
|
-
"""The built-in _increment_ tag."""
|
|
49
|
-
|
|
50
|
-
name = TAG_INCREMENT
|
|
51
|
-
block = False
|
|
52
|
-
node_class = IncrementNode
|
|
53
|
-
|
|
54
|
-
def parse(self, stream: TokenStream) -> IncrementNode:
|
|
55
|
-
"""Parse tokens from _stream_ into an AST node."""
|
|
56
|
-
token = stream.eat(TOKEN_TAG)
|
|
57
|
-
return self.node_class(
|
|
58
|
-
token,
|
|
59
|
-
name=parse_identifier(self.env, stream.into_inner(tag=token, eat=False)),
|
|
60
|
-
)
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"""The built-in inline comment tag."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import re
|
|
6
|
-
import sys
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
8
|
-
|
|
9
|
-
from liquid.builtin.tags.comment_tag import CommentNode
|
|
10
|
-
from liquid.exceptions import LiquidSyntaxError
|
|
11
|
-
from liquid.tag import Tag
|
|
12
|
-
from liquid.token import TOKEN_EXPRESSION
|
|
13
|
-
from liquid.token import TOKEN_TAG
|
|
14
|
-
|
|
15
|
-
if TYPE_CHECKING:
|
|
16
|
-
from liquid.stream import TokenStream
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
RE_INVALID_INLINE_COMMENT = re.compile(r"\n\s*[^#\s]")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class InlineCommentNode(CommentNode):
|
|
23
|
-
"""The built-in inline comment tag."""
|
|
24
|
-
|
|
25
|
-
def __str__(self) -> str:
|
|
26
|
-
return f"{{% # {self.text} %}}"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class InlineCommentTag(Tag):
|
|
30
|
-
"""The built-in inline comment tag."""
|
|
31
|
-
|
|
32
|
-
block = False
|
|
33
|
-
name = sys.intern("#")
|
|
34
|
-
node_class = InlineCommentNode
|
|
35
|
-
|
|
36
|
-
def parse(self, stream: TokenStream) -> InlineCommentNode:
|
|
37
|
-
"""Parse tokens from _stream_ into an AST node."""
|
|
38
|
-
token = stream.expect(TOKEN_TAG)
|
|
39
|
-
# Empty comment tag?
|
|
40
|
-
if stream.peek.kind == TOKEN_EXPRESSION:
|
|
41
|
-
next(stream)
|
|
42
|
-
if RE_INVALID_INLINE_COMMENT.search(stream.current.value):
|
|
43
|
-
raise LiquidSyntaxError(
|
|
44
|
-
"every line of an inline comment must start with a '#' character",
|
|
45
|
-
token=stream.current,
|
|
46
|
-
)
|
|
47
|
-
return self.node_class(token, text=stream.current.value)
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"""The built-in _liquid_ tag."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import re
|
|
6
|
-
import sys
|
|
7
|
-
from functools import partial
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
9
|
-
from typing import Iterable
|
|
10
|
-
from typing import Iterator
|
|
11
|
-
from typing import Optional
|
|
12
|
-
from typing import Pattern
|
|
13
|
-
from typing import TextIO
|
|
14
|
-
|
|
15
|
-
from liquid.ast import BlockNode
|
|
16
|
-
from liquid.ast import Node
|
|
17
|
-
from liquid.exceptions import LiquidSyntaxError
|
|
18
|
-
from liquid.parser import get_parser
|
|
19
|
-
from liquid.stream import TokenStream
|
|
20
|
-
from liquid.tag import Tag
|
|
21
|
-
from liquid.token import TOKEN_EOF
|
|
22
|
-
from liquid.token import TOKEN_EXPRESSION
|
|
23
|
-
from liquid.token import TOKEN_ILLEGAL
|
|
24
|
-
from liquid.token import TOKEN_TAG
|
|
25
|
-
from liquid.token import Token
|
|
26
|
-
|
|
27
|
-
if TYPE_CHECKING:
|
|
28
|
-
from liquid import Environment
|
|
29
|
-
from liquid import RenderContext
|
|
30
|
-
|
|
31
|
-
TAG_LIQUID = sys.intern("liquid")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class LiquidNode(Node):
|
|
35
|
-
"""The built-in _liquid_ tag."""
|
|
36
|
-
|
|
37
|
-
__slots__ = (
|
|
38
|
-
"liquid_token",
|
|
39
|
-
"block",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
def __init__(self, token: Token, liquid_token: Optional[Token], block: BlockNode):
|
|
43
|
-
super().__init__(token)
|
|
44
|
-
self.liquid_token = liquid_token
|
|
45
|
-
self.block = block
|
|
46
|
-
self.blank = block.blank
|
|
47
|
-
|
|
48
|
-
def __str__(self) -> str:
|
|
49
|
-
# NOTE: We're using a string representation of the token, not the node.
|
|
50
|
-
# Which might cause issues later.
|
|
51
|
-
expr = self.liquid_token.value if self.liquid_token else ""
|
|
52
|
-
return f"{{% liquid {expr} %}}"
|
|
53
|
-
|
|
54
|
-
def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
|
|
55
|
-
"""Render the node to the output buffer."""
|
|
56
|
-
return self.block.render(context, buffer)
|
|
57
|
-
|
|
58
|
-
async def render_to_output_async(
|
|
59
|
-
self, context: RenderContext, buffer: TextIO
|
|
60
|
-
) -> int:
|
|
61
|
-
"""Render the node to the output buffer."""
|
|
62
|
-
return await self.block.render_async(context, buffer)
|
|
63
|
-
|
|
64
|
-
def children(
|
|
65
|
-
self,
|
|
66
|
-
static_context: RenderContext, # noqa: ARG002
|
|
67
|
-
*,
|
|
68
|
-
include_partials: bool = True, # noqa: ARG002
|
|
69
|
-
) -> Iterable[Node]:
|
|
70
|
-
"""Return this node's children."""
|
|
71
|
-
yield self.block
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class LiquidTag(Tag):
|
|
75
|
-
"""The built-in "liquid" tag."""
|
|
76
|
-
|
|
77
|
-
name = TAG_LIQUID
|
|
78
|
-
block = False
|
|
79
|
-
node_class = LiquidNode
|
|
80
|
-
|
|
81
|
-
def __init__(self, env: Environment):
|
|
82
|
-
super().__init__(env)
|
|
83
|
-
|
|
84
|
-
comment_start_string = env.comment_start_string.replace("{", "")
|
|
85
|
-
|
|
86
|
-
if comment_start_string:
|
|
87
|
-
seq = re.escape(comment_start_string)
|
|
88
|
-
rules = (
|
|
89
|
-
(
|
|
90
|
-
"LIQUID_EXPR",
|
|
91
|
-
rf"[ \t]*(?P<name>(\w+|{seq}))[ \t]*(?P<expr>.*?)[ \t\r]*?(\n+|$)",
|
|
92
|
-
),
|
|
93
|
-
("SKIP", r"[\r\n]+"),
|
|
94
|
-
(TOKEN_ILLEGAL, r"."),
|
|
95
|
-
)
|
|
96
|
-
else:
|
|
97
|
-
rules = (
|
|
98
|
-
(
|
|
99
|
-
"LIQUID_EXPR",
|
|
100
|
-
r"[ \t]*(?P<name>#|\w+)[ \t]*(?P<expr>.*?)[ \t\r]*?(\n+|$)",
|
|
101
|
-
),
|
|
102
|
-
("SKIP", r"[\r\n]+"),
|
|
103
|
-
(TOKEN_ILLEGAL, r"."),
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
self._tokenize = partial(
|
|
107
|
-
_tokenize_liquid_expression,
|
|
108
|
-
rules=_compile_rules(rules),
|
|
109
|
-
comment_start_string=comment_start_string,
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
def parse(self, stream: TokenStream) -> Node:
|
|
113
|
-
"""Parse tokens from _stream_ into an AST node."""
|
|
114
|
-
token = stream.eat(TOKEN_TAG)
|
|
115
|
-
token_: Optional[Token] = None
|
|
116
|
-
|
|
117
|
-
if stream.current.kind == TOKEN_EOF:
|
|
118
|
-
# Empty liquid tag. Empty block.
|
|
119
|
-
block = BlockNode(token, [])
|
|
120
|
-
elif stream.current.kind == TOKEN_TAG:
|
|
121
|
-
parser = get_parser(self.env)
|
|
122
|
-
block = parser.parse_block(stream, end=())
|
|
123
|
-
else:
|
|
124
|
-
token_ = stream.expect(TOKEN_EXPRESSION)
|
|
125
|
-
block = get_parser(self.env).parse_block(
|
|
126
|
-
TokenStream(
|
|
127
|
-
self._tokenize(
|
|
128
|
-
token_.value,
|
|
129
|
-
token=token_,
|
|
130
|
-
),
|
|
131
|
-
block_depth_carry=stream.block_depth,
|
|
132
|
-
),
|
|
133
|
-
end=(),
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
return self.node_class(token, token_, block=block)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def _tokenize_liquid_expression(
|
|
140
|
-
source: str,
|
|
141
|
-
rules: Pattern[str],
|
|
142
|
-
token: Token,
|
|
143
|
-
comment_start_string: str,
|
|
144
|
-
) -> Iterator[Token]:
|
|
145
|
-
"""Tokenize a "{% liquid %}" tag."""
|
|
146
|
-
for match in rules.finditer(source):
|
|
147
|
-
kind = match.lastgroup
|
|
148
|
-
assert kind is not None
|
|
149
|
-
|
|
150
|
-
value = match.group()
|
|
151
|
-
|
|
152
|
-
if kind == "LIQUID_EXPR":
|
|
153
|
-
name = match.group("name")
|
|
154
|
-
if name == comment_start_string:
|
|
155
|
-
continue
|
|
156
|
-
|
|
157
|
-
yield Token(
|
|
158
|
-
TOKEN_TAG,
|
|
159
|
-
value=name,
|
|
160
|
-
start_index=token.start_index + match.start("name"),
|
|
161
|
-
source=token.source,
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
if match.group("expr"):
|
|
165
|
-
yield Token(
|
|
166
|
-
TOKEN_EXPRESSION,
|
|
167
|
-
value=match.group("expr"),
|
|
168
|
-
start_index=token.start_index + match.start("expr"),
|
|
169
|
-
source=token.source,
|
|
170
|
-
)
|
|
171
|
-
elif kind == "SKIP":
|
|
172
|
-
continue
|
|
173
|
-
else:
|
|
174
|
-
raise LiquidSyntaxError(
|
|
175
|
-
f"expected newline delimited tag expressions, found {value!r}",
|
|
176
|
-
token=token,
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def _compile_rules(rules: Iterable[tuple[str, str]]) -> Pattern[str]:
|
|
181
|
-
"""Compile the given rules into a single regular expression."""
|
|
182
|
-
pattern = "|".join(f"(?P<{name}>{pattern})" for name, pattern in rules)
|
|
183
|
-
return re.compile(pattern, re.DOTALL)
|