python-liquid 2.3.5__tar.gz → 3.0.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 (218) hide show
  1. {python_liquid-2.3.5 → python_liquid-3.0.0}/.gitignore +4 -1
  2. {python_liquid-2.3.5 → python_liquid-3.0.0}/LICENSE +1 -1
  3. {python_liquid-2.3.5 → python_liquid-3.0.0}/PKG-INFO +32 -39
  4. {python_liquid-2.3.5 → python_liquid-3.0.0}/README.md +27 -30
  5. python_liquid-3.0.0/liquid/__about__.py +1 -0
  6. python_liquid-3.0.0/liquid/__init__.py +220 -0
  7. python_liquid-3.0.0/liquid/_base_tag.py +66 -0
  8. python_liquid-2.3.5/liquid/utils/chain_map.py → python_liquid-3.0.0/liquid/_chain_map.py +18 -19
  9. python_liquid-3.0.0/liquid/_context.py +465 -0
  10. python_liquid-3.0.0/liquid/_drop.py +108 -0
  11. python_liquid-3.0.0/liquid/_filter.py +174 -0
  12. python_liquid-3.0.0/liquid/_io.py +18 -0
  13. python_liquid-3.0.0/liquid/_lax_parser_mixin.py +100 -0
  14. python_liquid-3.0.0/liquid/_lexer.py +93 -0
  15. python_liquid-3.0.0/liquid/_lexer_extended.py +623 -0
  16. python_liquid-3.0.0/liquid/_lexer_legacy.py +536 -0
  17. python_liquid-3.0.0/liquid/_loader.py +84 -0
  18. python_liquid-2.3.5/liquid/utils/lru_cache.py → python_liquid-3.0.0/liquid/_lru_cache.py +28 -36
  19. python_liquid-3.0.0/liquid/_markup.py +253 -0
  20. python_liquid-3.0.0/liquid/_messages.py +292 -0
  21. python_liquid-3.0.0/liquid/_missing.py +3 -0
  22. python_liquid-3.0.0/liquid/_nothing.py +28 -0
  23. python_liquid-3.0.0/liquid/_parser.py +460 -0
  24. python_liquid-3.0.0/liquid/_parser_extended.py +293 -0
  25. python_liquid-3.0.0/liquid/_parser_legacy.py +819 -0
  26. python_liquid-3.0.0/liquid/_resource_limits.py +50 -0
  27. python_liquid-3.0.0/liquid/_slice.py +41 -0
  28. python_liquid-3.0.0/liquid/_static_analysis.py +503 -0
  29. python_liquid-3.0.0/liquid/_strftime.py +186 -0
  30. python_liquid-3.0.0/liquid/_template.py +536 -0
  31. python_liquid-3.0.0/liquid/drops/__init__.py +20 -0
  32. python_liquid-3.0.0/liquid/drops/forloop.py +78 -0
  33. python_liquid-3.0.0/liquid/drops/iterable.py +25 -0
  34. python_liquid-3.0.0/liquid/drops/range.py +76 -0
  35. python_liquid-3.0.0/liquid/drops/self_.py +45 -0
  36. python_liquid-3.0.0/liquid/drops/tablerowloop.py +142 -0
  37. python_liquid-3.0.0/liquid/drops/undefined.py +228 -0
  38. python_liquid-3.0.0/liquid/environment.py +777 -0
  39. python_liquid-3.0.0/liquid/exceptions.py +142 -0
  40. python_liquid-3.0.0/liquid/expressions.py +1184 -0
  41. python_liquid-3.0.0/liquid/filters/__init__.py +134 -0
  42. python_liquid-3.0.0/liquid/filters/abs.py +7 -0
  43. python_liquid-3.0.0/liquid/filters/append.py +5 -0
  44. python_liquid-3.0.0/liquid/filters/at_least.py +9 -0
  45. python_liquid-3.0.0/liquid/filters/at_most.py +9 -0
  46. python_liquid-3.0.0/liquid/filters/babel.py +560 -0
  47. python_liquid-3.0.0/liquid/filters/base64.py +26 -0
  48. python_liquid-3.0.0/liquid/filters/capitalize.py +5 -0
  49. python_liquid-3.0.0/liquid/filters/ceil.py +8 -0
  50. python_liquid-3.0.0/liquid/filters/compact.py +25 -0
  51. python_liquid-3.0.0/liquid/filters/concat.py +11 -0
  52. python_liquid-3.0.0/liquid/filters/date.py +27 -0
  53. python_liquid-3.0.0/liquid/filters/default.py +25 -0
  54. python_liquid-3.0.0/liquid/filters/divided_by.py +21 -0
  55. python_liquid-3.0.0/liquid/filters/downcase.py +5 -0
  56. python_liquid-3.0.0/liquid/filters/escape.py +56 -0
  57. python_liquid-3.0.0/liquid/filters/find.py +56 -0
  58. python_liquid-3.0.0/liquid/filters/first.py +19 -0
  59. python_liquid-3.0.0/liquid/filters/floor.py +8 -0
  60. python_liquid-3.0.0/liquid/filters/html.py +23 -0
  61. python_liquid-3.0.0/liquid/filters/index.py +12 -0
  62. python_liquid-3.0.0/liquid/filters/join.py +13 -0
  63. python_liquid-3.0.0/liquid/filters/json_.py +30 -0
  64. python_liquid-3.0.0/liquid/filters/last.py +17 -0
  65. python_liquid-3.0.0/liquid/filters/map.py +5 -0
  66. python_liquid-3.0.0/liquid/filters/minus.py +13 -0
  67. python_liquid-3.0.0/liquid/filters/modulo.py +18 -0
  68. python_liquid-3.0.0/liquid/filters/newline_to_br.py +16 -0
  69. python_liquid-3.0.0/liquid/filters/plus.py +13 -0
  70. python_liquid-3.0.0/liquid/filters/prepend.py +5 -0
  71. python_liquid-3.0.0/liquid/filters/reject.py +19 -0
  72. python_liquid-3.0.0/liquid/filters/remove.py +36 -0
  73. python_liquid-3.0.0/liquid/filters/replace.py +32 -0
  74. python_liquid-3.0.0/liquid/filters/reverse.py +5 -0
  75. python_liquid-3.0.0/liquid/filters/round.py +35 -0
  76. python_liquid-3.0.0/liquid/filters/size.py +25 -0
  77. python_liquid-3.0.0/liquid/filters/slice.py +39 -0
  78. python_liquid-3.0.0/liquid/filters/sort.py +148 -0
  79. python_liquid-3.0.0/liquid/filters/split.py +34 -0
  80. python_liquid-3.0.0/liquid/filters/squish.py +9 -0
  81. python_liquid-3.0.0/liquid/filters/strip.py +13 -0
  82. python_liquid-3.0.0/liquid/filters/strip_html.py +19 -0
  83. python_liquid-3.0.0/liquid/filters/strip_newlines.py +17 -0
  84. python_liquid-3.0.0/liquid/filters/sum.py +21 -0
  85. python_liquid-3.0.0/liquid/filters/times.py +13 -0
  86. python_liquid-3.0.0/liquid/filters/translate.py +380 -0
  87. python_liquid-3.0.0/liquid/filters/truncate.py +39 -0
  88. python_liquid-3.0.0/liquid/filters/uniq.py +11 -0
  89. python_liquid-3.0.0/liquid/filters/upcase.py +5 -0
  90. python_liquid-3.0.0/liquid/filters/url.py +18 -0
  91. python_liquid-3.0.0/liquid/filters/where.py +18 -0
  92. python_liquid-3.0.0/liquid/loaders/__init__.py +17 -0
  93. python_liquid-3.0.0/liquid/loaders/caching_file_system_loader.py +64 -0
  94. python_liquid-3.0.0/liquid/loaders/choice_loader.py +93 -0
  95. python_liquid-3.0.0/liquid/loaders/dict_loader.py +56 -0
  96. python_liquid-3.0.0/liquid/loaders/file_system_loader.py +143 -0
  97. {python_liquid-2.3.5/liquid/builtin → python_liquid-3.0.0/liquid}/loaders/mixins.py +40 -52
  98. python_liquid-3.0.0/liquid/loaders/package_loader.py +110 -0
  99. python_liquid-3.0.0/liquid/tags/__init__.py +61 -0
  100. python_liquid-3.0.0/liquid/tags/assign.py +68 -0
  101. python_liquid-3.0.0/liquid/tags/capture.py +83 -0
  102. python_liquid-3.0.0/liquid/tags/case.py +199 -0
  103. python_liquid-3.0.0/liquid/tags/comment.py +56 -0
  104. python_liquid-3.0.0/liquid/tags/cycle.py +131 -0
  105. python_liquid-3.0.0/liquid/tags/decrement.py +55 -0
  106. python_liquid-3.0.0/liquid/tags/doc.py +56 -0
  107. python_liquid-3.0.0/liquid/tags/echo.py +65 -0
  108. python_liquid-3.0.0/liquid/tags/else_.py +53 -0
  109. python_liquid-3.0.0/liquid/tags/error.py +51 -0
  110. python_liquid-3.0.0/liquid/tags/extends.py +448 -0
  111. python_liquid-3.0.0/liquid/tags/for_.py +421 -0
  112. python_liquid-3.0.0/liquid/tags/if_.py +156 -0
  113. python_liquid-3.0.0/liquid/tags/ifchanged.py +69 -0
  114. python_liquid-3.0.0/liquid/tags/include.py +223 -0
  115. python_liquid-3.0.0/liquid/tags/increment.py +55 -0
  116. python_liquid-3.0.0/liquid/tags/inline_comment.py +70 -0
  117. python_liquid-3.0.0/liquid/tags/liquid.py +56 -0
  118. python_liquid-3.0.0/liquid/tags/macro.py +281 -0
  119. python_liquid-3.0.0/liquid/tags/output.py +58 -0
  120. python_liquid-3.0.0/liquid/tags/raw.py +60 -0
  121. python_liquid-3.0.0/liquid/tags/render.py +245 -0
  122. python_liquid-3.0.0/liquid/tags/tablerow.py +254 -0
  123. python_liquid-3.0.0/liquid/tags/template_comment.py +56 -0
  124. python_liquid-3.0.0/liquid/tags/translate.py +316 -0
  125. python_liquid-3.0.0/liquid/tags/unless.py +121 -0
  126. python_liquid-3.0.0/liquid/tags/with_.py +83 -0
  127. python_liquid-3.0.0/liquid/tokens.py +370 -0
  128. python_liquid-3.0.0/pyproject.toml +167 -0
  129. python_liquid-2.3.5/liquid/__init__.py +0 -246
  130. python_liquid-2.3.5/liquid/analyze_tags.py +0 -214
  131. python_liquid-2.3.5/liquid/ast.py +0 -252
  132. python_liquid-2.3.5/liquid/builtin/__init__.py +0 -203
  133. python_liquid-2.3.5/liquid/builtin/content.py +0 -41
  134. python_liquid-2.3.5/liquid/builtin/drops/__init__.py +0 -3
  135. python_liquid-2.3.5/liquid/builtin/drops/drops.py +0 -13
  136. python_liquid-2.3.5/liquid/builtin/expressions/__init__.py +0 -47
  137. python_liquid-2.3.5/liquid/builtin/expressions/_tokenize.py +0 -186
  138. python_liquid-2.3.5/liquid/builtin/expressions/arguments.py +0 -207
  139. python_liquid-2.3.5/liquid/builtin/expressions/filtered.py +0 -387
  140. python_liquid-2.3.5/liquid/builtin/expressions/logical.py +0 -672
  141. python_liquid-2.3.5/liquid/builtin/expressions/loop.py +0 -275
  142. python_liquid-2.3.5/liquid/builtin/expressions/path.py +0 -163
  143. python_liquid-2.3.5/liquid/builtin/expressions/primitive.py +0 -417
  144. python_liquid-2.3.5/liquid/builtin/filters/__init__.py +0 -0
  145. python_liquid-2.3.5/liquid/builtin/filters/array.py +0 -329
  146. python_liquid-2.3.5/liquid/builtin/filters/extra.py +0 -83
  147. python_liquid-2.3.5/liquid/builtin/filters/math.py +0 -129
  148. python_liquid-2.3.5/liquid/builtin/filters/misc.py +0 -137
  149. python_liquid-2.3.5/liquid/builtin/filters/string.py +0 -416
  150. python_liquid-2.3.5/liquid/builtin/illegal.py +0 -28
  151. python_liquid-2.3.5/liquid/builtin/loaders/__init__.py +0 -0
  152. python_liquid-2.3.5/liquid/builtin/loaders/caching_file_system_loader.py +0 -61
  153. python_liquid-2.3.5/liquid/builtin/loaders/choice_loader.py +0 -94
  154. python_liquid-2.3.5/liquid/builtin/loaders/dict_loader.py +0 -64
  155. python_liquid-2.3.5/liquid/builtin/loaders/file_system_loader.py +0 -146
  156. python_liquid-2.3.5/liquid/builtin/loaders/package_loader.py +0 -132
  157. python_liquid-2.3.5/liquid/builtin/output.py +0 -74
  158. python_liquid-2.3.5/liquid/builtin/tags/__init__.py +0 -0
  159. python_liquid-2.3.5/liquid/builtin/tags/assign_tag.py +0 -75
  160. python_liquid-2.3.5/liquid/builtin/tags/capture_tag.py +0 -98
  161. python_liquid-2.3.5/liquid/builtin/tags/case_tag.py +0 -287
  162. python_liquid-2.3.5/liquid/builtin/tags/comment_tag.py +0 -77
  163. python_liquid-2.3.5/liquid/builtin/tags/cycle_tag.py +0 -132
  164. python_liquid-2.3.5/liquid/builtin/tags/decrement_tag.py +0 -59
  165. python_liquid-2.3.5/liquid/builtin/tags/doc_tag.py +0 -68
  166. python_liquid-2.3.5/liquid/builtin/tags/echo_tag.py +0 -45
  167. python_liquid-2.3.5/liquid/builtin/tags/for_tag.py +0 -337
  168. python_liquid-2.3.5/liquid/builtin/tags/if_tag.py +0 -209
  169. python_liquid-2.3.5/liquid/builtin/tags/ifchanged_tag.py +0 -94
  170. python_liquid-2.3.5/liquid/builtin/tags/include_tag.py +0 -251
  171. python_liquid-2.3.5/liquid/builtin/tags/increment_tag.py +0 -60
  172. python_liquid-2.3.5/liquid/builtin/tags/inline_comment_tag.py +0 -47
  173. python_liquid-2.3.5/liquid/builtin/tags/liquid_tag.py +0 -183
  174. python_liquid-2.3.5/liquid/builtin/tags/render_tag.py +0 -371
  175. python_liquid-2.3.5/liquid/builtin/tags/tablerow_tag.py +0 -320
  176. python_liquid-2.3.5/liquid/builtin/tags/unless_tag.py +0 -218
  177. python_liquid-2.3.5/liquid/context.py +0 -615
  178. python_liquid-2.3.5/liquid/environment.py +0 -611
  179. python_liquid-2.3.5/liquid/exceptions.py +0 -289
  180. python_liquid-2.3.5/liquid/expression.py +0 -41
  181. python_liquid-2.3.5/liquid/extra/__init__.py +0 -98
  182. python_liquid-2.3.5/liquid/extra/filters/__init__.py +0 -33
  183. python_liquid-2.3.5/liquid/extra/filters/_json.py +0 -33
  184. python_liquid-2.3.5/liquid/extra/filters/array.py +0 -60
  185. python_liquid-2.3.5/liquid/extra/filters/babel.py +0 -552
  186. python_liquid-2.3.5/liquid/extra/filters/html.py +0 -32
  187. python_liquid-2.3.5/liquid/extra/filters/translate.py +0 -448
  188. python_liquid-2.3.5/liquid/extra/tags/__init__.py +0 -17
  189. python_liquid-2.3.5/liquid/extra/tags/_with.py +0 -80
  190. python_liquid-2.3.5/liquid/extra/tags/extends_tag.py +0 -576
  191. python_liquid-2.3.5/liquid/extra/tags/macro_tag.py +0 -270
  192. python_liquid-2.3.5/liquid/extra/tags/snippet_tag.py +0 -107
  193. python_liquid-2.3.5/liquid/extra/tags/translate_tag.py +0 -388
  194. python_liquid-2.3.5/liquid/filter.py +0 -230
  195. python_liquid-2.3.5/liquid/future/__init__.py +0 -3
  196. python_liquid-2.3.5/liquid/future/environment.py +0 -30
  197. python_liquid-2.3.5/liquid/future/filters/__init__.py +0 -1
  198. python_liquid-2.3.5/liquid/future/tags/__init__.py +0 -1
  199. python_liquid-2.3.5/liquid/lex.py +0 -260
  200. python_liquid-2.3.5/liquid/limits.py +0 -66
  201. python_liquid-2.3.5/liquid/loader.py +0 -141
  202. python_liquid-2.3.5/liquid/messages.py +0 -357
  203. python_liquid-2.3.5/liquid/mode.py +0 -12
  204. python_liquid-2.3.5/liquid/output.py +0 -35
  205. python_liquid-2.3.5/liquid/parser.py +0 -119
  206. python_liquid-2.3.5/liquid/span.py +0 -35
  207. python_liquid-2.3.5/liquid/static_analysis.py +0 -443
  208. python_liquid-2.3.5/liquid/stream.py +0 -148
  209. python_liquid-2.3.5/liquid/stringify.py +0 -31
  210. python_liquid-2.3.5/liquid/tag.py +0 -47
  211. python_liquid-2.3.5/liquid/template.py +0 -658
  212. python_liquid-2.3.5/liquid/token.py +0 -145
  213. python_liquid-2.3.5/liquid/undefined.py +0 -218
  214. python_liquid-2.3.5/liquid/utils/__init__.py +0 -15
  215. python_liquid-2.3.5/liquid/utils/html.py +0 -57
  216. python_liquid-2.3.5/liquid/utils/text.py +0 -28
  217. python_liquid-2.3.5/pyproject.toml +0 -234
  218. {python_liquid-2.3.5 → python_liquid-3.0.0}/liquid/py.typed +0 -0
@@ -52,6 +52,7 @@ coverage.xml
52
52
  # Sphinx documentation
53
53
  docs/_build/
54
54
  site/
55
+ .doctrees
55
56
 
56
57
  # PyBuilder
57
58
  target/
@@ -81,4 +82,6 @@ ENV/
81
82
 
82
83
  # Dev utils
83
84
  dev.py
84
- dump_golden.py
85
+ dump_golden.py
86
+ pr.md
87
+ pr.rst
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 - 2023 James Prior
3
+ Copyright (c) 2020 - 2026 James Prior
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-liquid
3
- Version: 2.3.5
3
+ Version: 3.0.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/
@@ -13,22 +13,18 @@ License-File: LICENSE
13
13
  Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python
20
17
  Classifier: Programming Language :: Python :: 3.12
21
18
  Classifier: Programming Language :: Python :: 3.13
22
19
  Classifier: Programming Language :: Python :: 3.14
23
20
  Classifier: Programming Language :: Python :: 3.15
24
21
  Classifier: Programming Language :: Python :: Implementation :: CPython
25
22
  Classifier: Programming Language :: Python :: Implementation :: PyPy
26
- Requires-Python: >=3.9
23
+ Requires-Python: >=3.12
27
24
  Requires-Dist: babel>=2
28
- Requires-Dist: importlib-resources>=5.12
29
25
  Requires-Dist: markupsafe>=3
30
- Requires-Dist: python-dateutil>=2.8.1
31
- Requires-Dist: pytz
26
+ Requires-Dist: python-dateutil
27
+ Requires-Dist: tzdata
32
28
  Description-Content-Type: text/markdown
33
29
 
34
30
  <h1 align="center">Python Liquid</h1>
@@ -77,28 +73,37 @@ We follow <a href="https://github.com/Shopify/liquid">Shopify/Liquid</a> closely
77
73
 
78
74
  ## Install
79
75
 
80
- Install Python Liquid using [Pipenv](https://pipenv.pypa.io/en/latest/):
76
+ Install `python-liquid` from [PyPi](https://pypi.org/project/python-liquid) using your favorite package manager.
77
+
78
+ **pip**
79
+
80
+ ```shell
81
+ pip install python-liquid
82
+ ```
83
+
84
+ **uv**
81
85
 
82
86
  ```shell
83
- $ pipenv install -u python-liquid
87
+ uv add python-liquid
84
88
  ```
85
89
 
86
- Or [pip](https://pip.pypa.io/en/stable/getting-started/):
90
+ **pipenv**
87
91
 
88
92
  ```shell
89
- $ pip install python-liquid
93
+ pipenv install -u python-liquid
90
94
  ```
91
95
 
92
- Or from [conda-forge](https://anaconda.org/conda-forge/python-liquid):
96
+ **conda**
93
97
 
94
98
  ```shell
95
- $ conda install -c conda-forge python-liquid
99
+ conda install -c conda-forge python-liquid
96
100
  ```
97
101
 
98
102
  ## Links
99
103
 
100
104
  - Documentation: https://jg-rp.github.io/liquid/
101
105
  - Documentation for Python Liquid version 1.x: https://jg-rp.github.io/python-liquid-docs-archive/
106
+ - Documentation for Python Liquid version 2.x: https://github.com/jg-rp/liquid/blob/v2.3.6/docs/index.md
102
107
  - Change Log: https://github.com/jg-rp/liquid/blob/main/CHANGES.md
103
108
  - PyPi: https://pypi.org/project/python-liquid/
104
109
  - Source Code: https://github.com/jg-rp/liquid
@@ -115,18 +120,17 @@ $ conda install -c conda-forge python-liquid
115
120
 
116
121
  ### `render()`
117
122
 
118
- This example renders a template from a string of text with the package-level `render()` function. The template has just one placeholder variable `you`, which we've given the value `"World"`.
123
+ Use the package-level `liquid.render()` function to render a template with Liquid's default configuration. The first argument is template source code and the second is a dictionary of template variables.
119
124
 
120
125
  ```python
121
- from liquid import render
126
+ import liquid
122
127
 
123
- print(render("Hello, {{ you }}!", you="World"))
124
- # Hello, World!
128
+ print(liquid.render("Hello, {{ you }}!", {"you": "World"})) # Hello, World!
125
129
  ```
126
130
 
127
131
  ### `parse()`
128
132
 
129
- Often you'll want to render the same template several times with different variables. We can parse source text without immediately rendering it using the `parse()` function. `parse()` returns a `BoundTemplate` instance with a `render()` method.
133
+ Use `liquid.parse()` to get an instance of `liquid.Template`, which can be rendered multiple times with different data.
130
134
 
131
135
  ```python
132
136
  from liquid import parse
@@ -138,33 +142,22 @@ print(template.render(you="Liquid")) # Hello, Liquid!
138
142
 
139
143
  ### Configure
140
144
 
141
- Both `parse()` and `render()` are convenience functions that use the default Liquid environment. For all but the simplest cases, you'll want to configure an instance of `Environment`, then load and render templates from that.
145
+ You'll need to create an instance of `liquid.Environment` to configure Liquid, then load and render templates from that.
142
146
 
143
147
  ```python
144
- from liquid import CachingFileSystemLoader
145
- from liquid import Environment
148
+ from liquid import Environment, CachingFileSystemLoader
146
149
 
147
150
  env = Environment(
148
- autoescape=True,
149
- loader=CachingFileSystemLoader("./templates"),
151
+ autoescape=True,
152
+ loader=CachingFileSystemLoader("./templates")
150
153
  )
151
- ```
152
-
153
- Then, using `env.parse()` or `env.get_template()`, we can create a `BoundTemplate` from a string or read from the file system, respectively.
154
-
155
- ```python
156
- # ... continued from above
157
- template = env.parse("Hello, {{ you }}!")
158
- print(template.render(you="World")) # Hello, World!
159
154
 
160
- # Try to load "./templates/index.html"
161
- another_template = env.get_template("index.html")
162
- data = {"some": {"thing": [1, 2, 3]}}
163
- result = another_template.render(**data)
155
+ template = env.get_template("index.liquid")
156
+ another_template = env.parse("{% render 'greeting' %}, World!")
164
157
  ```
165
158
 
166
- Unless you happen to have a relative folder called `templates` with a file called `index.html` within it, we would expect a `TemplateNotFoundError` to be raised when running the example above.
159
+ See [docs](https://jg-rp.github.io/liquid/).
167
160
 
168
161
  ## Contributing
169
162
 
170
- Please see [Contributing to Python Liquid](https://github.com/jg-rp/liquid/blob/main/contributing.md).
163
+ Please see [Contributing to Python Liquid](https://github.com/jg-rp/liquid/blob/main/CONTRIBUTING.md).
@@ -44,28 +44,37 @@ We follow <a href="https://github.com/Shopify/liquid">Shopify/Liquid</a> closely
44
44
 
45
45
  ## Install
46
46
 
47
- Install Python Liquid using [Pipenv](https://pipenv.pypa.io/en/latest/):
47
+ Install `python-liquid` from [PyPi](https://pypi.org/project/python-liquid) using your favorite package manager.
48
+
49
+ **pip**
50
+
51
+ ```shell
52
+ pip install python-liquid
53
+ ```
54
+
55
+ **uv**
48
56
 
49
57
  ```shell
50
- $ pipenv install -u python-liquid
58
+ uv add python-liquid
51
59
  ```
52
60
 
53
- Or [pip](https://pip.pypa.io/en/stable/getting-started/):
61
+ **pipenv**
54
62
 
55
63
  ```shell
56
- $ pip install python-liquid
64
+ pipenv install -u python-liquid
57
65
  ```
58
66
 
59
- Or from [conda-forge](https://anaconda.org/conda-forge/python-liquid):
67
+ **conda**
60
68
 
61
69
  ```shell
62
- $ conda install -c conda-forge python-liquid
70
+ conda install -c conda-forge python-liquid
63
71
  ```
64
72
 
65
73
  ## Links
66
74
 
67
75
  - Documentation: https://jg-rp.github.io/liquid/
68
76
  - Documentation for Python Liquid version 1.x: https://jg-rp.github.io/python-liquid-docs-archive/
77
+ - Documentation for Python Liquid version 2.x: https://github.com/jg-rp/liquid/blob/v2.3.6/docs/index.md
69
78
  - Change Log: https://github.com/jg-rp/liquid/blob/main/CHANGES.md
70
79
  - PyPi: https://pypi.org/project/python-liquid/
71
80
  - Source Code: https://github.com/jg-rp/liquid
@@ -82,18 +91,17 @@ $ conda install -c conda-forge python-liquid
82
91
 
83
92
  ### `render()`
84
93
 
85
- This example renders a template from a string of text with the package-level `render()` function. The template has just one placeholder variable `you`, which we've given the value `"World"`.
94
+ Use the package-level `liquid.render()` function to render a template with Liquid's default configuration. The first argument is template source code and the second is a dictionary of template variables.
86
95
 
87
96
  ```python
88
- from liquid import render
97
+ import liquid
89
98
 
90
- print(render("Hello, {{ you }}!", you="World"))
91
- # Hello, World!
99
+ print(liquid.render("Hello, {{ you }}!", {"you": "World"})) # Hello, World!
92
100
  ```
93
101
 
94
102
  ### `parse()`
95
103
 
96
- Often you'll want to render the same template several times with different variables. We can parse source text without immediately rendering it using the `parse()` function. `parse()` returns a `BoundTemplate` instance with a `render()` method.
104
+ Use `liquid.parse()` to get an instance of `liquid.Template`, which can be rendered multiple times with different data.
97
105
 
98
106
  ```python
99
107
  from liquid import parse
@@ -105,33 +113,22 @@ print(template.render(you="Liquid")) # Hello, Liquid!
105
113
 
106
114
  ### Configure
107
115
 
108
- Both `parse()` and `render()` are convenience functions that use the default Liquid environment. For all but the simplest cases, you'll want to configure an instance of `Environment`, then load and render templates from that.
116
+ You'll need to create an instance of `liquid.Environment` to configure Liquid, then load and render templates from that.
109
117
 
110
118
  ```python
111
- from liquid import CachingFileSystemLoader
112
- from liquid import Environment
119
+ from liquid import Environment, CachingFileSystemLoader
113
120
 
114
121
  env = Environment(
115
- autoescape=True,
116
- loader=CachingFileSystemLoader("./templates"),
122
+ autoescape=True,
123
+ loader=CachingFileSystemLoader("./templates")
117
124
  )
118
- ```
119
-
120
- Then, using `env.parse()` or `env.get_template()`, we can create a `BoundTemplate` from a string or read from the file system, respectively.
121
-
122
- ```python
123
- # ... continued from above
124
- template = env.parse("Hello, {{ you }}!")
125
- print(template.render(you="World")) # Hello, World!
126
125
 
127
- # Try to load "./templates/index.html"
128
- another_template = env.get_template("index.html")
129
- data = {"some": {"thing": [1, 2, 3]}}
130
- result = another_template.render(**data)
126
+ template = env.get_template("index.liquid")
127
+ another_template = env.parse("{% render 'greeting' %}, World!")
131
128
  ```
132
129
 
133
- Unless you happen to have a relative folder called `templates` with a file called `index.html` within it, we would expect a `TemplateNotFoundError` to be raised when running the example above.
130
+ See [docs](https://jg-rp.github.io/liquid/).
134
131
 
135
132
  ## Contributing
136
133
 
137
- Please see [Contributing to Python Liquid](https://github.com/jg-rp/liquid/blob/main/contributing.md).
134
+ Please see [Contributing to Python Liquid](https://github.com/jg-rp/liquid/blob/main/CONTRIBUTING.md).
@@ -0,0 +1 @@
1
+ __version__ = "3.0.0"
@@ -0,0 +1,220 @@
1
+ from collections.abc import Iterator, Mapping
2
+ from typing import Any, TextIO
3
+
4
+ from ._base_tag import AbstractTag
5
+ from ._context import RenderContext
6
+ from ._drop import (
7
+ ContextHint,
8
+ LiquidAsyncIterable,
9
+ LiquidAsyncObject,
10
+ LiquidIterable,
11
+ LiquidObject,
12
+ LiquidPrimitive,
13
+ )
14
+ from ._filter import Filter, FilterContext
15
+ from ._io import StringBuffer
16
+ from ._lax_parser_mixin import LaxParserMixin
17
+ from ._lexer import Lexer
18
+ from ._lexer_extended import ExtendedLexer
19
+ from ._lexer_legacy import LegacyLexer
20
+ from ._loader import Loader, TemplateSource
21
+ from ._markup import (
22
+ Block,
23
+ Expression,
24
+ Markup,
25
+ Node,
26
+ Partial,
27
+ Scope,
28
+ Tag,
29
+ render_block,
30
+ render_block_async,
31
+ )
32
+ from ._messages import (
33
+ MessageTuple,
34
+ Translations,
35
+ extract_from_template,
36
+ extract_from_templates,
37
+ )
38
+ from ._nothing import NOTHING
39
+ from ._parser import Parser
40
+ from ._parser_extended import ExtendedParser
41
+ from ._parser_legacy import LegacyParser
42
+ from ._resource_limits import ResourceLimits
43
+ from ._slice import slice_range, slice_sequence
44
+ from ._static_analysis import Segments, TemplateAnalysis
45
+ from ._template import Template
46
+ from .drops import (
47
+ AsyncIterableDrop,
48
+ FalsyStrictUndefined,
49
+ IterableDrop,
50
+ StrictUndefined,
51
+ Undefined,
52
+ )
53
+ from .environment import Environment
54
+ from .loaders import (
55
+ CachingChoiceLoader,
56
+ CachingDictLoader,
57
+ CachingFileSystemLoader,
58
+ CachingLoaderMixin,
59
+ ChoiceLoader,
60
+ DictLoader,
61
+ FileSystemLoader,
62
+ PackageLoader,
63
+ )
64
+ from .tokens import TOKEN_NAMES, Token, TokenKind, span, token_value
65
+
66
+ __all__ = (
67
+ "NOTHING",
68
+ "TOKEN_NAMES",
69
+ "AbstractTag",
70
+ "AsyncIterableDrop",
71
+ "Block",
72
+ "CachingChoiceLoader",
73
+ "CachingDictLoader",
74
+ "CachingFileSystemLoader",
75
+ "CachingLoaderMixin",
76
+ "ChoiceLoader",
77
+ "ContextHint",
78
+ "DictLoader",
79
+ "Environment",
80
+ "Expression",
81
+ "ExtendedLexer",
82
+ "ExtendedParser",
83
+ "FalsyStrictUndefined",
84
+ "FileSystemLoader",
85
+ "Filter",
86
+ "FilterContext",
87
+ "IterableDrop",
88
+ "LaxExtendedParser",
89
+ "LaxLegacyParser",
90
+ "LaxParserMixin",
91
+ "LegacyLexer",
92
+ "LegacyParser",
93
+ "Lexer",
94
+ "LiquidAsyncIterable",
95
+ "LiquidAsyncObject",
96
+ "LiquidIterable",
97
+ "LiquidObject",
98
+ "LiquidPrimitive",
99
+ "Loader",
100
+ "Markup",
101
+ "Node",
102
+ "PackageLoader",
103
+ "Parser",
104
+ "Partial",
105
+ "RenderContext",
106
+ "ResourceLimits",
107
+ "Scope",
108
+ "Segments",
109
+ "StrictUndefined",
110
+ "StringBuffer",
111
+ "Tag",
112
+ "Template",
113
+ "TemplateAnalysis",
114
+ "TemplateSource",
115
+ "Token",
116
+ "TokenKind",
117
+ "Translations",
118
+ "Undefined",
119
+ "extract_from_templates",
120
+ "render_block",
121
+ "render_block_async",
122
+ "slice_range",
123
+ "slice_sequence",
124
+ "span",
125
+ "token_value",
126
+ )
127
+
128
+
129
+ DEFAULT_ENVIRONMENT = Environment()
130
+
131
+
132
+ def parse(
133
+ source: str,
134
+ *,
135
+ name: str = "",
136
+ globals: Mapping[str, Any] | None = None,
137
+ ) -> Template:
138
+ """Parse template source code `source` using the default Liquid environment.
139
+
140
+ Parameters
141
+ ----------
142
+ source
143
+ Template source code.
144
+ name
145
+ A name for the template that will appear in error messages.
146
+ globals
147
+ Variables to pin to the resulting template.
148
+ """
149
+ return DEFAULT_ENVIRONMENT.parse(source, name=name, globals=globals)
150
+
151
+
152
+ def render(source: str, data: Mapping[str, Any] | None = None) -> str:
153
+ """Parse and render `source` with variables from `data` using the default environment.
154
+
155
+ Parameters
156
+ ----------
157
+ source
158
+ Template source code.
159
+ data
160
+ Template variables.
161
+ """
162
+ return DEFAULT_ENVIRONMENT.render(source, data)
163
+
164
+
165
+ async def render_async(source: str, data: Mapping[str, Any] | None = None) -> str:
166
+ """Parse and render `source` asynchronously with variables from `data` using the
167
+ default environment.
168
+
169
+ Parameters
170
+ ----------
171
+ source
172
+ Template source code.
173
+ data
174
+ Template variables.
175
+ """
176
+ return await DEFAULT_ENVIRONMENT.render_async(source, data)
177
+
178
+
179
+ class LaxLegacyParser(LaxParserMixin, LegacyParser):
180
+ """A legacy parser that suppresses Liquid syntax errors.
181
+
182
+ Use ``Template.errors()`` to get a list of available ``LiquidSyntaxError`` instances.
183
+ """
184
+
185
+
186
+ class LaxExtendedParser(LaxParserMixin, ExtendedParser):
187
+ """An extended parser that suppresses Liquid syntax errors.
188
+
189
+ Use ``Template.errors()`` to get a list of available ``LiquidSyntaxError`` instances.
190
+ """
191
+
192
+
193
+ def extract_liquid(
194
+ fileobj: TextIO,
195
+ keywords: list[str],
196
+ comment_tags: list[str] | None = None,
197
+ options: Mapping[str, Any] | None = None,
198
+ ) -> Iterator[MessageTuple]:
199
+ """A babel compatible translation message extraction method for Liquid templates.
200
+
201
+ See https://babel.pocoo.org/en/latest/messages.html
202
+
203
+ Keywords are the names of Liquid filters or tags operating on translatable strings.
204
+ For a filter to contribute to message extraction, it must also appear as a child of
205
+ a `FilteredExpression` and be a `TranslatableFilter`. Similarly, tags must produce a
206
+ node that is a `TranslatableTag`.
207
+
208
+ Where a Liquid comment contains a prefix in `comment_tags`, the comment will be
209
+ attached to the translatable filter or tag immediately following the comment. Python
210
+ Liquid's non-standard shorthand comments are not supported.
211
+
212
+ Use ``extract_from_template`` to extract messages from an existing template bound to
213
+ an existing environment.
214
+ """
215
+ template = Environment(extra=True).parse(fileobj.read())
216
+ return extract_from_template(
217
+ template=template,
218
+ keywords=keywords,
219
+ comment_tags=comment_tags,
220
+ )
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from ._context import RenderContext
8
+ from ._io import StringBuffer
9
+ from ._markup import Expression, Markup, Partial
10
+ from ._parser import Parser
11
+ from .expressions import Name
12
+ from .tokens import Token
13
+
14
+
15
+ class AbstractTag(ABC):
16
+ """An abstract class for implementing custom tags.
17
+
18
+ It is not necessary to inherit from `AbstractTag` when implementing custom tags, but
19
+ it can be convenient if you're not interested in methods supporting static template
20
+ analysis, saving you from writing several empty methods.
21
+
22
+ Internally we use the `Markup` and `Tag` protocols, without a required base class.
23
+ """
24
+
25
+ @classmethod
26
+ @abstractmethod
27
+ def parse(cls, token: Token, parser: Parser) -> Markup:
28
+ """Parse tokens or text from `parser` into a markup node."""
29
+
30
+ @abstractmethod
31
+ def render(self, context: RenderContext, buffer: StringBuffer) -> None:
32
+ """Render this node to `buffer` using data from `context`."""
33
+
34
+ @abstractmethod
35
+ async def render_async(self, context: RenderContext, buffer: StringBuffer) -> None:
36
+ """Render this node to `buffer` asynchronously using data from `context`."""
37
+
38
+ def children(self) -> list[Markup]:
39
+ """Returns markup nodes that are direct children of this node in the syntax tree.
40
+
41
+ This is used to traverse template syntax trees during static analysis.
42
+ """
43
+ return []
44
+
45
+ def expressions(self) -> list[Expression]:
46
+ """Return expression owned by this node."""
47
+ return []
48
+
49
+ def block_scope(self) -> list[Name]:
50
+ """Return variable names that are in scope for the duration of this node's block.
51
+
52
+ This is used during static analysis to isolate and report global variables.
53
+ """
54
+ return []
55
+
56
+ def template_scope(self) -> list[Name]:
57
+ """Return variable names that this tag adds to the template scope."""
58
+ return []
59
+
60
+ def partials(self, context: RenderContext) -> list[Partial]:
61
+ """Return meta data about partial templates rendered from this node."""
62
+ return []
63
+
64
+ async def partials_async(self, context: RenderContext) -> list[Partial]:
65
+ """Return meta data about partial templates rendered from this node."""
66
+ return []
@@ -3,26 +3,29 @@
3
3
  Based on "A greatly simplified read-only version of Chainmap" by Raymond Hettinger.
4
4
  https://code.activestate.com/recipes/305268/
5
5
  """
6
+
6
7
  from collections import deque
8
+ from collections.abc import Iterator, Mapping
7
9
  from itertools import chain
8
- from typing import Any
9
- from typing import Iterator
10
- from typing import Mapping
10
+
11
+ from ._missing import MISSING
11
12
 
12
13
 
13
14
  class ReadOnlyChainMap(Mapping[str, object]):
14
- """Combine multiple mappings for sequential lookup."""
15
+ """Combine multiple mappings for sequential lookup.
16
+
17
+ This chain map never raises a KeyError. It returns the MISSING sentinel
18
+ if a key does not exist in the chain.
19
+ """
15
20
 
16
21
  def __init__(self, *maps: Mapping[str, object]):
17
22
  self._maps = deque(maps)
18
23
 
19
24
  def __getitem__(self, key: str) -> object:
20
25
  for mapping in self._maps:
21
- try:
26
+ if key in mapping:
22
27
  return mapping[key]
23
- except KeyError:
24
- pass
25
- raise KeyError(key)
28
+ return MISSING
26
29
 
27
30
  def __iter__(self) -> Iterator[str]:
28
31
  return chain(*self._maps)
@@ -30,21 +33,17 @@ class ReadOnlyChainMap(Mapping[str, object]):
30
33
  def __len__(self) -> int:
31
34
  return sum(len(_map) for _map in self._maps)
32
35
 
36
+ def __contains__(self, key: object) -> bool:
37
+ return any(key in mapping for mapping in self._maps)
38
+
33
39
  def size(self) -> int:
34
- """Return the number of maps in the chain."""
35
40
  return len(self._maps)
36
41
 
37
- def get(self, key: str, default: object = None) -> object:
38
- """Return the value for key if key is in the dictionary, else default."""
39
- try:
40
- return self[key]
41
- except KeyError:
42
- return default
42
+ def get(self, key: str, default: object = MISSING) -> object:
43
+ return self[key]
43
44
 
44
- def push(self, namespace: Mapping[Any, Any]) -> None:
45
- """Add a mapping to the front of the chain map."""
45
+ def push(self, namespace: Mapping[str, object]) -> None:
46
46
  self._maps.appendleft(namespace)
47
47
 
48
- def pop(self) -> Mapping[Any, Any]:
49
- """Remove a mapping from the front of the chain map."""
48
+ def pop(self) -> Mapping[str, object]:
50
49
  return self._maps.popleft()