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/template.py
DELETED
|
@@ -1,658 +0,0 @@
|
|
|
1
|
-
"""Liquid template definition."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from io import StringIO
|
|
6
|
-
from itertools import chain
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
9
|
-
from typing import Any
|
|
10
|
-
from typing import Awaitable
|
|
11
|
-
from typing import Iterator
|
|
12
|
-
from typing import Mapping
|
|
13
|
-
from typing import Optional
|
|
14
|
-
from typing import TextIO
|
|
15
|
-
from typing import Type
|
|
16
|
-
from typing import TypedDict
|
|
17
|
-
from typing import Union
|
|
18
|
-
|
|
19
|
-
from .builtin.tags.comment_tag import CommentNode
|
|
20
|
-
from .builtin.tags.doc_tag import DocNode
|
|
21
|
-
from .context import FutureContext
|
|
22
|
-
from .context import RenderContext
|
|
23
|
-
from .exceptions import LiquidError
|
|
24
|
-
from .exceptions import LiquidInterrupt
|
|
25
|
-
from .exceptions import LiquidSyntaxError
|
|
26
|
-
from .exceptions import StopRender
|
|
27
|
-
from .output import LimitedStringIO
|
|
28
|
-
from .static_analysis import analyze
|
|
29
|
-
from .static_analysis import analyze_async
|
|
30
|
-
from .utils import ReadOnlyChainMap
|
|
31
|
-
|
|
32
|
-
if TYPE_CHECKING:
|
|
33
|
-
from collections import abc
|
|
34
|
-
|
|
35
|
-
from .ast import Node
|
|
36
|
-
from .environment import Environment
|
|
37
|
-
from .loader import UpToDate
|
|
38
|
-
from .static_analysis import Segments
|
|
39
|
-
from .static_analysis import TemplateAnalysis
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
__all__ = (
|
|
43
|
-
"AwareBoundTemplate",
|
|
44
|
-
"BoundTemplate",
|
|
45
|
-
"FutureAwareBoundTemplate",
|
|
46
|
-
"FutureBoundTemplate",
|
|
47
|
-
"TemplateDrop",
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class BoundTemplate:
|
|
52
|
-
"""A liquid template that has been parsed and is bound to a `liquid.Environment`.
|
|
53
|
-
|
|
54
|
-
You probably don't want to instantiate `BoundTemplate` directly. Use
|
|
55
|
-
`liquid.Environment.from_string()` or `liquid.Environment.get_template()`
|
|
56
|
-
instead.
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
env: The environment this template is bound to.
|
|
60
|
-
nodes: The parse tree representing this template.
|
|
61
|
-
name: Optional name of the template. Defaults to an empty string.
|
|
62
|
-
path: Optional origin path or identifier for the template.
|
|
63
|
-
globals: An optional mapping of context variables made available every
|
|
64
|
-
time the resulting template is rendered. Defaults to `None`.
|
|
65
|
-
matter: Optional mapping containing variables associated with the template.
|
|
66
|
-
Could be "front matter" or other meta data.
|
|
67
|
-
uptodate: Optional callable that will return `True` if the template is up
|
|
68
|
-
to date, or `False` if it needs to be reloaded. Defaults to `None`.
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
# Subclass `BoundTemplate` and override `context_class` to use a subclass of
|
|
72
|
-
# `Context` when rendering templates.
|
|
73
|
-
context_class: Type[RenderContext] = RenderContext
|
|
74
|
-
|
|
75
|
-
def __init__(
|
|
76
|
-
self,
|
|
77
|
-
env: Environment,
|
|
78
|
-
nodes: list[Node],
|
|
79
|
-
name: str = "",
|
|
80
|
-
path: Optional[Union[str, Path]] = None,
|
|
81
|
-
globals: Optional[Mapping[str, object]] = None, # noqa: A002
|
|
82
|
-
matter: Optional[Mapping[str, object]] = None,
|
|
83
|
-
uptodate: UpToDate = None,
|
|
84
|
-
):
|
|
85
|
-
self.env = env
|
|
86
|
-
self.nodes = nodes
|
|
87
|
-
self.globals = globals or {}
|
|
88
|
-
self.matter = matter or {}
|
|
89
|
-
self.name = name
|
|
90
|
-
self.path = path
|
|
91
|
-
self.uptodate = uptodate
|
|
92
|
-
|
|
93
|
-
def __str__(self) -> str:
|
|
94
|
-
return "".join(str(node) for node in self.nodes)
|
|
95
|
-
|
|
96
|
-
def full_name(self) -> str:
|
|
97
|
-
"""Return this template's path, if available, joined with its name."""
|
|
98
|
-
if self.path:
|
|
99
|
-
path = Path(self.path)
|
|
100
|
-
return str(path / self.name if not path.name else path)
|
|
101
|
-
return self.name
|
|
102
|
-
|
|
103
|
-
def render(self, *args: Any, **kwargs: Any) -> str:
|
|
104
|
-
"""Render the template with `args` and `kwargs` included in the render context.
|
|
105
|
-
|
|
106
|
-
Accepts the same arguments as the `dict` constructor.
|
|
107
|
-
"""
|
|
108
|
-
context = self.context_class(
|
|
109
|
-
self,
|
|
110
|
-
globals=self.make_globals(dict(*args, **kwargs)),
|
|
111
|
-
)
|
|
112
|
-
buf = self._get_buffer()
|
|
113
|
-
self.render_with_context(context, buf)
|
|
114
|
-
return buf.getvalue()
|
|
115
|
-
|
|
116
|
-
async def render_async(self, *args: Any, **kwargs: Any) -> str:
|
|
117
|
-
"""An async version of `liquid.template.BoundTemplate.render`."""
|
|
118
|
-
context = self.context_class(
|
|
119
|
-
self,
|
|
120
|
-
globals=self.make_globals(dict(*args, **kwargs)),
|
|
121
|
-
)
|
|
122
|
-
buf = self._get_buffer()
|
|
123
|
-
await self.render_with_context_async(context, buf)
|
|
124
|
-
return buf.getvalue()
|
|
125
|
-
|
|
126
|
-
def _get_buffer(self) -> StringIO:
|
|
127
|
-
if self.env.output_stream_limit is None:
|
|
128
|
-
return StringIO()
|
|
129
|
-
return LimitedStringIO(limit=self.env.output_stream_limit)
|
|
130
|
-
|
|
131
|
-
def render_with_context(
|
|
132
|
-
self,
|
|
133
|
-
context: RenderContext,
|
|
134
|
-
buffer: TextIO,
|
|
135
|
-
*args: Any,
|
|
136
|
-
partial: bool = False,
|
|
137
|
-
block_scope: bool = False,
|
|
138
|
-
**kwargs: Any,
|
|
139
|
-
) -> None:
|
|
140
|
-
"""Render the template using an existing context and output buffer.
|
|
141
|
-
|
|
142
|
-
Args:
|
|
143
|
-
context: A render context.
|
|
144
|
-
buffer: File-like object to which rendered text is written.
|
|
145
|
-
partial: If `True`, indicates that the current template has been
|
|
146
|
-
included using either a "render" or "include" tag. Defaults to `False`.
|
|
147
|
-
block_scope: If `True`, indicates that assigns, breaks and continues
|
|
148
|
-
from this template will not leak into the parent context. Defaults to
|
|
149
|
-
`False`.
|
|
150
|
-
args: Passed to the `dict` constructor. The resulting dictionary is added to
|
|
151
|
-
the render context.
|
|
152
|
-
kwargs: Passed to the `dict` constructor. The resulting dictionary is added
|
|
153
|
-
to the render context.
|
|
154
|
-
"""
|
|
155
|
-
# "template" could get overridden from args/kwargs, "partial" will not.
|
|
156
|
-
namespace = self.make_partial_namespace(partial, dict(*args, **kwargs))
|
|
157
|
-
|
|
158
|
-
with context.extend(namespace=namespace):
|
|
159
|
-
for node in self.nodes:
|
|
160
|
-
try:
|
|
161
|
-
node.render(context, buffer)
|
|
162
|
-
except LiquidInterrupt as err:
|
|
163
|
-
# If this is an "included" template, there could be a for loop
|
|
164
|
-
# in a parent template. A for loop that could be interrupted
|
|
165
|
-
# from an included template.
|
|
166
|
-
#
|
|
167
|
-
# Convert the interrupt to a syntax error if there is no parent.
|
|
168
|
-
if not partial or block_scope:
|
|
169
|
-
self.env.error(
|
|
170
|
-
LiquidSyntaxError(f"unexpected '{err}'", token=node.token)
|
|
171
|
-
)
|
|
172
|
-
else:
|
|
173
|
-
raise
|
|
174
|
-
except StopRender:
|
|
175
|
-
break
|
|
176
|
-
except LiquidError as err:
|
|
177
|
-
# Raise or warn according to the current mode.
|
|
178
|
-
self.env.error(err, token=node.token)
|
|
179
|
-
|
|
180
|
-
async def render_with_context_async(
|
|
181
|
-
self,
|
|
182
|
-
context: RenderContext,
|
|
183
|
-
buffer: TextIO,
|
|
184
|
-
*args: Any,
|
|
185
|
-
partial: bool = False,
|
|
186
|
-
block_scope: bool = False,
|
|
187
|
-
**kwargs: Any,
|
|
188
|
-
) -> None:
|
|
189
|
-
"""An async version of `render_with_context`."""
|
|
190
|
-
# "template" could get overridden from args/kwargs, "partial" will not.
|
|
191
|
-
namespace = self.make_partial_namespace(partial, dict(*args, **kwargs))
|
|
192
|
-
|
|
193
|
-
with context.extend(namespace=namespace):
|
|
194
|
-
for node in self.nodes:
|
|
195
|
-
try:
|
|
196
|
-
await node.render_async(context, buffer)
|
|
197
|
-
except LiquidInterrupt as err:
|
|
198
|
-
# If this is an "included" template, there could be a for loop
|
|
199
|
-
# in a parent template. A for loop that could be interrupted
|
|
200
|
-
# from an included template.
|
|
201
|
-
#
|
|
202
|
-
# Convert the interrupt to a syntax error if there is no parent.
|
|
203
|
-
if not partial or block_scope:
|
|
204
|
-
self.env.error(
|
|
205
|
-
LiquidSyntaxError(f"unexpected '{err}'", token=node.token)
|
|
206
|
-
)
|
|
207
|
-
else:
|
|
208
|
-
raise
|
|
209
|
-
except StopRender:
|
|
210
|
-
break
|
|
211
|
-
except LiquidError as err:
|
|
212
|
-
# Raise or warn according to the current mode.
|
|
213
|
-
self.env.error(err, token=node.token)
|
|
214
|
-
|
|
215
|
-
def is_up_to_date(self) -> bool:
|
|
216
|
-
"""`False` if the template has bee modified, `True` otherwise."""
|
|
217
|
-
if not self.uptodate:
|
|
218
|
-
return True
|
|
219
|
-
|
|
220
|
-
uptodate = self.uptodate()
|
|
221
|
-
if not isinstance(uptodate, bool):
|
|
222
|
-
raise LiquidError(
|
|
223
|
-
f"expected a boolean from uptodate, found {type(uptodate).__name__}",
|
|
224
|
-
token=None,
|
|
225
|
-
)
|
|
226
|
-
return uptodate
|
|
227
|
-
|
|
228
|
-
async def is_up_to_date_async(self) -> bool:
|
|
229
|
-
"""An async version of the `is_up_to_date` property.
|
|
230
|
-
|
|
231
|
-
If `template.uptodate` is a coroutine, it wil be awaited. Otherwise it will be
|
|
232
|
-
called just like `is_up_to_date`
|
|
233
|
-
"""
|
|
234
|
-
if not self.uptodate:
|
|
235
|
-
return True
|
|
236
|
-
|
|
237
|
-
uptodate = self.uptodate()
|
|
238
|
-
if isinstance(uptodate, Awaitable):
|
|
239
|
-
return await uptodate
|
|
240
|
-
return uptodate
|
|
241
|
-
|
|
242
|
-
def make_globals(self, render_args: Mapping[str, object]) -> Mapping[str, object]:
|
|
243
|
-
"""Return a mapping including render arguments and template globals."""
|
|
244
|
-
return ReadOnlyChainMap(
|
|
245
|
-
render_args,
|
|
246
|
-
self.matter,
|
|
247
|
-
self.globals,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
def make_partial_namespace(
|
|
251
|
-
self,
|
|
252
|
-
partial: bool,
|
|
253
|
-
render_args: Mapping[str, object],
|
|
254
|
-
) -> Mapping[str, object]:
|
|
255
|
-
"""Return a namespace dictionary.
|
|
256
|
-
|
|
257
|
-
This is used by `render_with_context` to extend an existing context.
|
|
258
|
-
"""
|
|
259
|
-
return {**render_args, "partial": partial}
|
|
260
|
-
|
|
261
|
-
def analyze(self, *, include_partials: bool = True) -> TemplateAnalysis:
|
|
262
|
-
"""Statically analyze this template and any included/rendered templates.
|
|
263
|
-
|
|
264
|
-
Args:
|
|
265
|
-
include_partials: If `True`, we will try to load partial templates and
|
|
266
|
-
analyze those templates too.
|
|
267
|
-
"""
|
|
268
|
-
return analyze(self, include_partials=include_partials)
|
|
269
|
-
|
|
270
|
-
async def analyze_async(self, *, include_partials: bool = True) -> TemplateAnalysis:
|
|
271
|
-
"""An async version of `analyze`."""
|
|
272
|
-
return await analyze_async(self, include_partials=include_partials)
|
|
273
|
-
|
|
274
|
-
def variables(self, *, include_partials: bool = True) -> list[str]:
|
|
275
|
-
"""Return a list of variables used in this template without path segments.
|
|
276
|
-
|
|
277
|
-
Includes variables that are _local_ to the template, like those crated with
|
|
278
|
-
`{% assign %}` and `{% capture %}`.
|
|
279
|
-
|
|
280
|
-
See also [global_variables][liquid.BoundTemplate.global_variables].
|
|
281
|
-
|
|
282
|
-
Args:
|
|
283
|
-
include_partials: If `True`, will try to load and find variables in
|
|
284
|
-
included/rendered templates too.
|
|
285
|
-
|
|
286
|
-
Returns:
|
|
287
|
-
A list of distinct _root segments_ for variables in this template.
|
|
288
|
-
"""
|
|
289
|
-
return list(self.analyze(include_partials=include_partials).variables)
|
|
290
|
-
|
|
291
|
-
async def variables_async(self, *, include_partials: bool = True) -> list[str]:
|
|
292
|
-
"""Return a list of variables used in this template without path segments.
|
|
293
|
-
|
|
294
|
-
Includes variables that are _local_ to the template, like those crated with
|
|
295
|
-
`{% assign %}` and `{% capture %}`.
|
|
296
|
-
|
|
297
|
-
See also [global_variables][liquid.BoundTemplate.global_variables].
|
|
298
|
-
|
|
299
|
-
Args:
|
|
300
|
-
include_partials: If `True`, will try to load and find variables in
|
|
301
|
-
included/rendered templates too.
|
|
302
|
-
|
|
303
|
-
Returns:
|
|
304
|
-
A list of distinct _root segments_ for variables in this template.
|
|
305
|
-
"""
|
|
306
|
-
return list(
|
|
307
|
-
(await self.analyze_async(include_partials=include_partials)).variables
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
def variable_paths(self, *, include_partials: bool = True) -> list[str]:
|
|
311
|
-
"""Return a list of variables used in this template including all path segments.
|
|
312
|
-
|
|
313
|
-
Includes variables that are _local_ to the template, like those crated with
|
|
314
|
-
`{% assign %}` and `{% capture %}`.
|
|
315
|
-
|
|
316
|
-
See also [global_variable_paths][liquid.BoundTemplate.global_variable_paths].
|
|
317
|
-
|
|
318
|
-
Args:
|
|
319
|
-
include_partials: If `True`, will try to load and find variables in
|
|
320
|
-
included/rendered templates too.
|
|
321
|
-
|
|
322
|
-
Returns:
|
|
323
|
-
A list of distinct paths for variables in this template.
|
|
324
|
-
"""
|
|
325
|
-
analysis = self.analyze(include_partials=include_partials)
|
|
326
|
-
return list(
|
|
327
|
-
{str(v) for v in chain.from_iterable(list(analysis.variables.values()))}
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
async def variable_paths_async(self, *, include_partials: bool = True) -> list[str]:
|
|
331
|
-
"""Return a list of variables used in this template including all path segments.
|
|
332
|
-
|
|
333
|
-
Includes variables that are _local_ to the template, like those crated with
|
|
334
|
-
`{% assign %}` and `{% capture %}`.
|
|
335
|
-
|
|
336
|
-
See also [global_variable_paths][liquid.BoundTemplate.global_variable_paths].
|
|
337
|
-
|
|
338
|
-
Args:
|
|
339
|
-
include_partials: If `True`, will try to load and find variables in
|
|
340
|
-
included/rendered templates too.
|
|
341
|
-
|
|
342
|
-
Returns:
|
|
343
|
-
A list of distinct paths for variables in this template.
|
|
344
|
-
"""
|
|
345
|
-
analysis = await self.analyze_async(include_partials=include_partials)
|
|
346
|
-
return list(
|
|
347
|
-
{str(v) for v in chain.from_iterable(list(analysis.variables.values()))}
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
def variable_segments(self, *, include_partials: bool = True) -> list[Segments]:
|
|
351
|
-
"""Return a list of variables used in this template, each as a list of segments.
|
|
352
|
-
|
|
353
|
-
Includes variables that are _local_ to the template, like those crated with
|
|
354
|
-
`{% assign %}` and `{% capture %}`.
|
|
355
|
-
|
|
356
|
-
See [global_variable_segments][liquid.BoundTemplate.global_variable_segments].
|
|
357
|
-
|
|
358
|
-
Args:
|
|
359
|
-
include_partials: If `True`, will try to load and find variables in
|
|
360
|
-
included/rendered templates too.
|
|
361
|
-
|
|
362
|
-
Returns:
|
|
363
|
-
A list of distinct paths for variables in this template.
|
|
364
|
-
"""
|
|
365
|
-
analysis = self.analyze(include_partials=include_partials)
|
|
366
|
-
return [
|
|
367
|
-
v.segments
|
|
368
|
-
for v in set(chain.from_iterable(list(analysis.variables.values())))
|
|
369
|
-
]
|
|
370
|
-
|
|
371
|
-
async def variable_segments_async(
|
|
372
|
-
self, *, include_partials: bool = True
|
|
373
|
-
) -> list[Segments]:
|
|
374
|
-
"""Return a list of variables used in this template, each as a list of segments.
|
|
375
|
-
|
|
376
|
-
Includes variables that are _local_ to the template, like those crated with
|
|
377
|
-
`{% assign %}` and `{% capture %}`.
|
|
378
|
-
|
|
379
|
-
See [global_variable_segments][liquid.BoundTemplate.global_variable_segments].
|
|
380
|
-
|
|
381
|
-
Args:
|
|
382
|
-
include_partials: If `True`, will try to load and find variables in
|
|
383
|
-
included/rendered templates too.
|
|
384
|
-
|
|
385
|
-
Returns:
|
|
386
|
-
A list of distinct paths for variables in this template.
|
|
387
|
-
"""
|
|
388
|
-
analysis = await self.analyze_async(include_partials=include_partials)
|
|
389
|
-
return [
|
|
390
|
-
v.segments
|
|
391
|
-
for v in set(chain.from_iterable(list(analysis.variables.values())))
|
|
392
|
-
]
|
|
393
|
-
|
|
394
|
-
def global_variables(self, *, include_partials: bool = True) -> list[str]:
|
|
395
|
-
"""Return a list of variables used in this template without path segments.
|
|
396
|
-
|
|
397
|
-
Excludes variables that are _local_ to the template, like those crated with
|
|
398
|
-
`{% assign %}` and `{% capture %}`.
|
|
399
|
-
|
|
400
|
-
Args:
|
|
401
|
-
include_partials: If `True`, will try to load and find variables in
|
|
402
|
-
included/rendered templates too.
|
|
403
|
-
|
|
404
|
-
Returns:
|
|
405
|
-
A list of distinct _root segments_ for variables in this template.
|
|
406
|
-
"""
|
|
407
|
-
return list(self.analyze(include_partials=include_partials).globals)
|
|
408
|
-
|
|
409
|
-
async def global_variables_async(
|
|
410
|
-
self, *, include_partials: bool = True
|
|
411
|
-
) -> list[str]:
|
|
412
|
-
"""Return a list of variables used in this template without path segments.
|
|
413
|
-
|
|
414
|
-
Excludes variables that are _local_ to the template, like those crated with
|
|
415
|
-
`{% assign %}` and `{% capture %}`.
|
|
416
|
-
|
|
417
|
-
Args:
|
|
418
|
-
include_partials: If `True`, will try to load and find variables in
|
|
419
|
-
included/rendered templates too.
|
|
420
|
-
|
|
421
|
-
Returns:
|
|
422
|
-
A list of distinct _root segments_ for variables in this template.
|
|
423
|
-
"""
|
|
424
|
-
return list(
|
|
425
|
-
(await self.analyze_async(include_partials=include_partials)).globals
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
def global_variable_paths(self, *, include_partials: bool = True) -> list[str]:
|
|
429
|
-
"""Return a list of variables used in this template including all path segments.
|
|
430
|
-
|
|
431
|
-
Excludes variables that are _local_ to the template, like those crated with
|
|
432
|
-
`{% assign %}` and `{% capture %}`.
|
|
433
|
-
|
|
434
|
-
Args:
|
|
435
|
-
include_partials: If `True`, will try to load and find variables in
|
|
436
|
-
included/rendered templates too.
|
|
437
|
-
|
|
438
|
-
Returns:
|
|
439
|
-
A list of distinct paths for variables in this template.
|
|
440
|
-
"""
|
|
441
|
-
analysis = self.analyze(include_partials=include_partials)
|
|
442
|
-
return list(
|
|
443
|
-
{str(v) for v in chain.from_iterable(list(analysis.globals.values()))}
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
async def global_variable_paths_async(
|
|
447
|
-
self, *, include_partials: bool = True
|
|
448
|
-
) -> list[str]:
|
|
449
|
-
"""Return a list of variables used in this template including all path segments.
|
|
450
|
-
|
|
451
|
-
Excludes variables that are _local_ to the template, like those crated with
|
|
452
|
-
`{% assign %}` and `{% capture %}`.
|
|
453
|
-
|
|
454
|
-
Args:
|
|
455
|
-
include_partials: If `True`, will try to load and find variables in
|
|
456
|
-
included/rendered templates too.
|
|
457
|
-
|
|
458
|
-
Returns:
|
|
459
|
-
A list of distinct paths for variables in this template.
|
|
460
|
-
"""
|
|
461
|
-
analysis = await self.analyze_async(include_partials=include_partials)
|
|
462
|
-
return list(
|
|
463
|
-
{str(v) for v in chain.from_iterable(list(analysis.globals.values()))}
|
|
464
|
-
)
|
|
465
|
-
|
|
466
|
-
def global_variable_segments(
|
|
467
|
-
self, *, include_partials: bool = True
|
|
468
|
-
) -> list[Segments]:
|
|
469
|
-
"""Return a list of variables used in this template, each as a list of segments.
|
|
470
|
-
|
|
471
|
-
Excludes variables that are _local_ to the template, like those crated with
|
|
472
|
-
`{% assign %}` and `{% capture %}`.
|
|
473
|
-
|
|
474
|
-
Args:
|
|
475
|
-
include_partials: If `True`, will try to load and find variables in
|
|
476
|
-
included/rendered templates too.
|
|
477
|
-
|
|
478
|
-
Returns:
|
|
479
|
-
A list of distinct paths for variables in this template.
|
|
480
|
-
"""
|
|
481
|
-
analysis = self.analyze(include_partials=include_partials)
|
|
482
|
-
return [
|
|
483
|
-
v.segments
|
|
484
|
-
for v in set(chain.from_iterable(list(analysis.globals.values())))
|
|
485
|
-
]
|
|
486
|
-
|
|
487
|
-
async def global_variable_segments_async(
|
|
488
|
-
self, *, include_partials: bool = True
|
|
489
|
-
) -> list[Segments]:
|
|
490
|
-
"""Return a list of variables used in this template, each as a list of segments.
|
|
491
|
-
|
|
492
|
-
Excludes variables that are _local_ to the template, like those crated with
|
|
493
|
-
`{% assign %}` and `{% capture %}`.
|
|
494
|
-
|
|
495
|
-
Args:
|
|
496
|
-
include_partials: If `True`, will try to load and find variables in
|
|
497
|
-
included/rendered templates too.
|
|
498
|
-
|
|
499
|
-
Returns:
|
|
500
|
-
A list of distinct paths for variables in this template.
|
|
501
|
-
"""
|
|
502
|
-
analysis = await self.analyze_async(include_partials=include_partials)
|
|
503
|
-
return [
|
|
504
|
-
v.segments
|
|
505
|
-
for v in set(chain.from_iterable(list(analysis.globals.values())))
|
|
506
|
-
]
|
|
507
|
-
|
|
508
|
-
def filter_names(self, *, include_partials: bool = True) -> list[str]:
|
|
509
|
-
"""Return a list of filter names used in this template."""
|
|
510
|
-
return list(self.analyze(include_partials=include_partials).filters)
|
|
511
|
-
|
|
512
|
-
async def filter_names_async(self, *, include_partials: bool = True) -> list[str]:
|
|
513
|
-
"""Return a list of filter names used in this template."""
|
|
514
|
-
return list(
|
|
515
|
-
(await self.analyze_async(include_partials=include_partials)).filters
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
def tag_names(self, *, include_partials: bool = True) -> list[str]:
|
|
519
|
-
"""Return a list of tag names used in this template."""
|
|
520
|
-
return list(self.analyze(include_partials=include_partials).tags)
|
|
521
|
-
|
|
522
|
-
async def tag_names_async(self, *, include_partials: bool = True) -> list[str]:
|
|
523
|
-
"""Return a list of tag names used in this template."""
|
|
524
|
-
return list((await self.analyze_async(include_partials=include_partials)).tags)
|
|
525
|
-
|
|
526
|
-
def comments(self) -> list[CommentNode]:
|
|
527
|
-
"""Return a list of comment tag nodes found in this template.
|
|
528
|
-
|
|
529
|
-
Instances of `CommentNode` and `InlineCommentNode` have `token` and
|
|
530
|
-
`text` properties. Use `[node.text for node in template.comments()]` to
|
|
531
|
-
get a list of comment strings.
|
|
532
|
-
|
|
533
|
-
Note that this method does not try to load included or rendered
|
|
534
|
-
templates.
|
|
535
|
-
"""
|
|
536
|
-
context = RenderContext(self)
|
|
537
|
-
nodes: list[CommentNode] = []
|
|
538
|
-
|
|
539
|
-
def visit(node: Node) -> None:
|
|
540
|
-
if isinstance(node, CommentNode):
|
|
541
|
-
nodes.append(node)
|
|
542
|
-
|
|
543
|
-
for child in node.children(context, include_partials=False):
|
|
544
|
-
visit(child)
|
|
545
|
-
|
|
546
|
-
for child in self.nodes:
|
|
547
|
-
visit(child)
|
|
548
|
-
|
|
549
|
-
return nodes
|
|
550
|
-
|
|
551
|
-
def docs(self) -> list[DocNode]:
|
|
552
|
-
"""Return a list of doc tag nodes found in this template.
|
|
553
|
-
|
|
554
|
-
Instances of `DocNode` have `token` and `text` properties. Use
|
|
555
|
-
`[node.text for node in template.docs()]` to get a list of doc strings.
|
|
556
|
-
"""
|
|
557
|
-
context = RenderContext(self)
|
|
558
|
-
nodes: list[DocNode] = []
|
|
559
|
-
|
|
560
|
-
def visit(node: Node) -> None:
|
|
561
|
-
if isinstance(node, DocNode):
|
|
562
|
-
nodes.append(node)
|
|
563
|
-
|
|
564
|
-
for child in node.children(context, include_partials=False):
|
|
565
|
-
visit(child)
|
|
566
|
-
|
|
567
|
-
for child in self.nodes:
|
|
568
|
-
visit(child)
|
|
569
|
-
|
|
570
|
-
return nodes
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
class AwareBoundTemplate(BoundTemplate):
|
|
574
|
-
"""A `BoundTemplate` subclass with a `TemplateDrop` in the global namespace."""
|
|
575
|
-
|
|
576
|
-
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
577
|
-
super().__init__(*args, **kwargs)
|
|
578
|
-
self.drop = TemplateDrop(self.name, self.path)
|
|
579
|
-
|
|
580
|
-
def make_partial_namespace( # noqa: D102
|
|
581
|
-
self,
|
|
582
|
-
partial: bool,
|
|
583
|
-
render_args: Mapping[str, object],
|
|
584
|
-
) -> abc.Mapping[str, object]:
|
|
585
|
-
return {
|
|
586
|
-
"template": self.drop,
|
|
587
|
-
**super().make_partial_namespace(partial, render_args),
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
class FutureBoundTemplate(BoundTemplate):
|
|
592
|
-
"""A `BoundTemplate` configured to use the `FutureContext` render context.
|
|
593
|
-
|
|
594
|
-
See `liquid.future.Environment`.
|
|
595
|
-
"""
|
|
596
|
-
|
|
597
|
-
context_class = FutureContext
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
class FutureAwareBoundTemplate(AwareBoundTemplate):
|
|
601
|
-
"""A `BoundTemplate` configured to use the `FutureContext` render context.
|
|
602
|
-
|
|
603
|
-
Inserts a `TemplateDrop` into the global namespace automatically.
|
|
604
|
-
|
|
605
|
-
See `liquid.future.Environment`.
|
|
606
|
-
"""
|
|
607
|
-
|
|
608
|
-
context_class = FutureContext
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
class _TemplateDropItems(TypedDict):
|
|
612
|
-
directory: str
|
|
613
|
-
name: str
|
|
614
|
-
suffix: str | None
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
class TemplateDrop(Mapping[str, Optional[str]]):
|
|
618
|
-
"""Template meta data mapping."""
|
|
619
|
-
|
|
620
|
-
def __init__(self, name: str, path: Optional[Union[str, Path]]):
|
|
621
|
-
self.name = name
|
|
622
|
-
self.path = path
|
|
623
|
-
|
|
624
|
-
if not self.path or isinstance(self.path, str):
|
|
625
|
-
self.path = Path(self.name)
|
|
626
|
-
|
|
627
|
-
self.stem = self.path.stem
|
|
628
|
-
self.suffix: Optional[str] = None
|
|
629
|
-
|
|
630
|
-
if "." in self.stem:
|
|
631
|
-
self.suffix = self.stem.split(".")[-1]
|
|
632
|
-
|
|
633
|
-
self._items: _TemplateDropItems = {
|
|
634
|
-
"directory": self.path.parent.name,
|
|
635
|
-
"name": self.path.name.split(".")[0],
|
|
636
|
-
"suffix": self.suffix,
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
def __str__(self) -> str:
|
|
640
|
-
return self.stem
|
|
641
|
-
|
|
642
|
-
def __repr__(self) -> str:
|
|
643
|
-
return (
|
|
644
|
-
f"TemplateDrop(directory='{self['directory']}', "
|
|
645
|
-
f"name='{self['name']}', suffix='{self['suffix']}')"
|
|
646
|
-
) # pragma: no cover
|
|
647
|
-
|
|
648
|
-
def __contains__(self, item: object) -> bool:
|
|
649
|
-
return item in self._items
|
|
650
|
-
|
|
651
|
-
def __getitem__(self, key: object) -> Optional[str]:
|
|
652
|
-
return self._items[str(key)] # type: ignore
|
|
653
|
-
|
|
654
|
-
def __len__(self) -> int:
|
|
655
|
-
return len(self._items)
|
|
656
|
-
|
|
657
|
-
def __iter__(self) -> Iterator[str]:
|
|
658
|
-
return iter(self._items)
|