python-liquid 2.2.2__tar.gz → 2.3.0__tar.gz

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 (97) hide show
  1. {python_liquid-2.2.2 → python_liquid-2.3.0}/PKG-INFO +1 -1
  2. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/__init__.py +1 -1
  3. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/loop.py +12 -18
  4. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/primitive.py +14 -1
  5. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/loaders/file_system_loader.py +5 -1
  6. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/liquid_tag.py +2 -1
  7. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/environment.py +6 -1
  8. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/exceptions.py +4 -0
  9. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/parser.py +6 -0
  10. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/stream.py +2 -4
  11. {python_liquid-2.2.2 → python_liquid-2.3.0}/pyproject.toml +1 -0
  12. {python_liquid-2.2.2 → python_liquid-2.3.0}/.gitignore +0 -0
  13. {python_liquid-2.2.2 → python_liquid-2.3.0}/LICENSE +0 -0
  14. {python_liquid-2.2.2 → python_liquid-2.3.0}/README.md +0 -0
  15. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/analyze_tags.py +0 -0
  16. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/ast.py +0 -0
  17. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/__init__.py +0 -0
  18. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/content.py +0 -0
  19. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/drops/__init__.py +0 -0
  20. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/drops/drops.py +0 -0
  21. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/__init__.py +0 -0
  22. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/_tokenize.py +0 -0
  23. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/arguments.py +0 -0
  24. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/filtered.py +0 -0
  25. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/logical.py +0 -0
  26. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/expressions/path.py +0 -0
  27. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/filters/__init__.py +0 -0
  28. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/filters/array.py +0 -0
  29. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/filters/extra.py +0 -0
  30. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/filters/math.py +0 -0
  31. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/filters/misc.py +0 -0
  32. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/filters/string.py +0 -0
  33. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/illegal.py +0 -0
  34. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/loaders/__init__.py +0 -0
  35. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/loaders/caching_file_system_loader.py +0 -0
  36. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/loaders/choice_loader.py +0 -0
  37. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/loaders/dict_loader.py +0 -0
  38. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/loaders/mixins.py +0 -0
  39. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/loaders/package_loader.py +0 -0
  40. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/output.py +0 -0
  41. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/__init__.py +0 -0
  42. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/assign_tag.py +0 -0
  43. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/capture_tag.py +0 -0
  44. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/case_tag.py +0 -0
  45. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/comment_tag.py +0 -0
  46. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/cycle_tag.py +0 -0
  47. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/decrement_tag.py +0 -0
  48. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/doc_tag.py +0 -0
  49. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/echo_tag.py +0 -0
  50. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/for_tag.py +0 -0
  51. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/if_tag.py +0 -0
  52. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/ifchanged_tag.py +0 -0
  53. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/include_tag.py +0 -0
  54. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/increment_tag.py +0 -0
  55. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/inline_comment_tag.py +0 -0
  56. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/render_tag.py +0 -0
  57. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/tablerow_tag.py +0 -0
  58. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/builtin/tags/unless_tag.py +0 -0
  59. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/context.py +0 -0
  60. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/expression.py +0 -0
  61. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/__init__.py +0 -0
  62. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/filters/__init__.py +0 -0
  63. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/filters/_json.py +0 -0
  64. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/filters/array.py +0 -0
  65. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/filters/babel.py +0 -0
  66. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/filters/html.py +0 -0
  67. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/filters/translate.py +0 -0
  68. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/tags/__init__.py +0 -0
  69. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/tags/_with.py +0 -0
  70. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/tags/extends_tag.py +0 -0
  71. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/tags/macro_tag.py +0 -0
  72. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/tags/snippet_tag.py +0 -0
  73. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/extra/tags/translate_tag.py +0 -0
  74. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/filter.py +0 -0
  75. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/future/__init__.py +0 -0
  76. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/future/environment.py +0 -0
  77. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/future/filters/__init__.py +0 -0
  78. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/future/tags/__init__.py +0 -0
  79. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/lex.py +0 -0
  80. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/limits.py +0 -0
  81. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/loader.py +0 -0
  82. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/messages.py +0 -0
  83. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/mode.py +0 -0
  84. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/output.py +0 -0
  85. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/py.typed +0 -0
  86. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/span.py +0 -0
  87. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/static_analysis.py +0 -0
  88. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/stringify.py +0 -0
  89. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/tag.py +0 -0
  90. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/template.py +0 -0
  91. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/token.py +0 -0
  92. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/undefined.py +0 -0
  93. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/utils/__init__.py +0 -0
  94. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/utils/chain_map.py +0 -0
  95. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/utils/html.py +0 -0
  96. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/utils/lru_cache.py +0 -0
  97. {python_liquid-2.2.2 → python_liquid-2.3.0}/liquid/utils/text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-liquid
3
- Version: 2.2.2
3
+ Version: 2.3.0
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/
@@ -56,7 +56,7 @@ from .tag import Tag
56
56
 
57
57
  from . import future
58
58
 
59
- __version__ = "2.2.2"
59
+ __version__ = "2.3.0"
60
60
 
61
61
  __all__ = (
62
62
  "AwareBoundTemplate",
@@ -123,29 +123,23 @@ class LoopExpression(Expression):
123
123
  ) -> tuple[Iterator[object], int]:
124
124
  offset_key = f"{self.identifier}-{self.iterable}"
125
125
 
126
- if limit is None and offset is None:
127
- context.stopindex(key=offset_key, index=length)
128
- if self.reversed:
129
- return reversed(list(it)), length
130
- return it, length
131
-
132
126
  if offset == "continue":
133
- offset = context.stopindex(key=offset_key)
134
- length = max(length - offset, 0)
135
- elif offset is not None:
136
- assert isinstance(offset, int), f"found {offset!r}"
137
- length = max(length - offset, 0)
127
+ start = context.stopindex(offset_key)
128
+ else:
129
+ start = int(offset or 0)
130
+
131
+ stop = None if limit is None else limit + start
138
132
 
139
- if limit is not None:
140
- length = min(length, limit)
133
+ start_ = min(max(start, 0), length)
134
+ stop_ = min(stop or length, length)
135
+ length_ = max(stop_ - start_, 0)
141
136
 
142
- stop = offset + length if offset else length
143
- context.stopindex(key=offset_key, index=stop)
144
- it = islice(it, offset, stop)
137
+ context.stopindex(key=offset_key, index=stop_)
138
+ it = islice(it, start_, stop_)
145
139
 
146
140
  if self.reversed:
147
- return reversed(list(it)), length
148
- return it, length
141
+ return reversed(list(it)), length_
142
+ return it, length_
149
143
 
150
144
  def evaluate(self, context: RenderContext) -> tuple[Iterator[object], int]:
151
145
  it, length = self._to_iter(self.iterable.evaluate(context), context)
@@ -39,6 +39,12 @@ 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
+
42
48
  class Nil(Expression):
43
49
  __slots__ = ()
44
50
 
@@ -249,7 +255,9 @@ class RangeLiteral(Expression):
249
255
  if start > stop:
250
256
  return range(0)
251
257
 
252
- return range(start, stop + 1)
258
+ return range(
259
+ clamp(start, MIN_RANGE, MAX_RANGE), clamp(stop + 1, MIN_RANGE, MAX_RANGE)
260
+ )
253
261
 
254
262
  def evaluate(self, context: RenderContext) -> range:
255
263
  return self._make_range(
@@ -415,3 +423,8 @@ def parse_string_or_path(
415
423
  def is_empty(obj: object) -> bool:
416
424
  """Return True if _obj_ is considered empty."""
417
425
  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))
@@ -76,7 +76,11 @@ class FileSystemLoader(BaseLoader):
76
76
  for base in self.search_path:
77
77
  source_path = base.joinpath(template_path)
78
78
 
79
- if not source_path.exists() or not source_path.is_file():
79
+ try:
80
+ if not source_path.exists() or not source_path.is_file():
81
+ continue
82
+ except OSError:
83
+ # "File name too long", for example
80
84
  continue
81
85
 
82
86
  if self.reject_symlinks:
@@ -127,7 +127,8 @@ class LiquidTag(Tag):
127
127
  self._tokenize(
128
128
  token_.value,
129
129
  token=token_,
130
- )
130
+ ),
131
+ block_depth_carry=stream.block_depth,
131
132
  ),
132
133
  end=(),
133
134
  )
@@ -18,6 +18,7 @@ from . import builtin
18
18
  from .analyze_tags import InnerTagMap
19
19
  from .analyze_tags import TagAnalysis
20
20
  from .builtin import DictLoader
21
+ from .exceptions import BlockNestingError
21
22
  from .exceptions import LiquidError
22
23
  from .exceptions import LiquidSyntaxError
23
24
  from .exceptions import TemplateInheritanceError
@@ -79,6 +80,10 @@ class Environment:
79
80
  template loaded from this environment.
80
81
  """
81
82
 
83
+ block_nesting_limit: ClassVar[int] = 30
84
+ """The maximum markup block nesting depth allowed before a `BlockNestingError`
85
+ exception is raised."""
86
+
82
87
  context_depth_limit: ClassVar[int] = 30
83
88
  """The maximum number of times a render context can be extended or wrapped before
84
89
  raising a `ContextDepthError`."""
@@ -275,7 +280,7 @@ class Environment:
275
280
  """
276
281
  try:
277
282
  nodes = self._parse(source)
278
- except (LiquidSyntaxError, TemplateInheritanceError) as err:
283
+ except (LiquidSyntaxError, TemplateInheritanceError, BlockNestingError) as err:
279
284
  err.template_name = path
280
285
  raise err
281
286
  except Exception as err: # noqa: BLE001
@@ -191,6 +191,10 @@ class ResourceLimitError(LiquidError):
191
191
  """Base class for exceptions relating to resource limits."""
192
192
 
193
193
 
194
+ class BlockNestingError(ResourceLimitError):
195
+ """The exception raised when block nesting limit is reached."""
196
+
197
+
194
198
  class ContextDepthError(ResourceLimitError):
195
199
  """Exception raised when the maximum context depth is reached.
196
200
 
@@ -9,6 +9,7 @@ from typing import Iterator
9
9
 
10
10
  from .ast import BlockNode
11
11
  from .ast import Node
12
+ from .exceptions import BlockNestingError
12
13
  from .exceptions import LiquidError
13
14
  from .token import TOKEN_COMMENT
14
15
  from .token import TOKEN_CONTENT
@@ -69,6 +70,10 @@ class Parser:
69
70
  output = tags[TOKEN_OUTPUT]
70
71
 
71
72
  nodes: list[Node] = []
73
+ stream.block_depth += 1
74
+
75
+ if stream.block_depth > self.env.block_nesting_limit:
76
+ raise BlockNestingError("block nesting limit reached", token=None)
72
77
 
73
78
  while stream.current.kind != TOKEN_EOF:
74
79
  if stream.current.kind == TOKEN_TAG and stream.current.value in end:
@@ -92,6 +97,7 @@ class Parser:
92
97
 
93
98
  next(stream)
94
99
 
100
+ stream.block_depth -= 1
95
101
  return BlockNode(stream.current, nodes)
96
102
 
97
103
 
@@ -18,12 +18,10 @@ class TokenStream:
18
18
 
19
19
  eof = Token(TOKEN_EOF, TOKEN_EOF, -1, "")
20
20
 
21
- def __init__(
22
- self,
23
- tokens: Iterator[Token],
24
- ):
21
+ def __init__(self, tokens: Iterator[Token], block_depth_carry: int = 0):
25
22
  self.tokens = list(tokens)
26
23
  self.pos = 0
24
+ self.block_depth = block_depth_carry
27
25
 
28
26
  def __next__(self) -> Token:
29
27
  return self.next_token()
@@ -160,6 +160,7 @@ convention = "google"
160
160
  "liquid/builtin/loaders/__init__.py" = ["D104", "I001"]
161
161
  "liquid/builtin/tags/__init__.py" = ["D104", "I001"]
162
162
  "tests/*" = ["D100", "D101", "D104", "D103", "D102", "D209", "D205", "SIM117"]
163
+ "tests/test_compliance.py" = ["E501"]
163
164
  "performance/*" = ["D103", "PLR2004", "T201", "D100"]
164
165
  "liquid/builtin/expressions/primitive.py" = ["D101", "D102"]
165
166
  "liquid/builtin/expressions/path.py" = ["D101", "D102"]
File without changes
File without changes
File without changes