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.
Files changed (212) hide show
  1. liquid/__about__.py +1 -0
  2. liquid/__init__.py +161 -187
  3. liquid/_base_tag.py +66 -0
  4. liquid/{utils/chain_map.py → _chain_map.py} +18 -19
  5. liquid/_context.py +465 -0
  6. liquid/_drop.py +108 -0
  7. liquid/_filter.py +174 -0
  8. liquid/_io.py +18 -0
  9. liquid/_lax_parser_mixin.py +100 -0
  10. liquid/_lexer.py +93 -0
  11. liquid/_lexer_extended.py +623 -0
  12. liquid/_lexer_legacy.py +536 -0
  13. liquid/_loader.py +84 -0
  14. liquid/{utils/lru_cache.py → _lru_cache.py} +28 -36
  15. liquid/_markup.py +253 -0
  16. liquid/_messages.py +292 -0
  17. liquid/_missing.py +3 -0
  18. liquid/_nothing.py +28 -0
  19. liquid/_parser.py +460 -0
  20. liquid/_parser_extended.py +293 -0
  21. liquid/_parser_legacy.py +819 -0
  22. liquid/_resource_limits.py +50 -0
  23. liquid/_slice.py +41 -0
  24. liquid/_static_analysis.py +503 -0
  25. liquid/_strftime.py +186 -0
  26. liquid/_template.py +536 -0
  27. liquid/drops/__init__.py +20 -0
  28. liquid/drops/forloop.py +78 -0
  29. liquid/drops/iterable.py +25 -0
  30. liquid/drops/range.py +76 -0
  31. liquid/drops/self_.py +45 -0
  32. liquid/drops/tablerowloop.py +142 -0
  33. liquid/drops/undefined.py +228 -0
  34. liquid/environment.py +717 -551
  35. liquid/exceptions.py +67 -214
  36. liquid/expressions.py +1184 -0
  37. liquid/filters/__init__.py +134 -0
  38. liquid/filters/abs.py +7 -0
  39. liquid/filters/append.py +5 -0
  40. liquid/filters/at_least.py +9 -0
  41. liquid/filters/at_most.py +9 -0
  42. liquid/filters/babel.py +560 -0
  43. liquid/filters/base64.py +26 -0
  44. liquid/filters/capitalize.py +5 -0
  45. liquid/filters/ceil.py +8 -0
  46. liquid/filters/compact.py +25 -0
  47. liquid/filters/concat.py +11 -0
  48. liquid/filters/date.py +27 -0
  49. liquid/filters/default.py +25 -0
  50. liquid/filters/divided_by.py +21 -0
  51. liquid/filters/downcase.py +5 -0
  52. liquid/filters/escape.py +56 -0
  53. liquid/filters/find.py +56 -0
  54. liquid/filters/first.py +19 -0
  55. liquid/filters/floor.py +8 -0
  56. liquid/filters/html.py +23 -0
  57. liquid/filters/index.py +12 -0
  58. liquid/filters/join.py +13 -0
  59. liquid/filters/json_.py +30 -0
  60. liquid/filters/last.py +17 -0
  61. liquid/filters/map.py +5 -0
  62. liquid/filters/minus.py +13 -0
  63. liquid/filters/modulo.py +18 -0
  64. liquid/filters/newline_to_br.py +16 -0
  65. liquid/filters/plus.py +13 -0
  66. liquid/filters/prepend.py +5 -0
  67. liquid/filters/reject.py +19 -0
  68. liquid/filters/remove.py +36 -0
  69. liquid/filters/replace.py +32 -0
  70. liquid/filters/reverse.py +5 -0
  71. liquid/filters/round.py +35 -0
  72. liquid/filters/size.py +25 -0
  73. liquid/filters/slice.py +39 -0
  74. liquid/filters/sort.py +148 -0
  75. liquid/filters/split.py +34 -0
  76. liquid/filters/squish.py +9 -0
  77. liquid/filters/strip.py +13 -0
  78. liquid/filters/strip_html.py +19 -0
  79. liquid/filters/strip_newlines.py +17 -0
  80. liquid/filters/sum.py +21 -0
  81. liquid/filters/times.py +13 -0
  82. liquid/filters/translate.py +380 -0
  83. liquid/filters/truncate.py +39 -0
  84. liquid/filters/uniq.py +11 -0
  85. liquid/filters/upcase.py +5 -0
  86. liquid/filters/url.py +18 -0
  87. liquid/filters/where.py +18 -0
  88. liquid/loaders/__init__.py +17 -0
  89. liquid/loaders/caching_file_system_loader.py +64 -0
  90. liquid/loaders/choice_loader.py +93 -0
  91. liquid/loaders/dict_loader.py +56 -0
  92. liquid/{builtin/loaders → loaders}/file_system_loader.py +51 -55
  93. liquid/{builtin/loaders → loaders}/mixins.py +39 -51
  94. liquid/loaders/package_loader.py +110 -0
  95. liquid/tags/__init__.py +61 -0
  96. liquid/tags/assign.py +68 -0
  97. liquid/tags/capture.py +83 -0
  98. liquid/tags/case.py +199 -0
  99. liquid/tags/comment.py +56 -0
  100. liquid/tags/cycle.py +131 -0
  101. liquid/tags/decrement.py +55 -0
  102. liquid/tags/doc.py +56 -0
  103. liquid/tags/echo.py +65 -0
  104. liquid/tags/else_.py +53 -0
  105. liquid/tags/error.py +51 -0
  106. liquid/tags/extends.py +448 -0
  107. liquid/tags/for_.py +421 -0
  108. liquid/tags/if_.py +156 -0
  109. liquid/tags/ifchanged.py +69 -0
  110. liquid/tags/include.py +223 -0
  111. liquid/tags/increment.py +55 -0
  112. liquid/tags/inline_comment.py +70 -0
  113. liquid/tags/liquid.py +56 -0
  114. liquid/tags/macro.py +281 -0
  115. liquid/tags/output.py +58 -0
  116. liquid/tags/raw.py +60 -0
  117. liquid/tags/render.py +245 -0
  118. liquid/tags/tablerow.py +254 -0
  119. liquid/tags/template_comment.py +56 -0
  120. liquid/tags/translate.py +316 -0
  121. liquid/tags/unless.py +121 -0
  122. liquid/tags/with_.py +83 -0
  123. liquid/tokens.py +370 -0
  124. {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/METADATA +32 -39
  125. python_liquid-3.0.0.dist-info/RECORD +128 -0
  126. {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/WHEEL +1 -1
  127. {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/licenses/LICENSE +1 -1
  128. liquid/analyze_tags.py +0 -214
  129. liquid/ast.py +0 -252
  130. liquid/builtin/__init__.py +0 -203
  131. liquid/builtin/content.py +0 -41
  132. liquid/builtin/drops/__init__.py +0 -3
  133. liquid/builtin/drops/drops.py +0 -13
  134. liquid/builtin/expressions/__init__.py +0 -47
  135. liquid/builtin/expressions/_tokenize.py +0 -186
  136. liquid/builtin/expressions/arguments.py +0 -207
  137. liquid/builtin/expressions/filtered.py +0 -387
  138. liquid/builtin/expressions/logical.py +0 -672
  139. liquid/builtin/expressions/loop.py +0 -275
  140. liquid/builtin/expressions/path.py +0 -163
  141. liquid/builtin/expressions/primitive.py +0 -417
  142. liquid/builtin/filters/__init__.py +0 -0
  143. liquid/builtin/filters/array.py +0 -329
  144. liquid/builtin/filters/extra.py +0 -83
  145. liquid/builtin/filters/math.py +0 -129
  146. liquid/builtin/filters/misc.py +0 -137
  147. liquid/builtin/filters/string.py +0 -416
  148. liquid/builtin/illegal.py +0 -28
  149. liquid/builtin/loaders/__init__.py +0 -0
  150. liquid/builtin/loaders/caching_file_system_loader.py +0 -61
  151. liquid/builtin/loaders/choice_loader.py +0 -94
  152. liquid/builtin/loaders/dict_loader.py +0 -64
  153. liquid/builtin/loaders/package_loader.py +0 -132
  154. liquid/builtin/output.py +0 -74
  155. liquid/builtin/tags/__init__.py +0 -0
  156. liquid/builtin/tags/assign_tag.py +0 -75
  157. liquid/builtin/tags/capture_tag.py +0 -98
  158. liquid/builtin/tags/case_tag.py +0 -287
  159. liquid/builtin/tags/comment_tag.py +0 -77
  160. liquid/builtin/tags/cycle_tag.py +0 -132
  161. liquid/builtin/tags/decrement_tag.py +0 -59
  162. liquid/builtin/tags/doc_tag.py +0 -68
  163. liquid/builtin/tags/echo_tag.py +0 -45
  164. liquid/builtin/tags/for_tag.py +0 -337
  165. liquid/builtin/tags/if_tag.py +0 -209
  166. liquid/builtin/tags/ifchanged_tag.py +0 -94
  167. liquid/builtin/tags/include_tag.py +0 -251
  168. liquid/builtin/tags/increment_tag.py +0 -60
  169. liquid/builtin/tags/inline_comment_tag.py +0 -47
  170. liquid/builtin/tags/liquid_tag.py +0 -183
  171. liquid/builtin/tags/render_tag.py +0 -371
  172. liquid/builtin/tags/tablerow_tag.py +0 -320
  173. liquid/builtin/tags/unless_tag.py +0 -218
  174. liquid/context.py +0 -615
  175. liquid/expression.py +0 -41
  176. liquid/extra/__init__.py +0 -98
  177. liquid/extra/filters/__init__.py +0 -33
  178. liquid/extra/filters/_json.py +0 -33
  179. liquid/extra/filters/array.py +0 -60
  180. liquid/extra/filters/babel.py +0 -552
  181. liquid/extra/filters/html.py +0 -32
  182. liquid/extra/filters/translate.py +0 -448
  183. liquid/extra/tags/__init__.py +0 -17
  184. liquid/extra/tags/_with.py +0 -80
  185. liquid/extra/tags/extends_tag.py +0 -576
  186. liquid/extra/tags/macro_tag.py +0 -270
  187. liquid/extra/tags/snippet_tag.py +0 -107
  188. liquid/extra/tags/translate_tag.py +0 -388
  189. liquid/filter.py +0 -230
  190. liquid/future/__init__.py +0 -3
  191. liquid/future/environment.py +0 -30
  192. liquid/future/filters/__init__.py +0 -1
  193. liquid/future/tags/__init__.py +0 -1
  194. liquid/lex.py +0 -260
  195. liquid/limits.py +0 -66
  196. liquid/loader.py +0 -141
  197. liquid/messages.py +0 -357
  198. liquid/mode.py +0 -12
  199. liquid/output.py +0 -35
  200. liquid/parser.py +0 -119
  201. liquid/span.py +0 -35
  202. liquid/static_analysis.py +0 -443
  203. liquid/stream.py +0 -148
  204. liquid/stringify.py +0 -31
  205. liquid/tag.py +0 -47
  206. liquid/template.py +0 -658
  207. liquid/token.py +0 -145
  208. liquid/undefined.py +0 -218
  209. liquid/utils/__init__.py +0 -15
  210. liquid/utils/html.py +0 -57
  211. liquid/utils/text.py +0 -28
  212. 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)