python-liquid 2.3.5__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. liquid/__about__.py +1 -0
  2. liquid/__init__.py +161 -187
  3. liquid/_base_tag.py +66 -0
  4. liquid/{utils/chain_map.py → _chain_map.py} +18 -19
  5. liquid/_context.py +465 -0
  6. liquid/_drop.py +108 -0
  7. liquid/_filter.py +174 -0
  8. liquid/_io.py +18 -0
  9. liquid/_lax_parser_mixin.py +100 -0
  10. liquid/_lexer.py +93 -0
  11. liquid/_lexer_extended.py +623 -0
  12. liquid/_lexer_legacy.py +536 -0
  13. liquid/_loader.py +84 -0
  14. liquid/{utils/lru_cache.py → _lru_cache.py} +28 -36
  15. liquid/_markup.py +253 -0
  16. liquid/_messages.py +292 -0
  17. liquid/_missing.py +3 -0
  18. liquid/_nothing.py +28 -0
  19. liquid/_parser.py +460 -0
  20. liquid/_parser_extended.py +293 -0
  21. liquid/_parser_legacy.py +819 -0
  22. liquid/_resource_limits.py +50 -0
  23. liquid/_slice.py +41 -0
  24. liquid/_static_analysis.py +503 -0
  25. liquid/_strftime.py +186 -0
  26. liquid/_template.py +536 -0
  27. liquid/drops/__init__.py +20 -0
  28. liquid/drops/forloop.py +78 -0
  29. liquid/drops/iterable.py +25 -0
  30. liquid/drops/range.py +76 -0
  31. liquid/drops/self_.py +45 -0
  32. liquid/drops/tablerowloop.py +142 -0
  33. liquid/drops/undefined.py +228 -0
  34. liquid/environment.py +717 -551
  35. liquid/exceptions.py +67 -214
  36. liquid/expressions.py +1184 -0
  37. liquid/filters/__init__.py +134 -0
  38. liquid/filters/abs.py +7 -0
  39. liquid/filters/append.py +5 -0
  40. liquid/filters/at_least.py +9 -0
  41. liquid/filters/at_most.py +9 -0
  42. liquid/filters/babel.py +560 -0
  43. liquid/filters/base64.py +26 -0
  44. liquid/filters/capitalize.py +5 -0
  45. liquid/filters/ceil.py +8 -0
  46. liquid/filters/compact.py +25 -0
  47. liquid/filters/concat.py +11 -0
  48. liquid/filters/date.py +27 -0
  49. liquid/filters/default.py +25 -0
  50. liquid/filters/divided_by.py +21 -0
  51. liquid/filters/downcase.py +5 -0
  52. liquid/filters/escape.py +56 -0
  53. liquid/filters/find.py +56 -0
  54. liquid/filters/first.py +19 -0
  55. liquid/filters/floor.py +8 -0
  56. liquid/filters/html.py +23 -0
  57. liquid/filters/index.py +12 -0
  58. liquid/filters/join.py +13 -0
  59. liquid/filters/json_.py +30 -0
  60. liquid/filters/last.py +17 -0
  61. liquid/filters/map.py +5 -0
  62. liquid/filters/minus.py +13 -0
  63. liquid/filters/modulo.py +18 -0
  64. liquid/filters/newline_to_br.py +16 -0
  65. liquid/filters/plus.py +13 -0
  66. liquid/filters/prepend.py +5 -0
  67. liquid/filters/reject.py +19 -0
  68. liquid/filters/remove.py +36 -0
  69. liquid/filters/replace.py +32 -0
  70. liquid/filters/reverse.py +5 -0
  71. liquid/filters/round.py +35 -0
  72. liquid/filters/size.py +25 -0
  73. liquid/filters/slice.py +39 -0
  74. liquid/filters/sort.py +148 -0
  75. liquid/filters/split.py +34 -0
  76. liquid/filters/squish.py +9 -0
  77. liquid/filters/strip.py +13 -0
  78. liquid/filters/strip_html.py +19 -0
  79. liquid/filters/strip_newlines.py +17 -0
  80. liquid/filters/sum.py +21 -0
  81. liquid/filters/times.py +13 -0
  82. liquid/filters/translate.py +380 -0
  83. liquid/filters/truncate.py +39 -0
  84. liquid/filters/uniq.py +11 -0
  85. liquid/filters/upcase.py +5 -0
  86. liquid/filters/url.py +18 -0
  87. liquid/filters/where.py +18 -0
  88. liquid/loaders/__init__.py +17 -0
  89. liquid/loaders/caching_file_system_loader.py +64 -0
  90. liquid/loaders/choice_loader.py +93 -0
  91. liquid/loaders/dict_loader.py +56 -0
  92. liquid/loaders/file_system_loader.py +143 -0
  93. liquid/{builtin/loaders → loaders}/mixins.py +40 -52
  94. liquid/loaders/package_loader.py +110 -0
  95. liquid/tags/__init__.py +61 -0
  96. liquid/tags/assign.py +68 -0
  97. liquid/tags/capture.py +83 -0
  98. liquid/tags/case.py +199 -0
  99. liquid/tags/comment.py +56 -0
  100. liquid/tags/cycle.py +131 -0
  101. liquid/tags/decrement.py +55 -0
  102. liquid/tags/doc.py +56 -0
  103. liquid/tags/echo.py +65 -0
  104. liquid/tags/else_.py +53 -0
  105. liquid/tags/error.py +51 -0
  106. liquid/tags/extends.py +448 -0
  107. liquid/tags/for_.py +421 -0
  108. liquid/tags/if_.py +156 -0
  109. liquid/tags/ifchanged.py +69 -0
  110. liquid/tags/include.py +223 -0
  111. liquid/tags/increment.py +55 -0
  112. liquid/tags/inline_comment.py +70 -0
  113. liquid/tags/liquid.py +56 -0
  114. liquid/tags/macro.py +281 -0
  115. liquid/tags/output.py +58 -0
  116. liquid/tags/raw.py +60 -0
  117. liquid/tags/render.py +245 -0
  118. liquid/tags/tablerow.py +254 -0
  119. liquid/tags/template_comment.py +56 -0
  120. liquid/tags/translate.py +316 -0
  121. liquid/tags/unless.py +121 -0
  122. liquid/tags/with_.py +83 -0
  123. liquid/tokens.py +370 -0
  124. {python_liquid-2.3.5.dist-info → python_liquid-3.0.0.dist-info}/METADATA +32 -39
  125. python_liquid-3.0.0.dist-info/RECORD +128 -0
  126. {python_liquid-2.3.5.dist-info → python_liquid-3.0.0.dist-info}/WHEEL +1 -1
  127. {python_liquid-2.3.5.dist-info → python_liquid-3.0.0.dist-info}/licenses/LICENSE +1 -1
  128. liquid/analyze_tags.py +0 -214
  129. liquid/ast.py +0 -252
  130. liquid/builtin/__init__.py +0 -203
  131. liquid/builtin/content.py +0 -41
  132. liquid/builtin/drops/__init__.py +0 -3
  133. liquid/builtin/drops/drops.py +0 -13
  134. liquid/builtin/expressions/__init__.py +0 -47
  135. liquid/builtin/expressions/_tokenize.py +0 -186
  136. liquid/builtin/expressions/arguments.py +0 -207
  137. liquid/builtin/expressions/filtered.py +0 -387
  138. liquid/builtin/expressions/logical.py +0 -672
  139. liquid/builtin/expressions/loop.py +0 -275
  140. liquid/builtin/expressions/path.py +0 -163
  141. liquid/builtin/expressions/primitive.py +0 -417
  142. liquid/builtin/filters/__init__.py +0 -0
  143. liquid/builtin/filters/array.py +0 -329
  144. liquid/builtin/filters/extra.py +0 -83
  145. liquid/builtin/filters/math.py +0 -129
  146. liquid/builtin/filters/misc.py +0 -137
  147. liquid/builtin/filters/string.py +0 -416
  148. liquid/builtin/illegal.py +0 -28
  149. liquid/builtin/loaders/__init__.py +0 -0
  150. liquid/builtin/loaders/caching_file_system_loader.py +0 -61
  151. liquid/builtin/loaders/choice_loader.py +0 -94
  152. liquid/builtin/loaders/dict_loader.py +0 -64
  153. liquid/builtin/loaders/file_system_loader.py +0 -146
  154. liquid/builtin/loaders/package_loader.py +0 -132
  155. liquid/builtin/output.py +0 -74
  156. liquid/builtin/tags/__init__.py +0 -0
  157. liquid/builtin/tags/assign_tag.py +0 -75
  158. liquid/builtin/tags/capture_tag.py +0 -98
  159. liquid/builtin/tags/case_tag.py +0 -287
  160. liquid/builtin/tags/comment_tag.py +0 -77
  161. liquid/builtin/tags/cycle_tag.py +0 -132
  162. liquid/builtin/tags/decrement_tag.py +0 -59
  163. liquid/builtin/tags/doc_tag.py +0 -68
  164. liquid/builtin/tags/echo_tag.py +0 -45
  165. liquid/builtin/tags/for_tag.py +0 -337
  166. liquid/builtin/tags/if_tag.py +0 -209
  167. liquid/builtin/tags/ifchanged_tag.py +0 -94
  168. liquid/builtin/tags/include_tag.py +0 -251
  169. liquid/builtin/tags/increment_tag.py +0 -60
  170. liquid/builtin/tags/inline_comment_tag.py +0 -47
  171. liquid/builtin/tags/liquid_tag.py +0 -183
  172. liquid/builtin/tags/render_tag.py +0 -371
  173. liquid/builtin/tags/tablerow_tag.py +0 -320
  174. liquid/builtin/tags/unless_tag.py +0 -218
  175. liquid/context.py +0 -615
  176. liquid/expression.py +0 -41
  177. liquid/extra/__init__.py +0 -98
  178. liquid/extra/filters/__init__.py +0 -33
  179. liquid/extra/filters/_json.py +0 -33
  180. liquid/extra/filters/array.py +0 -60
  181. liquid/extra/filters/babel.py +0 -552
  182. liquid/extra/filters/html.py +0 -32
  183. liquid/extra/filters/translate.py +0 -448
  184. liquid/extra/tags/__init__.py +0 -17
  185. liquid/extra/tags/_with.py +0 -80
  186. liquid/extra/tags/extends_tag.py +0 -576
  187. liquid/extra/tags/macro_tag.py +0 -270
  188. liquid/extra/tags/snippet_tag.py +0 -107
  189. liquid/extra/tags/translate_tag.py +0 -388
  190. liquid/filter.py +0 -230
  191. liquid/future/__init__.py +0 -3
  192. liquid/future/environment.py +0 -30
  193. liquid/future/filters/__init__.py +0 -1
  194. liquid/future/tags/__init__.py +0 -1
  195. liquid/lex.py +0 -260
  196. liquid/limits.py +0 -66
  197. liquid/loader.py +0 -141
  198. liquid/messages.py +0 -357
  199. liquid/mode.py +0 -12
  200. liquid/output.py +0 -35
  201. liquid/parser.py +0 -119
  202. liquid/span.py +0 -35
  203. liquid/static_analysis.py +0 -443
  204. liquid/stream.py +0 -148
  205. liquid/stringify.py +0 -31
  206. liquid/tag.py +0 -47
  207. liquid/template.py +0 -658
  208. liquid/token.py +0 -145
  209. liquid/undefined.py +0 -218
  210. liquid/utils/__init__.py +0 -15
  211. liquid/utils/html.py +0 -57
  212. liquid/utils/text.py +0 -28
  213. python_liquid-2.3.5.dist-info/RECORD +0 -96
liquid/__about__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "3.0.0"
liquid/__init__.py CHANGED
@@ -1,242 +1,216 @@
1
- from pathlib import Path
2
- from typing import Iterable
3
- from typing import Iterator
4
- from typing import Optional
5
- from typing import TextIO
6
- from typing import Union
7
-
8
- from markupsafe import Markup
9
- from markupsafe import escape
10
- from markupsafe import soft_str
11
-
12
- from .mode import Mode
13
- from .token import Token
14
- from .expression import Expression
15
-
16
- from .context import RenderContext
17
- from .context import FutureContext
18
- from .undefined import DebugUndefined
19
- from .undefined import is_undefined
20
- from .undefined import FalsyStrictUndefined
21
- from .undefined import StrictDefaultUndefined
22
- from .undefined import StrictUndefined
23
- from .undefined import Undefined
24
-
1
+ from collections.abc import Iterator, Mapping
2
+ from typing import Any, TextIO
3
+
4
+ from ._base_tag import AbstractTag
5
+ from ._context import RenderContext
6
+ from ._drop import (
7
+ ContextHint,
8
+ LiquidAsyncIterable,
9
+ LiquidAsyncObject,
10
+ LiquidIterable,
11
+ LiquidObject,
12
+ LiquidPrimitive,
13
+ )
14
+ from ._filter import Filter, FilterContext
15
+ from ._io import StringBuffer
16
+ from ._lax_parser_mixin import LaxParserMixin
17
+ from ._lexer import Lexer
18
+ from ._lexer_extended import ExtendedLexer
19
+ from ._lexer_legacy import LegacyLexer
20
+ from ._loader import Loader, TemplateSource
21
+ from ._markup import (
22
+ Block,
23
+ Expression,
24
+ Markup,
25
+ Node,
26
+ Partial,
27
+ Scope,
28
+ Tag,
29
+ render_block,
30
+ render_block_async,
31
+ )
32
+ from ._messages import (
33
+ MessageTuple,
34
+ Translations,
35
+ extract_from_template,
36
+ extract_from_templates,
37
+ )
38
+ from ._nothing import NOTHING
39
+ from ._parser import Parser
40
+ from ._parser_extended import ExtendedParser
41
+ from ._parser_legacy import LegacyParser
42
+ from ._resource_limits import ResourceLimits
43
+ from ._slice import slice_range, slice_sequence
44
+ from ._static_analysis import Segments, TemplateAnalysis
45
+ from ._template import Template
46
+ from .drops import (
47
+ AsyncIterableDrop,
48
+ FalsyStrictUndefined,
49
+ IterableDrop,
50
+ StrictUndefined,
51
+ Undefined,
52
+ )
25
53
  from .environment import Environment
26
- from .environment import Template
27
-
28
- from .template import AwareBoundTemplate
29
- from .template import BoundTemplate
30
- from .template import FutureAwareBoundTemplate
31
- from .template import FutureBoundTemplate
32
-
33
- from .builtin import CachingDictLoader
34
- from .builtin import DictLoader
35
- from .builtin import ChoiceLoader
36
- from .builtin import CachingChoiceLoader
37
- from .builtin import CachingFileSystemLoader
38
- from .builtin import FileSystemLoader
39
- from .builtin import PackageLoader
40
- from .builtin import CachingLoaderMixin
41
- from .loader import BaseLoader
42
-
43
- from .ast import Node
44
- from .ast import BlockNode
45
- from .ast import ConditionalBlockNode
46
-
47
- from .analyze_tags import TagAnalysis
48
- from .analyze_tags import DEFAULT_INNER_TAG_MAP
49
-
50
- from .messages import MessageTuple
51
- from .messages import Translations
52
- from .messages import extract_from_template
53
-
54
- from .stream import TokenStream
55
- from .tag import Tag
56
-
57
- from . import future
58
-
59
- __version__ = "2.3.5"
54
+ from .loaders import (
55
+ CachingChoiceLoader,
56
+ CachingDictLoader,
57
+ CachingFileSystemLoader,
58
+ CachingLoaderMixin,
59
+ ChoiceLoader,
60
+ DictLoader,
61
+ FileSystemLoader,
62
+ PackageLoader,
63
+ )
64
+ from .tokens import TOKEN_NAMES, Token, TokenKind, span, token_value
60
65
 
61
66
  __all__ = (
62
- "AwareBoundTemplate",
63
- "BlockNode",
64
- "BoundTemplate",
67
+ "NOTHING",
68
+ "TOKEN_NAMES",
69
+ "AbstractTag",
70
+ "AsyncIterableDrop",
71
+ "Block",
65
72
  "CachingChoiceLoader",
66
73
  "CachingDictLoader",
67
74
  "CachingFileSystemLoader",
68
75
  "CachingLoaderMixin",
69
76
  "ChoiceLoader",
70
- "ConditionalBlockNode",
71
- "DebugUndefined",
72
- "DEFAULT_INNER_TAG_MAP",
77
+ "ContextHint",
73
78
  "DictLoader",
74
79
  "Environment",
75
- "escape",
76
80
  "Expression",
77
- "extract_from_template",
81
+ "ExtendedLexer",
82
+ "ExtendedParser",
78
83
  "FalsyStrictUndefined",
79
84
  "FileSystemLoader",
80
- "future",
81
- "FutureAwareBoundTemplate",
82
- "FutureBoundTemplate",
83
- "FutureContext",
84
- "is_undefined",
85
- "make_choice_loader",
86
- "make_file_system_loader",
85
+ "Filter",
86
+ "FilterContext",
87
+ "IterableDrop",
88
+ "LaxExtendedParser",
89
+ "LaxLegacyParser",
90
+ "LaxParserMixin",
91
+ "LegacyLexer",
92
+ "LegacyParser",
93
+ "Lexer",
94
+ "LiquidAsyncIterable",
95
+ "LiquidAsyncObject",
96
+ "LiquidIterable",
97
+ "LiquidObject",
98
+ "LiquidPrimitive",
99
+ "Loader",
87
100
  "Markup",
88
- "MessageTuple",
89
- "Mode",
90
101
  "Node",
91
102
  "PackageLoader",
92
- "parse",
93
- "render_async",
94
- "render",
103
+ "Parser",
104
+ "Partial",
95
105
  "RenderContext",
96
- "soft_str",
97
- "StrictDefaultUndefined",
106
+ "ResourceLimits",
107
+ "Scope",
108
+ "Segments",
98
109
  "StrictUndefined",
110
+ "StringBuffer",
99
111
  "Tag",
100
- "TagAnalysis",
101
112
  "Template",
113
+ "TemplateAnalysis",
114
+ "TemplateSource",
102
115
  "Token",
103
- "TokenStream",
116
+ "TokenKind",
104
117
  "Translations",
105
118
  "Undefined",
119
+ "extract_from_templates",
120
+ "render_block",
121
+ "render_block_async",
122
+ "slice_range",
123
+ "slice_sequence",
124
+ "span",
125
+ "token_value",
106
126
  )
107
127
 
128
+
108
129
  DEFAULT_ENVIRONMENT = Environment()
109
130
 
110
131
 
111
- def parse(source: str) -> BoundTemplate:
112
- """Parse template source text using the default environment."""
113
- return DEFAULT_ENVIRONMENT.from_string(source)
132
+ def parse(
133
+ source: str,
134
+ *,
135
+ name: str = "",
136
+ globals: Mapping[str, Any] | None = None,
137
+ ) -> Template:
138
+ """Parse template source code `source` using the default Liquid environment.
139
+
140
+ Parameters
141
+ ----------
142
+ source
143
+ Template source code.
144
+ name
145
+ A name for the template that will appear in error messages.
146
+ globals
147
+ Variables to pin to the resulting template.
148
+ """
149
+ return DEFAULT_ENVIRONMENT.parse(source, name=name, globals=globals)
114
150
 
115
151
 
116
- def render(source: str, **data: object) -> str:
117
- """Parse and render source text using the default environment."""
118
- return DEFAULT_ENVIRONMENT.render(source, **data)
152
+ def render(source: str, data: Mapping[str, Any] | None = None) -> str:
153
+ """Parse and render `source` with variables from `data` using the default environment.
119
154
 
155
+ Parameters
156
+ ----------
157
+ source
158
+ Template source code.
159
+ data
160
+ Template variables.
161
+ """
162
+ return DEFAULT_ENVIRONMENT.render(source, data)
120
163
 
121
- async def render_async(source: str, **data: object) -> str:
122
- """Parse and render source text using the default environment."""
123
- return await DEFAULT_ENVIRONMENT.render_async(source, **data)
124
164
 
165
+ async def render_async(source: str, data: Mapping[str, Any] | None = None) -> str:
166
+ """Parse and render `source` asynchronously with variables from `data` using the
167
+ default environment.
125
168
 
126
- def make_file_system_loader(
127
- search_path: Union[str, Path, Iterable[Union[str, Path]]],
128
- *,
129
- encoding: str = "utf-8",
130
- ext: str = ".liquid",
131
- auto_reload: bool = True,
132
- namespace_key: str = "",
133
- cache_size: int = 300,
134
- ) -> BaseLoader:
135
- """A _file system_ template loader factory.
136
-
137
- Returns one of `CachingFileSystemLoader` or `FileSystemLoader` depending in
138
- the given arguments.
139
-
140
- A `CachingFileSystemLoader` is returned if _cache_size_ is greater than 0.
141
- Otherwise a `FileExtensionLoader` is returned if _ext_ is not empty.
142
- If _ext_ is empty, a `FileSystemLoader` is returned.
143
-
144
- _auto_reload_ and _namespace_key_ are ignored if _cache_key_ is less than 1.
145
-
146
- Args:
147
- search_path: One or more paths to search.
148
- encoding: Open template files with the given encoding.
149
- ext: A default file extension. Should include a leading period.
150
- auto_reload: If `True`, automatically reload a cached template if it has been
151
- updated.
152
- namespace_key: The name of a global render context variable or loader keyword
153
- argument that resolves to the current loader "namespace" or "scope".
154
-
155
- If you're developing a multi-user application, a good namespace might be
156
- `uid`, where `uid` is a unique identifier for a user and templates are
157
- arranged in folders named for each `uid` inside the search path.
158
- cache_size: The maximum number of templates to hold in the cache before removing
159
- the least recently used template.
160
-
161
- _New in version 1.12.0_
169
+ Parameters
170
+ ----------
171
+ source
172
+ Template source code.
173
+ data
174
+ Template variables.
162
175
  """
163
- if cache_size > 0:
164
- return CachingFileSystemLoader(
165
- search_path=search_path,
166
- encoding=encoding,
167
- ext=ext,
168
- auto_reload=auto_reload,
169
- namespace_key=namespace_key,
170
- capacity=cache_size,
171
- )
176
+ return await DEFAULT_ENVIRONMENT.render_async(source, data)
172
177
 
173
- return FileSystemLoader(search_path=search_path, encoding=encoding, ext=ext)
174
178
 
179
+ class LaxLegacyParser(LaxParserMixin, LegacyParser):
180
+ """A legacy parser that suppresses Liquid syntax errors.
175
181
 
176
- def make_choice_loader(
177
- loaders: list[BaseLoader],
178
- *,
179
- auto_reload: bool = True,
180
- namespace_key: str = "",
181
- cache_size: int = 300,
182
- ) -> BaseLoader:
183
- """A _choice loader_ factory.
184
-
185
- Returns one of `CachingChoiceLoader` or `ChoiceLoader` depending on the
186
- given arguments.
187
-
188
- A `CachingChoiceLoader` is returned if _cache_size_ > 0, otherwise a
189
- `ChoiceLoader` is returned.
190
-
191
- _auto_reload_ and _namespace_key_ are ignored if _cache_key_ is less than 1.
192
-
193
- Args:
194
- loaders: A list of loaders implementing `liquid.loaders.BaseLoader`.
195
- auto_reload: If `True`, automatically reload a cached template if it
196
- has been updated.
197
- namespace_key: The name of a global render context variable or loader
198
- keyword argument that resolves to the current loader "namespace" or
199
- "scope".
200
- cache_size: The maximum number of templates to hold in the cache before
201
- removing the least recently used template.
202
-
203
- _New in version 1.12.0_
182
+ Use ``Template.errors()`` to get a list of available ``LiquidSyntaxError`` instances.
204
183
  """
205
- if cache_size > 0:
206
- return CachingChoiceLoader(
207
- loaders=loaders,
208
- auto_reload=auto_reload,
209
- namespace_key=namespace_key,
210
- capacity=cache_size,
211
- )
212
184
 
213
- return ChoiceLoader(loaders=loaders)
185
+
186
+ class LaxExtendedParser(LaxParserMixin, ExtendedParser):
187
+ """An extended parser that suppresses Liquid syntax errors.
188
+
189
+ Use ``Template.errors()`` to get a list of available ``LiquidSyntaxError`` instances.
190
+ """
214
191
 
215
192
 
216
193
  def extract_liquid(
217
194
  fileobj: TextIO,
218
195
  keywords: list[str],
219
- comment_tags: Optional[list[str]] = None,
220
- options: Optional[dict[object, object]] = None, # noqa: ARG001
196
+ comment_tags: list[str] | None = None,
197
+ options: Mapping[str, Any] | None = None,
221
198
  ) -> Iterator[MessageTuple]:
222
199
  """A babel compatible translation message extraction method for Liquid templates.
223
200
 
224
201
  See https://babel.pocoo.org/en/latest/messages.html
225
202
 
226
- Keywords are the names of Liquid filters or tags operating on translatable
227
- strings. For a filter to contribute to message extraction, it must also
228
- appear as a child of a `FilteredExpression` and be a `TranslatableFilter`.
229
- Similarly, tags must produce a node that is a `TranslatableTag`.
203
+ Keywords are the names of Liquid filters or tags operating on translatable strings.
204
+ For a filter to contribute to message extraction, it must also appear as a child of
205
+ a `FilteredExpression` and be a `TranslatableFilter`. Similarly, tags must produce a
206
+ node that is a `TranslatableTag`.
230
207
 
231
- Where a Liquid comment contains a prefix in `comment_tags`, the comment
232
- will be attached to the translatable filter or tag immediately following
233
- the comment. Python Liquid's non-standard shorthand comments are not
234
- supported.
208
+ Where a Liquid comment contains a prefix in `comment_tags`, the comment will be
209
+ attached to the translatable filter or tag immediately following the comment. Python
210
+ Liquid's non-standard shorthand comments are not supported.
235
211
 
236
- Options are arguments passed to the `liquid.Template` constructor with the
237
- contents of `fileobj` as the template's source. Use `extract_from_template`
238
- to extract messages from an existing template bound to an existing
239
- environment.
212
+ Use ``extract_from_template`` to extract messages from an existing template bound to
213
+ an existing environment.
240
214
  """
241
215
  template = Environment(extra=True).parse(fileobj.read())
242
216
  return extract_from_template(
liquid/_base_tag.py ADDED
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from ._context import RenderContext
8
+ from ._io import StringBuffer
9
+ from ._markup import Expression, Markup, Partial
10
+ from ._parser import Parser
11
+ from .expressions import Name
12
+ from .tokens import Token
13
+
14
+
15
+ class AbstractTag(ABC):
16
+ """An abstract class for implementing custom tags.
17
+
18
+ It is not necessary to inherit from `AbstractTag` when implementing custom tags, but
19
+ it can be convenient if you're not interested in methods supporting static template
20
+ analysis, saving you from writing several empty methods.
21
+
22
+ Internally we use the `Markup` and `Tag` protocols, without a required base class.
23
+ """
24
+
25
+ @classmethod
26
+ @abstractmethod
27
+ def parse(cls, token: Token, parser: Parser) -> Markup:
28
+ """Parse tokens or text from `parser` into a markup node."""
29
+
30
+ @abstractmethod
31
+ def render(self, context: RenderContext, buffer: StringBuffer) -> None:
32
+ """Render this node to `buffer` using data from `context`."""
33
+
34
+ @abstractmethod
35
+ async def render_async(self, context: RenderContext, buffer: StringBuffer) -> None:
36
+ """Render this node to `buffer` asynchronously using data from `context`."""
37
+
38
+ def children(self) -> list[Markup]:
39
+ """Returns markup nodes that are direct children of this node in the syntax tree.
40
+
41
+ This is used to traverse template syntax trees during static analysis.
42
+ """
43
+ return []
44
+
45
+ def expressions(self) -> list[Expression]:
46
+ """Return expression owned by this node."""
47
+ return []
48
+
49
+ def block_scope(self) -> list[Name]:
50
+ """Return variable names that are in scope for the duration of this node's block.
51
+
52
+ This is used during static analysis to isolate and report global variables.
53
+ """
54
+ return []
55
+
56
+ def template_scope(self) -> list[Name]:
57
+ """Return variable names that this tag adds to the template scope."""
58
+ return []
59
+
60
+ def partials(self, context: RenderContext) -> list[Partial]:
61
+ """Return meta data about partial templates rendered from this node."""
62
+ return []
63
+
64
+ async def partials_async(self, context: RenderContext) -> list[Partial]:
65
+ """Return meta data about partial templates rendered from this node."""
66
+ return []
@@ -3,26 +3,29 @@
3
3
  Based on "A greatly simplified read-only version of Chainmap" by Raymond Hettinger.
4
4
  https://code.activestate.com/recipes/305268/
5
5
  """
6
+
6
7
  from collections import deque
8
+ from collections.abc import Iterator, Mapping
7
9
  from itertools import chain
8
- from typing import Any
9
- from typing import Iterator
10
- from typing import Mapping
10
+
11
+ from ._missing import MISSING
11
12
 
12
13
 
13
14
  class ReadOnlyChainMap(Mapping[str, object]):
14
- """Combine multiple mappings for sequential lookup."""
15
+ """Combine multiple mappings for sequential lookup.
16
+
17
+ This chain map never raises a KeyError. It returns the MISSING sentinel
18
+ if a key does not exist in the chain.
19
+ """
15
20
 
16
21
  def __init__(self, *maps: Mapping[str, object]):
17
22
  self._maps = deque(maps)
18
23
 
19
24
  def __getitem__(self, key: str) -> object:
20
25
  for mapping in self._maps:
21
- try:
26
+ if key in mapping:
22
27
  return mapping[key]
23
- except KeyError:
24
- pass
25
- raise KeyError(key)
28
+ return MISSING
26
29
 
27
30
  def __iter__(self) -> Iterator[str]:
28
31
  return chain(*self._maps)
@@ -30,21 +33,17 @@ class ReadOnlyChainMap(Mapping[str, object]):
30
33
  def __len__(self) -> int:
31
34
  return sum(len(_map) for _map in self._maps)
32
35
 
36
+ def __contains__(self, key: object) -> bool:
37
+ return any(key in mapping for mapping in self._maps)
38
+
33
39
  def size(self) -> int:
34
- """Return the number of maps in the chain."""
35
40
  return len(self._maps)
36
41
 
37
- def get(self, key: str, default: object = None) -> object:
38
- """Return the value for key if key is in the dictionary, else default."""
39
- try:
40
- return self[key]
41
- except KeyError:
42
- return default
42
+ def get(self, key: str, default: object = MISSING) -> object:
43
+ return self[key]
43
44
 
44
- def push(self, namespace: Mapping[Any, Any]) -> None:
45
- """Add a mapping to the front of the chain map."""
45
+ def push(self, namespace: Mapping[str, object]) -> None:
46
46
  self._maps.appendleft(namespace)
47
47
 
48
- def pop(self) -> Mapping[Any, Any]:
49
- """Remove a mapping from the front of the chain map."""
48
+ def pop(self) -> Mapping[str, object]:
50
49
  return self._maps.popleft()