python-liquid 2.3.0__py3-none-any.whl → 2.3.2__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/loop.py +7 -1
- liquid/builtin/expressions/primitive.py +1 -14
- liquid/builtin/filters/array.py +10 -2
- liquid/builtin/loaders/package_loader.py +26 -12
- {python_liquid-2.3.0.dist-info → python_liquid-2.3.2.dist-info}/METADATA +3 -2
- {python_liquid-2.3.0.dist-info → python_liquid-2.3.2.dist-info}/RECORD +9 -9
- {python_liquid-2.3.0.dist-info → python_liquid-2.3.2.dist-info}/WHEEL +1 -1
- {python_liquid-2.3.0.dist-info → python_liquid-2.3.2.dist-info}/licenses/LICENSE +0 -0
liquid/__init__.py
CHANGED
|
@@ -13,6 +13,7 @@ from typing import Union
|
|
|
13
13
|
from liquid import Mode
|
|
14
14
|
from liquid.exceptions import LiquidSyntaxError
|
|
15
15
|
from liquid.exceptions import LiquidTypeError
|
|
16
|
+
from liquid.exceptions import LiquidValueError
|
|
16
17
|
from liquid.expression import Expression
|
|
17
18
|
from liquid.limits import to_int
|
|
18
19
|
from liquid.token import TOKEN_ASSIGN
|
|
@@ -95,7 +96,12 @@ class LoopExpression(Expression):
|
|
|
95
96
|
if isinstance(obj, Mapping):
|
|
96
97
|
return iter(obj.items()), len(obj)
|
|
97
98
|
if isinstance(obj, range):
|
|
98
|
-
|
|
99
|
+
try:
|
|
100
|
+
return iter(obj), len(obj)
|
|
101
|
+
except OverflowError as err:
|
|
102
|
+
raise LiquidValueError(
|
|
103
|
+
"range literal length limit reached", token=None
|
|
104
|
+
) from err
|
|
99
105
|
if isinstance(obj, str) and not context.env.string_sequences:
|
|
100
106
|
return (iter([]), 0) if not obj else (iter([obj]), 1)
|
|
101
107
|
if isinstance(obj, Sequence):
|
|
@@ -39,12 +39,6 @@ if TYPE_CHECKING:
|
|
|
39
39
|
from liquid import TokenStream
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
# MAX_RANGE = (2**53) - 1
|
|
43
|
-
# MIN_RANGE = -(2**53) + 1
|
|
44
|
-
MAX_RANGE = 1024
|
|
45
|
-
MIN_RANGE = -1024
|
|
46
|
-
|
|
47
|
-
|
|
48
42
|
class Nil(Expression):
|
|
49
43
|
__slots__ = ()
|
|
50
44
|
|
|
@@ -255,9 +249,7 @@ class RangeLiteral(Expression):
|
|
|
255
249
|
if start > stop:
|
|
256
250
|
return range(0)
|
|
257
251
|
|
|
258
|
-
return range(
|
|
259
|
-
clamp(start, MIN_RANGE, MAX_RANGE), clamp(stop + 1, MIN_RANGE, MAX_RANGE)
|
|
260
|
-
)
|
|
252
|
+
return range(start, stop + 1)
|
|
261
253
|
|
|
262
254
|
def evaluate(self, context: RenderContext) -> range:
|
|
263
255
|
return self._make_range(
|
|
@@ -423,8 +415,3 @@ def parse_string_or_path(
|
|
|
423
415
|
def is_empty(obj: object) -> bool:
|
|
424
416
|
"""Return True if _obj_ is considered empty."""
|
|
425
417
|
return isinstance(obj, (list, dict, str)) and not obj
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
def clamp(n: int, min_value: int, max_value: int) -> int:
|
|
429
|
-
"""Clamp _n_ between _min_value_ and _max_value_."""
|
|
430
|
-
return max(min_value, min(n, max_value))
|
liquid/builtin/filters/array.py
CHANGED
|
@@ -59,13 +59,17 @@ def join(
|
|
|
59
59
|
return separator.join(_str_if_not(item) for item in left)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
@with_environment
|
|
62
63
|
@liquid_filter
|
|
63
|
-
def first(left: Any) -> object:
|
|
64
|
+
def first(left: Any, *, environment: Environment) -> object:
|
|
64
65
|
"""Return the first item in collection _left_.
|
|
65
66
|
|
|
66
67
|
If _left_ is not a collection or it is empty, `None` is returned.
|
|
67
68
|
"""
|
|
68
69
|
if isinstance(left, str):
|
|
70
|
+
# Honor the opt-in Ruby-compat flag, matching the `.first` property path.
|
|
71
|
+
if environment.string_first_and_last:
|
|
72
|
+
return left[0] if left else None
|
|
69
73
|
return None
|
|
70
74
|
|
|
71
75
|
if isinstance(left, dict):
|
|
@@ -77,13 +81,17 @@ def first(left: Any) -> object:
|
|
|
77
81
|
return None
|
|
78
82
|
|
|
79
83
|
|
|
84
|
+
@with_environment
|
|
80
85
|
@liquid_filter
|
|
81
|
-
def last(obj: Sequence[Any]) -> object:
|
|
86
|
+
def last(obj: Sequence[Any], *, environment: Environment) -> object:
|
|
82
87
|
"""Return the last item in collection _left_.
|
|
83
88
|
|
|
84
89
|
If _left_ is not a collection or it is empty, `None` is returned.
|
|
85
90
|
"""
|
|
86
91
|
if isinstance(obj, str):
|
|
92
|
+
# Honor the opt-in Ruby-compat flag, matching the `.last` property path.
|
|
93
|
+
if environment.string_first_and_last:
|
|
94
|
+
return obj[-1] if obj else None
|
|
87
95
|
return None
|
|
88
96
|
|
|
89
97
|
try:
|
|
@@ -4,19 +4,19 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import os
|
|
7
|
-
from importlib.resources import files
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from typing import TYPE_CHECKING
|
|
10
9
|
from typing import Iterable
|
|
11
10
|
from typing import Optional
|
|
12
11
|
from typing import Union
|
|
13
12
|
|
|
13
|
+
import importlib_resources
|
|
14
|
+
|
|
14
15
|
from liquid.exceptions import TemplateNotFoundError
|
|
15
16
|
from liquid.loader import BaseLoader
|
|
16
17
|
from liquid.loader import TemplateSource
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
|
-
from importlib.abc import Traversable
|
|
20
20
|
from types import ModuleType
|
|
21
21
|
|
|
22
22
|
from liquid import Environment
|
|
@@ -44,31 +44,45 @@ class PackageLoader(BaseLoader):
|
|
|
44
44
|
ext: str = ".liquid",
|
|
45
45
|
) -> None:
|
|
46
46
|
if isinstance(package_path, str):
|
|
47
|
-
self.paths = [files(package).joinpath(package_path)]
|
|
47
|
+
self.paths = [importlib_resources.files(package).joinpath(package_path)]
|
|
48
48
|
else:
|
|
49
|
-
_package = files(package)
|
|
49
|
+
_package = importlib_resources.files(package)
|
|
50
50
|
self.paths = [_package.joinpath(path) for path in package_path]
|
|
51
51
|
|
|
52
52
|
self.encoding = encoding
|
|
53
53
|
self.ext = ext
|
|
54
54
|
|
|
55
|
-
def _resolve_path(self, template_name: str) ->
|
|
55
|
+
def _resolve_path(self, template_name: str) -> Path:
|
|
56
56
|
template_path = Path(template_name)
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
# Does ".." appear in template_name?
|
|
60
|
-
if os.path.pardir in template_path.parts:
|
|
58
|
+
if not template_path.name:
|
|
61
59
|
raise TemplateNotFoundError(template_name)
|
|
62
60
|
|
|
63
61
|
# Add suffix self.ext if template name does not have a suffix.
|
|
64
62
|
if not template_path.suffix:
|
|
65
63
|
template_path = template_path.with_suffix(self.ext)
|
|
66
64
|
|
|
65
|
+
if os.path.pardir in template_path.parts or template_path.is_absolute():
|
|
66
|
+
raise TemplateNotFoundError(template_name)
|
|
67
|
+
|
|
67
68
|
for path in self.paths:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
with importlib_resources.as_file(path) as base:
|
|
70
|
+
source_path: Path = base.joinpath(template_path)
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
if not source_path.exists() or not source_path.is_file():
|
|
74
|
+
continue
|
|
75
|
+
except OSError:
|
|
76
|
+
# "File name too long", for example
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
resolved = source_path.resolve(strict=False)
|
|
80
|
+
base_resolved = base.resolve(strict=False)
|
|
81
|
+
|
|
82
|
+
if not resolved.is_relative_to(base_resolved):
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
return source_path
|
|
72
86
|
|
|
73
87
|
raise TemplateNotFoundError(template_name)
|
|
74
88
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: python-liquid
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
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/
|
|
@@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
24
24
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
25
25
|
Requires-Python: >=3.9
|
|
26
26
|
Requires-Dist: babel>=2
|
|
27
|
+
Requires-Dist: importlib-resources>=5.12
|
|
27
28
|
Requires-Dist: markupsafe>=3
|
|
28
29
|
Requires-Dist: python-dateutil>=2.8.1
|
|
29
30
|
Requires-Dist: pytz
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
liquid/__init__.py,sha256=
|
|
1
|
+
liquid/__init__.py,sha256=qJCQnUp4SFeIjlrsTo6I07bqqVUFptfOj9xbwHXb3nQ,7696
|
|
2
2
|
liquid/analyze_tags.py,sha256=Uc1nueKLRiIejs2JyQIC7u2pxp9l-5HJAMWlg-Qj1m8,7615
|
|
3
3
|
liquid/ast.py,sha256=4ZqSbuW4mv9IvoIMPk3OICUaOI1vPh8P8uFCrXI_3XA,8140
|
|
4
4
|
liquid/context.py,sha256=fEuMsj9MVguX3Kzy9_k99TWekOwAToL0RtbGm9yZ71o,21718
|
|
@@ -33,11 +33,11 @@ liquid/builtin/expressions/_tokenize.py,sha256=p7RbeOg8iwXDnW0fIL4_7FSrBulkPYrCm
|
|
|
33
33
|
liquid/builtin/expressions/arguments.py,sha256=hlZh7LLFT5UismTo7mTlVjM87_ihUJo0BYuCxyTwKMI,6448
|
|
34
34
|
liquid/builtin/expressions/filtered.py,sha256=FqPNZLjwDdlCfivtBa7e0wuQYwcJdKUl3_UY9AS3zG4,13073
|
|
35
35
|
liquid/builtin/expressions/logical.py,sha256=S8T9IgsuWgv0s7GYcGvIJFOqYMiMFRpat7zF3nCZHR0,20388
|
|
36
|
-
liquid/builtin/expressions/loop.py,sha256=
|
|
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=Gzaeb-dF84zsMn5ZYhe-FOosnSbkkM2C6JLqjLvK5ns,11352
|
|
39
39
|
liquid/builtin/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
-
liquid/builtin/filters/array.py,sha256=
|
|
40
|
+
liquid/builtin/filters/array.py,sha256=jy35hoouAWI1yG2JX5OMG39Az2dmP--4IcuwqT59JMY,10281
|
|
41
41
|
liquid/builtin/filters/extra.py,sha256=m13_1nbZyRBl3CZV9WWGLlUZueRPt8cKPEHRAioqM_o,2710
|
|
42
42
|
liquid/builtin/filters/math.py,sha256=A_K2wg5gxm5Ncj9zMGDX_Lt7jjx4bRr3CywbCiCtqxo,3925
|
|
43
43
|
liquid/builtin/filters/misc.py,sha256=t2ZsaDLKRc1-8XxZaiEOh7oAMqFJ4N5-tzfbpLHbmIQ,3466
|
|
@@ -48,7 +48,7 @@ liquid/builtin/loaders/choice_loader.py,sha256=FE23RLMOiWmsRGZv9t6QmlG7bgMEUSbln
|
|
|
48
48
|
liquid/builtin/loaders/dict_loader.py,sha256=8TdMJqaYPLwllmIiEmuZNXqH6UOOsi4HuJ2t5sztOOw,1785
|
|
49
49
|
liquid/builtin/loaders/file_system_loader.py,sha256=kKPUmznPgNFq3fRDeoOTbWjNwxuLrYPAteZ_Do1-Mks,4924
|
|
50
50
|
liquid/builtin/loaders/mixins.py,sha256=4Hj5wWGMOi3LqA9ZJYsUb4LFbG0a5DDb9H975G8nn58,5209
|
|
51
|
-
liquid/builtin/loaders/package_loader.py,sha256=
|
|
51
|
+
liquid/builtin/loaders/package_loader.py,sha256=PNg-ygoRjfiALFu3bzEDRdps30rtsTIkt0voJKs9zCo,4061
|
|
52
52
|
liquid/builtin/tags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
53
|
liquid/builtin/tags/assign_tag.py,sha256=VTfkPbF-i0KLqMmaacBItFQHaBTP7ECWR-RvVh9Q6lY,2374
|
|
54
54
|
liquid/builtin/tags/capture_tag.py,sha256=7bOG7qVwcqRx1eStb96AZKQ9tKLBjmzaYAe27LJTIm4,3037
|
|
@@ -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.2.dist-info/METADATA,sha256=FpoUkgvAUbCL5Xg2He4xOmqVvCU1E_xvZpPn8ci9FYw,6786
|
|
94
|
+
python_liquid-2.3.2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
95
|
+
python_liquid-2.3.2.dist-info/licenses/LICENSE,sha256=yAFURzud5ERNHt1rZIPnTLJ92ep7q8y5yG9g5DUMR_E,1075
|
|
96
|
+
python_liquid-2.3.2.dist-info/RECORD,,
|
|
File without changes
|