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.
- liquid/__about__.py +1 -0
- liquid/__init__.py +161 -187
- liquid/_base_tag.py +66 -0
- liquid/{utils/chain_map.py → _chain_map.py} +18 -19
- liquid/_context.py +465 -0
- liquid/_drop.py +108 -0
- liquid/_filter.py +174 -0
- liquid/_io.py +18 -0
- liquid/_lax_parser_mixin.py +100 -0
- liquid/_lexer.py +93 -0
- liquid/_lexer_extended.py +623 -0
- liquid/_lexer_legacy.py +536 -0
- liquid/_loader.py +84 -0
- liquid/{utils/lru_cache.py → _lru_cache.py} +28 -36
- liquid/_markup.py +253 -0
- liquid/_messages.py +292 -0
- liquid/_missing.py +3 -0
- liquid/_nothing.py +28 -0
- liquid/_parser.py +460 -0
- liquid/_parser_extended.py +293 -0
- liquid/_parser_legacy.py +819 -0
- liquid/_resource_limits.py +50 -0
- liquid/_slice.py +41 -0
- liquid/_static_analysis.py +503 -0
- liquid/_strftime.py +186 -0
- liquid/_template.py +536 -0
- liquid/drops/__init__.py +20 -0
- liquid/drops/forloop.py +78 -0
- liquid/drops/iterable.py +25 -0
- liquid/drops/range.py +76 -0
- liquid/drops/self_.py +45 -0
- liquid/drops/tablerowloop.py +142 -0
- liquid/drops/undefined.py +228 -0
- liquid/environment.py +717 -551
- liquid/exceptions.py +67 -214
- liquid/expressions.py +1184 -0
- liquid/filters/__init__.py +134 -0
- liquid/filters/abs.py +7 -0
- liquid/filters/append.py +5 -0
- liquid/filters/at_least.py +9 -0
- liquid/filters/at_most.py +9 -0
- liquid/filters/babel.py +560 -0
- liquid/filters/base64.py +26 -0
- liquid/filters/capitalize.py +5 -0
- liquid/filters/ceil.py +8 -0
- liquid/filters/compact.py +25 -0
- liquid/filters/concat.py +11 -0
- liquid/filters/date.py +27 -0
- liquid/filters/default.py +25 -0
- liquid/filters/divided_by.py +21 -0
- liquid/filters/downcase.py +5 -0
- liquid/filters/escape.py +56 -0
- liquid/filters/find.py +56 -0
- liquid/filters/first.py +19 -0
- liquid/filters/floor.py +8 -0
- liquid/filters/html.py +23 -0
- liquid/filters/index.py +12 -0
- liquid/filters/join.py +13 -0
- liquid/filters/json_.py +30 -0
- liquid/filters/last.py +17 -0
- liquid/filters/map.py +5 -0
- liquid/filters/minus.py +13 -0
- liquid/filters/modulo.py +18 -0
- liquid/filters/newline_to_br.py +16 -0
- liquid/filters/plus.py +13 -0
- liquid/filters/prepend.py +5 -0
- liquid/filters/reject.py +19 -0
- liquid/filters/remove.py +36 -0
- liquid/filters/replace.py +32 -0
- liquid/filters/reverse.py +5 -0
- liquid/filters/round.py +35 -0
- liquid/filters/size.py +25 -0
- liquid/filters/slice.py +39 -0
- liquid/filters/sort.py +148 -0
- liquid/filters/split.py +34 -0
- liquid/filters/squish.py +9 -0
- liquid/filters/strip.py +13 -0
- liquid/filters/strip_html.py +19 -0
- liquid/filters/strip_newlines.py +17 -0
- liquid/filters/sum.py +21 -0
- liquid/filters/times.py +13 -0
- liquid/filters/translate.py +380 -0
- liquid/filters/truncate.py +39 -0
- liquid/filters/uniq.py +11 -0
- liquid/filters/upcase.py +5 -0
- liquid/filters/url.py +18 -0
- liquid/filters/where.py +18 -0
- liquid/loaders/__init__.py +17 -0
- liquid/loaders/caching_file_system_loader.py +64 -0
- liquid/loaders/choice_loader.py +93 -0
- liquid/loaders/dict_loader.py +56 -0
- liquid/{builtin/loaders → loaders}/file_system_loader.py +51 -55
- liquid/{builtin/loaders → loaders}/mixins.py +39 -51
- liquid/loaders/package_loader.py +110 -0
- liquid/tags/__init__.py +61 -0
- liquid/tags/assign.py +68 -0
- liquid/tags/capture.py +83 -0
- liquid/tags/case.py +199 -0
- liquid/tags/comment.py +56 -0
- liquid/tags/cycle.py +131 -0
- liquid/tags/decrement.py +55 -0
- liquid/tags/doc.py +56 -0
- liquid/tags/echo.py +65 -0
- liquid/tags/else_.py +53 -0
- liquid/tags/error.py +51 -0
- liquid/tags/extends.py +448 -0
- liquid/tags/for_.py +421 -0
- liquid/tags/if_.py +156 -0
- liquid/tags/ifchanged.py +69 -0
- liquid/tags/include.py +223 -0
- liquid/tags/increment.py +55 -0
- liquid/tags/inline_comment.py +70 -0
- liquid/tags/liquid.py +56 -0
- liquid/tags/macro.py +281 -0
- liquid/tags/output.py +58 -0
- liquid/tags/raw.py +60 -0
- liquid/tags/render.py +245 -0
- liquid/tags/tablerow.py +254 -0
- liquid/tags/template_comment.py +56 -0
- liquid/tags/translate.py +316 -0
- liquid/tags/unless.py +121 -0
- liquid/tags/with_.py +83 -0
- liquid/tokens.py +370 -0
- {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/METADATA +32 -39
- python_liquid-3.0.0.dist-info/RECORD +128 -0
- {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/WHEEL +1 -1
- {python_liquid-2.3.6.dist-info → python_liquid-3.0.0.dist-info}/licenses/LICENSE +1 -1
- liquid/analyze_tags.py +0 -214
- liquid/ast.py +0 -252
- liquid/builtin/__init__.py +0 -203
- liquid/builtin/content.py +0 -41
- liquid/builtin/drops/__init__.py +0 -3
- liquid/builtin/drops/drops.py +0 -13
- liquid/builtin/expressions/__init__.py +0 -47
- liquid/builtin/expressions/_tokenize.py +0 -186
- liquid/builtin/expressions/arguments.py +0 -207
- liquid/builtin/expressions/filtered.py +0 -387
- liquid/builtin/expressions/logical.py +0 -672
- liquid/builtin/expressions/loop.py +0 -275
- liquid/builtin/expressions/path.py +0 -163
- liquid/builtin/expressions/primitive.py +0 -417
- liquid/builtin/filters/__init__.py +0 -0
- liquid/builtin/filters/array.py +0 -329
- liquid/builtin/filters/extra.py +0 -83
- liquid/builtin/filters/math.py +0 -129
- liquid/builtin/filters/misc.py +0 -137
- liquid/builtin/filters/string.py +0 -416
- liquid/builtin/illegal.py +0 -28
- liquid/builtin/loaders/__init__.py +0 -0
- liquid/builtin/loaders/caching_file_system_loader.py +0 -61
- liquid/builtin/loaders/choice_loader.py +0 -94
- liquid/builtin/loaders/dict_loader.py +0 -64
- liquid/builtin/loaders/package_loader.py +0 -132
- liquid/builtin/output.py +0 -74
- liquid/builtin/tags/__init__.py +0 -0
- liquid/builtin/tags/assign_tag.py +0 -75
- liquid/builtin/tags/capture_tag.py +0 -98
- liquid/builtin/tags/case_tag.py +0 -287
- liquid/builtin/tags/comment_tag.py +0 -77
- liquid/builtin/tags/cycle_tag.py +0 -132
- liquid/builtin/tags/decrement_tag.py +0 -59
- liquid/builtin/tags/doc_tag.py +0 -68
- liquid/builtin/tags/echo_tag.py +0 -45
- liquid/builtin/tags/for_tag.py +0 -337
- liquid/builtin/tags/if_tag.py +0 -209
- liquid/builtin/tags/ifchanged_tag.py +0 -94
- liquid/builtin/tags/include_tag.py +0 -251
- liquid/builtin/tags/increment_tag.py +0 -60
- liquid/builtin/tags/inline_comment_tag.py +0 -47
- liquid/builtin/tags/liquid_tag.py +0 -183
- liquid/builtin/tags/render_tag.py +0 -371
- liquid/builtin/tags/tablerow_tag.py +0 -320
- liquid/builtin/tags/unless_tag.py +0 -218
- liquid/context.py +0 -615
- liquid/expression.py +0 -41
- liquid/extra/__init__.py +0 -98
- liquid/extra/filters/__init__.py +0 -33
- liquid/extra/filters/_json.py +0 -33
- liquid/extra/filters/array.py +0 -60
- liquid/extra/filters/babel.py +0 -552
- liquid/extra/filters/html.py +0 -32
- liquid/extra/filters/translate.py +0 -448
- liquid/extra/tags/__init__.py +0 -17
- liquid/extra/tags/_with.py +0 -80
- liquid/extra/tags/extends_tag.py +0 -576
- liquid/extra/tags/macro_tag.py +0 -270
- liquid/extra/tags/snippet_tag.py +0 -107
- liquid/extra/tags/translate_tag.py +0 -388
- liquid/filter.py +0 -230
- liquid/future/__init__.py +0 -3
- liquid/future/environment.py +0 -30
- liquid/future/filters/__init__.py +0 -1
- liquid/future/tags/__init__.py +0 -1
- liquid/lex.py +0 -260
- liquid/limits.py +0 -66
- liquid/loader.py +0 -141
- liquid/messages.py +0 -357
- liquid/mode.py +0 -12
- liquid/output.py +0 -35
- liquid/parser.py +0 -119
- liquid/span.py +0 -35
- liquid/static_analysis.py +0 -443
- liquid/stream.py +0 -148
- liquid/stringify.py +0 -31
- liquid/tag.py +0 -47
- liquid/template.py +0 -658
- liquid/token.py +0 -145
- liquid/undefined.py +0 -218
- liquid/utils/__init__.py +0 -15
- liquid/utils/html.py +0 -57
- liquid/utils/text.py +0 -28
- python_liquid-2.3.6.dist-info/RECORD +0 -96
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from .._filter import FilterContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def divided_by(
|
|
7
|
+
context: FilterContext, left: object, right: object
|
|
8
|
+
) -> int | float | Decimal:
|
|
9
|
+
left = context.to_numeric(left, 0)
|
|
10
|
+
right = context.to_numeric(right, 0)
|
|
11
|
+
|
|
12
|
+
if right == 0:
|
|
13
|
+
raise context.error("divide by zero")
|
|
14
|
+
|
|
15
|
+
if isinstance(left, int) and isinstance(right, int):
|
|
16
|
+
return left // right
|
|
17
|
+
|
|
18
|
+
if isinstance(left, Decimal) or isinstance(right, Decimal):
|
|
19
|
+
return float(Decimal(str(left)) / Decimal(str(right)))
|
|
20
|
+
|
|
21
|
+
return left / right
|
liquid/filters/escape.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import html
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
import markupsafe
|
|
5
|
+
|
|
6
|
+
from .._filter import FilterContext
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def escape(context: FilterContext, left: object) -> str:
|
|
10
|
+
left_ = context.to_str(left, "")
|
|
11
|
+
return (
|
|
12
|
+
markupsafe.escape(str(left_))
|
|
13
|
+
if context.env.autoescape
|
|
14
|
+
else html.escape(left_).replace("'", "'")
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def escape_once(context: FilterContext, left: object) -> str:
|
|
19
|
+
left_ = context.to_str(left, "")
|
|
20
|
+
return (
|
|
21
|
+
markupsafe.Markup(left_).unescape()
|
|
22
|
+
if context.env.autoescape
|
|
23
|
+
else html.escape(html.unescape(left_)).replace("'", "'")
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_ESCAPE_MAP = {
|
|
28
|
+
"\\": "\\u005C",
|
|
29
|
+
"'": "\\u0027",
|
|
30
|
+
'"': "\\u0022",
|
|
31
|
+
">": "\\u003E",
|
|
32
|
+
"<": "\\u003C",
|
|
33
|
+
"&": "\\u0026",
|
|
34
|
+
"=": "\\u003D",
|
|
35
|
+
"-": "\\u002D",
|
|
36
|
+
";": "\\u003B",
|
|
37
|
+
"`": "\\u0060",
|
|
38
|
+
"\u2028": "\\u2028",
|
|
39
|
+
"\u2029": "\\u2029",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_ESCAPE_MAP.update({chr(c): f"\\u{c:04X}" for c in range(32)})
|
|
43
|
+
_ESCAPE_RE = re.compile("[" + re.escape("".join(_ESCAPE_MAP.keys())) + "]")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def escapejs(context: FilterContext, left: object) -> str:
|
|
47
|
+
escaped = _ESCAPE_RE.sub(lambda m: _ESCAPE_MAP[m.group()], context.to_str(left, ""))
|
|
48
|
+
if context.env.autoescape:
|
|
49
|
+
return markupsafe.Markup(escaped)
|
|
50
|
+
return escaped
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def safe(context: FilterContext, left: object) -> str:
|
|
54
|
+
return (
|
|
55
|
+
markupsafe.Markup(left) if context.env.autoescape else context.to_str(left, "")
|
|
56
|
+
)
|
liquid/filters/find.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from .._filter import FilterContext
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def find(
|
|
5
|
+
context: FilterContext, left: object, prop: object, value: object = None
|
|
6
|
+
) -> object:
|
|
7
|
+
left = context.input_array(left)
|
|
8
|
+
|
|
9
|
+
if context.is_nil(value):
|
|
10
|
+
for obj in left:
|
|
11
|
+
val = context.get_item(obj, prop)
|
|
12
|
+
if not context.is_nil(val) and val is not False:
|
|
13
|
+
return obj
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
for obj in left:
|
|
17
|
+
if context.get_item(obj, prop) == value:
|
|
18
|
+
return obj
|
|
19
|
+
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def find_index(
|
|
24
|
+
context: FilterContext, left: object, prop: object, value: object = None
|
|
25
|
+
) -> int | None:
|
|
26
|
+
left = context.input_array(left)
|
|
27
|
+
|
|
28
|
+
if context.is_nil(value):
|
|
29
|
+
for i, obj in enumerate(left):
|
|
30
|
+
val = context.get_item(obj, prop)
|
|
31
|
+
if not context.is_nil(val) and val is not False:
|
|
32
|
+
return i
|
|
33
|
+
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
for i, obj in enumerate(left):
|
|
37
|
+
if context.get_item(obj, prop) == value:
|
|
38
|
+
return i
|
|
39
|
+
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def has(
|
|
44
|
+
context: FilterContext, left: object, prop: object, value: object = None
|
|
45
|
+
) -> bool:
|
|
46
|
+
left = context.input_array(left)
|
|
47
|
+
|
|
48
|
+
if context.is_nil(value):
|
|
49
|
+
for obj in left:
|
|
50
|
+
val = context.get_item(obj, prop)
|
|
51
|
+
if not context.is_nil(val) and val is not False:
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
return any(context.get_item(obj, prop) == value for obj in left)
|
liquid/filters/first.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
from itertools import islice
|
|
3
|
+
|
|
4
|
+
from .._drop import liquid_iterable
|
|
5
|
+
from .._filter import FilterContext
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def first(context: FilterContext, left: object) -> object:
|
|
9
|
+
if liquid_iterable(left):
|
|
10
|
+
it = left.__liquid_iter__(0, None, False)
|
|
11
|
+
return next(iter(it), None)
|
|
12
|
+
|
|
13
|
+
if isinstance(left, Sequence):
|
|
14
|
+
return left[0] if left else None # pyright: ignore[reportUnknownVariableType]
|
|
15
|
+
|
|
16
|
+
if isinstance(left, Mapping):
|
|
17
|
+
return next(islice(left.items(), 1), None) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
|
|
18
|
+
|
|
19
|
+
return None
|
liquid/filters/floor.py
ADDED
liquid/filters/html.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import html
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import markupsafe
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from .._filter import FilterContext
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def stylesheet_tag(context: FilterContext, left: object) -> str:
|
|
13
|
+
tag = '<link href="{}" rel="stylesheet" type="text/css" media="all" />'
|
|
14
|
+
if context.env.autoescape:
|
|
15
|
+
return markupsafe.Markup(tag).format(str(left))
|
|
16
|
+
return tag.format(html.escape(context.to_str(left, "")))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def script_tag(context: FilterContext, left: object) -> str:
|
|
20
|
+
tag = '<script src="{}" type="text/javascript"></script>'
|
|
21
|
+
if context.env.autoescape:
|
|
22
|
+
return markupsafe.Markup(tag).format(str(left))
|
|
23
|
+
return tag.format(html.escape(context.to_str(left, "")))
|
liquid/filters/index.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .._filter import FilterContext
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def index(context: FilterContext, left: object, obj: object) -> object:
|
|
5
|
+
"""Return the zero-based index of an array item.
|
|
6
|
+
|
|
7
|
+
`None` is returned if `obj` is not in `left`.
|
|
8
|
+
"""
|
|
9
|
+
try:
|
|
10
|
+
return context.input_array(left).index(obj)
|
|
11
|
+
except ValueError:
|
|
12
|
+
return None
|
liquid/filters/join.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import markupsafe
|
|
2
|
+
|
|
3
|
+
from .._filter import FilterContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def join(context: FilterContext, left: object, sep: object = " ") -> str:
|
|
7
|
+
left_ = context.input_array(left)
|
|
8
|
+
sep_ = context.to_str(sep, " ")
|
|
9
|
+
|
|
10
|
+
if context.env.autoescape and sep_ == " ":
|
|
11
|
+
sep_ = markupsafe.Markup(sep_)
|
|
12
|
+
|
|
13
|
+
return sep_.join(context.to_str(obj, "") for obj in left_)
|
liquid/filters/json_.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .._filter import FilterContext
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class JSON:
|
|
9
|
+
"""Serialize an object to a JSON formatted string.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
default
|
|
14
|
+
A function passed to ``json.dumps``. This function is called in the event that the
|
|
15
|
+
JSONEncoder does not know how to serialize an object. Defaults to `None`.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
name = "json"
|
|
19
|
+
|
|
20
|
+
def __init__(self, default: Callable[[Any], Any] | None = None):
|
|
21
|
+
self.default = default
|
|
22
|
+
|
|
23
|
+
def __call__(
|
|
24
|
+
self,
|
|
25
|
+
context: FilterContext,
|
|
26
|
+
left: object,
|
|
27
|
+
indent: object | None = None,
|
|
28
|
+
) -> str:
|
|
29
|
+
indent = context.to_int(indent) if indent else None
|
|
30
|
+
return json.dumps(left, default=self.default, indent=indent)
|
liquid/filters/last.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
from .._filter import FilterContext
|
|
4
|
+
from ..drops import Range, Undefined
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def last(context: FilterContext, left: object) -> object:
|
|
8
|
+
if isinstance(left, Range):
|
|
9
|
+
return left.stop
|
|
10
|
+
|
|
11
|
+
if isinstance(left, Sequence):
|
|
12
|
+
return left[-1] if left else None # pyright: ignore[reportUnknownVariableType]
|
|
13
|
+
|
|
14
|
+
if isinstance(left, Undefined):
|
|
15
|
+
left.poke()
|
|
16
|
+
|
|
17
|
+
return None
|
liquid/filters/map.py
ADDED
liquid/filters/minus.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from .._filter import FilterContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def minus(context: FilterContext, left: object, right: object) -> int | float:
|
|
7
|
+
left = context.to_numeric(left, 0)
|
|
8
|
+
right = context.to_numeric(right, 0)
|
|
9
|
+
|
|
10
|
+
if isinstance(left, int) and isinstance(right, int):
|
|
11
|
+
return left - right
|
|
12
|
+
|
|
13
|
+
return float(Decimal(str(left)) - Decimal(str(right)))
|
liquid/filters/modulo.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from .._filter import FilterContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def modulo(
|
|
7
|
+
context: FilterContext, left: object, right: object
|
|
8
|
+
) -> int | float | Decimal:
|
|
9
|
+
left = context.to_numeric(left, 0)
|
|
10
|
+
right = context.to_numeric(right, 0)
|
|
11
|
+
|
|
12
|
+
if right == 0:
|
|
13
|
+
raise context.error("divide by zero")
|
|
14
|
+
|
|
15
|
+
if isinstance(left, int) and isinstance(right, int):
|
|
16
|
+
return left % right
|
|
17
|
+
|
|
18
|
+
return float(Decimal(str(left)) % Decimal(str(right)))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import markupsafe
|
|
4
|
+
|
|
5
|
+
from .._filter import FilterContext
|
|
6
|
+
|
|
7
|
+
RE_LINETERM = re.compile(r"\r?\n")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def newline_to_br(context: FilterContext, left: object) -> str:
|
|
11
|
+
if context.env.autoescape:
|
|
12
|
+
return markupsafe.Markup(
|
|
13
|
+
RE_LINETERM.sub("<br />\n", markupsafe.escape(context.to_str(left, "")))
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
return RE_LINETERM.sub("<br />\n", context.to_str(left, ""))
|
liquid/filters/plus.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from .._filter import FilterContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def plus(context: FilterContext, left: object, right: object) -> int | float:
|
|
7
|
+
left = context.to_numeric(left, 0)
|
|
8
|
+
right = context.to_numeric(right, 0)
|
|
9
|
+
|
|
10
|
+
if isinstance(left, int) and isinstance(right, int):
|
|
11
|
+
return left + right
|
|
12
|
+
|
|
13
|
+
return float(Decimal(str(left)) + Decimal(str(right)))
|
liquid/filters/reject.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .._filter import FilterContext
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def reject(
|
|
5
|
+
context: FilterContext, left: object, prop: object, value: object = None
|
|
6
|
+
) -> list[object]:
|
|
7
|
+
left = context.input_array(left)
|
|
8
|
+
|
|
9
|
+
if context.is_nil(prop):
|
|
10
|
+
return []
|
|
11
|
+
|
|
12
|
+
def _filter(obj: object) -> bool:
|
|
13
|
+
val = context.get_item(obj, prop)
|
|
14
|
+
return not context.truthy(val)
|
|
15
|
+
|
|
16
|
+
if context.is_nil(value):
|
|
17
|
+
return list(filter(_filter, left))
|
|
18
|
+
|
|
19
|
+
return [obj for obj in left if context.get_item(obj, prop) != value]
|
liquid/filters/remove.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from .._filter import FilterContext
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def remove(context: FilterContext, left: object, right: object) -> str:
|
|
5
|
+
left_ = context.to_str(left, "")
|
|
6
|
+
right_ = context.to_str(right, "")
|
|
7
|
+
|
|
8
|
+
if not right_:
|
|
9
|
+
return left_
|
|
10
|
+
|
|
11
|
+
return left_.replace(right_, "")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def remove_first(context: FilterContext, left: object, right: object) -> str:
|
|
15
|
+
left_ = context.to_str(left, "")
|
|
16
|
+
right_ = context.to_str(right, "")
|
|
17
|
+
|
|
18
|
+
if not right_:
|
|
19
|
+
return left_
|
|
20
|
+
|
|
21
|
+
return left_.replace(right_, "", 1)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def remove_last(context: FilterContext, left: object, right: object) -> str:
|
|
25
|
+
left_ = context.to_str(left, "")
|
|
26
|
+
right_ = context.to_str(right, "")
|
|
27
|
+
|
|
28
|
+
if not right_:
|
|
29
|
+
return left_
|
|
30
|
+
|
|
31
|
+
before, _, after = left_.rpartition(right_)
|
|
32
|
+
|
|
33
|
+
if before:
|
|
34
|
+
return before + after
|
|
35
|
+
|
|
36
|
+
return left_
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from .._filter import FilterContext
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def replace(
|
|
5
|
+
context: FilterContext, left: object, sub: object, rep: object = None
|
|
6
|
+
) -> str:
|
|
7
|
+
return context.to_str(left, "").replace(
|
|
8
|
+
context.to_str(sub, ""), context.to_str(rep, "")
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def replace_first(
|
|
13
|
+
context: FilterContext, left: object, sub: object, rep: object = None
|
|
14
|
+
) -> str:
|
|
15
|
+
return context.to_str(left, "").replace(
|
|
16
|
+
context.to_str(sub, ""), context.to_str(rep, ""), 1
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def replace_last(context: FilterContext, left: object, sub: object, rep: object) -> str:
|
|
21
|
+
left_ = context.to_str(left, "")
|
|
22
|
+
sub_ = context.to_str(sub, "")
|
|
23
|
+
|
|
24
|
+
if not sub_:
|
|
25
|
+
return left_ + context.to_str(rep, "")
|
|
26
|
+
|
|
27
|
+
before, _, after = left_.rpartition(sub_)
|
|
28
|
+
|
|
29
|
+
if before:
|
|
30
|
+
return before + context.to_str(rep, "") + after
|
|
31
|
+
|
|
32
|
+
return left_
|
liquid/filters/round.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from decimal import ROUND_HALF_UP, Decimal
|
|
3
|
+
|
|
4
|
+
from .._filter import FilterContext
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def round_(
|
|
8
|
+
context: FilterContext, left: object, decimal_places: object = None
|
|
9
|
+
) -> int | float | Decimal:
|
|
10
|
+
left = context.to_numeric(left, 0)
|
|
11
|
+
|
|
12
|
+
if context.is_nil(decimal_places):
|
|
13
|
+
return _round_up(left)
|
|
14
|
+
|
|
15
|
+
to = context.to_numeric(decimal_places, 0)
|
|
16
|
+
|
|
17
|
+
if to == 0:
|
|
18
|
+
return _round_up(left)
|
|
19
|
+
|
|
20
|
+
if to < 0:
|
|
21
|
+
return 0
|
|
22
|
+
|
|
23
|
+
if left == 0:
|
|
24
|
+
return 0
|
|
25
|
+
|
|
26
|
+
return _round_up(left, math.floor(to))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _round_up(x: float | Decimal, digits: int = 0) -> int | float:
|
|
30
|
+
if isinstance(x, int):
|
|
31
|
+
return x
|
|
32
|
+
|
|
33
|
+
quantum = Decimal(1).scaleb(-digits)
|
|
34
|
+
result = Decimal(str(x)).quantize(quantum, rounding=ROUND_HALF_UP)
|
|
35
|
+
return int(result) if digits == 0 else float(result)
|
liquid/filters/size.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
from .._drop import liquid_iterable
|
|
5
|
+
from .._filter import FilterContext
|
|
6
|
+
from ..drops import Undefined
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def size(context: FilterContext, left: object) -> int:
|
|
10
|
+
if isinstance(left, (float, bool)):
|
|
11
|
+
return 0
|
|
12
|
+
|
|
13
|
+
if isinstance(left, (int, Decimal)):
|
|
14
|
+
return 8
|
|
15
|
+
|
|
16
|
+
if isinstance(left, Undefined):
|
|
17
|
+
return 0
|
|
18
|
+
|
|
19
|
+
if isinstance(left, (Sequence, Mapping)):
|
|
20
|
+
return len(left) # pyright: ignore[reportUnknownArgumentType]
|
|
21
|
+
|
|
22
|
+
if liquid_iterable(left):
|
|
23
|
+
return left.__liquid_iter__(0, None, False).length
|
|
24
|
+
|
|
25
|
+
return 0
|
liquid/filters/slice.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from .._filter import FilterContext
|
|
4
|
+
|
|
5
|
+
MAX_SLICE_ARG: int = (2**53) - 1
|
|
6
|
+
MIN_SLICE_ARG: int = -(2**53) + 1
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def slice_(
|
|
10
|
+
context: FilterContext, left: object, offset: object, length: object = None
|
|
11
|
+
) -> str | list[object]:
|
|
12
|
+
if not isinstance(left, (list, tuple, str)):
|
|
13
|
+
left = context.to_str(left, "")
|
|
14
|
+
|
|
15
|
+
offset = _clamp(_to_integer(context, offset))
|
|
16
|
+
length = 1 if context.is_nil(length) else _clamp(_to_integer(context, length))
|
|
17
|
+
|
|
18
|
+
start = len(left) + offset if offset < 0 else offset # type: ignore
|
|
19
|
+
return [] if length < 1 else left[start : start + length] # type: ignore
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _clamp(i: int) -> int:
|
|
23
|
+
return max(min(i, MAX_SLICE_ARG), MIN_SLICE_ARG)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _to_integer(context: FilterContext, obj: object) -> int:
|
|
27
|
+
if isinstance(obj, int):
|
|
28
|
+
return obj
|
|
29
|
+
|
|
30
|
+
if isinstance(obj, (float, Decimal)) and "." not in str(obj):
|
|
31
|
+
return int(obj)
|
|
32
|
+
|
|
33
|
+
if context.is_nil(obj):
|
|
34
|
+
raise context.error("invalid integer")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
return int(context.to_str(obj, "").lstrip())
|
|
38
|
+
except ValueError:
|
|
39
|
+
raise context.error("invalid integer") from None
|