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
liquid/extra/tags/extends_tag.py
DELETED
|
@@ -1,576 +0,0 @@
|
|
|
1
|
-
"""The standard _extends_ and _block_ tags."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
7
|
-
from typing import DefaultDict
|
|
8
|
-
from typing import Iterable
|
|
9
|
-
from typing import Iterator
|
|
10
|
-
from typing import Mapping
|
|
11
|
-
from typing import Optional
|
|
12
|
-
from typing import Sequence
|
|
13
|
-
from typing import TextIO
|
|
14
|
-
|
|
15
|
-
from markupsafe import Markup as Markupsafe
|
|
16
|
-
|
|
17
|
-
from liquid.ast import BlockNode as TemplateBlock
|
|
18
|
-
from liquid.ast import Node
|
|
19
|
-
from liquid.ast import Partial
|
|
20
|
-
from liquid.ast import PartialScope
|
|
21
|
-
from liquid.builtin.expressions import Identifier
|
|
22
|
-
from liquid.builtin.expressions import parse_name
|
|
23
|
-
from liquid.exceptions import RequiredBlockError
|
|
24
|
-
from liquid.exceptions import StopRender
|
|
25
|
-
from liquid.exceptions import TemplateInheritanceError
|
|
26
|
-
from liquid.exceptions import TemplateNotFoundError
|
|
27
|
-
from liquid.parser import get_parser
|
|
28
|
-
from liquid.tag import Tag
|
|
29
|
-
from liquid.token import TOKEN_EOF
|
|
30
|
-
from liquid.token import TOKEN_EXPRESSION
|
|
31
|
-
from liquid.token import TOKEN_REQUIRED
|
|
32
|
-
from liquid.token import TOKEN_TAG
|
|
33
|
-
|
|
34
|
-
if TYPE_CHECKING:
|
|
35
|
-
from liquid import BoundTemplate
|
|
36
|
-
from liquid import RenderContext
|
|
37
|
-
from liquid import Token
|
|
38
|
-
from liquid import TokenStream
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class ExtendsNode(Node):
|
|
42
|
-
"""The standard _extends_ tag."""
|
|
43
|
-
|
|
44
|
-
__slots__ = ("name",)
|
|
45
|
-
tag = "extends"
|
|
46
|
-
|
|
47
|
-
def __init__(self, token: Token, name: Identifier) -> None:
|
|
48
|
-
super().__init__(token)
|
|
49
|
-
self.name = name
|
|
50
|
-
self.blank = False
|
|
51
|
-
|
|
52
|
-
def __str__(self) -> str:
|
|
53
|
-
return f"{{% extends {self.name} %}}"
|
|
54
|
-
|
|
55
|
-
def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
|
|
56
|
-
"""Render the node to the output buffer."""
|
|
57
|
-
base_template = _build_block_stacks(context, context.template, "extends")
|
|
58
|
-
|
|
59
|
-
base_template.render_with_context(context, buffer)
|
|
60
|
-
context.tag_namespace["extends"].clear()
|
|
61
|
-
raise StopRender
|
|
62
|
-
|
|
63
|
-
async def render_to_output_async(
|
|
64
|
-
self, context: RenderContext, buffer: TextIO
|
|
65
|
-
) -> int:
|
|
66
|
-
"""Render the node to the output buffer."""
|
|
67
|
-
base_template = await _build_block_stacks_async(
|
|
68
|
-
context, context.template, "extends"
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
await base_template.render_with_context_async(context, buffer)
|
|
72
|
-
context.tag_namespace["extends"].clear()
|
|
73
|
-
raise StopRender
|
|
74
|
-
|
|
75
|
-
def children(
|
|
76
|
-
self, static_context: RenderContext, *, include_partials: bool = True
|
|
77
|
-
) -> Iterable[Node]:
|
|
78
|
-
"""Return this node's children."""
|
|
79
|
-
if include_partials:
|
|
80
|
-
try:
|
|
81
|
-
parent = static_context.env.get_template(
|
|
82
|
-
self.name, context=static_context, tag=self.tag
|
|
83
|
-
)
|
|
84
|
-
yield from parent.nodes
|
|
85
|
-
except TemplateNotFoundError as err:
|
|
86
|
-
err.token = self.name.token
|
|
87
|
-
err.template_name = static_context.template.full_name()
|
|
88
|
-
raise
|
|
89
|
-
|
|
90
|
-
async def children_async(
|
|
91
|
-
self, static_context: RenderContext, *, include_partials: bool = True
|
|
92
|
-
) -> Iterable[Node]:
|
|
93
|
-
"""Return this node's children."""
|
|
94
|
-
if include_partials:
|
|
95
|
-
try:
|
|
96
|
-
parent = await static_context.env.get_template_async(
|
|
97
|
-
self.name, context=static_context, tag=self.tag
|
|
98
|
-
)
|
|
99
|
-
return parent.nodes
|
|
100
|
-
except TemplateNotFoundError as err:
|
|
101
|
-
err.token = self.name.token
|
|
102
|
-
err.template_name = static_context.template.full_name()
|
|
103
|
-
raise
|
|
104
|
-
return []
|
|
105
|
-
|
|
106
|
-
def partial_scope(self) -> Optional[Partial]:
|
|
107
|
-
"""Return information about a partial template loaded by this node."""
|
|
108
|
-
return Partial(
|
|
109
|
-
name=self.name,
|
|
110
|
-
scope=PartialScope.INHERITED,
|
|
111
|
-
in_scope=[],
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class ExtendsTag(Tag):
|
|
116
|
-
"""The standard _extends_ tag."""
|
|
117
|
-
|
|
118
|
-
name = "extends"
|
|
119
|
-
block = False
|
|
120
|
-
node_class = ExtendsNode
|
|
121
|
-
|
|
122
|
-
def parse(self, stream: TokenStream) -> Node:
|
|
123
|
-
"""Parse tokens from _stream_ into an AST node."""
|
|
124
|
-
token = stream.eat(TOKEN_TAG)
|
|
125
|
-
tokens = stream.into_inner(tag=token, eat=False)
|
|
126
|
-
name = parse_name(self.env, tokens)
|
|
127
|
-
tokens.expect_eos()
|
|
128
|
-
return self.node_class(token=token, name=name)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
class BlockNode(Node):
|
|
132
|
-
"""The standard _block_ tag."""
|
|
133
|
-
|
|
134
|
-
__slots__ = ("name", "block", "required")
|
|
135
|
-
tag = "block"
|
|
136
|
-
|
|
137
|
-
def __init__(
|
|
138
|
-
self,
|
|
139
|
-
token: Token,
|
|
140
|
-
name: str,
|
|
141
|
-
block: TemplateBlock,
|
|
142
|
-
*,
|
|
143
|
-
required: bool,
|
|
144
|
-
) -> None:
|
|
145
|
-
super().__init__(token)
|
|
146
|
-
self.name = name
|
|
147
|
-
self.block = block
|
|
148
|
-
self.required = required
|
|
149
|
-
self.blank = False
|
|
150
|
-
|
|
151
|
-
def __str__(self) -> str:
|
|
152
|
-
required = " required" if self.required else ""
|
|
153
|
-
return (
|
|
154
|
-
f"{{% block {self.name}{required} %}}"
|
|
155
|
-
f"{self.block}"
|
|
156
|
-
f"{{% endblock {self.name} %}}"
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
def render_to_output(self, context: RenderContext, buffer: TextIO) -> int:
|
|
160
|
-
"""Render the node to the output buffer."""
|
|
161
|
-
# We should be in a base template. Render the block at the top of the "stack".
|
|
162
|
-
block_stack: Sequence[_BlockStackItem] = context.tag_namespace.get(
|
|
163
|
-
"extends", {}
|
|
164
|
-
).get(self.name)
|
|
165
|
-
|
|
166
|
-
if not block_stack:
|
|
167
|
-
# This base template is being rendered directly.
|
|
168
|
-
if self.required:
|
|
169
|
-
raise RequiredBlockError(
|
|
170
|
-
f"block {self.name!r} must be overridden", token=self.token
|
|
171
|
-
)
|
|
172
|
-
with context.extend(
|
|
173
|
-
{
|
|
174
|
-
"block": BlockDrop(
|
|
175
|
-
token=self.token,
|
|
176
|
-
context=context,
|
|
177
|
-
buffer=buffer,
|
|
178
|
-
name=self.name,
|
|
179
|
-
parent=None,
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
|
-
):
|
|
183
|
-
return self.block.render(context, buffer)
|
|
184
|
-
|
|
185
|
-
stack_item = block_stack[0]
|
|
186
|
-
|
|
187
|
-
if stack_item.required:
|
|
188
|
-
raise RequiredBlockError(
|
|
189
|
-
f"block {self.name!r} must be overridden",
|
|
190
|
-
token=self.token,
|
|
191
|
-
template_name=stack_item.source_name,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
ctx = context.copy(
|
|
195
|
-
namespace={
|
|
196
|
-
"block": BlockDrop(
|
|
197
|
-
token=self.token,
|
|
198
|
-
context=context,
|
|
199
|
-
buffer=buffer,
|
|
200
|
-
name=self.name,
|
|
201
|
-
parent=stack_item.parent,
|
|
202
|
-
)
|
|
203
|
-
},
|
|
204
|
-
carry_loop_iterations=True,
|
|
205
|
-
block_scope=True,
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
return stack_item.block.block.render(ctx, buffer)
|
|
209
|
-
|
|
210
|
-
async def render_to_output_async(
|
|
211
|
-
self, context: RenderContext, buffer: TextIO
|
|
212
|
-
) -> int:
|
|
213
|
-
"""Render the node to the output buffer."""
|
|
214
|
-
# We should be in a base template. Render the block at the top of the "stack".
|
|
215
|
-
block_stack: Sequence[_BlockStackItem] = context.tag_namespace.get(
|
|
216
|
-
"extends", {}
|
|
217
|
-
).get(self.name)
|
|
218
|
-
|
|
219
|
-
if not block_stack:
|
|
220
|
-
# This base template is being rendered directly.
|
|
221
|
-
if self.required:
|
|
222
|
-
raise RequiredBlockError(
|
|
223
|
-
f"block {self.name!r} must be overridden", token=self.token
|
|
224
|
-
)
|
|
225
|
-
with context.extend(
|
|
226
|
-
{
|
|
227
|
-
"block": BlockDrop(
|
|
228
|
-
token=self.token,
|
|
229
|
-
context=context,
|
|
230
|
-
buffer=buffer,
|
|
231
|
-
name=self.name,
|
|
232
|
-
parent=None,
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
):
|
|
236
|
-
return await self.block.render_async(context, buffer)
|
|
237
|
-
|
|
238
|
-
stack_item = block_stack[0]
|
|
239
|
-
|
|
240
|
-
if stack_item.required:
|
|
241
|
-
raise RequiredBlockError(
|
|
242
|
-
f"block {self.name!r} must be overridden",
|
|
243
|
-
token=self.token,
|
|
244
|
-
template_name=stack_item.source_name,
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
ctx = context.copy(
|
|
248
|
-
namespace={
|
|
249
|
-
"block": BlockDrop(
|
|
250
|
-
token=self.token,
|
|
251
|
-
context=context,
|
|
252
|
-
buffer=buffer,
|
|
253
|
-
name=self.name,
|
|
254
|
-
parent=stack_item.parent,
|
|
255
|
-
)
|
|
256
|
-
},
|
|
257
|
-
carry_loop_iterations=True,
|
|
258
|
-
block_scope=True,
|
|
259
|
-
)
|
|
260
|
-
return await stack_item.block.block.render_async(ctx, buffer)
|
|
261
|
-
|
|
262
|
-
def children(
|
|
263
|
-
self,
|
|
264
|
-
static_context: RenderContext, # noqa: ARG002
|
|
265
|
-
*,
|
|
266
|
-
include_partials: bool = True, # noqa: ARG002
|
|
267
|
-
) -> Iterable[Node]:
|
|
268
|
-
"""Return this node's expressions."""
|
|
269
|
-
yield self.block
|
|
270
|
-
|
|
271
|
-
def block_scope(self) -> Iterable[Identifier]:
|
|
272
|
-
"""Return variables this node adds to the node's block scope."""
|
|
273
|
-
yield Identifier("block", token=self.token)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
class BlockTag(Tag):
|
|
277
|
-
"""The standard _extends_ tag."""
|
|
278
|
-
|
|
279
|
-
name = "block"
|
|
280
|
-
block = True
|
|
281
|
-
node_class = BlockNode
|
|
282
|
-
end_block = frozenset(["endblock"])
|
|
283
|
-
|
|
284
|
-
def parse(self, stream: TokenStream) -> Node:
|
|
285
|
-
"""Parse tokens from _stream_ into an AST node."""
|
|
286
|
-
token = stream.eat(TOKEN_TAG)
|
|
287
|
-
tokens = stream.into_inner(tag=token)
|
|
288
|
-
block_name = parse_name(self.env, tokens)
|
|
289
|
-
|
|
290
|
-
if tokens.current.kind == TOKEN_REQUIRED:
|
|
291
|
-
required = True
|
|
292
|
-
next(tokens)
|
|
293
|
-
else:
|
|
294
|
-
required = False
|
|
295
|
-
|
|
296
|
-
tokens.expect_eos()
|
|
297
|
-
|
|
298
|
-
parse_block = get_parser(self.env).parse_block
|
|
299
|
-
block = parse_block(stream, self.end_block)
|
|
300
|
-
|
|
301
|
-
stream.expect(TOKEN_TAG, "endblock")
|
|
302
|
-
end_block_token = stream.current
|
|
303
|
-
|
|
304
|
-
if stream.peek.kind == TOKEN_EXPRESSION:
|
|
305
|
-
next(stream)
|
|
306
|
-
tokens = stream.into_inner(tag=token, eat=False)
|
|
307
|
-
if tokens.current.kind != TOKEN_EOF:
|
|
308
|
-
end_block_name = parse_name(self.env, tokens)
|
|
309
|
-
if end_block_name != block_name:
|
|
310
|
-
raise TemplateInheritanceError(
|
|
311
|
-
f"expected endblock for '{block_name}, "
|
|
312
|
-
f"found '{end_block_name}'",
|
|
313
|
-
token=end_block_token,
|
|
314
|
-
)
|
|
315
|
-
next(tokens)
|
|
316
|
-
tokens.expect_eos()
|
|
317
|
-
|
|
318
|
-
return self.node_class(
|
|
319
|
-
token=token,
|
|
320
|
-
name=block_name,
|
|
321
|
-
block=block,
|
|
322
|
-
required=required,
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
@dataclass
|
|
327
|
-
class _BlockStackItem:
|
|
328
|
-
token: Token
|
|
329
|
-
block: BlockNode
|
|
330
|
-
required: bool
|
|
331
|
-
source_name: str
|
|
332
|
-
parent: Optional[_BlockStackItem] = None
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
class BlockDrop(Mapping[str, object]):
|
|
336
|
-
"""A `block` object with a `super` property."""
|
|
337
|
-
|
|
338
|
-
__slots__ = ("token", "buffer", "context", "name", "parent")
|
|
339
|
-
|
|
340
|
-
def __init__(
|
|
341
|
-
self,
|
|
342
|
-
*,
|
|
343
|
-
token: Token,
|
|
344
|
-
context: RenderContext,
|
|
345
|
-
buffer: TextIO,
|
|
346
|
-
name: str,
|
|
347
|
-
parent: Optional[_BlockStackItem],
|
|
348
|
-
) -> None:
|
|
349
|
-
self.token = token
|
|
350
|
-
self.buffer = buffer
|
|
351
|
-
self.context = context
|
|
352
|
-
self.name = name
|
|
353
|
-
self.parent = parent
|
|
354
|
-
|
|
355
|
-
def __str__(self) -> str: # pragma: no cover
|
|
356
|
-
return f"BlockDrop({self.name})"
|
|
357
|
-
|
|
358
|
-
def __getitem__(self, key: str) -> object:
|
|
359
|
-
if key != "super":
|
|
360
|
-
raise KeyError(key)
|
|
361
|
-
|
|
362
|
-
if not self.parent:
|
|
363
|
-
return self.context.env.undefined("super", token=self.token)
|
|
364
|
-
|
|
365
|
-
# NOTE: We're not allowing chaining of references to `super` for now.
|
|
366
|
-
# Just the immediate parent.
|
|
367
|
-
buf = self.context.get_buffer(self.buffer)
|
|
368
|
-
with self.context.extend(
|
|
369
|
-
{
|
|
370
|
-
"block": BlockDrop(
|
|
371
|
-
token=self.parent.token,
|
|
372
|
-
context=self.context,
|
|
373
|
-
buffer=buf,
|
|
374
|
-
name=self.parent.source_name,
|
|
375
|
-
parent=self.parent.parent,
|
|
376
|
-
)
|
|
377
|
-
}
|
|
378
|
-
):
|
|
379
|
-
self.parent.block.block.render(self.context, buf)
|
|
380
|
-
|
|
381
|
-
if self.context.autoescape:
|
|
382
|
-
return Markupsafe(buf.getvalue())
|
|
383
|
-
return buf.getvalue()
|
|
384
|
-
|
|
385
|
-
def __len__(self) -> int: # pragma: no cover
|
|
386
|
-
return 1
|
|
387
|
-
|
|
388
|
-
def __iter__(self) -> Iterator[str]: # pragma: no cover
|
|
389
|
-
return iter(["super"])
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
def _build_block_stacks(
|
|
393
|
-
context: RenderContext,
|
|
394
|
-
template: BoundTemplate,
|
|
395
|
-
tag: str,
|
|
396
|
-
) -> BoundTemplate:
|
|
397
|
-
"""Build a stack for each `{% block %}` in the inheritance chain.
|
|
398
|
-
|
|
399
|
-
Blocks defined in the base template will be at the top of the stack.
|
|
400
|
-
|
|
401
|
-
Args:
|
|
402
|
-
context: A render context to build the block stacks in.
|
|
403
|
-
template: A leaf template with an `extends` tag.
|
|
404
|
-
parent_name: The name of the immediate parent template as a string literal.
|
|
405
|
-
tag: The name of the `extends` tag, if it is overridden.
|
|
406
|
-
"""
|
|
407
|
-
# Guard against recursive `extends`.
|
|
408
|
-
seen: set[str] = set()
|
|
409
|
-
|
|
410
|
-
def _stack_template_blocks(template: BoundTemplate) -> Optional[BoundTemplate]:
|
|
411
|
-
extends_node, _ = _stack_blocks(context, template)
|
|
412
|
-
|
|
413
|
-
if not extends_node:
|
|
414
|
-
return None
|
|
415
|
-
|
|
416
|
-
if extends_node.name in seen:
|
|
417
|
-
raise TemplateInheritanceError(
|
|
418
|
-
f"circular extends {extends_node.name!r}",
|
|
419
|
-
token=extends_node.token,
|
|
420
|
-
template_name=template.name,
|
|
421
|
-
)
|
|
422
|
-
|
|
423
|
-
seen.add(extends_node.name)
|
|
424
|
-
|
|
425
|
-
try:
|
|
426
|
-
return context.env.get_template(extends_node.name, context=context, tag=tag)
|
|
427
|
-
except TemplateNotFoundError as err:
|
|
428
|
-
err.token = extends_node.name.token
|
|
429
|
-
err.template_name = template.full_name()
|
|
430
|
-
raise
|
|
431
|
-
|
|
432
|
-
base = next_template = _stack_template_blocks(template)
|
|
433
|
-
|
|
434
|
-
while next_template:
|
|
435
|
-
next_template = _stack_template_blocks(next_template)
|
|
436
|
-
if next_template:
|
|
437
|
-
base = next_template
|
|
438
|
-
|
|
439
|
-
assert base
|
|
440
|
-
return base
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
async def _build_block_stacks_async(
|
|
444
|
-
context: RenderContext,
|
|
445
|
-
template: BoundTemplate,
|
|
446
|
-
tag: str,
|
|
447
|
-
) -> BoundTemplate:
|
|
448
|
-
"""Build a stack for each `{% block %}` in the inheritance chain.
|
|
449
|
-
|
|
450
|
-
Blocks defined in the base template will be at the top of the stack.
|
|
451
|
-
|
|
452
|
-
Args:
|
|
453
|
-
context: A render context to build the block stacks in.
|
|
454
|
-
template: A leaf template with an `extends` tag.
|
|
455
|
-
parent_name: The name of the immediate parent template as a string.
|
|
456
|
-
tag: The name of the `extends` tag, if it is overridden.
|
|
457
|
-
"""
|
|
458
|
-
# Guard against recursive `extends`.
|
|
459
|
-
seen: set[str] = set()
|
|
460
|
-
|
|
461
|
-
async def _stack_template_blocks(
|
|
462
|
-
template: BoundTemplate,
|
|
463
|
-
) -> Optional[BoundTemplate]:
|
|
464
|
-
extends_node, _ = _stack_blocks(context, template)
|
|
465
|
-
|
|
466
|
-
if not extends_node:
|
|
467
|
-
return None
|
|
468
|
-
|
|
469
|
-
if extends_node.name in seen:
|
|
470
|
-
raise TemplateInheritanceError(
|
|
471
|
-
f"circular extends {extends_node.name!r}",
|
|
472
|
-
token=extends_node.token,
|
|
473
|
-
template_name=template.name,
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
seen.add(extends_node.name)
|
|
477
|
-
|
|
478
|
-
try:
|
|
479
|
-
return await context.env.get_template_async(
|
|
480
|
-
extends_node.name, context=context, tag=tag
|
|
481
|
-
)
|
|
482
|
-
except TemplateNotFoundError as err:
|
|
483
|
-
err.token = extends_node.name.token
|
|
484
|
-
err.template_name = template.full_name()
|
|
485
|
-
raise
|
|
486
|
-
|
|
487
|
-
base = next_template = await _stack_template_blocks(template)
|
|
488
|
-
|
|
489
|
-
while next_template:
|
|
490
|
-
next_template = await _stack_template_blocks(next_template)
|
|
491
|
-
if next_template:
|
|
492
|
-
base = next_template
|
|
493
|
-
|
|
494
|
-
assert base
|
|
495
|
-
return base
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
def _find_inheritance_nodes(
|
|
499
|
-
template: BoundTemplate, context: RenderContext
|
|
500
|
-
) -> tuple[list["ExtendsNode"], list[BlockNode]]:
|
|
501
|
-
"""Return lists of `extends` and `block` nodes from the given template."""
|
|
502
|
-
extends_nodes: list["ExtendsNode"] = []
|
|
503
|
-
block_nodes: list[BlockNode] = []
|
|
504
|
-
|
|
505
|
-
def _visit_node(node: Node, context: RenderContext) -> None:
|
|
506
|
-
if isinstance(node, BlockNode):
|
|
507
|
-
block_nodes.append(node)
|
|
508
|
-
|
|
509
|
-
if isinstance(node, ExtendsNode):
|
|
510
|
-
extends_nodes.append(node)
|
|
511
|
-
|
|
512
|
-
for child in node.children(context, include_partials=False):
|
|
513
|
-
_visit_node(child, context=context)
|
|
514
|
-
|
|
515
|
-
for node in template.nodes:
|
|
516
|
-
_visit_node(node, context=context)
|
|
517
|
-
|
|
518
|
-
return extends_nodes, block_nodes
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
def _stack_blocks(
|
|
522
|
-
context: RenderContext, template: BoundTemplate
|
|
523
|
-
) -> tuple[Optional[ExtendsNode], list[BlockNode]]:
|
|
524
|
-
"""Find template inheritance nodes in `template`.
|
|
525
|
-
|
|
526
|
-
Each node found is pushed on to the appropriate block stack.
|
|
527
|
-
"""
|
|
528
|
-
extends, blocks = _find_inheritance_nodes(template, context)
|
|
529
|
-
template_name = str(template.path or template.name)
|
|
530
|
-
|
|
531
|
-
if len(extends) > 1:
|
|
532
|
-
raise TemplateInheritanceError(
|
|
533
|
-
"too many 'extends' tags",
|
|
534
|
-
token=extends[1].token,
|
|
535
|
-
template_name=template_name,
|
|
536
|
-
)
|
|
537
|
-
|
|
538
|
-
seen_block_names: set[str] = set()
|
|
539
|
-
for block in blocks:
|
|
540
|
-
if block.name in seen_block_names:
|
|
541
|
-
raise TemplateInheritanceError(
|
|
542
|
-
f"duplicate block {block.name}",
|
|
543
|
-
token=block.token,
|
|
544
|
-
)
|
|
545
|
-
seen_block_names.add(block.name)
|
|
546
|
-
|
|
547
|
-
_store_blocks(context, blocks, template_name)
|
|
548
|
-
|
|
549
|
-
if not extends:
|
|
550
|
-
return None, blocks
|
|
551
|
-
# return extends[0].name.evaluate(context), blocks
|
|
552
|
-
return extends[0], blocks
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
def _store_blocks(
|
|
556
|
-
context: RenderContext, blocks: list[BlockNode], source_name: str
|
|
557
|
-
) -> None:
|
|
558
|
-
block_stacks: DefaultDict[str, list[_BlockStackItem]] = context.tag_namespace[
|
|
559
|
-
"extends"
|
|
560
|
-
]
|
|
561
|
-
|
|
562
|
-
for block in blocks:
|
|
563
|
-
stack = block_stacks[block.name]
|
|
564
|
-
required = False if stack and not block.required else block.required
|
|
565
|
-
|
|
566
|
-
stack.append(
|
|
567
|
-
_BlockStackItem(
|
|
568
|
-
token=block.token,
|
|
569
|
-
block=block,
|
|
570
|
-
required=required,
|
|
571
|
-
source_name=source_name,
|
|
572
|
-
)
|
|
573
|
-
)
|
|
574
|
-
|
|
575
|
-
if len(stack) > 1:
|
|
576
|
-
stack[-2].parent = stack[-1]
|