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/builtin/content.py DELETED
@@ -1,41 +0,0 @@
1
- """Pseudo tag and node definition for template content."""
2
-
3
- from typing import TextIO
4
-
5
- from liquid.ast import Node
6
- from liquid.context import RenderContext
7
- from liquid.stream import TokenStream
8
- from liquid.tag import Tag
9
- from liquid.token import TOKEN_CONTENT
10
- from liquid.token import Token
11
-
12
-
13
- class ContentNode(Node):
14
- """Parse tree node for template content."""
15
-
16
- __slots__ = ("text",)
17
-
18
- def __init__(self, token: Token, text: str):
19
- super().__init__(token)
20
- self.blank = not text or text.isspace()
21
- self.text = text
22
-
23
- def __str__(self) -> str:
24
- return self.text
25
-
26
- def render_to_output(self, _: RenderContext, buffer: TextIO) -> int:
27
- """Render the node to the output buffer."""
28
- return buffer.write(self.text)
29
-
30
-
31
- class Literal(Tag):
32
- """Pseudo "tag" for template content."""
33
-
34
- name = TOKEN_CONTENT
35
- block = False
36
- node_class = ContentNode
37
-
38
- def parse(self, stream: TokenStream) -> ContentNode:
39
- """Parse tokens from _stream_ into an AST node."""
40
- token = stream.expect(TOKEN_CONTENT)
41
- return self.node_class(token, token.value)
@@ -1,3 +0,0 @@
1
- from .drops import IterableDrop # noqa: D104
2
-
3
- __all__ = ("IterableDrop",)
@@ -1,13 +0,0 @@
1
- """Legacy iterable drop base class."""
2
- from abc import ABC
3
- from abc import abstractmethod
4
- from typing import Any
5
- from typing import Collection
6
-
7
-
8
- class IterableDrop(ABC, Collection[Any]):
9
- """Base class for drops that can be iterated with `include` and `render` tags."""
10
-
11
- @abstractmethod
12
- def step(self, item: Any) -> None:
13
- """Step the iterator."""
@@ -1,47 +0,0 @@
1
- from ._tokenize import tokenize # noqa: D104
2
- from .arguments import KeywordArgument # noqa: D104
3
- from .arguments import Parameter
4
- from .arguments import PositionalArgument
5
- from .arguments import parse_arguments
6
- from .filtered import Filter
7
- from .filtered import FilteredExpression
8
- from .filtered import TernaryFilteredExpression
9
- from .logical import BooleanExpression
10
- from .loop import LoopExpression
11
- from .path import Location
12
- from .path import Path
13
- from .path import Segments
14
- from .primitive import Identifier
15
- from .primitive import Literal
16
- from .primitive import Nil
17
- from .primitive import StringLiteral
18
- from .primitive import is_empty
19
- from .primitive import parse_identifier
20
- from .primitive import parse_name
21
- from .primitive import parse_primitive
22
- from .primitive import parse_string_or_path
23
-
24
- __all__ = (
25
- "BooleanExpression",
26
- "FilteredExpression",
27
- "Identifier",
28
- "is_empty",
29
- "KeywordArgument",
30
- "Literal",
31
- "Location",
32
- "LoopExpression",
33
- "Nil",
34
- "parse_arguments",
35
- "parse_identifier",
36
- "parse_primitive",
37
- "parse_string_or_path",
38
- "Path",
39
- "PositionalArgument",
40
- "Segments",
41
- "StringLiteral",
42
- "TernaryFilteredExpression",
43
- "tokenize",
44
- "parse_name",
45
- "Parameter",
46
- "Filter",
47
- )
@@ -1,186 +0,0 @@
1
- """Tokenize a liquid expression."""
2
-
3
- import re
4
- import sys
5
- from typing import Iterator
6
-
7
- from liquid.exceptions import LiquidSyntaxError
8
- from liquid.token import TOKEN_AND
9
- from liquid.token import TOKEN_AS
10
- from liquid.token import TOKEN_BLANK
11
- from liquid.token import TOKEN_COLON
12
- from liquid.token import TOKEN_COLS
13
- from liquid.token import TOKEN_COMMA
14
- from liquid.token import TOKEN_CONTAINS
15
- from liquid.token import TOKEN_CONTINUE
16
- from liquid.token import TOKEN_DOT
17
- from liquid.token import TOKEN_DPIPE
18
- from liquid.token import TOKEN_ELSE
19
- from liquid.token import TOKEN_EMPTY
20
- from liquid.token import TOKEN_FALSE
21
- from liquid.token import TOKEN_FLOAT
22
- from liquid.token import TOKEN_FOR
23
- from liquid.token import TOKEN_IDENTINDEX
24
- from liquid.token import TOKEN_IDENTSTRING
25
- from liquid.token import TOKEN_IF
26
- from liquid.token import TOKEN_ILLEGAL
27
- from liquid.token import TOKEN_IN
28
- from liquid.token import TOKEN_INTEGER
29
- from liquid.token import TOKEN_LBRACKET
30
- from liquid.token import TOKEN_LIMIT
31
- from liquid.token import TOKEN_LPAREN
32
- from liquid.token import TOKEN_NIL
33
- from liquid.token import TOKEN_NOT
34
- from liquid.token import TOKEN_NULL
35
- from liquid.token import TOKEN_OFFSET
36
- from liquid.token import TOKEN_OR
37
- from liquid.token import TOKEN_PIPE
38
- from liquid.token import TOKEN_RANGE
39
- from liquid.token import TOKEN_RANGE_LITERAL
40
- from liquid.token import TOKEN_RBRACKET
41
- from liquid.token import TOKEN_REQUIRED
42
- from liquid.token import TOKEN_REVERSED
43
- from liquid.token import TOKEN_RPAREN
44
- from liquid.token import TOKEN_SKIP
45
- from liquid.token import TOKEN_STRING
46
- from liquid.token import TOKEN_TRUE
47
- from liquid.token import TOKEN_WITH
48
- from liquid.token import TOKEN_WORD
49
- from liquid.token import Token
50
- from liquid.token import operators
51
-
52
- GROUP_QUOTE = sys.intern("quote")
53
- GROUP_QUOTED = sys.intern("quoted")
54
- GROUP_IDENTINDEX = sys.intern("path_index")
55
- GROUP_IDENTQUOTE = sys.intern("identquote")
56
- GROUP_IDENTQUOTED = sys.intern("identquoted")
57
-
58
- # The reference implementation will accept identifiers that start with an integer or
59
- # hyphen when in lax mode, but will raise a syntax error in strict mode. For historic
60
- # reasons we'll stick the following pattern until at least version 2 of Python Liquid.
61
- IDENTIFIER_PATTERN = r"\w[\w\-]*\??"
62
-
63
- # Trailing ? is not allowed in assignment names
64
- ASSIGN_IDENTIFIER_PATTERN = r"\w[\w\-]*"
65
-
66
- # ["ident"] or ['ident']
67
- IDENTSTRING_PATTERN = (
68
- rf"\[\s*(?P<{GROUP_IDENTQUOTE}>[\"'])"
69
- rf"(?P<{GROUP_IDENTQUOTED}>.*?)"
70
- rf"(?P={GROUP_IDENTQUOTE})\s*]"
71
- )
72
-
73
- # [0] or [-1]
74
- IDENTINDEX_PATTERN = rf"\[\s*(?P<{GROUP_IDENTINDEX}>\-?\d+)\s*]"
75
-
76
- # 'something' or "something"
77
- STRING_PATTERN = rf"(?P<{GROUP_QUOTE}>[\"'])(?P<{GROUP_QUOTED}>.*?)(?P={GROUP_QUOTE})"
78
-
79
- # Rules for the standard boolean expression.
80
- # Does not support grouping with parentheses.
81
- _rules = (
82
- (TOKEN_RANGE_LITERAL, r"\((?=.+?\.\.)"),
83
- (TOKEN_IDENTINDEX, IDENTINDEX_PATTERN),
84
- (TOKEN_IDENTSTRING, IDENTSTRING_PATTERN),
85
- (TOKEN_STRING, STRING_PATTERN),
86
- (TOKEN_RANGE, r"\.\."),
87
- (TOKEN_FLOAT, r"-?\d+\.(?!\.)\d*"),
88
- (TOKEN_INTEGER, r"-?\d+\b"),
89
- (TOKEN_DOT, r"\."),
90
- (TOKEN_WORD, IDENTIFIER_PATTERN),
91
- (TOKEN_LPAREN, r"\("),
92
- (TOKEN_RPAREN, r"\)"),
93
- (TOKEN_LBRACKET, r"\["),
94
- (TOKEN_RBRACKET, r"]"),
95
- (TOKEN_COLON, r":"),
96
- (TOKEN_COMMA, r","),
97
- (TOKEN_DPIPE, r"\|\|"),
98
- (TOKEN_PIPE, r"\|"),
99
- ("OP", r"[!=<>]{1,2}"),
100
- (TOKEN_SKIP, r"[ \n\t\r]+"),
101
- (TOKEN_ILLEGAL, r"."),
102
- )
103
-
104
- # Keywords for the standard boolean expression.
105
- # Excludes `not`.
106
- _keywords = frozenset(
107
- [
108
- TOKEN_TRUE,
109
- TOKEN_FALSE,
110
- TOKEN_NIL,
111
- TOKEN_NULL,
112
- TOKEN_EMPTY,
113
- TOKEN_BLANK,
114
- TOKEN_AND,
115
- TOKEN_OR,
116
- TOKEN_CONTAINS,
117
- TOKEN_NOT,
118
- TOKEN_IN,
119
- TOKEN_OFFSET,
120
- TOKEN_LIMIT,
121
- TOKEN_REVERSED,
122
- TOKEN_COLS,
123
- TOKEN_CONTINUE,
124
- TOKEN_WITH,
125
- TOKEN_FOR,
126
- TOKEN_AS,
127
- TOKEN_IF,
128
- TOKEN_ELSE,
129
- TOKEN_REQUIRED,
130
- ]
131
- )
132
-
133
- _RE = re.compile(
134
- "|".join(f"(?P<{name}>{pattern})" for name, pattern in _rules),
135
- re.DOTALL,
136
- )
137
-
138
-
139
- def tokenize(source: str, parent_token: Token) -> Iterator[Token]:
140
- """Tokenize a liquid expression."""
141
- for match in _RE.finditer(source):
142
- kind = match.lastgroup
143
- assert kind is not None
144
-
145
- value = match.group()
146
-
147
- if kind == TOKEN_WORD and value in _keywords:
148
- kind = value
149
- elif kind == TOKEN_IDENTINDEX:
150
- value = match.group(GROUP_IDENTINDEX)
151
- elif kind == TOKEN_IDENTSTRING:
152
- value = match.group(GROUP_IDENTQUOTED)
153
- elif kind == TOKEN_STRING:
154
- value = match.group(GROUP_QUOTED)
155
- elif kind == "OP":
156
- try:
157
- kind = operators[value]
158
- except KeyError as err:
159
- raise LiquidSyntaxError(
160
- f"unknown operator {value!r}",
161
- token=Token(
162
- kind,
163
- value,
164
- start_index=parent_token.start_index + match.start(),
165
- source=parent_token.source,
166
- ),
167
- ) from err
168
- elif kind == TOKEN_SKIP:
169
- continue
170
- elif kind == TOKEN_ILLEGAL:
171
- raise LiquidSyntaxError(
172
- f"unexpected {value!r}",
173
- token=Token(
174
- kind,
175
- value,
176
- start_index=parent_token.start_index + match.start(),
177
- source=parent_token.source,
178
- ),
179
- )
180
-
181
- yield Token(
182
- kind,
183
- value,
184
- start_index=parent_token.start_index + match.start(),
185
- source=parent_token.source,
186
- )
@@ -1,207 +0,0 @@
1
- """Tag expression argument helpers."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import TYPE_CHECKING
6
-
7
- from liquid import Mode
8
- from liquid.exceptions import LiquidSyntaxError
9
- from liquid.token import TOKEN_ASSIGN
10
- from liquid.token import TOKEN_COLON
11
- from liquid.token import TOKEN_COMMA
12
- from liquid.token import TOKEN_EOF
13
- from liquid.token import TOKEN_WORD
14
-
15
- from .primitive import parse_primitive
16
-
17
- if TYPE_CHECKING:
18
- from liquid import Environment
19
- from liquid import RenderContext
20
- from liquid import Token
21
- from liquid import TokenStream
22
- from liquid.expression import Expression
23
-
24
-
25
- class KeywordArgument:
26
- """A name/value pair."""
27
-
28
- __slots__ = ("token", "name", "value")
29
-
30
- def __init__(self, token: Token, name: str, value: Expression) -> None:
31
- self.token = token
32
- self.name = name
33
- self.value = value
34
-
35
- def __str__(self) -> str:
36
- return f"{self.name}:{self.value}"
37
-
38
- def evaluate(self, context: RenderContext) -> tuple[str, object]:
39
- return (self.name, self.value.evaluate(context))
40
-
41
- async def evaluate_async(self, context: RenderContext) -> tuple[str, object]:
42
- return (self.name, await self.value.evaluate_async(context))
43
-
44
- @staticmethod
45
- def parse(env: Environment, tokens: TokenStream) -> list[KeywordArgument]:
46
- """Parse a keyword arguments from _tokens_."""
47
- argument_separators = (
48
- (TOKEN_COLON, TOKEN_ASSIGN) if env.keyword_assignment else (TOKEN_COLON,)
49
- )
50
-
51
- args: list[KeywordArgument] = []
52
-
53
- # Leading commas are OK
54
- if tokens.current.kind == TOKEN_COMMA:
55
- next(tokens)
56
-
57
- while True:
58
- token = next(tokens)
59
-
60
- if token.kind == TOKEN_COMMA:
61
- token = next(tokens)
62
-
63
- if token.kind == TOKEN_EOF:
64
- break
65
-
66
- if token.kind == TOKEN_WORD:
67
- tokens.eat_one_of(*argument_separators)
68
- value = parse_primitive(env, tokens)
69
- args.append(KeywordArgument(token, token.value, value))
70
- if env.mode == Mode.STRICT and tokens.current.kind == TOKEN_WORD:
71
- raise LiquidSyntaxError(
72
- "expected a comma separated list of arguments, "
73
- f"found {tokens.current.kind}",
74
- token=tokens.current,
75
- )
76
- else:
77
- raise LiquidSyntaxError(
78
- f"expected an argument name, found {token.kind}", token=token
79
- )
80
-
81
- return args
82
-
83
-
84
- class PositionalArgument:
85
- """Just a value."""
86
-
87
- __slots__ = (
88
- "token",
89
- "value",
90
- )
91
-
92
- def __init__(self, value: Expression) -> None:
93
- self.token = value.token
94
- self.value = value
95
-
96
- def __str__(self) -> str:
97
- return str(self.value)
98
-
99
- def evaluate(self, context: RenderContext) -> tuple[None, object]:
100
- return (None, self.value.evaluate(context))
101
-
102
- async def evaluate_async(self, context: RenderContext) -> tuple[None, object]:
103
- return (None, await self.value.evaluate_async(context))
104
-
105
- @staticmethod
106
- def parse(env: Environment, tokens: TokenStream) -> list[PositionalArgument]:
107
- """Parse a positional arguments from _tokens_."""
108
- args: list[PositionalArgument] = []
109
-
110
- while True:
111
- if tokens.current.kind == TOKEN_COMMA:
112
- next(tokens)
113
-
114
- if tokens.current.kind == TOKEN_EOF:
115
- break
116
-
117
- args.append(PositionalArgument(parse_primitive(env, tokens)))
118
-
119
- return args
120
-
121
-
122
- class Parameter:
123
- """A name, possibly with a default value."""
124
-
125
- __slots__ = ("token", "name", "value")
126
-
127
- def __init__(self, token: Token, name: str, value: Expression | None) -> None:
128
- self.token = token
129
- self.name = name
130
- self.value = value
131
-
132
- def __str__(self) -> str:
133
- return f"{self.name}:{self.value}" if self.value else self.name
134
-
135
- @staticmethod
136
- def parse(env: Environment, tokens: TokenStream) -> dict[str, Parameter]:
137
- """Parse macro tag parameters from _tokens_."""
138
- params: dict[str, Parameter] = {}
139
- argument_separators = (
140
- (TOKEN_COLON, TOKEN_ASSIGN) if env.keyword_assignment else (TOKEN_COLON,)
141
- )
142
-
143
- while True:
144
- token = next(tokens)
145
-
146
- if token.kind == TOKEN_COMMA:
147
- # Leading and/or trailing commas are OK.
148
- token = next(tokens)
149
-
150
- if token.kind == TOKEN_EOF:
151
- break
152
-
153
- if token.kind == TOKEN_WORD:
154
- if tokens.current.kind in argument_separators:
155
- # A parameter with a default value
156
- next(tokens) # Move past ":" or "="
157
- value = parse_primitive(env, tokens)
158
- params[token.value] = Parameter(token, token.value, value)
159
- else:
160
- params[token.value] = Parameter(token, token.value, None)
161
- else:
162
- raise LiquidSyntaxError(
163
- f"expected a parameter list, found {token.kind}",
164
- token=token,
165
- )
166
-
167
- return params
168
-
169
-
170
- def parse_arguments(
171
- env: Environment, tokens: TokenStream
172
- ) -> tuple[list[PositionalArgument], list[KeywordArgument]]:
173
- """Parse arguments from _tokens_ until end of input."""
174
- args: list[PositionalArgument] = []
175
- kwargs: list[KeywordArgument] = []
176
- argument_separators = (
177
- (TOKEN_COLON, TOKEN_ASSIGN) if env.keyword_assignment else (TOKEN_COLON,)
178
- )
179
-
180
- # Leading commas are OK
181
- if tokens.current.kind == TOKEN_COMMA:
182
- next(tokens)
183
-
184
- while True:
185
- token = tokens.current
186
-
187
- if token.kind == TOKEN_EOF:
188
- break
189
-
190
- if token.kind == TOKEN_WORD:
191
- if tokens.peek.kind in argument_separators:
192
- name_token = next(tokens)
193
- next(tokens) # = or :
194
- value = parse_primitive(env, tokens)
195
- kwargs.append(KeywordArgument(name_token, token.value, value))
196
- else:
197
- args.append(PositionalArgument(parse_primitive(env, tokens)))
198
- else:
199
- # A primitive as a positional argument
200
- args.append(PositionalArgument(parse_primitive(env, tokens)))
201
-
202
- if tokens.current.kind != TOKEN_COMMA:
203
- break
204
-
205
- tokens.eat(TOKEN_COMMA)
206
-
207
- return args, kwargs