Cython 3.3.0a1__cp315-cp315-win_amd64.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 (335) hide show
  1. Cython/Build/BuildExecutable.py +156 -0
  2. Cython/Build/Cache.py +199 -0
  3. Cython/Build/Cythonize.py +349 -0
  4. Cython/Build/Dependencies.py +1276 -0
  5. Cython/Build/Distutils.py +1 -0
  6. Cython/Build/Inline.py +467 -0
  7. Cython/Build/IpythonMagic.py +559 -0
  8. Cython/Build/SharedModule.py +84 -0
  9. Cython/Build/Tests/TestCyCache.py +195 -0
  10. Cython/Build/Tests/TestCythonizeArgsParser.py +480 -0
  11. Cython/Build/Tests/TestDependencies.py +133 -0
  12. Cython/Build/Tests/TestInline.py +177 -0
  13. Cython/Build/Tests/TestIpythonMagic.py +303 -0
  14. Cython/Build/Tests/TestRecythonize.py +212 -0
  15. Cython/Build/Tests/TestStripLiterals.py +155 -0
  16. Cython/Build/Tests/__init__.py +1 -0
  17. Cython/Build/__init__.py +11 -0
  18. Cython/CodeWriter.py +815 -0
  19. Cython/Compiler/AnalysedTreeTransforms.py +97 -0
  20. Cython/Compiler/Annotate.py +328 -0
  21. Cython/Compiler/AutoDocTransforms.py +320 -0
  22. Cython/Compiler/Buffer.py +680 -0
  23. Cython/Compiler/Builtin.py +997 -0
  24. Cython/Compiler/CmdLine.py +263 -0
  25. Cython/Compiler/Code.cp315-win_amd64.pyd +0 -0
  26. Cython/Compiler/Code.pxd +152 -0
  27. Cython/Compiler/Code.py +3907 -0
  28. Cython/Compiler/CodeGeneration.py +33 -0
  29. Cython/Compiler/CythonScope.py +194 -0
  30. Cython/Compiler/Dataclass.py +890 -0
  31. Cython/Compiler/DebugFlags.py +24 -0
  32. Cython/Compiler/Errors.py +310 -0
  33. Cython/Compiler/ExprNodes.py +15983 -0
  34. Cython/Compiler/FlowControl.cp315-win_amd64.pyd +0 -0
  35. Cython/Compiler/FlowControl.pxd +99 -0
  36. Cython/Compiler/FlowControl.py +1571 -0
  37. Cython/Compiler/FusedNode.cp315-win_amd64.pyd +0 -0
  38. Cython/Compiler/FusedNode.py +976 -0
  39. Cython/Compiler/Future.py +16 -0
  40. Cython/Compiler/Interpreter.py +57 -0
  41. Cython/Compiler/Lexicon.py +422 -0
  42. Cython/Compiler/LineTable.cp315-win_amd64.pyd +0 -0
  43. Cython/Compiler/LineTable.py +114 -0
  44. Cython/Compiler/Main.py +856 -0
  45. Cython/Compiler/MatchCaseNodes.py +2197 -0
  46. Cython/Compiler/MemoryView.py +930 -0
  47. Cython/Compiler/ModuleNode.py +4517 -0
  48. Cython/Compiler/Naming.py +367 -0
  49. Cython/Compiler/Nodes.py +10941 -0
  50. Cython/Compiler/Optimize.py +5455 -0
  51. Cython/Compiler/Options.py +838 -0
  52. Cython/Compiler/ParseTreeTransforms.pxd +79 -0
  53. Cython/Compiler/ParseTreeTransforms.py +4744 -0
  54. Cython/Compiler/Parsing.cp315-win_amd64.pyd +0 -0
  55. Cython/Compiler/Parsing.pxd +9 -0
  56. Cython/Compiler/Parsing.py +4792 -0
  57. Cython/Compiler/Pipeline.py +439 -0
  58. Cython/Compiler/PyrexTypes.py +6111 -0
  59. Cython/Compiler/Pythran.py +232 -0
  60. Cython/Compiler/Scanning.cp315-win_amd64.pyd +0 -0
  61. Cython/Compiler/Scanning.pxd +70 -0
  62. Cython/Compiler/Scanning.py +720 -0
  63. Cython/Compiler/StringEncoding.py +297 -0
  64. Cython/Compiler/Symtab.py +3092 -0
  65. Cython/Compiler/Tests/TestBuffer.py +105 -0
  66. Cython/Compiler/Tests/TestBuiltin.py +117 -0
  67. Cython/Compiler/Tests/TestCmdLine.py +587 -0
  68. Cython/Compiler/Tests/TestCode.py +145 -0
  69. Cython/Compiler/Tests/TestFlowControl.py +65 -0
  70. Cython/Compiler/Tests/TestGrammar.py +202 -0
  71. Cython/Compiler/Tests/TestMemView.py +71 -0
  72. Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
  73. Cython/Compiler/Tests/TestScanning.py +132 -0
  74. Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
  75. Cython/Compiler/Tests/TestStringEncoding.py +20 -0
  76. Cython/Compiler/Tests/TestTreeFragment.py +63 -0
  77. Cython/Compiler/Tests/TestTreePath.py +103 -0
  78. Cython/Compiler/Tests/TestTypes.py +118 -0
  79. Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
  80. Cython/Compiler/Tests/TestVisitor.py +61 -0
  81. Cython/Compiler/Tests/Utils.py +36 -0
  82. Cython/Compiler/Tests/__init__.py +1 -0
  83. Cython/Compiler/TreeFragment.py +278 -0
  84. Cython/Compiler/TreePath.py +303 -0
  85. Cython/Compiler/TypeInference.py +611 -0
  86. Cython/Compiler/TypeSlots.py +1329 -0
  87. Cython/Compiler/UFuncs.py +311 -0
  88. Cython/Compiler/UtilNodes.py +413 -0
  89. Cython/Compiler/UtilityCode.py +348 -0
  90. Cython/Compiler/Version.py +8 -0
  91. Cython/Compiler/Visitor.cp315-win_amd64.pyd +0 -0
  92. Cython/Compiler/Visitor.pxd +53 -0
  93. Cython/Compiler/Visitor.py +864 -0
  94. Cython/Compiler/__init__.py +1 -0
  95. Cython/Coverage.py +448 -0
  96. Cython/Debugger/Cygdb.py +177 -0
  97. Cython/Debugger/DebugWriter.py +82 -0
  98. Cython/Debugger/Tests/TestLibCython.py +280 -0
  99. Cython/Debugger/Tests/__init__.py +1 -0
  100. Cython/Debugger/Tests/cfuncs.c +8 -0
  101. Cython/Debugger/Tests/codefile +49 -0
  102. Cython/Debugger/Tests/test_libcython_in_gdb.py +580 -0
  103. Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
  104. Cython/Debugger/__init__.py +1 -0
  105. Cython/Debugger/libcython.py +1548 -0
  106. Cython/Debugger/libpython.py +2821 -0
  107. Cython/Debugging.py +20 -0
  108. Cython/Distutils/__init__.py +2 -0
  109. Cython/Distutils/build_ext.py +139 -0
  110. Cython/Distutils/extension.py +96 -0
  111. Cython/Distutils/old_build_ext.py +351 -0
  112. Cython/Includes/cpython/__init__.pxd +173 -0
  113. Cython/Includes/cpython/array.pxd +152 -0
  114. Cython/Includes/cpython/bool.pxd +37 -0
  115. Cython/Includes/cpython/buffer.pxd +112 -0
  116. Cython/Includes/cpython/bytearray.pxd +33 -0
  117. Cython/Includes/cpython/bytes.pxd +200 -0
  118. Cython/Includes/cpython/cellobject.pxd +35 -0
  119. Cython/Includes/cpython/ceval.pxd +8 -0
  120. Cython/Includes/cpython/codecs.pxd +121 -0
  121. Cython/Includes/cpython/complex.pxd +60 -0
  122. Cython/Includes/cpython/contextvars.pxd +145 -0
  123. Cython/Includes/cpython/conversion.pxd +36 -0
  124. Cython/Includes/cpython/datetime.pxd +395 -0
  125. Cython/Includes/cpython/descr.pxd +26 -0
  126. Cython/Includes/cpython/dict.pxd +268 -0
  127. Cython/Includes/cpython/exc.pxd +263 -0
  128. Cython/Includes/cpython/fileobject.pxd +57 -0
  129. Cython/Includes/cpython/float.pxd +56 -0
  130. Cython/Includes/cpython/frozendict.pxd +37 -0
  131. Cython/Includes/cpython/function.pxd +65 -0
  132. Cython/Includes/cpython/genobject.pxd +25 -0
  133. Cython/Includes/cpython/getargs.pxd +12 -0
  134. Cython/Includes/cpython/instance.pxd +25 -0
  135. Cython/Includes/cpython/iterator.pxd +36 -0
  136. Cython/Includes/cpython/iterobject.pxd +24 -0
  137. Cython/Includes/cpython/list.pxd +144 -0
  138. Cython/Includes/cpython/long.pxd +180 -0
  139. Cython/Includes/cpython/longintrepr.pxd +14 -0
  140. Cython/Includes/cpython/mapping.pxd +63 -0
  141. Cython/Includes/cpython/marshal.pxd +66 -0
  142. Cython/Includes/cpython/mem.pxd +120 -0
  143. Cython/Includes/cpython/memoryview.pxd +50 -0
  144. Cython/Includes/cpython/method.pxd +49 -0
  145. Cython/Includes/cpython/module.pxd +208 -0
  146. Cython/Includes/cpython/number.pxd +258 -0
  147. Cython/Includes/cpython/object.pxd +430 -0
  148. Cython/Includes/cpython/pycapsule.pxd +143 -0
  149. Cython/Includes/cpython/pylifecycle.pxd +68 -0
  150. Cython/Includes/cpython/pyport.pxd +8 -0
  151. Cython/Includes/cpython/pystate.pxd +95 -0
  152. Cython/Includes/cpython/pythread.pxd +53 -0
  153. Cython/Includes/cpython/ref.pxd +141 -0
  154. Cython/Includes/cpython/sentinel.pxd +17 -0
  155. Cython/Includes/cpython/sequence.pxd +134 -0
  156. Cython/Includes/cpython/set.pxd +119 -0
  157. Cython/Includes/cpython/slice.pxd +70 -0
  158. Cython/Includes/cpython/time.pxd +129 -0
  159. Cython/Includes/cpython/tuple.pxd +72 -0
  160. Cython/Includes/cpython/type.pxd +146 -0
  161. Cython/Includes/cpython/unicode.pxd +639 -0
  162. Cython/Includes/cpython/version.pxd +32 -0
  163. Cython/Includes/cpython/weakref.pxd +78 -0
  164. Cython/Includes/libc/__init__.pxd +1 -0
  165. Cython/Includes/libc/complex.pxd +35 -0
  166. Cython/Includes/libc/errno.pxd +127 -0
  167. Cython/Includes/libc/float.pxd +43 -0
  168. Cython/Includes/libc/limits.pxd +28 -0
  169. Cython/Includes/libc/locale.pxd +46 -0
  170. Cython/Includes/libc/math.pxd +209 -0
  171. Cython/Includes/libc/setjmp.pxd +10 -0
  172. Cython/Includes/libc/signal.pxd +64 -0
  173. Cython/Includes/libc/stddef.pxd +9 -0
  174. Cython/Includes/libc/stdint.pxd +105 -0
  175. Cython/Includes/libc/stdio.pxd +80 -0
  176. Cython/Includes/libc/stdlib.pxd +72 -0
  177. Cython/Includes/libc/string.pxd +50 -0
  178. Cython/Includes/libc/threads.pxd +234 -0
  179. Cython/Includes/libc/time.pxd +52 -0
  180. Cython/Includes/libcpp/__init__.pxd +4 -0
  181. Cython/Includes/libcpp/algorithm.pxd +320 -0
  182. Cython/Includes/libcpp/any.pxd +16 -0
  183. Cython/Includes/libcpp/atomic.pxd +59 -0
  184. Cython/Includes/libcpp/barrier.pxd +22 -0
  185. Cython/Includes/libcpp/bit.pxd +29 -0
  186. Cython/Includes/libcpp/cast.pxd +12 -0
  187. Cython/Includes/libcpp/cmath.pxd +518 -0
  188. Cython/Includes/libcpp/complex.pxd +106 -0
  189. Cython/Includes/libcpp/condition_variable.pxd +322 -0
  190. Cython/Includes/libcpp/deque.pxd +165 -0
  191. Cython/Includes/libcpp/exception.pxd +216 -0
  192. Cython/Includes/libcpp/execution.pxd +15 -0
  193. Cython/Includes/libcpp/forward_list.pxd +63 -0
  194. Cython/Includes/libcpp/functional.pxd +26 -0
  195. Cython/Includes/libcpp/future.pxd +103 -0
  196. Cython/Includes/libcpp/iterator.pxd +34 -0
  197. Cython/Includes/libcpp/latch.pxd +17 -0
  198. Cython/Includes/libcpp/limits.pxd +61 -0
  199. Cython/Includes/libcpp/list.pxd +117 -0
  200. Cython/Includes/libcpp/map.pxd +252 -0
  201. Cython/Includes/libcpp/memory.pxd +115 -0
  202. Cython/Includes/libcpp/mutex.pxd +387 -0
  203. Cython/Includes/libcpp/numbers.pxd +15 -0
  204. Cython/Includes/libcpp/numeric.pxd +131 -0
  205. Cython/Includes/libcpp/optional.pxd +34 -0
  206. Cython/Includes/libcpp/pair.pxd +1 -0
  207. Cython/Includes/libcpp/queue.pxd +25 -0
  208. Cython/Includes/libcpp/random.pxd +166 -0
  209. Cython/Includes/libcpp/semaphore.pxd +43 -0
  210. Cython/Includes/libcpp/set.pxd +228 -0
  211. Cython/Includes/libcpp/shared_mutex.pxd +96 -0
  212. Cython/Includes/libcpp/span.pxd +87 -0
  213. Cython/Includes/libcpp/stack.pxd +11 -0
  214. Cython/Includes/libcpp/stop_token.pxd +117 -0
  215. Cython/Includes/libcpp/string.pxd +355 -0
  216. Cython/Includes/libcpp/string_view.pxd +183 -0
  217. Cython/Includes/libcpp/typeindex.pxd +15 -0
  218. Cython/Includes/libcpp/typeinfo.pxd +10 -0
  219. Cython/Includes/libcpp/unordered_map.pxd +193 -0
  220. Cython/Includes/libcpp/unordered_set.pxd +152 -0
  221. Cython/Includes/libcpp/utility.pxd +30 -0
  222. Cython/Includes/libcpp/vector.pxd +186 -0
  223. Cython/Includes/numpy/math.pxd +150 -0
  224. Cython/Includes/openmp.pxd +50 -0
  225. Cython/Includes/posix/__init__.pxd +1 -0
  226. Cython/Includes/posix/dlfcn.pxd +14 -0
  227. Cython/Includes/posix/fcntl.pxd +86 -0
  228. Cython/Includes/posix/ioctl.pxd +4 -0
  229. Cython/Includes/posix/mman.pxd +101 -0
  230. Cython/Includes/posix/resource.pxd +57 -0
  231. Cython/Includes/posix/select.pxd +21 -0
  232. Cython/Includes/posix/signal.pxd +73 -0
  233. Cython/Includes/posix/stat.pxd +98 -0
  234. Cython/Includes/posix/stdio.pxd +37 -0
  235. Cython/Includes/posix/stdlib.pxd +29 -0
  236. Cython/Includes/posix/strings.pxd +9 -0
  237. Cython/Includes/posix/time.pxd +71 -0
  238. Cython/Includes/posix/types.pxd +30 -0
  239. Cython/Includes/posix/uio.pxd +26 -0
  240. Cython/Includes/posix/unistd.pxd +271 -0
  241. Cython/Includes/posix/wait.pxd +38 -0
  242. Cython/LZSS.py +170 -0
  243. Cython/Plex/Actions.cp315-win_amd64.pyd +0 -0
  244. Cython/Plex/Actions.pxd +24 -0
  245. Cython/Plex/Actions.py +119 -0
  246. Cython/Plex/DFA.cp315-win_amd64.pyd +0 -0
  247. Cython/Plex/DFA.pxd +14 -0
  248. Cython/Plex/DFA.py +164 -0
  249. Cython/Plex/Errors.py +48 -0
  250. Cython/Plex/Lexicons.py +178 -0
  251. Cython/Plex/Machines.cp315-win_amd64.pyd +0 -0
  252. Cython/Plex/Machines.pxd +36 -0
  253. Cython/Plex/Machines.py +238 -0
  254. Cython/Plex/Regexps.py +535 -0
  255. Cython/Plex/Scanners.cp315-win_amd64.pyd +0 -0
  256. Cython/Plex/Scanners.pxd +45 -0
  257. Cython/Plex/Scanners.py +328 -0
  258. Cython/Plex/Transitions.cp315-win_amd64.pyd +0 -0
  259. Cython/Plex/Transitions.pxd +14 -0
  260. Cython/Plex/Transitions.py +239 -0
  261. Cython/Plex/__init__.py +34 -0
  262. Cython/Runtime/__init__.py +1 -0
  263. Cython/Runtime/refnanny.cp315-win_amd64.pyd +0 -0
  264. Cython/Runtime/refnanny.pyx +237 -0
  265. Cython/Shadow.py +1167 -0
  266. Cython/StringIOTree.cp315-win_amd64.pyd +0 -0
  267. Cython/StringIOTree.py +169 -0
  268. Cython/Tempita/__init__.py +4 -0
  269. Cython/Tempita/_looper.py +154 -0
  270. Cython/Tempita/_tempita.cp315-win_amd64.pyd +0 -0
  271. Cython/Tempita/_tempita.py +1087 -0
  272. Cython/TestUtils.py +464 -0
  273. Cython/Tests/TestCodeWriter.py +128 -0
  274. Cython/Tests/TestCythonUtils.py +202 -0
  275. Cython/Tests/TestJediTyper.py +223 -0
  276. Cython/Tests/TestShadow.py +110 -0
  277. Cython/Tests/TestStringIOTree.py +68 -0
  278. Cython/Tests/TestTestUtils.py +89 -0
  279. Cython/Tests/__init__.py +1 -0
  280. Cython/Tests/xmlrunner.py +390 -0
  281. Cython/Utility/AsyncGen.c +1073 -0
  282. Cython/Utility/Buffer.c +866 -0
  283. Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
  284. Cython/Utility/Builtins.c +933 -0
  285. Cython/Utility/CConvert.pyx +149 -0
  286. Cython/Utility/CMath.c +104 -0
  287. Cython/Utility/CommonStructures.c +244 -0
  288. Cython/Utility/Complex.c +378 -0
  289. Cython/Utility/Coroutine.c +2337 -0
  290. Cython/Utility/CpdefEnums.pyx +107 -0
  291. Cython/Utility/CppConvert.pyx +282 -0
  292. Cython/Utility/CppSupport.cpp +151 -0
  293. Cython/Utility/CythonFunction.c +2072 -0
  294. Cython/Utility/Dataclasses.c +101 -0
  295. Cython/Utility/Embed.c +129 -0
  296. Cython/Utility/Exceptions.c +1038 -0
  297. Cython/Utility/ExtensionTypes.c +1158 -0
  298. Cython/Utility/FunctionArguments.c +1045 -0
  299. Cython/Utility/FusedFunction.pyx +44 -0
  300. Cython/Utility/ImportExport.c +930 -0
  301. Cython/Utility/MatchCase.c +979 -0
  302. Cython/Utility/MatchCase_Cy.pyx +12 -0
  303. Cython/Utility/MemoryView.pxd +108 -0
  304. Cython/Utility/MemoryView.pyx +1499 -0
  305. Cython/Utility/MemoryView_C.c +1056 -0
  306. Cython/Utility/ModuleSetupCode.c +3352 -0
  307. Cython/Utility/NumpyImportArray.c +46 -0
  308. Cython/Utility/ObjectHandling.c +3372 -0
  309. Cython/Utility/Optimize.c +2563 -0
  310. Cython/Utility/Overflow.c +378 -0
  311. Cython/Utility/Profile.c +736 -0
  312. Cython/Utility/StringTools.c +1414 -0
  313. Cython/Utility/Synchronization.c +438 -0
  314. Cython/Utility/TString.c +369 -0
  315. Cython/Utility/TestCyUtilityLoader.pyx +8 -0
  316. Cython/Utility/TestCythonScope.pyx +75 -0
  317. Cython/Utility/TestUtilityLoader.c +12 -0
  318. Cython/Utility/TypeConversion.c +1556 -0
  319. Cython/Utility/UFuncs.pyx +50 -0
  320. Cython/Utility/UFuncs_C.c +89 -0
  321. Cython/Utility/__init__.py +28 -0
  322. Cython/Utility/arrayarray.h +172 -0
  323. Cython/Utils.cp315-win_amd64.pyd +0 -0
  324. Cython/Utils.py +677 -0
  325. Cython/__init__.py +12 -0
  326. Cython/py.typed +0 -0
  327. cython-3.3.0a1.dist-info/METADATA +394 -0
  328. cython-3.3.0a1.dist-info/RECORD +335 -0
  329. cython-3.3.0a1.dist-info/WHEEL +5 -0
  330. cython-3.3.0a1.dist-info/entry_points.txt +4 -0
  331. cython-3.3.0a1.dist-info/top_level.txt +3 -0
  332. cython.py +29 -0
  333. pyximport/__init__.py +4 -0
  334. pyximport/pyxbuild.py +160 -0
  335. pyximport/pyximport.py +482 -0
@@ -0,0 +1,1087 @@
1
+ """
2
+ A small templating language
3
+
4
+ This implements a small templating language. This language implements
5
+ if/elif/else, for/continue/break, expressions, and blocks of Python
6
+ code. The syntax is::
7
+
8
+ {{any expression (function calls etc)}}
9
+ {{any expression | filter}}
10
+ {{for x in y}}...{{endfor}}
11
+ {{if x}}x{{elif y}}y{{else}}z{{endif}}
12
+ {{py:x=1}}
13
+ {{py:
14
+ def foo(bar):
15
+ return 'baz'
16
+ }}
17
+ {{default var = default_value}}
18
+ {{# comment}}
19
+
20
+ You use this with the ``Template`` class or the ``sub`` shortcut.
21
+ The ``Template`` class takes the template string and the name of
22
+ the template (for errors) and a default namespace. Then (like
23
+ ``string.Template``) you can call the ``tmpl.substitute(**kw)``
24
+ method to make a substitution (or ``tmpl.substitute(a_dict)``).
25
+
26
+ ``sub(content, **kw)`` substitutes the template immediately. You
27
+ can use ``__name='tmpl.html'`` to set the name of the template.
28
+
29
+ If there are syntax errors ``TemplateError`` will be raised.
30
+ """
31
+
32
+ import cython
33
+
34
+ import re
35
+ import sys
36
+ import os
37
+ import tokenize
38
+ from io import StringIO
39
+
40
+ from ._looper import looper
41
+
42
+ __all__ = ['TemplateError', 'Template', 'sub', 'bunch']
43
+
44
+ in_re = re.compile(r'\s+in\s+')
45
+ var_re = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
46
+
47
+ def coerce_text(v):
48
+ if not isinstance(v, str):
49
+ if hasattr(v, '__str__'):
50
+ return str(v)
51
+ else:
52
+ return bytes(v)
53
+ return v
54
+
55
+ class TemplateError(Exception):
56
+ """Exception raised while parsing a template
57
+ """
58
+
59
+ def __init__(self, message, position, name=None):
60
+ Exception.__init__(self, message)
61
+ self.position = position
62
+ self.name = name
63
+
64
+ def __str__(self):
65
+ msg = ' '.join(self.args)
66
+ if self.position:
67
+ msg = '%s at line %s column %s' % (
68
+ msg, self.position[0], self.position[1])
69
+ if self.name:
70
+ msg += ' in %s' % self.name
71
+ return msg
72
+
73
+
74
+ class _TemplateContinue(Exception):
75
+ pass
76
+
77
+
78
+ class _TemplateBreak(Exception):
79
+ pass
80
+
81
+
82
+ def get_file_template(name, from_template):
83
+ path = os.path.join(os.path.dirname(from_template.name), name)
84
+ return from_template.__class__.from_filename(
85
+ path, namespace=from_template.namespace,
86
+ get_template=from_template.get_template)
87
+
88
+
89
+ class Template:
90
+
91
+ default_namespace = {
92
+ 'start_braces': '{{',
93
+ 'end_braces': '}}',
94
+ 'looper': looper,
95
+ }
96
+
97
+ default_encoding = 'utf8'
98
+ default_inherit = None
99
+
100
+ def __init__(self, content, name=None, namespace=None, stacklevel=None,
101
+ get_template=None, default_inherit=None, line_offset=0,
102
+ delimiters=None, delimeters=None):
103
+ self.content = content
104
+
105
+ # set delimiters
106
+ if delimeters:
107
+ import warnings
108
+ warnings.warn(
109
+ "'delimeters' kwarg is being deprecated in favor of correctly"
110
+ " spelled 'delimiters'. Please adjust your code.",
111
+ DeprecationWarning
112
+ )
113
+ if delimiters is None:
114
+ delimiters = delimeters
115
+ if delimiters is None:
116
+ delimiters = (self.default_namespace['start_braces'],
117
+ self.default_namespace['end_braces'])
118
+ else:
119
+ #assert len(delimiters) == 2 and all([isinstance(delimiter, str)
120
+ # for delimiter in delimiters])
121
+ self.default_namespace = self.__class__.default_namespace.copy()
122
+ self.default_namespace['start_braces'] = delimiters[0]
123
+ self.default_namespace['end_braces'] = delimiters[1]
124
+ self.delimiters = self.delimeters = delimiters # Keep a legacy read-only copy, but don't use it.
125
+
126
+ self._unicode = isinstance(content, str)
127
+ if name is None and stacklevel is not None:
128
+ try:
129
+ caller = sys._getframe(stacklevel)
130
+ except ValueError:
131
+ pass
132
+ else:
133
+ globals = caller.f_globals
134
+ lineno = caller.f_lineno
135
+ if '__file__' in globals:
136
+ name = globals['__file__']
137
+ if name.endswith('.pyc') or name.endswith('.pyo'):
138
+ name = name[:-1]
139
+ elif '__name__' in globals:
140
+ name = globals['__name__']
141
+ else:
142
+ name = '<string>'
143
+ if lineno:
144
+ name += ':%s' % lineno
145
+ self.name = name
146
+ self._parsed = parse(content, name=name, line_offset=line_offset, delimiters=self.delimiters)
147
+ if namespace is None:
148
+ namespace = {}
149
+ self.namespace = namespace
150
+ self.get_template = get_template
151
+ if default_inherit is not None:
152
+ self.default_inherit = default_inherit
153
+
154
+ @classmethod
155
+ def from_filename(cls, filename, namespace=None, encoding=None,
156
+ default_inherit=None, get_template=get_file_template):
157
+ with open(filename, 'rb') as f:
158
+ c = f.read()
159
+ if encoding:
160
+ c = c.decode(encoding)
161
+ return cls(content=c, name=filename, namespace=namespace,
162
+ default_inherit=default_inherit, get_template=get_template)
163
+
164
+ def __repr__(self):
165
+ return '<%s %s name=%r>' % (
166
+ self.__class__.__name__,
167
+ hex(id(self))[2:], self.name)
168
+
169
+ def substitute(self, *args, **kw):
170
+ if args:
171
+ if kw:
172
+ raise TypeError(
173
+ "You can only give positional *or* keyword arguments")
174
+ if len(args) > 1:
175
+ raise TypeError(
176
+ "You can only give one positional argument")
177
+ if not hasattr(args[0], 'items'):
178
+ raise TypeError(
179
+ "If you pass in a single argument, you must pass in a dictionary-like object (with a .items() method); you gave %r"
180
+ % (args[0],))
181
+ kw = args[0]
182
+ ns = kw
183
+ ns['__template_name__'] = self.name
184
+ if self.namespace:
185
+ ns.update(self.namespace)
186
+ result, defs, inherit = self._interpret(ns)
187
+ if not inherit:
188
+ inherit = self.default_inherit
189
+ if inherit:
190
+ result = self._interpret_inherit(result, defs, inherit, ns)
191
+ return result
192
+
193
+ def _interpret(self, ns) -> tuple:
194
+ __traceback_hide__ = True
195
+ parts = []
196
+ defs = {}
197
+ self._interpret_codes(self._parsed, ns, out=parts, defs=defs)
198
+ if '__inherit__' in defs:
199
+ inherit = defs.pop('__inherit__')
200
+ else:
201
+ inherit = None
202
+ return ''.join(parts), defs, inherit
203
+
204
+ def _interpret_inherit(self, body, defs, inherit_template, ns):
205
+ __traceback_hide__ = True
206
+ if not self.get_template:
207
+ raise TemplateError(
208
+ 'You cannot use inheritance without passing in get_template',
209
+ position=None, name=self.name)
210
+ templ = self.get_template(inherit_template, self)
211
+ self_ = TemplateObject(self.name)
212
+ for name, value in defs.items():
213
+ setattr(self_, name, value)
214
+ self_.body = body
215
+ ns = ns.copy()
216
+ ns['self'] = self_
217
+ return templ.substitute(ns)
218
+
219
+ def _interpret_codes(self, codes, ns, out, defs):
220
+ __traceback_hide__ = True
221
+ for item in codes:
222
+ if isinstance(item, str):
223
+ out.append(item)
224
+ else:
225
+ self._interpret_code(item, ns, out, defs)
226
+
227
+ def _interpret_code(self, code, ns, out, defs):
228
+ __traceback_hide__ = True
229
+ name, pos = code[0], code[1]
230
+ if name == 'py':
231
+ self._exec(code[2], ns, pos)
232
+ elif name == 'continue':
233
+ raise _TemplateContinue()
234
+ elif name == 'break':
235
+ raise _TemplateBreak()
236
+ elif name == 'for':
237
+ vars, expr, content = code[2], code[3], code[4]
238
+ expr = self._eval(expr, ns, pos)
239
+ self._interpret_for(vars, expr, content, ns, out, defs)
240
+ elif name == 'cond':
241
+ parts = code[2:]
242
+ self._interpret_if(parts, ns, out, defs)
243
+ elif name == 'expr':
244
+ parts = code[2].split('|')
245
+ base = self._eval(parts[0], ns, pos)
246
+ for part in parts[1:]:
247
+ func = self._eval(part, ns, pos)
248
+ base = func(base)
249
+ out.append(self._repr(base, pos))
250
+ elif name == 'default':
251
+ var, expr = code[2], code[3]
252
+ if var not in ns:
253
+ result = self._eval(expr, ns, pos)
254
+ ns[var] = result
255
+ elif name == 'inherit':
256
+ expr = code[2]
257
+ value = self._eval(expr, ns, pos)
258
+ defs['__inherit__'] = value
259
+ elif name == 'def':
260
+ name = code[2]
261
+ signature = code[3]
262
+ parts = code[4]
263
+ ns[name] = defs[name] = TemplateDef(self, name, signature, body=parts, ns=ns,
264
+ pos=pos)
265
+ elif name == 'comment':
266
+ return
267
+ else:
268
+ assert 0, "Unknown code: %r" % name
269
+
270
+ def _interpret_for(self, vars, expr, content, ns, out, defs):
271
+ __traceback_hide__ = True
272
+ for item in expr:
273
+ if len(vars) == 1:
274
+ ns[vars[0]] = item
275
+ else:
276
+ if len(vars) != len(item):
277
+ raise ValueError(
278
+ 'Need %i items to unpack (got %i items)'
279
+ % (len(vars), len(item)))
280
+ for name, value in zip(vars, item):
281
+ ns[name] = value
282
+ try:
283
+ self._interpret_codes(content, ns, out, defs)
284
+ except _TemplateContinue:
285
+ continue
286
+ except _TemplateBreak:
287
+ break
288
+
289
+ def _interpret_if(self, parts, ns, out, defs):
290
+ __traceback_hide__ = True
291
+ # @@: if/else/else gets through
292
+ for part in parts:
293
+ assert not isinstance(part, str)
294
+ name, pos = part[0], part[1]
295
+ if name == 'else':
296
+ result = True
297
+ else:
298
+ result = self._eval(part[2], ns, pos)
299
+ if result:
300
+ self._interpret_codes(part[3], ns, out, defs)
301
+ break
302
+
303
+ def _eval(self, code, ns, pos):
304
+ __traceback_hide__ = True
305
+ try:
306
+ try:
307
+ value = eval(code, self.default_namespace, ns)
308
+ except SyntaxError as e:
309
+ raise SyntaxError(
310
+ 'invalid syntax in expression: %s' % code)
311
+ return value
312
+ except Exception as e:
313
+ if getattr(e, 'args', None):
314
+ arg0 = e.args[0]
315
+ else:
316
+ arg0 = coerce_text(e)
317
+ e.args = (self._add_line_info(arg0, pos),)
318
+ raise
319
+
320
+ def _exec(self, code, ns, pos):
321
+ __traceback_hide__ = True
322
+ try:
323
+ exec(code, self.default_namespace, ns)
324
+ except Exception as e:
325
+ if e.args:
326
+ e.args = (self._add_line_info(e.args[0], pos),)
327
+ else:
328
+ e.args = (self._add_line_info(None, pos),)
329
+ raise
330
+
331
+ def _repr(self, value, pos):
332
+ __traceback_hide__ = True
333
+ try:
334
+ if value is None:
335
+ return ''
336
+ if self._unicode:
337
+ try:
338
+ value = str(value)
339
+ except UnicodeDecodeError:
340
+ value = bytes(value)
341
+ else:
342
+ if not isinstance(value, str):
343
+ value = coerce_text(value)
344
+ if (isinstance(value, str)
345
+ and self.default_encoding):
346
+ value = value.encode(self.default_encoding)
347
+ except Exception as e:
348
+ e.args = (self._add_line_info(e.args[0], pos),)
349
+ raise
350
+ else:
351
+ if self._unicode and isinstance(value, bytes):
352
+ if not self.default_encoding:
353
+ raise UnicodeDecodeError(
354
+ 'Cannot decode bytes value %r into unicode '
355
+ '(no default_encoding provided)' % value)
356
+ try:
357
+ value = value.decode(self.default_encoding)
358
+ except UnicodeDecodeError as e:
359
+ raise UnicodeDecodeError(
360
+ e.encoding,
361
+ e.object,
362
+ e.start,
363
+ e.end,
364
+ e.reason + ' in string %r' % value)
365
+ elif not self._unicode and isinstance(value, str):
366
+ if not self.default_encoding:
367
+ raise UnicodeEncodeError(
368
+ 'Cannot encode unicode value %r into bytes '
369
+ '(no default_encoding provided)' % value)
370
+ value = value.encode(self.default_encoding)
371
+ return value
372
+
373
+ def _add_line_info(self, msg, pos):
374
+ msg = "%s at line %s column %s" % (
375
+ msg, pos[0], pos[1])
376
+ if self.name:
377
+ msg += " in file %s" % self.name
378
+ return msg
379
+
380
+
381
+ def sub(content, delimiters=None, **kw):
382
+ name = kw.get('__name')
383
+ delimeters = kw.pop('delimeters') if 'delimeters' in kw else None # for legacy code
384
+ tmpl = Template(content, name=name, delimiters=delimiters, delimeters=delimeters)
385
+ return tmpl.substitute(kw)
386
+
387
+
388
+ def paste_script_template_renderer(content, vars, filename=None):
389
+ tmpl = Template(content, name=filename)
390
+ return tmpl.substitute(vars)
391
+
392
+
393
+ class bunch(dict):
394
+
395
+ def __init__(self, **kw):
396
+ for name, value in kw.items():
397
+ setattr(self, name, value)
398
+
399
+ def __setattr__(self, name, value):
400
+ self[name] = value
401
+
402
+ def __getattr__(self, name):
403
+ try:
404
+ return self[name]
405
+ except KeyError:
406
+ raise AttributeError(name)
407
+
408
+ def __getitem__(self, key):
409
+ if 'default' in self:
410
+ try:
411
+ return dict.__getitem__(self, key)
412
+ except KeyError:
413
+ return dict.__getitem__(self, 'default')
414
+ else:
415
+ return dict.__getitem__(self, key)
416
+
417
+ def __repr__(self):
418
+ return '<%s %s>' % (
419
+ self.__class__.__name__,
420
+ ' '.join(['%s=%r' % (k, v) for k, v in sorted(self.items())]))
421
+
422
+
423
+ class TemplateDef:
424
+ def __init__(self, template, func_name, func_signature,
425
+ body, ns, pos, bound_self=None):
426
+ self._template = template
427
+ self._func_name = func_name
428
+ self._func_signature = func_signature
429
+ self._body = body
430
+ self._ns = ns
431
+ self._pos = pos
432
+ self._bound_self = bound_self
433
+
434
+ def __repr__(self):
435
+ return '<tempita function %s(%s) at %s:%s>' % (
436
+ self._func_name, self._func_signature,
437
+ self._template.name, self._pos)
438
+
439
+ def __str__(self):
440
+ return self()
441
+
442
+ def __call__(self, *args, **kw):
443
+ values = self._parse_signature(args, kw)
444
+ ns = self._ns.copy()
445
+ ns.update(values)
446
+ if self._bound_self is not None:
447
+ ns['self'] = self._bound_self
448
+ out = []
449
+ subdefs = {}
450
+ self._template._interpret_codes(self._body, ns, out, subdefs)
451
+ return ''.join(out)
452
+
453
+ def __get__(self, obj, type=None):
454
+ if obj is None:
455
+ return self
456
+ return self.__class__(
457
+ self._template, self._func_name, self._func_signature,
458
+ self._body, self._ns, self._pos, bound_self=obj)
459
+
460
+ def _parse_signature(self, args, kw):
461
+ values = {}
462
+ sig_args, var_args, var_kw, defaults = self._func_signature
463
+ extra_kw = {}
464
+ for name, value in kw.items():
465
+ if not var_kw and name not in sig_args:
466
+ raise TypeError(
467
+ 'Unexpected argument %s' % name)
468
+ if name in sig_args:
469
+ values[sig_args] = value
470
+ else:
471
+ extra_kw[name] = value
472
+ args = list(args)
473
+ sig_args = list(sig_args)
474
+ while args:
475
+ while sig_args and sig_args[0] in values:
476
+ sig_args.pop(0)
477
+ if sig_args:
478
+ name = sig_args.pop(0)
479
+ values[name] = args.pop(0)
480
+ elif var_args:
481
+ values[var_args] = tuple(args)
482
+ break
483
+ else:
484
+ raise TypeError(
485
+ 'Extra position arguments: %s'
486
+ % ', '.join([repr(v) for v in args]))
487
+ for name, value_expr in defaults.items():
488
+ if name not in values:
489
+ values[name] = self._template._eval(
490
+ value_expr, self._ns, self._pos)
491
+ for name in sig_args:
492
+ if name not in values:
493
+ raise TypeError(
494
+ 'Missing argument: %s' % name)
495
+ if var_kw:
496
+ values[var_kw] = extra_kw
497
+ return values
498
+
499
+
500
+ class TemplateObject:
501
+
502
+ def __init__(self, name):
503
+ self.__name = name
504
+ self.get = TemplateObjectGetter(self)
505
+
506
+ def __repr__(self):
507
+ return '<%s %s>' % (self.__class__.__name__, self.__name)
508
+
509
+
510
+ class TemplateObjectGetter:
511
+
512
+ def __init__(self, template_obj):
513
+ self.__template_obj = template_obj
514
+
515
+ def __getattr__(self, attr):
516
+ return getattr(self.__template_obj, attr, Empty)
517
+
518
+ def __repr__(self):
519
+ return '<%s around %r>' % (self.__class__.__name__, self.__template_obj)
520
+
521
+
522
+ class _Empty:
523
+ def __call__(self, *args, **kw):
524
+ return self
525
+
526
+ def __str__(self):
527
+ return ''
528
+
529
+ def __repr__(self):
530
+ return 'Empty'
531
+
532
+ def __unicode__(self):
533
+ return ''
534
+
535
+ def __iter__(self):
536
+ return iter(())
537
+
538
+ def __bool__(self):
539
+ return False
540
+
541
+ Empty = _Empty()
542
+ del _Empty
543
+
544
+ ############################################################
545
+ ## Lexing and Parsing
546
+ ############################################################
547
+
548
+
549
+ def lex(s, name=None, trim_whitespace=True, line_offset=0, delimiters=None) -> list:
550
+ """
551
+ Lex a string into chunks:
552
+
553
+ >>> lex('hey')
554
+ ['hey']
555
+ >>> lex('hey {{you}}')
556
+ ['hey ', ('you', (1, 7))]
557
+ >>> lex('hey {{')
558
+ Traceback (most recent call last):
559
+ ...
560
+ TemplateError: No }} to finish last expression at line 1 column 7
561
+ >>> lex('hey }}')
562
+ Traceback (most recent call last):
563
+ ...
564
+ TemplateError: }} outside expression at line 1 column 7
565
+ >>> lex('hey {{ {{')
566
+ Traceback (most recent call last):
567
+ ...
568
+ TemplateError: {{ inside expression at line 1 column 10
569
+
570
+ """
571
+ start_braces: str = delimiters[0] if delimiters is not None else Template.default_namespace['start_braces']
572
+ end_braces: str = delimiters[1] if delimiters is not None else Template.default_namespace['end_braces']
573
+
574
+ in_expr = False
575
+ chunks = []
576
+ last = 0
577
+ last_pos = (line_offset + 1, 1)
578
+
579
+ token_re = re.compile(r'%s|%s' % (re.escape(start_braces),
580
+ re.escape(end_braces)))
581
+ for match in token_re.finditer(s):
582
+ expr: str = match.group(0)
583
+ pos = find_position(s, match.end(), last, last_pos)
584
+ if expr == start_braces:
585
+ if in_expr:
586
+ raise TemplateError(f'{start_braces} inside expression', position=pos, name=name)
587
+ part = s[last:match.start()]
588
+ if part:
589
+ chunks.append(part)
590
+ in_expr = True
591
+ else:
592
+ if not in_expr:
593
+ raise TemplateError(f'{end_braces} outside expression', position=pos, name=name)
594
+ chunks.append((s[last:match.start()], last_pos))
595
+ in_expr = False
596
+ last = match.end()
597
+ last_pos = pos
598
+ if in_expr:
599
+ raise TemplateError(
600
+ f'No {end_braces} to finish last expression', name=name, position=last_pos)
601
+ part = s[last:]
602
+ if part:
603
+ chunks.append(part)
604
+ if trim_whitespace:
605
+ trim_lex(chunks)
606
+ return chunks
607
+
608
+ statement_re = re.compile(r'^(?:if |elif |for |def |inherit |default |py:)')
609
+ single_statements = ['else', 'endif', 'endfor', 'enddef', 'continue', 'break']
610
+ trail_whitespace_re = re.compile(r'\n\r?[\t ]*$')
611
+ lead_whitespace_re = re.compile(r'^[\t ]*\n')
612
+
613
+
614
+ def trim_lex(tokens: list):
615
+ r"""
616
+ Takes a lexed set of tokens, and removes whitespace when there is
617
+ a directive on a line by itself:
618
+
619
+ >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
620
+ >>> tokens
621
+ [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
622
+ >>> trim_lex(tokens)
623
+ >>> tokens
624
+ [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
625
+ """
626
+ i: cython.Py_ssize_t
627
+ last_trim = None
628
+ for i, current in enumerate(tokens):
629
+ if isinstance(current, str):
630
+ # we don't trim this
631
+ continue
632
+ item = current[0]
633
+ if not statement_re.search(item) and item not in single_statements:
634
+ continue
635
+ if not i:
636
+ prev = ''
637
+ else:
638
+ prev = tokens[i - 1]
639
+ if i + 1 >= len(tokens):
640
+ next_chunk = ''
641
+ else:
642
+ next_chunk = tokens[i + 1]
643
+ if (not isinstance(next_chunk, str)
644
+ or not isinstance(prev, str)):
645
+ continue
646
+ prev_ok = not prev or trail_whitespace_re.search(prev)
647
+ if i == 1 and not prev.strip():
648
+ prev_ok = True
649
+ if last_trim is not None and last_trim + 2 == i and not prev.strip():
650
+ prev_ok = 'last'
651
+ if (prev_ok
652
+ and (not next_chunk or lead_whitespace_re.search(next_chunk)
653
+ or (i == len(tokens) - 2 and not next_chunk.strip()))):
654
+ if prev:
655
+ if ((i == 1 and not prev.strip())
656
+ or prev_ok == 'last'):
657
+ tokens[i - 1] = ''
658
+ else:
659
+ m = trail_whitespace_re.search(prev)
660
+ # +1 to leave the leading \n on:
661
+ prev = prev[:m.start() + 1]
662
+ tokens[i - 1] = prev
663
+ if next_chunk:
664
+ last_trim = i
665
+ if i == len(tokens) - 2 and not next_chunk.strip():
666
+ tokens[i + 1] = ''
667
+ else:
668
+ m = lead_whitespace_re.search(next_chunk)
669
+ next_chunk = next_chunk[m.end():]
670
+ tokens[i + 1] = next_chunk
671
+
672
+
673
+ def find_position(string: str, index, last_index, last_pos) -> tuple:
674
+ """Given a string and index, return (line, column)"""
675
+ lines = string.count('\n', last_index, index)
676
+ if lines > 0:
677
+ column = index - string.rfind('\n', last_index, index)
678
+ else:
679
+ column = last_pos[1] + (index - last_index)
680
+ return (last_pos[0] + lines, column)
681
+
682
+
683
+ def parse(s, name=None, line_offset=0, delimiters=None):
684
+ r"""
685
+ Parses a string into a kind of AST
686
+
687
+ >>> parse('{{x}}')
688
+ [('expr', (1, 3), 'x')]
689
+ >>> parse('foo')
690
+ ['foo']
691
+ >>> parse('{{if x}}test{{endif}}')
692
+ [('cond', (1, 3), ('if', (1, 3), 'x', ['test']))]
693
+ >>> parse('series->{{for x in y}}x={{x}}{{endfor}}')
694
+ ['series->', ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])]
695
+ >>> parse('{{for x, y in z:}}{{continue}}{{endfor}}')
696
+ [('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])]
697
+ >>> parse('{{py:x=1}}')
698
+ [('py', (1, 3), 'x=1')]
699
+ >>> parse('{{if x}}a{{elif y}}b{{else}}c{{endif}}')
700
+ [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))]
701
+
702
+ Some exceptions::
703
+
704
+ >>> parse('{{continue}}')
705
+ Traceback (most recent call last):
706
+ ...
707
+ TemplateError: continue outside of for loop at line 1 column 3
708
+ >>> parse('{{if x}}foo')
709
+ Traceback (most recent call last):
710
+ ...
711
+ TemplateError: No {{endif}} at line 1 column 3
712
+ >>> parse('{{else}}')
713
+ Traceback (most recent call last):
714
+ ...
715
+ TemplateError: else outside of an if block at line 1 column 3
716
+ >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}')
717
+ Traceback (most recent call last):
718
+ ...
719
+ TemplateError: Unexpected endif at line 1 column 25
720
+ >>> parse('{{if}}{{endif}}')
721
+ Traceback (most recent call last):
722
+ ...
723
+ TemplateError: if with no expression at line 1 column 3
724
+ >>> parse('{{for x y}}{{endfor}}')
725
+ Traceback (most recent call last):
726
+ ...
727
+ TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
728
+ >>> parse('{{py:x=1\ny=2}}')
729
+ Traceback (most recent call last):
730
+ ...
731
+ TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
732
+ """
733
+ tokens = lex(s, name=name, line_offset=line_offset, delimiters=delimiters)
734
+ result = []
735
+ while tokens:
736
+ next_chunk, tokens = parse_expr(tokens, name)
737
+ result.append(next_chunk)
738
+ return result
739
+
740
+
741
+ def parse_expr(tokens: list, name, context=()) -> tuple:
742
+ if isinstance(tokens[0], str):
743
+ return tokens[0], tokens[1:]
744
+ expr: str
745
+ expr, pos = tokens[0]
746
+ expr = expr.strip()
747
+ if expr.startswith('py:'):
748
+ expr = expr[3:].lstrip(' \t')
749
+ if expr.startswith('\n') or expr.startswith('\r'):
750
+ expr = expr.lstrip('\r\n')
751
+ if '\r' in expr:
752
+ expr = expr.replace('\r\n', '\n')
753
+ expr = expr.replace('\r', '')
754
+ expr += '\n'
755
+ else:
756
+ if '\n' in expr:
757
+ raise TemplateError(
758
+ 'Multi-line py blocks must start with a newline',
759
+ position=pos, name=name)
760
+ return ('py', pos, expr), tokens[1:]
761
+ elif expr in ('continue', 'break'):
762
+ if 'for' not in context:
763
+ raise TemplateError(
764
+ 'continue outside of for loop',
765
+ position=pos, name=name)
766
+ return (expr, pos), tokens[1:]
767
+ elif expr.startswith('if '):
768
+ return parse_cond(tokens, name, context)
769
+ elif (expr.startswith('elif ')
770
+ or expr == 'else'):
771
+ raise TemplateError(
772
+ '%s outside of an if block' % expr.split()[0],
773
+ position=pos, name=name)
774
+ elif expr in ('if', 'elif', 'for'):
775
+ raise TemplateError(
776
+ '%s with no expression' % expr,
777
+ position=pos, name=name)
778
+ elif expr in ('endif', 'endfor', 'enddef'):
779
+ raise TemplateError(
780
+ 'Unexpected %s' % expr,
781
+ position=pos, name=name)
782
+ elif expr.startswith('for '):
783
+ return parse_for(tokens, name, context)
784
+ elif expr.startswith('default '):
785
+ return parse_default(tokens, name, context)
786
+ elif expr.startswith('inherit '):
787
+ return parse_inherit(tokens, name, context)
788
+ elif expr.startswith('def '):
789
+ return parse_def(tokens, name, context)
790
+ elif expr.startswith('#'):
791
+ return ('comment', pos, tokens[0][0]), tokens[1:]
792
+ return ('expr', pos, tokens[0][0]), tokens[1:]
793
+
794
+
795
+ def parse_cond(tokens: list, name, context) -> tuple:
796
+ start = tokens[0][1]
797
+ pieces = []
798
+ context = context + ('if',)
799
+ while 1:
800
+ if not tokens:
801
+ raise TemplateError(
802
+ 'Missing {{endif}}',
803
+ position=start, name=name)
804
+ if (isinstance(tokens[0], tuple)
805
+ and tokens[0][0] == 'endif'):
806
+ return ('cond', start) + tuple(pieces), tokens[1:]
807
+ next_chunk, tokens = parse_one_cond(tokens, name, context)
808
+ pieces.append(next_chunk)
809
+
810
+
811
+ def parse_one_cond(tokens: list, name, context) -> tuple:
812
+ first: str
813
+ (first, pos), tokens = tokens[0], tokens[1:]
814
+ content = []
815
+ if first.endswith(':'):
816
+ first = first[:-1]
817
+ if first.startswith('if '):
818
+ part = ('if', pos, first[3:].lstrip(), content)
819
+ elif first.startswith('elif '):
820
+ part = ('elif', pos, first[5:].lstrip(), content)
821
+ elif first == 'else':
822
+ part = ('else', pos, None, content)
823
+ else:
824
+ assert 0, "Unexpected token %r at %s" % (first, pos)
825
+ while 1:
826
+ if not tokens:
827
+ raise TemplateError(
828
+ 'No {{endif}}',
829
+ position=pos, name=name)
830
+ if (isinstance(tokens[0], tuple)
831
+ and (tokens[0][0] == 'endif'
832
+ or tokens[0][0].startswith('elif ')
833
+ or tokens[0][0] == 'else')):
834
+ return part, tokens
835
+ next_chunk, tokens = parse_expr(tokens, name, context)
836
+ content.append(next_chunk)
837
+
838
+
839
+ def parse_for(tokens: list, name, context) -> tuple:
840
+ first: str
841
+ first, pos = tokens[0]
842
+ tokens = tokens[1:]
843
+ context = ('for',) + context
844
+ content = []
845
+ assert first.startswith('for '), first
846
+ if first.endswith(':'):
847
+ first = first[:-1]
848
+ first = first[3:].strip()
849
+ match = in_re.search(first)
850
+ if not match:
851
+ raise TemplateError(
852
+ 'Bad for (no "in") in %r' % first,
853
+ position=pos, name=name)
854
+ vars_part: str = first[:match.start()]
855
+ if '(' in vars_part:
856
+ raise TemplateError(
857
+ f'You cannot have () in the variable section of a for loop ({vars_part!r})', position=pos, name=name)
858
+ vars = tuple([v.strip() for v in vars_part.split(',') if v.strip()])
859
+ expr = first[match.end():]
860
+ while 1:
861
+ if not tokens:
862
+ raise TemplateError(
863
+ 'No {{endfor}}',
864
+ position=pos, name=name)
865
+ if (isinstance(tokens[0], tuple)
866
+ and tokens[0][0] == 'endfor'):
867
+ return ('for', pos, vars, expr, content), tokens[1:]
868
+ next_chunk, tokens = parse_expr(tokens, name, context)
869
+ content.append(next_chunk)
870
+
871
+
872
+ def parse_default(tokens: list, name, context) -> tuple:
873
+ first: str
874
+ first, pos = tokens[0]
875
+ assert first.startswith('default ')
876
+ first = first.split(None, 1)[1]
877
+ parts = first.split('=', 1)
878
+ if len(parts) == 1:
879
+ raise TemplateError(
880
+ "Expression must be {{default var=value}}; no = found in %r" % first,
881
+ position=pos, name=name)
882
+ var: str = parts[0].strip()
883
+ if ',' in var:
884
+ raise TemplateError(
885
+ "{{default x, y = ...}} is not supported",
886
+ position=pos, name=name)
887
+ if not var_re.search(var):
888
+ raise TemplateError(
889
+ "Not a valid variable name for {{default}}: %r"
890
+ % var, position=pos, name=name)
891
+ expr = parts[1].strip()
892
+ return ('default', pos, var, expr), tokens[1:]
893
+
894
+
895
+ def parse_inherit(tokens: list, name, context) -> tuple:
896
+ first: str
897
+ first, pos = tokens[0]
898
+ assert first.startswith('inherit ')
899
+ expr = first.split(None, 1)[1]
900
+ return ('inherit', pos, expr), tokens[1:]
901
+
902
+
903
+ def parse_def(tokens: list, name, context) -> tuple:
904
+ first: str
905
+ first, start = tokens[0]
906
+ tokens = tokens[1:]
907
+ assert first.startswith('def ')
908
+ first = first.split(None, 1)[1]
909
+ if first.endswith(':'):
910
+ first = first[:-1]
911
+ if '(' not in first:
912
+ func_name = first
913
+ sig = ((), None, None, {})
914
+ elif not first.endswith(')'):
915
+ raise TemplateError("Function definition doesn't end with ): %s" % first,
916
+ position=start, name=name)
917
+ else:
918
+ first = first[:-1]
919
+ func_name, sig_text = first.split('(', 1)
920
+ sig = parse_signature(sig_text, name, start)
921
+ context = context + ('def',)
922
+ content = []
923
+ while 1:
924
+ if not tokens:
925
+ raise TemplateError(
926
+ 'Missing {{enddef}}',
927
+ position=start, name=name)
928
+ if (isinstance(tokens[0], tuple)
929
+ and tokens[0][0] == 'enddef'):
930
+ return ('def', start, func_name, sig, content), tokens[1:]
931
+ next_chunk, tokens = parse_expr(tokens, name, context)
932
+ content.append(next_chunk)
933
+
934
+
935
+ def parse_signature(sig_text: str, name, pos) -> tuple:
936
+ tokens = tokenize.generate_tokens(StringIO(sig_text).readline)
937
+ sig_args = []
938
+ var_arg = None
939
+ var_kw = None
940
+ defaults = {}
941
+
942
+ def get_token(pos=False) -> tuple:
943
+ try:
944
+ tok_type, tok_string, (srow, scol), (erow, ecol), line = next(tokens)
945
+ except StopIteration:
946
+ return tokenize.ENDMARKER, ''
947
+ if pos:
948
+ return tok_type, tok_string, (srow, scol), (erow, ecol)
949
+ else:
950
+ return tok_type, tok_string
951
+ while 1:
952
+ var_arg_type = None
953
+ tok_type: int
954
+ tok_string: str
955
+ tok_type, tok_string = get_token()
956
+ if tok_type == tokenize.ENDMARKER:
957
+ break
958
+ if tok_type == tokenize.OP and (tok_string == '*' or tok_string == '**'):
959
+ var_arg_type = tok_string
960
+ tok_type, tok_string = get_token()
961
+ if tok_type != tokenize.NAME:
962
+ raise TemplateError('Invalid signature: (%s)' % sig_text,
963
+ position=pos, name=name)
964
+ var_name = tok_string
965
+ tok_type, tok_string = get_token()
966
+ if tok_type == tokenize.ENDMARKER or (tok_type == tokenize.OP and tok_string == ','):
967
+ if var_arg_type == '*':
968
+ var_arg = var_name
969
+ elif var_arg_type == '**':
970
+ var_kw = var_name
971
+ else:
972
+ sig_args.append(var_name)
973
+ if tok_type == tokenize.ENDMARKER:
974
+ break
975
+ continue
976
+ if var_arg_type is not None:
977
+ raise TemplateError('Invalid signature: (%s)' % sig_text,
978
+ position=pos, name=name)
979
+ if tok_type == tokenize.OP and tok_string == '=':
980
+ nest_type = None
981
+ unnest_type = None
982
+ nest_count = 0
983
+ start_pos = end_pos = None
984
+ parts = []
985
+ while 1:
986
+ tok_type, tok_string, s, e = get_token(True)
987
+ if start_pos is None:
988
+ start_pos = s
989
+ end_pos = e
990
+ if tok_type == tokenize.ENDMARKER and nest_count:
991
+ raise TemplateError('Invalid signature: (%s)' % sig_text,
992
+ position=pos, name=name)
993
+ if (not nest_count and
994
+ (tok_type == tokenize.ENDMARKER or (tok_type == tokenize.OP and tok_string == ','))):
995
+ default_expr = isolate_expression(sig_text, start_pos, end_pos)
996
+ defaults[var_name] = default_expr
997
+ sig_args.append(var_name)
998
+ break
999
+ parts.append((tok_type, tok_string))
1000
+ if nest_count and tok_type == tokenize.OP and tok_string == nest_type:
1001
+ nest_count += 1
1002
+ elif nest_count and tok_type == tokenize.OP and tok_string == unnest_type:
1003
+ nest_count -= 1
1004
+ if not nest_count:
1005
+ nest_type = unnest_type = None
1006
+ elif not nest_count and tok_type == tokenize.OP and tok_string in ('(', '[', '{'):
1007
+ nest_type = tok_string
1008
+ nest_count = 1
1009
+ unnest_type = {'(': ')', '[': ']', '{': '}'}[nest_type]
1010
+ return sig_args, var_arg, var_kw, defaults
1011
+
1012
+
1013
+ def isolate_expression(string: str, start_pos, end_pos) -> str:
1014
+ srow, scol = start_pos
1015
+ srow -= 1
1016
+ erow, ecol = end_pos
1017
+ erow -= 1
1018
+ lines = string.splitlines(True)
1019
+ if srow == erow:
1020
+ return lines[srow][scol:ecol]
1021
+ parts = [lines[srow][scol:]]
1022
+ parts.extend(lines[srow+1:erow])
1023
+ if erow < len(lines):
1024
+ # It'll sometimes give (end_row_past_finish, 0)
1025
+ parts.append(lines[erow][:ecol])
1026
+ return ''.join(parts)
1027
+
1028
+ _fill_command_usage = """\
1029
+ %prog [OPTIONS] TEMPLATE arg=value
1030
+
1031
+ Use py:arg=value to set a Python value; otherwise all values are
1032
+ strings.
1033
+ """
1034
+
1035
+
1036
+ def fill_command(args=None):
1037
+ import sys
1038
+ import optparse
1039
+ import os
1040
+ if args is None:
1041
+ args = sys.argv[1:]
1042
+ parser = optparse.OptionParser(
1043
+ usage=_fill_command_usage)
1044
+ parser.add_option(
1045
+ '-o', '--output',
1046
+ dest='output',
1047
+ metavar="FILENAME",
1048
+ help="File to write output to (default stdout)")
1049
+ parser.add_option(
1050
+ '--env',
1051
+ dest='use_env',
1052
+ action='store_true',
1053
+ help="Put the environment in as top-level variables")
1054
+ options, args = parser.parse_args(args)
1055
+ if len(args) < 1:
1056
+ print('You must give a template filename')
1057
+ sys.exit(2)
1058
+ template_name = args[0]
1059
+ args = args[1:]
1060
+ vars = {}
1061
+ if options.use_env:
1062
+ vars.update(os.environ)
1063
+ for value in args:
1064
+ if '=' not in value:
1065
+ print('Bad argument: %r' % value)
1066
+ sys.exit(2)
1067
+ name, value = value.split('=', 1)
1068
+ if name.startswith('py:'):
1069
+ name = name[:3]
1070
+ value = eval(value)
1071
+ vars[name] = value
1072
+ if template_name == '-':
1073
+ template_content = sys.stdin.read()
1074
+ template_name = '<stdin>'
1075
+ else:
1076
+ with open(template_name, 'rb') as f:
1077
+ template_content = f.read()
1078
+ template = Template(template_content, name=template_name)
1079
+ result = template.substitute(vars)
1080
+ if options.output:
1081
+ with open(options.output, 'wb') as f:
1082
+ f.write(result)
1083
+ else:
1084
+ sys.stdout.write(result)
1085
+
1086
+ if __name__ == '__main__':
1087
+ fill_command()