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
@@ -1,552 +0,0 @@
1
- """A currency, date, time, number and unit formatting filters."""
2
-
3
- from __future__ import annotations
4
-
5
- from datetime import date
6
- from datetime import datetime
7
- from datetime import time
8
- from decimal import Decimal
9
- from functools import wraps
10
- from typing import TYPE_CHECKING
11
- from typing import Any
12
- from typing import Callable
13
- from typing import Optional
14
- from typing import Union
15
-
16
- import pytz
17
- from babel import Locale
18
- from babel import UnknownLocaleError
19
- from babel import dates
20
- from babel import numbers
21
- from babel import units
22
- from dateutil import parser
23
-
24
- from liquid.exceptions import LiquidTypeError
25
- from liquid.exceptions import LiquidValueError
26
- from liquid.filter import num_arg
27
- from liquid.filter import with_context
28
- from liquid.undefined import is_undefined
29
-
30
- if TYPE_CHECKING:
31
- from liquid import RenderContext
32
-
33
- FilterT = Callable[..., Any]
34
-
35
-
36
- def _resolve_locale(
37
- context: RenderContext,
38
- locale_var: str,
39
- default: Locale,
40
- ) -> Locale:
41
- _locale = context.resolve(locale_var)
42
-
43
- if is_undefined(_locale):
44
- locale = default
45
- elif isinstance(_locale, str):
46
- try:
47
- locale = Locale.parse(_locale)
48
- except UnknownLocaleError:
49
- locale = default
50
- else:
51
- raise LiquidTypeError(
52
- f"expected a string argument, found {_locale}", token=None
53
- )
54
-
55
- return locale
56
-
57
-
58
- @with_context
59
- class Currency:
60
- """A Liquid filter for formatting currency values.
61
-
62
- Args:
63
- currency_code_var: The name of a render context variable that resolves
64
- to the current currency code. Defaults to `"currency_code"`.
65
- default_currency_code: A fallback currency code if `currency_code_var`
66
- can not be resolved. Defaults to `"USD"`.
67
- locale_var: The name of a render context variable that resolves to the
68
- current locale. Defaults to `"locale"`.
69
- default_locale: A fallback locale to use if `locale_var` can not be
70
- resolved. Defaults to `"en_US"`.
71
- format_var: The name of a render context variable that resolves to the
72
- current currency format string. Defaults to `"currency_format"`.
73
- default_format: A fallback currency format that is used if `format_var`
74
- can not be resolved. Defaults to `None`, which means the standard format for
75
- the current locale will be used.
76
- currency_digits: Indicates if the format should override locale specific
77
- trailing digit behavior. Defaults to `False`.
78
- input_locale_var: The name of a render context variable that resolves to
79
- a locale suitable for parsing input strings to decimals. Defaults to
80
- `"input_locale"`.
81
- default_input_locale: A fallback locale to use if `input_locale_var`
82
- can not be resolved. Defaults to `"en_US"`.
83
- """
84
-
85
- def __init__(
86
- self,
87
- *,
88
- currency_code_var: str = "currency_code",
89
- default_currency_code: str = "USD",
90
- locale_var: str = "locale",
91
- default_locale: str = "en_US",
92
- format_var: str = "currency_format",
93
- default_format: Optional[str] = None,
94
- currency_digits: bool = True,
95
- input_locale_var: str = "input_locale",
96
- default_input_locale: str = "en_US",
97
- ) -> None:
98
- self.currency_code_var = currency_code_var
99
- self.default_currency_code = default_currency_code
100
- self.locale_var = locale_var
101
- self.default_locale = Locale.parse(default_locale)
102
- self.format_var = format_var
103
- self.default_format = default_format
104
- self.currency_digits = currency_digits
105
- self.input_locale_var = input_locale_var
106
- self.default_input_locale = Locale.parse(default_input_locale)
107
-
108
- def __call__(
109
- self,
110
- left: object,
111
- *,
112
- context: RenderContext,
113
- group_separator: bool = True,
114
- ) -> str:
115
- """Apply the filter and return the result."""
116
- locale = _resolve_locale(
117
- context,
118
- self.locale_var,
119
- default=self.default_locale,
120
- )
121
-
122
- input_locale = _resolve_locale(
123
- context,
124
- self.input_locale_var,
125
- default=self.default_input_locale,
126
- )
127
-
128
- _format = context.resolve(self.format_var, default=self.default_format)
129
-
130
- if _format is not None and not isinstance(_format, str):
131
- raise LiquidTypeError(
132
- f"expected a string argument, found {_format}", token=None
133
- )
134
-
135
- currency_code = context.resolve(
136
- self.currency_code_var,
137
- default=self.default_currency_code,
138
- )
139
-
140
- if not isinstance(currency_code, str):
141
- raise LiquidTypeError(
142
- f"expected a string argument, found {currency_code}", token=None
143
- )
144
-
145
- return numbers.format_currency(
146
- _parse_decimal(left, input_locale),
147
- currency_code,
148
- format=_format,
149
- locale=locale,
150
- group_separator=group_separator,
151
- currency_digits=self.currency_digits,
152
- )
153
-
154
-
155
- def _parse_decimal(val: object, locale: Union[str, Locale]) -> Decimal:
156
- if isinstance(val, str):
157
- try:
158
- return numbers.parse_decimal(val, locale)
159
- except numbers.NumberFormatError:
160
- return Decimal(0)
161
-
162
- if isinstance(val, (Decimal, float, int)):
163
- return Decimal(val)
164
-
165
- # Give objects that implement __int__ etc. a chance.
166
- return Decimal(num_arg(val, 0))
167
-
168
-
169
- @with_context
170
- class DateTime:
171
- """A Liquid filter for formatting datetime objects.
172
-
173
- Args:
174
- timezone_var: The name of a render context variable that resolves to
175
- a timezone. Defaults to `"timezone"`.
176
- default_timezone: A fallback timezone to use if `timezone_var` can
177
- not be resolved. Defaults to `"UTC"`.
178
- locale_var: The name of a render context variable that resolves to the
179
- current locale. Defaults to `"locale"`.
180
- default_locale: A fallback locale to use if `locale_var` can not be
181
- resolved. Defaults to `"en_US"`.
182
- format_var: The name of a render context variable that resolves to the
183
- current datetime format string. Defaults to `"datetime_format"`.
184
- default_format: A fallback datetime format that is used if `format_var`
185
- can not be resolved. Defaults to `"medium"`.
186
- input_timezone_var: The name of a render context variable that resolves to
187
- a timezone for parsing datetimes entered as strings. Defaults to
188
- `"input_timezone"`.
189
- default_input_timezone: A fallback timezone to use if `input_timezone_var`
190
- can not be resolved. Defaults to `"UTC"`.
191
- """
192
-
193
- formats = {
194
- "short": "short",
195
- "medium": "medium",
196
- "long": "long",
197
- "full": "full",
198
- }
199
-
200
- def __init__(
201
- self,
202
- *,
203
- timezone_var: str = "timezone",
204
- default_timezone: str = "UTC",
205
- locale_var: str = "locale",
206
- default_locale: str = "en_US",
207
- format_var: str = "datetime_format",
208
- default_format: str = "medium",
209
- input_timezone_var: str = "input_timezone",
210
- default_input_timezone: str = "UTC",
211
- ) -> None:
212
- self.timezone_var = timezone_var
213
- self.default_timezone = pytz.timezone(default_timezone)
214
- self.locale_var = locale_var
215
- self.default_locale = Locale.parse(default_locale)
216
- self.format_var = format_var
217
- self.default_format = self.formats.get(default_format, default_format)
218
- self.input_timezone_var = input_timezone_var
219
- self.default_input_timezone = pytz.timezone(default_input_timezone)
220
-
221
- def __call__(
222
- self,
223
- left: object,
224
- *,
225
- context: RenderContext,
226
- format: Optional[str] = None, # noqa: A002
227
- ) -> str:
228
- """Apply the filter and return the result."""
229
- locale = _resolve_locale(
230
- context,
231
- self.locale_var,
232
- default=self.default_locale,
233
- )
234
-
235
- if format:
236
- _format = self.formats.get(format, format)
237
- else:
238
- format_string = context.resolve(self.format_var)
239
-
240
- if is_undefined(format_string):
241
- _format = self.default_format
242
- elif not isinstance(format_string, str):
243
- raise LiquidTypeError(
244
- f"expected a string argument, found {format_string}",
245
- token=None,
246
- )
247
- else:
248
- _format = self.formats.get(format_string, format_string)
249
-
250
- tzinfo = self._resolve_timezone(
251
- context,
252
- self.timezone_var,
253
- default=self.default_timezone,
254
- )
255
- input_tzinfo = self._resolve_timezone(
256
- context,
257
- self.input_timezone_var,
258
- default=self.default_input_timezone,
259
- )
260
-
261
- return dates.format_datetime(
262
- _parse_datetime(left, input_tzinfo),
263
- format=_format,
264
- locale=locale,
265
- tzinfo=tzinfo,
266
- )
267
-
268
- def _resolve_timezone(
269
- self,
270
- context: RenderContext,
271
- timezone_var: str,
272
- default: pytz.BaseTzInfo,
273
- ) -> pytz.BaseTzInfo:
274
- _tz = context.resolve(timezone_var)
275
-
276
- if is_undefined(_tz):
277
- timezone = default
278
- elif isinstance(_tz, str):
279
- try:
280
- timezone = pytz.timezone(_tz)
281
- except pytz.UnknownTimeZoneError:
282
- timezone = default
283
- else:
284
- raise LiquidTypeError(
285
- f"expected a string argument, found {_tz}",
286
- token=None,
287
- )
288
-
289
- return timezone
290
-
291
-
292
- def _parse_number(val: str) -> Union[int, float]:
293
- try:
294
- return int(val)
295
- except ValueError:
296
- # Let the ValueError raise
297
- return float(val)
298
-
299
-
300
- def _parse_datetime(
301
- val: object,
302
- default_timezone: pytz.BaseTzInfo,
303
- ) -> Union[date, time, datetime, int, float, None]:
304
- if isinstance(val, str):
305
- # `date.format_datetime` will use the current timestamp if
306
- # given `None`.
307
- if val in ("now", "today"):
308
- return None
309
-
310
- # String representations of ints and floats need to be cast
311
- # to an int or float, but not passed to the fuzzy parser.
312
- try:
313
- return _parse_number(val)
314
- except ValueError:
315
- pass
316
-
317
- # Fuzzy parsing using dateutil.
318
- try:
319
- _dt = parser.parse(val)
320
- if _dt.tzinfo is None:
321
- return _dt.replace(tzinfo=default_timezone)
322
- return _dt
323
- except parser.ParserError as err:
324
- raise LiquidValueError(str(err), token=None) from err
325
-
326
- if not isinstance(val, (date, time, datetime, int, float)):
327
- raise LiquidTypeError(
328
- f"date expected date or time, found {type(val).__name__}", token=None
329
- )
330
-
331
- return val
332
-
333
-
334
- @with_context
335
- class Number:
336
- """A Liquid filter for formatting decimal values.
337
-
338
- Args:
339
- decimal_quantization_var: The name of a render context variable that
340
- resolves to the decimal quantization to be used. Defaults to
341
- `"decimal_quantization"`.
342
- default_decimal_quantization: A fallback decimal quantization if
343
- `decimal_quantization_var` can not be resolved. Defaults to `False`.
344
- locale_var: The name of a render context variable that resolves to the
345
- current locale. Defaults to `"locale"`.
346
- default_locale: A fallback locale to use if `locale_var` can not be
347
- resolved. Defaults to `"en_US"`.
348
- format_var: The name of a render context variable that resolves to the
349
- current decimal format string. Defaults to `"decimal_format"`.
350
- default_format: A fallback decimal format that is used if `format_var`
351
- can not be resolved. Defaults to `None`, which means the standard format for
352
- the current locale will be used.
353
- input_locale_var: The name of a render context variable that resolves to
354
- a locale suitable for parsing input strings to decimals. Defaults to
355
- `"input_locale"`.
356
- default_input_locale: A fallback locale to use if `input_locale_var`
357
- can not be resolved. Defaults to `"en_US"`.
358
- """
359
-
360
- def __init__(
361
- self,
362
- *,
363
- decimal_quantization_var: str = "decimal_quantization",
364
- default_decimal_quantization: bool = False,
365
- locale_var: str = "locale",
366
- default_locale: str = "en_US",
367
- format_var: str = "decimal_format",
368
- default_format: Optional[str] = None,
369
- input_locale_var: str = "input_locale",
370
- default_input_locale: str = "en_US",
371
- ) -> None:
372
- self.decimal_quantization_var = decimal_quantization_var
373
- self.default_decimal_quantization = default_decimal_quantization
374
- self.locale_var = locale_var
375
- self.default_locale = Locale.parse(default_locale)
376
- self.format_var = format_var
377
- self.default_format = default_format
378
- self.input_locale_var = input_locale_var
379
- self.default_input_locale = Locale.parse(default_input_locale)
380
-
381
- def __call__(
382
- self,
383
- left: object,
384
- *,
385
- context: RenderContext,
386
- group_separator: bool = True,
387
- ) -> str:
388
- """Apply the filter and return the result."""
389
- decimal_quantization = context.resolve(
390
- self.decimal_quantization_var,
391
- default=self.default_decimal_quantization,
392
- )
393
-
394
- if not isinstance(decimal_quantization, bool):
395
- raise LiquidTypeError(
396
- f"expected a Boolean argument, found {decimal_quantization}", token=None
397
- )
398
-
399
- locale = _resolve_locale(
400
- context,
401
- self.locale_var,
402
- default=self.default_locale,
403
- )
404
-
405
- input_locale = _resolve_locale(
406
- context,
407
- self.input_locale_var,
408
- default=self.default_input_locale,
409
- )
410
-
411
- _format = context.resolve(self.format_var, default=self.default_format)
412
-
413
- if _format is not None and not isinstance(_format, str):
414
- raise LiquidTypeError(
415
- f"expected a string argument, found {_format}", token=None
416
- )
417
-
418
- return numbers.format_decimal( # type: ignore
419
- _parse_decimal(left, input_locale),
420
- format=_format,
421
- locale=locale,
422
- group_separator=group_separator,
423
- decimal_quantization=decimal_quantization,
424
- )
425
-
426
-
427
- def unit_filter(_filter: FilterT) -> FilterT:
428
- """A filter function decorator that handles `TypeError` and `UnknownUnitError`."""
429
-
430
- @wraps(_filter)
431
- def wrapper(val: object, *args: Any, **kwargs: Any) -> Any:
432
- try:
433
- return _filter(val, *args, **kwargs)
434
- except TypeError as err:
435
- raise LiquidTypeError(err, token=None) from err
436
- except units.UnknownUnitError as err:
437
- raise LiquidValueError(err, token=None) from err
438
-
439
- return wrapper
440
-
441
-
442
- class Unit:
443
- """A Liquid filter for formatting units of measurement.
444
-
445
- Args:
446
- locale_var: The name of a render context variable that resolves to the
447
- current locale. Defaults to `"locale"`.
448
- default_locale: A fallback locale to use if `locale_var` can not be
449
- resolved. Defaults to `"en_US"`.
450
- length_var: The name of a render context variable that resolves to a
451
- unit format length. Should be one of "short", "long" or "narrow".
452
- Defaults to `"long"`.
453
- default_length: A fallback format length to use if `length_var` can
454
- not be resolved.
455
- format_var: The name of a render context variable that resolves to a
456
- decimal format string. Defaults to `"unit_format"`.
457
- default_format: A fallback decimal format to use if `format_var` can
458
- not be resolved. Defaults to `None`, meaning the locale's standard
459
- decimal format is used.
460
- input_locale_var: The name of a render context variable that resolves to
461
- a locale suitable for parsing input strings to decimals. Defaults to
462
- `"input_locale"`.
463
- default_input_locale: A fallback locale to use if `input_locale_var`
464
- can not be resolved. Defaults to `"en_US"`.
465
- """
466
-
467
- with_context = True
468
-
469
- def __init__(
470
- self,
471
- *,
472
- locale_var: str = "locale",
473
- default_locale: str = "en_US",
474
- length_var: str = "unit_length",
475
- default_length: str = "long",
476
- format_var: str = "unit_format",
477
- default_format: Optional[str] = None,
478
- input_locale_var: str = "input_locale",
479
- default_input_locale: str = "en_US",
480
- ) -> None:
481
- self.locale_var = locale_var
482
- self.default_locale = Locale.parse(default_locale)
483
- self.length_var = length_var
484
- self.default_length = default_length
485
- self.format_var = format_var
486
- self.default_format = default_format
487
- self.input_locale_var = input_locale_var
488
- self.default_input_locale = Locale.parse(default_input_locale)
489
-
490
- @unit_filter
491
- def __call__( # noqa: D102
492
- self,
493
- left: object,
494
- measurement_unit: str,
495
- *,
496
- context: RenderContext,
497
- denominator: object = None,
498
- denominator_unit: Optional[str] = None,
499
- length: Optional[str] = None,
500
- format: Optional[str] = None, # noqa: A002
501
- ) -> str:
502
- locale = _resolve_locale(
503
- context,
504
- self.locale_var,
505
- default=self.default_locale,
506
- )
507
-
508
- input_locale = _resolve_locale(
509
- context,
510
- self.input_locale_var,
511
- default=self.default_input_locale,
512
- )
513
-
514
- _length = length if length else context.resolve(self.length_var)
515
-
516
- if _length not in ("short", "long", "narrow"):
517
- _length = self.default_length
518
-
519
- assert isinstance(_length, str)
520
-
521
- if format:
522
- _format: Optional[str] = format
523
- else:
524
- format_string = context.resolve(self.format_var)
525
- if isinstance(format_string, str):
526
- _format = format_string
527
- else:
528
- _format = self.default_format
529
-
530
- if denominator is not None or denominator_unit is not None:
531
- _denominator = (
532
- _parse_decimal(denominator, input_locale)
533
- if denominator is not None
534
- else 1
535
- )
536
- return units.format_compound_unit(
537
- _parse_decimal(left, input_locale),
538
- numerator_unit=measurement_unit,
539
- denominator_value=_denominator, # type: ignore
540
- denominator_unit=denominator_unit,
541
- length=_length, # type: ignore
542
- format=_format,
543
- locale=locale,
544
- )
545
-
546
- return units.format_unit(
547
- _parse_decimal(left, input_locale),
548
- measurement_unit=measurement_unit,
549
- length=_length, # type: ignore
550
- format=_format,
551
- locale=locale,
552
- )
@@ -1,32 +0,0 @@
1
- """Extra HTML filters."""
2
- from __future__ import annotations
3
-
4
- import html
5
- from typing import TYPE_CHECKING
6
-
7
- from liquid import Markup
8
- from liquid.filter import string_filter
9
- from liquid.filter import with_environment
10
-
11
- if TYPE_CHECKING:
12
- from liquid import Environment
13
-
14
-
15
- @string_filter
16
- @with_environment
17
- def stylesheet_tag(url: str, *, environment: Environment) -> str:
18
- """Wrap a URL in an HTML stylesheet tag."""
19
- tag = '<link href="{}" rel="stylesheet" type="text/css" media="all" />'
20
- if environment.autoescape:
21
- return Markup(tag).format(str(url))
22
- return tag.format(html.escape(url))
23
-
24
-
25
- @string_filter
26
- @with_environment
27
- def script_tag(url: str, *, environment: Environment) -> str:
28
- """Wrap a URL in an HTML script tag."""
29
- tag = '<script src="{}" type="text/javascript"></script>'
30
- if environment.autoescape:
31
- return Markup(tag).format(str(url))
32
- return tag.format(html.escape(url))