python-liquid 2.3.2__py3-none-any.whl → 2.3.4__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/__init__.py +1 -1
- liquid/builtin/expressions/logical.py +37 -7
- liquid/builtin/expressions/primitive.py +2 -2
- liquid/builtin/tags/tablerow_tag.py +5 -1
- liquid/context.py +11 -7
- liquid/lex.py +11 -3
- {python_liquid-2.3.2.dist-info → python_liquid-2.3.4.dist-info}/METADATA +1 -1
- {python_liquid-2.3.2.dist-info → python_liquid-2.3.4.dist-info}/RECORD +10 -10
- {python_liquid-2.3.2.dist-info → python_liquid-2.3.4.dist-info}/WHEEL +0 -0
- {python_liquid-2.3.2.dist-info → python_liquid-2.3.4.dist-info}/licenses/LICENSE +0 -0
liquid/__init__.py
CHANGED
|
@@ -310,12 +310,12 @@ class LeExpression(Expression):
|
|
|
310
310
|
def evaluate(self, context: RenderContext) -> object:
|
|
311
311
|
left = self.left.evaluate(context)
|
|
312
312
|
right = self.right.evaluate(context)
|
|
313
|
-
return
|
|
313
|
+
return _le(left, right)
|
|
314
314
|
|
|
315
315
|
async def evaluate_async(self, context: RenderContext) -> object:
|
|
316
316
|
left = await self.left.evaluate_async(context)
|
|
317
317
|
right = await self.right.evaluate_async(context)
|
|
318
|
-
return
|
|
318
|
+
return _le(left, right)
|
|
319
319
|
|
|
320
320
|
def children(self) -> list[Expression]:
|
|
321
321
|
return [self.left, self.right]
|
|
@@ -335,12 +335,12 @@ class GeExpression(Expression):
|
|
|
335
335
|
def evaluate(self, context: RenderContext) -> object:
|
|
336
336
|
left = self.left.evaluate(context)
|
|
337
337
|
right = self.right.evaluate(context)
|
|
338
|
-
return
|
|
338
|
+
return _le(right, left)
|
|
339
339
|
|
|
340
340
|
async def evaluate_async(self, context: RenderContext) -> object:
|
|
341
341
|
left = await self.left.evaluate_async(context)
|
|
342
342
|
right = await self.right.evaluate_async(context)
|
|
343
|
-
return
|
|
343
|
+
return _le(right, left)
|
|
344
344
|
|
|
345
345
|
def children(self) -> list[Expression]:
|
|
346
346
|
return [self.left, self.right]
|
|
@@ -583,6 +583,12 @@ def is_truthy(obj: object) -> bool:
|
|
|
583
583
|
|
|
584
584
|
|
|
585
585
|
def _eq(left: object, right: object) -> bool:
|
|
586
|
+
if is_undefined(left):
|
|
587
|
+
return right is None or is_undefined(right)
|
|
588
|
+
|
|
589
|
+
if is_undefined(right):
|
|
590
|
+
return left is None or is_undefined(left)
|
|
591
|
+
|
|
586
592
|
if hasattr(left, "__liquid__"):
|
|
587
593
|
left = left.__liquid__()
|
|
588
594
|
|
|
@@ -612,14 +618,14 @@ def _lt(token: Token, left: object, right: object) -> bool:
|
|
|
612
618
|
if isinstance(left, str) and isinstance(right, str):
|
|
613
619
|
return left < right
|
|
614
620
|
|
|
615
|
-
if isinstance(left, bool) or isinstance(right, bool):
|
|
616
|
-
return False
|
|
617
|
-
|
|
618
621
|
if isinstance(left, (int, float, Decimal)) and isinstance(
|
|
619
622
|
right, (int, float, Decimal)
|
|
620
623
|
):
|
|
621
624
|
return left < right
|
|
622
625
|
|
|
626
|
+
if left is None or right is None:
|
|
627
|
+
return False
|
|
628
|
+
|
|
623
629
|
raise LiquidTypeError(
|
|
624
630
|
f"'<' and '>' are not supported between '{left.__class__.__name__}' "
|
|
625
631
|
f"and '{right.__class__.__name__}'",
|
|
@@ -627,6 +633,30 @@ def _lt(token: Token, left: object, right: object) -> bool:
|
|
|
627
633
|
)
|
|
628
634
|
|
|
629
635
|
|
|
636
|
+
def _le(left: object, right: object) -> bool:
|
|
637
|
+
if hasattr(left, "__liquid__"):
|
|
638
|
+
left = left.__liquid__()
|
|
639
|
+
|
|
640
|
+
if hasattr(right, "__liquid__"):
|
|
641
|
+
right = right.__liquid__()
|
|
642
|
+
|
|
643
|
+
if isinstance(left, str) and isinstance(right, str):
|
|
644
|
+
return left <= right
|
|
645
|
+
|
|
646
|
+
if isinstance(left, bool) or isinstance(right, bool):
|
|
647
|
+
return False
|
|
648
|
+
|
|
649
|
+
if isinstance(left, (int, float, Decimal)) and isinstance(
|
|
650
|
+
right, (int, float, Decimal)
|
|
651
|
+
):
|
|
652
|
+
return left <= right
|
|
653
|
+
|
|
654
|
+
if left is None or right is None:
|
|
655
|
+
return False
|
|
656
|
+
|
|
657
|
+
return _eq(left, right)
|
|
658
|
+
|
|
659
|
+
|
|
630
660
|
def _contains(token: Token, left: object, right: object) -> bool:
|
|
631
661
|
if not is_truthy(left) or not is_truthy(right):
|
|
632
662
|
return False
|
|
@@ -237,12 +237,12 @@ class RangeLiteral(Expression):
|
|
|
237
237
|
def _make_range(self, start: Any, stop: Any) -> range:
|
|
238
238
|
try:
|
|
239
239
|
start = to_int(start)
|
|
240
|
-
except ValueError:
|
|
240
|
+
except (ValueError, TypeError):
|
|
241
241
|
start = 0
|
|
242
242
|
|
|
243
243
|
try:
|
|
244
244
|
stop = to_int(stop)
|
|
245
|
-
except ValueError:
|
|
245
|
+
except (ValueError, TypeError):
|
|
246
246
|
stop = 0
|
|
247
247
|
|
|
248
248
|
# Descending ranges don't work
|
|
@@ -189,6 +189,10 @@ class TablerowNode(Node):
|
|
|
189
189
|
name = self.expression.identifier
|
|
190
190
|
loop_iter, length = self.expression.evaluate(context)
|
|
191
191
|
|
|
192
|
+
if not length:
|
|
193
|
+
# An empty iterable renders no rows.
|
|
194
|
+
return True
|
|
195
|
+
|
|
192
196
|
if self.expression.cols:
|
|
193
197
|
cols = self._int_or_zero(self.expression.cols.evaluate(context))
|
|
194
198
|
else:
|
|
@@ -204,7 +208,7 @@ class TablerowNode(Node):
|
|
|
204
208
|
buffer.write('<tr class="row1">\n')
|
|
205
209
|
_break = False
|
|
206
210
|
|
|
207
|
-
with context.
|
|
211
|
+
with context.loop(namespace, tablerow):
|
|
208
212
|
for item in tablerow:
|
|
209
213
|
namespace[name] = item
|
|
210
214
|
buffer.write(f'<td class="col{tablerow.col}">')
|
liquid/context.py
CHANGED
|
@@ -39,6 +39,7 @@ from .utils import ReadOnlyChainMap
|
|
|
39
39
|
|
|
40
40
|
if TYPE_CHECKING:
|
|
41
41
|
from .builtin.tags.for_tag import ForLoop
|
|
42
|
+
from .builtin.tags.tablerow_tag import TableRow
|
|
42
43
|
from .environment import Environment
|
|
43
44
|
from .template import BoundTemplate
|
|
44
45
|
from .token import Token
|
|
@@ -108,8 +109,10 @@ class RenderContext:
|
|
|
108
109
|
"macros": {},
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
# As stack of
|
|
112
|
-
|
|
112
|
+
# As stack of loop objects. Used for populating forloop.parentloop and
|
|
113
|
+
# calculating the effective loop count for the loop iteration resource
|
|
114
|
+
# limit.
|
|
115
|
+
self.loops: list[Union[ForLoop, TableRow]] = []
|
|
113
116
|
|
|
114
117
|
# A list of tags names that are disallowed in this context. For example,
|
|
115
118
|
# partial templates rendered using the "render" tag are not allowed to
|
|
@@ -368,7 +371,7 @@ class RenderContext:
|
|
|
368
371
|
|
|
369
372
|
@contextmanager
|
|
370
373
|
def loop(
|
|
371
|
-
self, namespace: Mapping[str, object], forloop: ForLoop
|
|
374
|
+
self, namespace: Mapping[str, object], forloop: ForLoop | TableRow
|
|
372
375
|
) -> Iterator[RenderContext]:
|
|
373
376
|
"""Just like `Context.extend`, but keeps track of ForLoop objects too."""
|
|
374
377
|
self.raise_for_loop_limit(forloop.length)
|
|
@@ -381,10 +384,11 @@ class RenderContext:
|
|
|
381
384
|
|
|
382
385
|
def parentloop(self) -> Union[Undefined, object]:
|
|
383
386
|
"""Return the last ForLoop object from the loop stack."""
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
387
|
+
for loop in reversed(self.loops):
|
|
388
|
+
if hasattr(loop, "parentloop"):
|
|
389
|
+
return loop
|
|
390
|
+
|
|
391
|
+
return self.env.undefined("parentloop", token=None)
|
|
388
392
|
|
|
389
393
|
def raise_for_loop_limit(self, length: int = 1) -> None:
|
|
390
394
|
"""Raise a `LoopIterationLimitError` if loop stack is bigger than the limit."""
|
liquid/lex.py
CHANGED
|
@@ -54,14 +54,21 @@ def compile_liquid_rules(
|
|
|
54
54
|
rf"{tag_s}-?\s*enddoc\s*(?P<rsd>-?){tag_e}"
|
|
55
55
|
)
|
|
56
56
|
|
|
57
|
-
output_pattern =
|
|
57
|
+
output_pattern = (
|
|
58
|
+
rf"{stmt_s}-?\s*"
|
|
59
|
+
rf"(?P<stmt>(?:(?!-?{stmt_e})[\s\S])*(?<!\s))\s*"
|
|
60
|
+
rf"(?P<rss>-?){stmt_e}"
|
|
61
|
+
)
|
|
58
62
|
|
|
59
63
|
# The "name" group is zero or more characters so that a malformed tag (one
|
|
60
64
|
# with no name) does not get treated as a literal.
|
|
61
65
|
#
|
|
62
66
|
# The `#` in the `name` group is specifically for the inline comment tag.
|
|
63
67
|
tag_pattern = (
|
|
64
|
-
rf"{tag_s}
|
|
68
|
+
rf"{tag_s}-?\s*"
|
|
69
|
+
rf"(?P<name>#|\w+)\s*"
|
|
70
|
+
rf"(?P<expr>(?:(?!-?{tag_e})[\s\S])*(?<!\s))\s*"
|
|
71
|
+
rf"(?P<rst>-?){tag_e}"
|
|
65
72
|
)
|
|
66
73
|
|
|
67
74
|
if not comment_start_string:
|
|
@@ -157,6 +164,7 @@ def _tokenize_template(source: str, rules: Pattern[str]) -> Iterator[Token]: #
|
|
|
157
164
|
|
|
158
165
|
lstrip = bool(match.group("rss"))
|
|
159
166
|
continue
|
|
167
|
+
|
|
160
168
|
elif kind == "TAG":
|
|
161
169
|
name = match.group("name")
|
|
162
170
|
yield Token(
|
|
@@ -219,7 +227,7 @@ def _tokenize_template(source: str, rules: Pattern[str]) -> Iterator[Token]: #
|
|
|
219
227
|
)
|
|
220
228
|
if value.startswith(r"{%"):
|
|
221
229
|
raise LiquidSyntaxError(
|
|
222
|
-
"
|
|
230
|
+
"missing tag name or closing delimiter",
|
|
223
231
|
token=Token(
|
|
224
232
|
TOKEN_EOF,
|
|
225
233
|
value=match.group(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-liquid
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.4
|
|
4
4
|
Summary: A Python engine for the Liquid template language.
|
|
5
5
|
Project-URL: Change Log, https://github.com/jg-rp/liquid/blob/main/CHANGES.md
|
|
6
6
|
Project-URL: Documentation, https://jg-rp.github.io/liquid/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
liquid/__init__.py,sha256=
|
|
1
|
+
liquid/__init__.py,sha256=8rD4h9wH94Wl88LcsiX_WhuqBWdF_iJdHBhlAv6fjWo,7696
|
|
2
2
|
liquid/analyze_tags.py,sha256=Uc1nueKLRiIejs2JyQIC7u2pxp9l-5HJAMWlg-Qj1m8,7615
|
|
3
3
|
liquid/ast.py,sha256=4ZqSbuW4mv9IvoIMPk3OICUaOI1vPh8P8uFCrXI_3XA,8140
|
|
4
|
-
liquid/context.py,sha256=
|
|
4
|
+
liquid/context.py,sha256=GYMG5-u3JI3w1jN_osEtyNsjziWgjBBMFZTilJEMap0,21931
|
|
5
5
|
liquid/environment.py,sha256=7lq5Y6jAaN6dzWMbqXYnUcSBuOke768xoPidP-WK2Y4,24157
|
|
6
6
|
liquid/exceptions.py,sha256=NNEWqVdY2QCjCvTtfqnV0_b1C0jL96IHOn6NlnabBXY,8451
|
|
7
7
|
liquid/expression.py,sha256=Ozgajah2R5eg1yWA4ITFGaMFoOLt8GIjkE1NKqbBEzk,1143
|
|
8
8
|
liquid/filter.py,sha256=3NtqGk6nHNwe6ux3X6ZIzzcEMJNA_SaMrBmMQA8hf-I,6735
|
|
9
|
-
liquid/lex.py,sha256=
|
|
9
|
+
liquid/lex.py,sha256=vM8mU7OnqH_nLlvnT0S3GJT2VxdnXjRXr_TRkp2y2ZE,8139
|
|
10
10
|
liquid/limits.py,sha256=BxqiC4Ax5iadmh6jDRcUNjFfOvbMkvZ1a046R1Cb8UA,1700
|
|
11
11
|
liquid/loader.py,sha256=R_RniSRszCXmRVZgXlu9WjV130cWvspVcC3vIVpf-qg,4381
|
|
12
12
|
liquid/messages.py,sha256=r6OcJG4-dLEULc9JtQ-mh3Jf6nNZ9Aa2reun7zYiUyc,11861
|
|
@@ -32,10 +32,10 @@ liquid/builtin/expressions/__init__.py,sha256=5scdgzOrMK9SxKpab3ebJnfO56Dl4bYLXO
|
|
|
32
32
|
liquid/builtin/expressions/_tokenize.py,sha256=p7RbeOg8iwXDnW0fIL4_7FSrBulkPYrCm3ialpEIsDE,5737
|
|
33
33
|
liquid/builtin/expressions/arguments.py,sha256=hlZh7LLFT5UismTo7mTlVjM87_ihUJo0BYuCxyTwKMI,6448
|
|
34
34
|
liquid/builtin/expressions/filtered.py,sha256=FqPNZLjwDdlCfivtBa7e0wuQYwcJdKUl3_UY9AS3zG4,13073
|
|
35
|
-
liquid/builtin/expressions/logical.py,sha256=
|
|
35
|
+
liquid/builtin/expressions/logical.py,sha256=6yCJjUOPvJ_ZE7xL6iDtc3-7bivoUVdGHYkUOp0WUgk,20982
|
|
36
36
|
liquid/builtin/expressions/loop.py,sha256=F2mBD9_bXtarzmEKstak_u50cXF9ijlKs9Ip2Q_BbsQ,8839
|
|
37
37
|
liquid/builtin/expressions/path.py,sha256=m1J33uIuEFlUaRXR_-G6qcsClOm0B5jbTZ0Pdt1P7Ak,5703
|
|
38
|
-
liquid/builtin/expressions/primitive.py,sha256=
|
|
38
|
+
liquid/builtin/expressions/primitive.py,sha256=MTREfbutdN-N0gDYrWrtP8X2cEUFUW3rs6nJmqv37rU,11378
|
|
39
39
|
liquid/builtin/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
40
|
liquid/builtin/filters/array.py,sha256=jy35hoouAWI1yG2JX5OMG39Az2dmP--4IcuwqT59JMY,10281
|
|
41
41
|
liquid/builtin/filters/extra.py,sha256=m13_1nbZyRBl3CZV9WWGLlUZueRPt8cKPEHRAioqM_o,2710
|
|
@@ -66,7 +66,7 @@ liquid/builtin/tags/increment_tag.py,sha256=SV2nrbE90l4iz36WCROqGE0RkkzztWUpU1_P
|
|
|
66
66
|
liquid/builtin/tags/inline_comment_tag.py,sha256=hLLtvaI-VpJxzFeoLTwDTj46g13A1ke6blqVo2MP0Cs,1357
|
|
67
67
|
liquid/builtin/tags/liquid_tag.py,sha256=o3ybbBmjIY94y_PpOUbgSwktENrGJ-jhZ124-rTlM4Q,5524
|
|
68
68
|
liquid/builtin/tags/render_tag.py,sha256=iYWjKYQBmGHQVQ9OZgPZvVhraygFdmPzITin7cKJI5E,14309
|
|
69
|
-
liquid/builtin/tags/tablerow_tag.py,sha256=
|
|
69
|
+
liquid/builtin/tags/tablerow_tag.py,sha256=IFG3G8e2zn94H-qFtlCaqYK46msNybp3LqbSlNIsI84,9052
|
|
70
70
|
liquid/builtin/tags/unless_tag.py,sha256=udlHvVxILJsYfDaWRkj6Ynmnz2-2FF5RfkP0eizXt7k,6932
|
|
71
71
|
liquid/extra/__init__.py,sha256=KrE9DAkwCAOLIaBHt73pnL6sb1tN5-UEKoQsX1b43Gg,2886
|
|
72
72
|
liquid/extra/filters/__init__.py,sha256=tsEfSmaJvd6xN8DfKISPOm_KcHOmBVc4RSQqViD8NvY,733
|
|
@@ -90,7 +90,7 @@ liquid/utils/chain_map.py,sha256=nxkw3wwF6ddlGarIuL7Ii2elm4dU80LySgdQx1oift0,151
|
|
|
90
90
|
liquid/utils/html.py,sha256=TmqOOpRMsy7fqZLj7X5ybd_XQnWW2YDAVDwTP1Gwf40,1706
|
|
91
91
|
liquid/utils/lru_cache.py,sha256=p7bXOGaUwJpLsREI2lGSzK6lLna5W_k_zNXKWnPJdos,4022
|
|
92
92
|
liquid/utils/text.py,sha256=1SwDECNMaqnnZ05je_AZZgxqzZd6U-mvq5jNU3W1-Qk,841
|
|
93
|
-
python_liquid-2.3.
|
|
94
|
-
python_liquid-2.3.
|
|
95
|
-
python_liquid-2.3.
|
|
96
|
-
python_liquid-2.3.
|
|
93
|
+
python_liquid-2.3.4.dist-info/METADATA,sha256=N9SfO0QdDYBd8wWCXbjtXEJORyxxk_yXUOOb3B-wP3M,6786
|
|
94
|
+
python_liquid-2.3.4.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
95
|
+
python_liquid-2.3.4.dist-info/licenses/LICENSE,sha256=yAFURzud5ERNHt1rZIPnTLJ92ep7q8y5yG9g5DUMR_E,1075
|
|
96
|
+
python_liquid-2.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|