Permalink
Browse files

First commit.

  • Loading branch information...
0 parents commit 117e89ac95e26565264d906a88dc07e87974b3bb @malthe committed Feb 20, 2011
Showing with 8,415 additions and 0 deletions.
  1. +6 −0 CHANGES.rst
  2. +5 −0 COPYRIGHT.txt
  3. +132 −0 LICENSE.txt
  4. +72 −0 README.rst
  5. +485 −0 distribute_setup.py
  6. +73 −0 setup.py
  7. +1 −0 src/chameleon/__init__.py
  8. +927 −0 src/chameleon/astutil.py
  9. +477 −0 src/chameleon/benchmark.py
  10. +210 −0 src/chameleon/codegen.py
  11. +873 −0 src/chameleon/compiler.py
  12. +43 −0 src/chameleon/config.py
  13. +63 −0 src/chameleon/exc.py
  14. +95 −0 src/chameleon/i18n.py
  15. +86 −0 src/chameleon/interfaces.py
  16. +115 −0 src/chameleon/loader.py
  17. +21 −0 src/chameleon/metal.py
  18. +9 −0 src/chameleon/namespaces.py
  19. +176 −0 src/chameleon/nodes.py
  20. +200 −0 src/chameleon/parser.py
  21. +43 −0 src/chameleon/program.py
  22. +378 −0 src/chameleon/tal.py
  23. +515 −0 src/chameleon/tales.py
  24. +290 −0 src/chameleon/template.py
  25. +1 −0 src/chameleon/tests/__init__.py
  26. +7 −0 src/chameleon/tests/inputs/001-variable-scope.html
  27. +12 −0 src/chameleon/tests/inputs/001-variable-scope.pt
  28. +4 −0 src/chameleon/tests/inputs/001.xml
  29. +8 −0 src/chameleon/tests/inputs/002-repeat-scope.pt
  30. +4 −0 src/chameleon/tests/inputs/002.xml
  31. +16 −0 src/chameleon/tests/inputs/003-content.pt
  32. +4 −0 src/chameleon/tests/inputs/003.xml
  33. +11 −0 src/chameleon/tests/inputs/004-attributes.pt
  34. +5 −0 src/chameleon/tests/inputs/004.xml
  35. +10 −0 src/chameleon/tests/inputs/005-default.pt
  36. +5 −0 src/chameleon/tests/inputs/005.xml
  37. +6 −0 src/chameleon/tests/inputs/006-attribute-interpolation.pt
  38. +5 −0 src/chameleon/tests/inputs/006.xml
  39. +6 −0 src/chameleon/tests/inputs/007-content-interpolation.pt
  40. +4 −0 src/chameleon/tests/inputs/007.xml
  41. +8 −0 src/chameleon/tests/inputs/008-builtins.pt
  42. +4 −0 src/chameleon/tests/inputs/008.xml
  43. +5 −0 src/chameleon/tests/inputs/009-literals.pt
  44. +4 −0 src/chameleon/tests/inputs/009.xml
  45. +7 −0 src/chameleon/tests/inputs/010-structure.pt
  46. +5 −0 src/chameleon/tests/inputs/010.xml
  47. +9 −0 src/chameleon/tests/inputs/011-messages.pt
  48. +5 −0 src/chameleon/tests/inputs/011.xml
  49. +21 −0 src/chameleon/tests/inputs/012-translation.pt
  50. +5 −0 src/chameleon/tests/inputs/012.xml
  51. +11 −0 src/chameleon/tests/inputs/013-repeat-nested.pt
  52. +5 −0 src/chameleon/tests/inputs/013.xml
  53. +7 −0 src/chameleon/tests/inputs/014-repeat-nested-similar.pt
  54. +5 −0 src/chameleon/tests/inputs/014.xml
  55. +10 −0 src/chameleon/tests/inputs/015-translation-nested.pt
  56. +5 −0 src/chameleon/tests/inputs/015.xml
  57. +11 −0 src/chameleon/tests/inputs/016-explicit-translation.pt
  58. +4 −0 src/chameleon/tests/inputs/016.xml
  59. +12 −0 src/chameleon/tests/inputs/017-omit-tag.pt
  60. +4 −0 src/chameleon/tests/inputs/017.xml
  61. +13 −0 src/chameleon/tests/inputs/018-translation-nested-dynamic.pt
  62. +4 −0 src/chameleon/tests/inputs/018.xml
  63. +13 −0 src/chameleon/tests/inputs/019-replace.pt
  64. +4 −0 src/chameleon/tests/inputs/019.xml
  65. +5 −0 src/chameleon/tests/inputs/020-on-error.pt
  66. +4 −0 src/chameleon/tests/inputs/020.xml
  67. +16 −0 src/chameleon/tests/inputs/021-translation-domain.pt
  68. +4 −0 src/chameleon/tests/inputs/021.xml
  69. +13 −0 src/chameleon/tests/inputs/022-switch.pt
  70. +4 −0 src/chameleon/tests/inputs/022.xml
  71. +6 −0 src/chameleon/tests/inputs/023-condition.pt
  72. +5 −0 src/chameleon/tests/inputs/023.xml
  73. +16 −0 src/chameleon/tests/inputs/024-namespace-elements.pt
  74. +6 −0 src/chameleon/tests/inputs/024.xml
  75. +14 −0 src/chameleon/tests/inputs/025-repeat-whitespace.pt
  76. +5 −0 src/chameleon/tests/inputs/025.xml
  77. +14 −0 src/chameleon/tests/inputs/026-repeat-variable.pt
  78. +5 −0 src/chameleon/tests/inputs/026.xml
  79. +11 −0 src/chameleon/tests/inputs/027-attribute-replacement.pt
  80. +5 −0 src/chameleon/tests/inputs/027.xml
  81. +7 −0 src/chameleon/tests/inputs/028-attribute-toggle.pt
  82. +5 −0 src/chameleon/tests/inputs/028.xml
  83. +5 −0 src/chameleon/tests/inputs/029-attribute-ordering.pt
  84. +5 −0 src/chameleon/tests/inputs/029.xml
  85. +7 −0 src/chameleon/tests/inputs/030-repeat-tuples.pt
  86. +5 −0 src/chameleon/tests/inputs/030.xml
  87. +7 −0 src/chameleon/tests/inputs/031-namespace-with-tal.pt
  88. +5 −0 src/chameleon/tests/inputs/031.xml
  89. +19 −0 src/chameleon/tests/inputs/032-master-template.pt
  90. +5 −0 src/chameleon/tests/inputs/032.xml
  91. +1 −0 src/chameleon/tests/inputs/033-use-macro-trivial.pt
  92. +5 −0 src/chameleon/tests/inputs/033.xml
  93. +1 −0 src/chameleon/tests/inputs/034-use-template-as-macro.pt
  94. +4 −0 src/chameleon/tests/inputs/034.xml
  95. +5 −0 src/chameleon/tests/inputs/035-use-macro-with-fill-slot.pt
  96. +4 −0 src/chameleon/tests/inputs/035.xml
  97. +2 −0 src/chameleon/tests/inputs/036-use-macro-inherits-dynamic-scope.pt
  98. +5 −0 src/chameleon/tests/inputs/036.xml
  99. +5 −0 src/chameleon/tests/inputs/037-use-macro-local-variable-scope.pt
  100. +6 −0 src/chameleon/tests/inputs/037.xml
  101. +6 −0 src/chameleon/tests/inputs/038-use-macro-globals.pt
  102. +6 −0 src/chameleon/tests/inputs/038.xml
  103. +1 −0 src/chameleon/tests/inputs/039-globals.pt
  104. +5 −0 src/chameleon/tests/inputs/039.xml
  105. +20 −0 src/chameleon/tests/inputs/040-macro-using-template-symbol.pt
  106. +5 −0 src/chameleon/tests/inputs/040.xml
  107. +22 −0 src/chameleon/tests/inputs/041-translate-nested-names.pt
  108. +5 −0 src/chameleon/tests/inputs/041.xml
  109. +3 −0 src/chameleon/tests/inputs/042-use-macro-fill-footer.pt
  110. +4 −0 src/chameleon/tests/inputs/042.xml
  111. +19 −0 src/chameleon/tests/inputs/043-macro-nested-dynamic-vars.pt
  112. +6 −0 src/chameleon/tests/inputs/043.xml
  113. +5 −0 src/chameleon/tests/inputs/044-tuple-define.pt
  114. +10 −0 src/chameleon/tests/inputs/044.xml
  115. +6 −0 src/chameleon/tests/inputs/045.xml
  116. +6 −0 src/chameleon/tests/inputs/046.xml
  117. +5 −0 src/chameleon/tests/inputs/047.xml
  118. +4 −0 src/chameleon/tests/inputs/048.xml
  119. BIN src/chameleon/tests/inputs/049.xml
  120. BIN src/chameleon/tests/inputs/050.xml
  121. BIN src/chameleon/tests/inputs/051.xml
  122. +4 −0 src/chameleon/tests/inputs/052.xml
  123. +6 −0 src/chameleon/tests/inputs/053.xml
  124. +10 −0 src/chameleon/tests/inputs/054.xml
  125. +5 −0 src/chameleon/tests/inputs/055.xml
  126. +4 −0 src/chameleon/tests/inputs/056.xml
  127. +4 −0 src/chameleon/tests/inputs/057.xml
  128. +5 −0 src/chameleon/tests/inputs/058.xml
  129. +10 −0 src/chameleon/tests/inputs/059.xml
  130. +4 −0 src/chameleon/tests/inputs/060.xml
  131. +4 −0 src/chameleon/tests/inputs/061.xml
  132. +4 −0 src/chameleon/tests/inputs/062.xml
  133. +4 −0 src/chameleon/tests/inputs/063.xml
  134. +4 −0 src/chameleon/tests/inputs/064.xml
  135. +5 −0 src/chameleon/tests/inputs/065.xml
  136. +7 −0 src/chameleon/tests/inputs/066.xml
  137. +4 −0 src/chameleon/tests/inputs/067.xml
  138. +5 −0 src/chameleon/tests/inputs/068.xml
  139. +5 −0 src/chameleon/tests/inputs/069.xml
  140. +5 −0 src/chameleon/tests/inputs/070.xml
  141. +5 −0 src/chameleon/tests/inputs/071.xml
  142. +5 −0 src/chameleon/tests/inputs/072.xml
  143. +5 −0 src/chameleon/tests/inputs/073.xml
  144. +5 −0 src/chameleon/tests/inputs/074.xml
  145. +5 −0 src/chameleon/tests/inputs/075.xml
  146. +7 −0 src/chameleon/tests/inputs/076.xml
  147. +5 −0 src/chameleon/tests/inputs/077.xml
  148. +5 −0 src/chameleon/tests/inputs/078.xml
  149. +5 −0 src/chameleon/tests/inputs/079.xml
  150. +5 −0 src/chameleon/tests/inputs/080.xml
  151. +7 −0 src/chameleon/tests/inputs/081.xml
  152. +5 −0 src/chameleon/tests/inputs/082.xml
  153. +5 −0 src/chameleon/tests/inputs/083.xml
  154. +1 −0 src/chameleon/tests/inputs/084.xml
  155. +6 −0 src/chameleon/tests/inputs/085.xml
  156. +6 −0 src/chameleon/tests/inputs/086.xml
  157. +6 −0 src/chameleon/tests/inputs/087.xml
  158. +5 −0 src/chameleon/tests/inputs/088.xml
  159. +5 −0 src/chameleon/tests/inputs/089.xml
  160. +7 −0 src/chameleon/tests/inputs/090.xml
  161. +7 −0 src/chameleon/tests/inputs/091.xml
  162. +10 −0 src/chameleon/tests/inputs/092.xml
  163. +5 −0 src/chameleon/tests/inputs/093.xml
  164. +6 −0 src/chameleon/tests/inputs/094.xml
  165. +6 −0 src/chameleon/tests/inputs/095.xml
  166. +5 −0 src/chameleon/tests/inputs/096.xml
  167. +8 −0 src/chameleon/tests/inputs/097.xml
  168. +5 −0 src/chameleon/tests/inputs/098.xml
  169. +5 −0 src/chameleon/tests/inputs/099.xml
  170. +5 −0 src/chameleon/tests/inputs/100.xml
  171. +5 −0 src/chameleon/tests/inputs/101-unclosed-tags.html
  172. +5 −0 src/chameleon/tests/inputs/101.xml
  173. +5 −0 src/chameleon/tests/inputs/102-unquoted-attributes.html
  174. +5 −0 src/chameleon/tests/inputs/102.xml
  175. +8 −0 src/chameleon/tests/inputs/103-simple-attribute.html
  176. +4 −0 src/chameleon/tests/inputs/103.xml
  177. +5 −0 src/chameleon/tests/inputs/104.xml
  178. +5 −0 src/chameleon/tests/inputs/105.xml
  179. +5 −0 src/chameleon/tests/inputs/106.xml
  180. +5 −0 src/chameleon/tests/inputs/107.xml
  181. +7 −0 src/chameleon/tests/inputs/108.xml
  182. +5 −0 src/chameleon/tests/inputs/109.xml
  183. +6 −0 src/chameleon/tests/inputs/110.xml
  184. +5 −0 src/chameleon/tests/inputs/111.xml
  185. +5 −0 src/chameleon/tests/inputs/112.xml
  186. +5 −0 src/chameleon/tests/inputs/113.xml
  187. +5 −0 src/chameleon/tests/inputs/114.xml
  188. +6 −0 src/chameleon/tests/inputs/115.xml
  189. +5 −0 src/chameleon/tests/inputs/116.xml
  190. +5 −0 src/chameleon/tests/inputs/117.xml
  191. +5 −0 src/chameleon/tests/inputs/118.xml
  192. +4 −0 src/chameleon/tests/inputs/119.xml
  193. +7 −0 src/chameleon/tests/outputs/001.html
  194. +10 −0 src/chameleon/tests/outputs/001.pt
  195. +13 −0 src/chameleon/tests/outputs/002.pt
  196. +16 −0 src/chameleon/tests/outputs/003.pt
  197. +11 −0 src/chameleon/tests/outputs/004.pt
  198. +10 −0 src/chameleon/tests/outputs/005.pt
  199. +6 −0 src/chameleon/tests/outputs/006.pt
  200. +6 −0 src/chameleon/tests/outputs/007.pt
  201. +8 −0 src/chameleon/tests/outputs/008.pt
  202. +5 −0 src/chameleon/tests/outputs/009.pt
  203. +7 −0 src/chameleon/tests/outputs/010.pt
  204. +9 −0 src/chameleon/tests/outputs/011-en.pt
  205. +9 −0 src/chameleon/tests/outputs/011.pt
  206. +9 −0 src/chameleon/tests/outputs/012-en.pt
  207. +9 −0 src/chameleon/tests/outputs/012.pt
  208. +22 −0 src/chameleon/tests/outputs/013.pt
  209. +12 −0 src/chameleon/tests/outputs/014.pt
  210. +5 −0 src/chameleon/tests/outputs/015-en.pt
  211. +5 −0 src/chameleon/tests/outputs/015.pt
  212. +9 −0 src/chameleon/tests/outputs/016-en.pt
  213. +9 −0 src/chameleon/tests/outputs/016.pt
  214. +12 −0 src/chameleon/tests/outputs/017.pt
  215. +4 −0 src/chameleon/tests/outputs/018-en.pt
  216. +4 −0 src/chameleon/tests/outputs/018.pt
  217. +13 −0 src/chameleon/tests/outputs/019.pt
  218. +3 −0 src/chameleon/tests/outputs/020.pt
  219. +12 −0 src/chameleon/tests/outputs/021-en.pt
  220. +12 −0 src/chameleon/tests/outputs/021.pt
  221. +13 −0 src/chameleon/tests/outputs/022.pt
  222. +6 −0 src/chameleon/tests/outputs/023.pt
  223. +14 −0 src/chameleon/tests/outputs/024.pt
  224. +23 −0 src/chameleon/tests/outputs/025.pt
  225. +18 −0 src/chameleon/tests/outputs/026.pt
  226. +8 −0 src/chameleon/tests/outputs/027.pt
  227. +7 −0 src/chameleon/tests/outputs/028.pt
  228. +4 −0 src/chameleon/tests/outputs/029.pt
  229. +10 −0 src/chameleon/tests/outputs/030.pt
  230. +9 −0 src/chameleon/tests/outputs/031.pt
  231. +15 −0 src/chameleon/tests/outputs/032.pt
  232. +15 −0 src/chameleon/tests/outputs/033.pt
  233. +15 −0 src/chameleon/tests/outputs/034.pt
  234. +17 −0 src/chameleon/tests/outputs/035.pt
  235. +15 −0 src/chameleon/tests/outputs/036.pt
  236. +15 −0 src/chameleon/tests/outputs/037.pt
  237. +6 −0 src/chameleon/tests/outputs/038.pt
  238. 0 src/chameleon/tests/outputs/039.pt
  239. +17 −0 src/chameleon/tests/outputs/040.pt
  240. +7 −0 src/chameleon/tests/outputs/041.pt
  241. +15 −0 src/chameleon/tests/outputs/042.pt
  242. +11 −0 src/chameleon/tests/outputs/043.pt
  243. +5 −0 src/chameleon/tests/outputs/044.pt
  244. +5 −0 src/chameleon/tests/outputs/101.html
  245. +5 −0 src/chameleon/tests/outputs/102.html
  246. +8 −0 src/chameleon/tests/outputs/103.html
  247. +23 −0 src/chameleon/tests/test_doctests.py
  248. +86 −0 src/chameleon/tests/test_parser.py
  249. +163 −0 src/chameleon/tests/test_sniffing.py
  250. +270 −0 src/chameleon/tests/test_templates.py
  251. +36 −0 src/chameleon/tests/test_tokenizer.py
  252. +135 −0 src/chameleon/tokenize.py
  253. +151 −0 src/chameleon/utils.py
  254. +1 −0 src/chameleon/zpt/__init__.py
  255. +534 −0 src/chameleon/zpt/program.py
  256. +132 −0 src/chameleon/zpt/template.py
6 CHANGES.rst
@@ -0,0 +1,6 @@
+Changes
+=======
+
+In next release...
+
+- Initial public release.
5 COPYRIGHT.txt
@@ -0,0 +1,5 @@
+Copyright (c) 2011 Malthe Borch and Contributors. All Rights Reserved.
+
+Portions (c) Zope Foundation and contributors (http://www.zope.org/).
+
+Portions (c) Edgewall Software.
132 LICENSE.txt
@@ -0,0 +1,132 @@
+The majority of the code in Chameleon is supplied under this license:
+
+ A copyright notice accompanies this license document that identifies
+ the copyright holders.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions in source code must retain the accompanying
+ copyright notice, this list of conditions, and the following
+ disclaimer.
+
+ 2. Redistributions in binary form must reproduce the accompanying
+ copyright notice, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ 3. Names of the copyright holders must not be used to endorse or
+ promote products derived from this software without prior
+ written permission from the copyright holders.
+
+ 4. If any files are modified, you must cause the modified files to
+ carry prominent notices stating that you changed the files and
+ the date of any change.
+
+ Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
+ ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+Portions of the code in Chameleon are supplied under the ZPL (headers
+within individiual files indicate that these portions are licensed
+under the ZPL):
+
+ Zope Public License (ZPL) Version 2.1
+ -------------------------------------
+
+ A copyright notice accompanies this license document that
+ identifies the copyright holders.
+
+ This license has been certified as open source. It has also
+ been designated as GPL compatible by the Free Software
+ Foundation (FSF).
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the
+ following conditions are met:
+
+ 1. Redistributions in source code must retain the
+ accompanying copyright notice, this list of conditions,
+ and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the accompanying
+ copyright notice, this list of conditions, and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+ 3. Names of the copyright holders must not be used to
+ endorse or promote products derived from this software
+ without prior written permission from the copyright
+ holders.
+
+ 4. The right to distribute this software or to use it for
+ any purpose does not give you the right to use
+ Servicemarks (sm) or Trademarks (tm) of the copyright
+ holders. Use of them is covered by separate agreement
+ with the copyright holders.
+
+ 5. If any files are modified, you must cause the modified
+ files to carry prominent notices stating that you changed
+ the files and the date of any change.
+
+ Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+ AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
+
+Portions of the code in Chameleon are supplied under the BSD license
+(headers within individiual files indicate that these portions are
+licensed under this license):
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
72 README.rst
@@ -0,0 +1,72 @@
+Overview
+========
+
+Chameleon is an HTML/XML [1]_ template language compiler.
+
+It includes a feature-complete engine for the Zope Page Templates
+(ZPT) language.
+
+The software is released on PyPi. To download and install the latest
+release::
+
+ $ easy-install -U Chameleon
+
+
+What's New in 2.x
+------------------
+
+The 2.x series is a complete rewrite of the library and supports both
+Python 2.7+ and Python 3.1+ with a single source code.
+
+For most users it should be an easy upgrade, however note that at
+present, there is no engine for the Genshi language.
+
+New parser
+~~~~~~~~~~
+
+This series features a new parser, implemented in pure Python. It
+parses both HTML and XML inputs (the previous parser relied on the
+expat system library and was more strict about its input).
+
+Language support
+~~~~~~~~~~~~~~~~
+
+The 2.x series supports a couple of new constructs:
+
+1) Support for the ``tal:on-error`` from the reference specification
+has been added.
+
+2) Inspired by the Genshi language, a pair of new attributes has been
+added: ``tal:switch`` and ``tal:case``, allowing flexible conditions.
+
+Expression engine
+~~~~~~~~~~~~~~~~~
+
+The expression engine has been redesigned to make it easier to
+understand and extend.
+
+The engine is built on the new ``ast`` module.
+
+Performance
+~~~~~~~~~~~
+
+The compiler output has been optimized for complex templates. For most
+applications, the engine should perform similarly to the 1.x
+series.
+
+Very simple templates with tight loops (such as that of the "big
+table" benchmark) will see a decrease in performance. The compiler is
+optimized for dynamic variable scope and this lowers performance for
+templates that require only a static scope.
+
+
+License and Copyright
+---------------------
+
+This software is made available as-is under a BSD-like license
+[2]_ (see included copyright notice).
+
+.. [1] There is currently no support for unstructured documents.
+
+.. [2] Licensed under the `Repoze <http://repoze.org/license.html>`_
+ license.
485 distribute_setup.py
@@ -0,0 +1,485 @@
+#!python
+"""Bootstrap distribute installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from distribute_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import sys
+import time
+import fnmatch
+import tempfile
+import tarfile
+from distutils import log
+
+try:
+ from site import USER_SITE
+except ImportError:
+ USER_SITE = None
+
+try:
+ import subprocess
+
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ return subprocess.call(args) == 0
+
+except ImportError:
+ # will be used for python 2.3
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
+
+DEFAULT_VERSION = "0.6.14"
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
+
+SETUPTOOLS_PKG_INFO = """\
+Metadata-Version: 1.0
+Name: setuptools
+Version: %s
+Summary: xxxx
+Home-page: xxx
+Author: xxx
+Author-email: xxx
+License: xxx
+Description: xxx
+""" % SETUPTOOLS_FAKED_VERSION
+
+
+def _install(tarball):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # installing
+ log.warn('Installing Distribute')
+ if not _python_cmd('setup.py', 'install'):
+ log.warn('Something went wrong during the installation.')
+ log.warn('See the error message above.')
+ finally:
+ os.chdir(old_wd)
+
+
+def _build_egg(egg, tarball, to_dir):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # building an egg
+ log.warn('Building a Distribute egg in %s', to_dir)
+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+ finally:
+ os.chdir(old_wd)
+ # returning the result
+ log.warn(egg)
+ if not os.path.exists(egg):
+ raise IOError('Could not build the egg.')
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
+ % (version, sys.version_info[0], sys.version_info[1]))
+ if not os.path.exists(egg):
+ tarball = download_setuptools(version, download_base,
+ to_dir, download_delay)
+ _build_egg(egg, tarball, to_dir)
+ sys.path.insert(0, egg)
+ import setuptools
+ setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, download_delay=15, no_fake=True):
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ was_imported = 'pkg_resources' in sys.modules or \
+ 'setuptools' in sys.modules
+ try:
+ try:
+ import pkg_resources
+ if not hasattr(pkg_resources, '_distribute'):
+ if not no_fake:
+ _fake_setuptools()
+ raise ImportError
+ except ImportError:
+ return _do_download(version, download_base, to_dir, download_delay)
+ try:
+ pkg_resources.require("distribute>="+version)
+ return
+ except pkg_resources.VersionConflict:
+ e = sys.exc_info()[1]
+ if was_imported:
+ sys.stderr.write(
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ except pkg_resources.DistributionNotFound:
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ finally:
+ if not no_fake:
+ _create_fake_setuptools_pkg_info(to_dir)
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, delay=15):
+ """Download distribute from a specified location and return its filename
+
+ `version` should be a valid distribute version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download
+ attempt.
+ """
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+ tgz_name = "distribute-%s.tar.gz" % version
+ url = download_base + tgz_name
+ saveto = os.path.join(to_dir, tgz_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ log.warn("Downloading %s", url)
+ src = urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = src.read()
+ dst = open(saveto, "wb")
+ dst.write(data)
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ return os.path.realpath(saveto)
+
+def _no_sandbox(function):
+ def __no_sandbox(*args, **kw):
+ try:
+ from setuptools.sandbox import DirectorySandbox
+ if not hasattr(DirectorySandbox, '_old'):
+ def violation(*args):
+ pass
+ DirectorySandbox._old = DirectorySandbox._violation
+ DirectorySandbox._violation = violation
+ patched = True
+ else:
+ patched = False
+ except ImportError:
+ patched = False
+
+ try:
+ return function(*args, **kw)
+ finally:
+ if patched:
+ DirectorySandbox._violation = DirectorySandbox._old
+ del DirectorySandbox._old
+
+ return __no_sandbox
+
+def _patch_file(path, content):
+ """Will backup the file then patch it"""
+ existing_content = open(path).read()
+ if existing_content == content:
+ # already patched
+ log.warn('Already patched.')
+ return False
+ log.warn('Patching...')
+ _rename_path(path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ return True
+
+_patch_file = _no_sandbox(_patch_file)
+
+def _same_content(path, content):
+ return open(path).read() == content
+
+def _rename_path(path):
+ new_name = path + '.OLD.%s' % time.time()
+ log.warn('Renaming %s into %s', path, new_name)
+ os.rename(path, new_name)
+ return new_name
+
+def _remove_flat_installation(placeholder):
+ if not os.path.isdir(placeholder):
+ log.warn('Unkown installation at %s', placeholder)
+ return False
+ found = False
+ for file in os.listdir(placeholder):
+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
+ found = True
+ break
+ if not found:
+ log.warn('Could not locate setuptools*.egg-info')
+ return
+
+ log.warn('Removing elements out of the way...')
+ pkg_info = os.path.join(placeholder, file)
+ if os.path.isdir(pkg_info):
+ patched = _patch_egg_dir(pkg_info)
+ else:
+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
+
+ if not patched:
+ log.warn('%s already patched.', pkg_info)
+ return False
+ # now let's move the files out of the way
+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
+ element = os.path.join(placeholder, element)
+ if os.path.exists(element):
+ _rename_path(element)
+ else:
+ log.warn('Could not find the %s element of the '
+ 'Setuptools distribution', element)
+ return True
+
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
+def _after_install(dist):
+ log.warn('After install bootstrap.')
+ placeholder = dist.get_command_obj('install').install_purelib
+ _create_fake_setuptools_pkg_info(placeholder)
+
+def _create_fake_setuptools_pkg_info(placeholder):
+ if not placeholder or not os.path.exists(placeholder):
+ log.warn('Could not find the install location')
+ return
+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
+ (SETUPTOOLS_FAKED_VERSION, pyver)
+ pkg_info = os.path.join(placeholder, setuptools_file)
+ if os.path.exists(pkg_info):
+ log.warn('%s already exists', pkg_info)
+ return
+
+ log.warn('Creating %s', pkg_info)
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+
+ pth_file = os.path.join(placeholder, 'setuptools.pth')
+ log.warn('Creating %s', pth_file)
+ f = open(pth_file, 'w')
+ try:
+ f.write(os.path.join(os.curdir, setuptools_file))
+ finally:
+ f.close()
+
+_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
+
+def _patch_egg_dir(path):
+ # let's check if it's already patched
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ if os.path.exists(pkg_info):
+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
+ log.warn('%s already patched.', pkg_info)
+ return False
+ _rename_path(path)
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, 'EGG-INFO'))
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+ return True
+
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
+def _before_install():
+ log.warn('Before install bootstrap.')
+ _fake_setuptools()
+
+
+def _under_prefix(location):
+ if 'install' not in sys.argv:
+ return True
+ args = sys.argv[sys.argv.index('install')+1:]
+ for index, arg in enumerate(args):
+ for option in ('--root', '--prefix'):
+ if arg.startswith('%s=' % option):
+ top_dir = arg.split('root=')[-1]
+ return location.startswith(top_dir)
+ elif arg == option:
+ if len(args) > index:
+ top_dir = args[index+1]
+ return location.startswith(top_dir)
+ if arg == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
+ return True
+
+
+def _fake_setuptools():
+ log.warn('Scanning installed packages')
+ try:
+ import pkg_resources
+ except ImportError:
+ # we're cool
+ log.warn('Setuptools or Distribute does not seem to be installed.')
+ return
+ ws = pkg_resources.working_set
+ try:
+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
+ replacement=False))
+ except TypeError:
+ # old distribute API
+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
+
+ if setuptools_dist is None:
+ log.warn('No setuptools distribution found')
+ return
+ # detecting if it was already faked
+ setuptools_location = setuptools_dist.location
+ log.warn('Setuptools installation detected at %s', setuptools_location)
+
+ # if --root or --preix was provided, and if
+ # setuptools is not located in them, we don't patch it
+ if not _under_prefix(setuptools_location):
+ log.warn('Not patching, --root or --prefix is installing Distribute'
+ ' in another location')
+ return
+
+ # let's see if its an egg
+ if not setuptools_location.endswith('.egg'):
+ log.warn('Non-egg installation')
+ res = _remove_flat_installation(setuptools_location)
+ if not res:
+ return
+ else:
+ log.warn('Egg installation')
+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
+ if (os.path.exists(pkg_info) and
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ log.warn('Already patched.')
+ return
+ log.warn('Patching...')
+ # let's create a fake egg replacing setuptools one
+ res = _patch_egg_dir(setuptools_location)
+ if not res:
+ return
+ log.warn('Patched done.')
+ _relaunch()
+
+
+def _relaunch():
+ log.warn('Relaunching...')
+ # we have to relaunch the process
+ # pip marker to avoid a relaunch bug
+ if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
+ sys.argv[0] = 'setup.py'
+ args = [sys.executable] + sys.argv
+ sys.exit(subprocess.call(args))
+
+
+def _extractall(self, path=".", members=None):
+ """Extract all members from the archive to the current working
+ directory and set owner, modification time and permissions on
+ directories afterwards. `path' specifies a different directory
+ to extract to. `members' is optional and must be a subset of the
+ list returned by getmembers().
+ """
+ import copy
+ import operator
+ from tarfile import ExtractError
+ directories = []
+
+ if members is None:
+ members = self
+
+ for tarinfo in members:
+ if tarinfo.isdir():
+ # Extract directories with a safe mode.
+ directories.append(tarinfo)
+ tarinfo = copy.copy(tarinfo)
+ tarinfo.mode = 448 # decimal for oct 0700
+ self.extract(tarinfo, path)
+
+ # Reverse sort directories.
+ if sys.version_info < (2, 4):
+ def sorter(dir1, dir2):
+ return cmp(dir1.name, dir2.name)
+ directories.sort(sorter)
+ directories.reverse()
+ else:
+ directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ dirpath = os.path.join(path, tarinfo.name)
+ try:
+ self.chown(tarinfo, dirpath)
+ self.utime(tarinfo, dirpath)
+ self.chmod(tarinfo, dirpath)
+ except ExtractError:
+ e = sys.exc_info()[1]
+ if self.errorlevel > 1:
+ raise
+ else:
+ self._dbg(1, "tarfile: %s" % e)
+
+
+def main(argv, version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ tarball = download_setuptools()
+ _install(tarball)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
73 setup.py
@@ -0,0 +1,73 @@
+__version__ = '2.0-dev'
+
+import os
+import sys
+
+from distribute_setup import use_setuptools
+use_setuptools()
+
+from setuptools import setup, find_packages
+from setuptools.command.test import test
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.rst')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.rst')).read()
+
+install_requires = []
+
+version = sys.version_info[:3]
+if version < (2, 7, 0):
+ install_requires.append("ordereddict")
+
+class Benchmark(test):
+ description = "Run benchmarks"
+ user_options = []
+ test_suite = None
+
+ def initialize_options(self):
+ """init options"""
+ pass
+
+ def finalize_options(self):
+ """finalize options"""
+
+ self.distribution.tests_require = [
+ 'zope.pagetemplate',
+ 'zope.component',
+ 'zope.i18n',
+ 'zope.testing']
+
+ def run(self):
+ test.run(self)
+ self.with_project_on_sys_path(self.run_benchmark)
+
+ def run_benchmark(self):
+ from chameleon import benchmark
+ print("running benchmark...")
+
+ benchmark.start()
+
+setup(
+ name="Chameleon",
+ version=__version__,
+ description="Fast XML template engine for Python.",
+ long_description="\n\n".join((README, CHANGES)),
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "Intended Audience :: Developers",
+ "Programming Language :: Python",
+ ],
+ author="Malthe Borch",
+ author_email="mborch@gmail.com",
+ license='BSD-like (http://repoze.org/license.html)',
+ packages=find_packages('src'),
+ package_dir = {'': 'src'},
+ include_package_data=True,
+ install_requires=install_requires,
+ zip_safe=False,
+ test_suite="chameleon.tests",
+ cmdclass={
+ 'benchmark': Benchmark,
+ }
+ )
+
1 src/chameleon/__init__.py
@@ -0,0 +1 @@
+
927 src/chameleon/astutil.py
@@ -0,0 +1,927 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Support classes for generating code from abstract syntax trees."""
+
+import ast
+
+from copy import copy as copy_node
+
+
+__docformat__ = 'restructuredtext en'
+
+
+def parse(source, mode='eval'):
+ return compile(source, '', mode, ast.PyCF_ONLY_AST)
+
+
+def load(name):
+ return ast.Name(name, ast.Load())
+
+
+def store(name):
+ return ast.Name(name, ast.Store())
+
+
+def param(name):
+ return ast.Name(name, ast.Param())
+
+
+def delete(name):
+ return ast.Name(name, ast.Del())
+
+
+def subscript(name, target, ctx):
+ return ast.Subscript(
+ target,
+ ast.Index(ast.Str(name)),
+ ctx,
+ )
+
+
+def walk_names(target, mode):
+ for node in ast.walk(target):
+ if isinstance(node, ast.Name) and \
+ isinstance(node.ctx, mode):
+ yield node.id
+
+
+def copy(source, target):
+ target.__class__ = source.__class__
+ target.__dict__ = source.__dict__
+
+
+def swap(root, replacement, name):
+ for node in ast.walk(root):
+ if (isinstance(node, ast.Name) and
+ isinstance(node.ctx, ast.Load) and
+ node.id == name):
+ copy(replacement, node)
+
+
+class Node(ast.AST):
+ """AST baseclass that gives us a convenient initialization
+ method. We explicitly declare and use the ``_fields`` attribute."""
+
+ _fields = ()
+
+ def __init__(self, *args, **kwargs):
+ assert isinstance(self._fields, tuple)
+ self.__dict__.update(kwargs)
+ for name, value in zip(self._fields, args):
+ setattr(self, name, value)
+
+ def __repr__(self):
+ """Poor man's single-line pretty printer."""
+
+ name = type(self).__name__
+ return '<%s%s at %x>' % (
+ name,
+ "".join(" %s=%r" % (name, getattr(self, name, "\"?\""))
+ for name in self._fields),
+ id(self)
+ )
+
+
+class Builtin(Node, ast.Name):
+ """Represents a Python builtin.
+
+ Used when a builtin is used internally by the compiler, to avoid
+ clashing with a user assignment (e.g. ``help`` is a builtin, but
+ also commonly assigned in templates).
+ """
+
+ _fields = "id", "ctx"
+
+ ctx = ast.Load()
+
+
+class Undefined(Node, ast.Expr):
+ _fields = "id",
+
+
+class Symbol(Node, ast.expr):
+ """Represents an importable symbol."""
+
+ _fields = "value",
+
+
+class Static(Node, ast.expr):
+ """Represents a static value."""
+
+ _fields = "value", "name"
+
+ name = None
+
+
+class Comment(Node, ast.stmt):
+ _fields = "text", "space", "stmt"
+
+ stmt = None
+ space = ""
+
+
+class ASTCodeGenerator(object):
+ """General purpose base class for AST transformations.
+
+ Every visitor method can be overridden to return an AST node that has been
+ altered or replaced in some way.
+ """
+
+ def __init__(self, tree):
+ self.lines_info = []
+ self.line_info = None
+ self.lines = []
+ self.line = None
+ self.last = None
+ self.indent = 0
+ self.blame_stack = []
+ self.visit(tree)
+
+ if self.line.strip():
+ self._new_line()
+
+ self.line = None
+ self.line_info = None
+
+ # strip trivial lines
+ self.code = "\n".join(
+ line.strip() and line or ""
+ for line in self.lines
+ )
+
+ def _change_indent(self, delta):
+ self.indent += delta
+
+ def _new_line(self):
+ if self.line is not None:
+ self.lines.append(self.line)
+ self.lines_info.append(self.line_info)
+ self.line = ' ' * 4 * self.indent
+ if len(self.blame_stack) == 0:
+ self.line_info = []
+ self.last = None
+ else:
+ self.line_info = [(0, self.blame_stack[-1],)]
+ self.last = self.blame_stack[-1]
+
+ def _write(self, s):
+ if len(s) == 0:
+ return
+ if len(self.blame_stack) == 0:
+ if self.last is not None:
+ self.last = None
+ self.line_info.append((len(self.line), self.last))
+ else:
+ if self.last != self.blame_stack[-1]:
+ self.last = self.blame_stack[-1]
+ self.line_info.append((len(self.line), self.last))
+ self.line += s
+
+ def visit(self, node):
+ if node is None:
+ return None
+ if type(node) is tuple:
+ return tuple([self.visit(n) for n in node])
+ try:
+ self.blame_stack.append((node.lineno, node.col_offset,))
+ info = True
+ except AttributeError:
+ info = False
+ visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
+ if visitor is None:
+ raise Exception('No handler for ``%s`` (%s).' % (
+ node.__class__.__name__, repr(node)))
+ ret = visitor(node)
+ if info:
+ self.blame_stack.pop()
+ return ret
+
+ def visit_Module(self, node):
+ for n in node.body:
+ self.visit(n)
+ visit_Interactive = visit_Module
+ visit_Suite = visit_Module
+
+ def visit_Expression(self, node):
+ return self.visit(node.body)
+
+ # arguments = (expr* args, identifier? vararg,
+ # identifier? kwarg, expr* defaults)
+ def visit_arguments(self, node):
+ first = True
+ no_default_count = len(node.args) - len(node.defaults)
+ for i, arg in enumerate(node.args):
+ if not first:
+ self._write(', ')
+ else:
+ first = False
+ self.visit(arg)
+ if i >= no_default_count:
+ self._write('=')
+ self.visit(node.defaults[i - no_default_count])
+ if getattr(node, 'vararg', None):
+ if not first:
+ self._write(', ')
+ else:
+ first = False
+ self._write('*' + node.vararg)
+ if getattr(node, 'kwarg', None):
+ if not first:
+ self._write(', ')
+ else:
+ first = False
+ self._write('**' + node.kwarg)
+
+ # FunctionDef(identifier name, arguments args,
+ # stmt* body, expr* decorators)
+ def visit_FunctionDef(self, node):
+ self._new_line()
+ for decorator in getattr(node, 'decorator_list', ()):
+ self._new_line()
+ self._write('@')
+ self.visit(decorator)
+ self._new_line()
+ self._write('def ' + node.name + '(')
+ self.visit(node.args)
+ self._write('):')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # ClassDef(identifier name, expr* bases, stmt* body)
+ def visit_ClassDef(self, node):
+ self._new_line()
+ self._write('class ' + node.name)
+ if node.bases:
+ self._write('(')
+ self.visit(node.bases[0])
+ for base in node.bases[1:]:
+ self._write(', ')
+ self.visit(base)
+ self._write(')')
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # Return(expr? value)
+ def visit_Return(self, node):
+ self._new_line()
+ self._write('return')
+ if getattr(node, 'value', None):
+ self._write(' ')
+ self.visit(node.value)
+
+ # Delete(expr* targets)
+ def visit_Delete(self, node):
+ self._new_line()
+ self._write('del ')
+ self.visit(node.targets[0])
+ for target in node.targets[1:]:
+ self._write(', ')
+ self.visit(target)
+
+ # Assign(expr* targets, expr value)
+ def visit_Assign(self, node):
+ self._new_line()
+ for target in node.targets:
+ self.visit(target)
+ self._write(' = ')
+ self.visit(node.value)
+
+ # AugAssign(expr target, operator op, expr value)
+ def visit_AugAssign(self, node):
+ self._new_line()
+ self.visit(node.target)
+ self._write(' ' + self.binary_operators[node.op.__class__] + '= ')
+ self.visit(node.value)
+
+ # Print(expr? dest, expr* values, bool nl)
+ def visit_Print(self, node):
+ self._new_line()
+ self._write('print')
+ if getattr(node, 'dest', None):
+ self._write(' >> ')
+ self.visit(node.dest)
+ if getattr(node, 'values', None):
+ self._write(', ')
+ else:
+ self._write(' ')
+ if getattr(node, 'values', None):
+ self.visit(node.values[0])
+ for value in node.values[1:]:
+ self._write(', ')
+ self.visit(value)
+ if not node.nl:
+ self._write(',')
+
+ # For(expr target, expr iter, stmt* body, stmt* orelse)
+ def visit_For(self, node):
+ self._new_line()
+ self._write('for ')
+ self.visit(node.target)
+ self._write(' in ')
+ self.visit(node.iter)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'orelse', None):
+ self._new_line()
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # While(expr test, stmt* body, stmt* orelse)
+ def visit_While(self, node):
+ self._new_line()
+ self._write('while ')
+ self.visit(node.test)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'orelse', None):
+ self._new_line()
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # If(expr test, stmt* body, stmt* orelse)
+ def visit_If(self, node):
+ self._new_line()
+ self._write('if ')
+ self.visit(node.test)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'orelse', None):
+ self._new_line()
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # With(expr context_expr, expr? optional_vars, stmt* body)
+ def visit_With(self, node):
+ self._new_line()
+ self._write('with ')
+ self.visit(node.context_expr)
+ if getattr(node, 'optional_vars', None):
+ self._write(' as ')
+ self.visit(node.optional_vars)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # Raise(expr? type, expr? inst, expr? tback)
+ def visit_Raise(self, node):
+ self._new_line()
+ self._write('raise')
+ if not getattr(node, "type", None):
+ return
+ self._write(' ')
+ self.visit(node.type)
+ if not node.inst:
+ return
+ self._write(', ')
+ self.visit(node.inst)
+ if not node.tback:
+ return
+ self._write(', ')
+ self.visit(node.tback)
+
+ # TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
+ def visit_TryExcept(self, node):
+ self._new_line()
+ self._write('try:')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'handlers', None):
+ for handler in node.handlers:
+ self.visit(handler)
+ self._new_line()
+ if getattr(node, 'orelse', None):
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # excepthandler = (expr? type, expr? name, stmt* body)
+ def visit_ExceptHandler(self, node):
+ self._new_line()
+ self._write('except')
+ if getattr(node, 'type', None):
+ self._write(' ')
+ self.visit(node.type)
+ if getattr(node, 'name', None):
+ self._write(', ')
+ self.visit(node.name)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ visit_excepthandler = visit_ExceptHandler
+
+ # TryFinally(stmt* body, stmt* finalbody)
+ def visit_TryFinally(self, node):
+ self._new_line()
+ self._write('try:')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ if getattr(node, 'finalbody', None):
+ self._new_line()
+ self._write('finally:')
+ self._change_indent(1)
+ for statement in node.finalbody:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # Assert(expr test, expr? msg)
+ def visit_Assert(self, node):
+ self._new_line()
+ self._write('assert ')
+ self.visit(node.test)
+ if getattr(node, 'msg', None):
+ self._write(', ')
+ self.visit(node.msg)
+
+ def visit_alias(self, node):
+ self._write(node.name)
+ if getattr(node, 'asname', None):
+ self._write(' as ')
+ self._write(node.asname)
+
+ # Import(alias* names)
+ def visit_Import(self, node):
+ self._new_line()
+ self._write('import ')
+ self.visit(node.names[0])
+ for name in node.names[1:]:
+ self._write(', ')
+ self.visit(name)
+
+ # ImportFrom(identifier module, alias* names, int? level)
+ def visit_ImportFrom(self, node):
+ self._new_line()
+ self._write('from ')
+ if node.level:
+ self._write('.' * node.level)
+ self._write(node.module)
+ self._write(' import ')
+ self.visit(node.names[0])
+ for name in node.names[1:]:
+ self._write(', ')
+ self.visit(name)
+
+ # Exec(expr body, expr? globals, expr? locals)
+ def visit_Exec(self, node):
+ self._new_line()
+ self._write('exec ')
+ self.visit(node.body)
+ if not node.globals:
+ return
+ self._write(', ')
+ self.visit(node.globals)
+ if not node.locals:
+ return
+ self._write(', ')
+ self.visit(node.locals)
+
+ # Global(identifier* names)
+ def visit_Global(self, node):
+ self._new_line()
+ self._write('global ')
+ self.visit(node.names[0])
+ for name in node.names[1:]:
+ self._write(', ')
+ self.visit(name)
+
+ # Expr(expr value)
+ def visit_Expr(self, node):
+ self._new_line()
+ self.visit(node.value)
+
+ # Pass
+ def visit_Pass(self, node):
+ self._new_line()
+ self._write('pass')
+
+ # Break
+ def visit_Break(self, node):
+ self._new_line()
+ self._write('break')
+
+ # Continue
+ def visit_Continue(self, node):
+ self._new_line()
+ self._write('continue')
+
+ ### EXPRESSIONS
+ def with_parens(f):
+ def _f(self, node):
+ self._write('(')
+ f(self, node)
+ self._write(')')
+ return _f
+
+ bool_operators = {ast.And: 'and', ast.Or: 'or'}
+
+ # BoolOp(boolop op, expr* values)
+ @with_parens
+ def visit_BoolOp(self, node):
+ joiner = ' ' + self.bool_operators[node.op.__class__] + ' '
+ self.visit(node.values[0])
+ for value in node.values[1:]:
+ self._write(joiner)
+ self.visit(value)
+
+ binary_operators = {
+ ast.Add: '+',
+ ast.Sub: '-',
+ ast.Mult: '*',
+ ast.Div: '/',
+ ast.Mod: '%',
+ ast.Pow: '**',
+ ast.LShift: '<<',
+ ast.RShift: '>>',
+ ast.BitOr: '|',
+ ast.BitXor: '^',
+ ast.BitAnd: '&',
+ ast.FloorDiv: '//'
+ }
+
+ # BinOp(expr left, operator op, expr right)
+ @with_parens
+ def visit_BinOp(self, node):
+ self.visit(node.left)
+ self._write(' ' + self.binary_operators[node.op.__class__] + ' ')
+ self.visit(node.right)
+
+ unary_operators = {
+ ast.Invert: '~',
+ ast.Not: 'not',
+ ast.UAdd: '+',
+ ast.USub: '-',
+ }
+
+ # UnaryOp(unaryop op, expr operand)
+ def visit_UnaryOp(self, node):
+ self._write(self.unary_operators[node.op.__class__] + ' ')
+ self.visit(node.operand)
+
+ # Lambda(arguments args, expr body)
+ @with_parens
+ def visit_Lambda(self, node):
+ self._write('lambda ')
+ self.visit(node.args)
+ self._write(': ')
+ self.visit(node.body)
+
+ # IfExp(expr test, expr body, expr orelse)
+ @with_parens
+ def visit_IfExp(self, node):
+ self.visit(node.body)
+ self._write(' if ')
+ self.visit(node.test)
+ self._write(' else ')
+ self.visit(node.orelse)
+
+ # Dict(expr* keys, expr* values)
+ def visit_Dict(self, node):
+ self._write('{')
+ for key, value in zip(node.keys, node.values):
+ self.visit(key)
+ self._write(': ')
+ self.visit(value)
+ self._write(', ')
+ self._write('}')
+
+ # ListComp(expr elt, comprehension* generators)
+ def visit_ListComp(self, node):
+ self._write('[')
+ self.visit(node.elt)
+ for generator in node.generators:
+ # comprehension = (expr target, expr iter, expr* ifs)
+ self._write(' for ')
+ self.visit(generator.target)
+ self._write(' in ')
+ self.visit(generator.iter)
+ for ifexpr in generator.ifs:
+ self._write(' if ')
+ self.visit(ifexpr)
+ self._write(']')
+
+ # GeneratorExp(expr elt, comprehension* generators)
+ def visit_GeneratorExp(self, node):
+ self._write('(')
+ self.visit(node.elt)
+ for generator in node.generators:
+ # comprehension = (expr target, expr iter, expr* ifs)
+ self._write(' for ')
+ self.visit(generator.target)
+ self._write(' in ')
+ self.visit(generator.iter)
+ for ifexpr in generator.ifs:
+ self._write(' if ')
+ self.visit(ifexpr)
+ self._write(')')
+
+ # Yield(expr? value)
+ def visit_Yield(self, node):
+ self._write('yield')
+ if getattr(node, 'value', None):
+ self._write(' ')
+ self.visit(node.value)
+
+ comparison_operators = {
+ ast.Eq: '==',
+ ast.NotEq: '!=',
+ ast.Lt: '<',
+ ast.LtE: '<=',
+ ast.Gt: '>',
+ ast.GtE: '>=',
+ ast.Is: 'is',
+ ast.IsNot: 'is not',
+ ast.In: 'in',
+ ast.NotIn: 'not in',
+ }
+
+ # Compare(expr left, cmpop* ops, expr* comparators)
+ @with_parens
+ def visit_Compare(self, node):
+ self.visit(node.left)
+ for op, comparator in zip(node.ops, node.comparators):
+ self._write(' ' + self.comparison_operators[op.__class__] + ' ')
+ self.visit(comparator)
+
+ # Call(expr func, expr* args, keyword* keywords,
+ # expr? starargs, expr? kwargs)
+ def visit_Call(self, node):
+ self.visit(node.func)
+ self._write('(')
+ first = True
+ for arg in node.args:
+ if not first:
+ self._write(', ')
+ first = False
+ self.visit(arg)
+
+ for keyword in node.keywords:
+ if not first:
+ self._write(', ')
+ first = False
+ # keyword = (identifier arg, expr value)
+ self._write(keyword.arg)
+ self._write('=')
+ self.visit(keyword.value)
+ if getattr(node, 'starargs', None):
+ if not first:
+ self._write(', ')
+ first = False
+ self._write('*')
+ self.visit(node.starargs)
+
+ if getattr(node, 'kwargs', None):
+ if not first:
+ self._write(', ')
+ first = False
+ self._write('**')
+ self.visit(node.kwargs)
+ self._write(')')
+
+ # Repr(expr value)
+ def visit_Repr(self, node):
+ self._write('`')
+ self.visit(node.value)
+ self._write('`')
+
+ # Num(object n)
+ def visit_Num(self, node):
+ self._write(repr(node.n))
+
+ # Str(string s)
+ def visit_Str(self, node):
+ self._write(repr(node.s))
+
+ # Attribute(expr value, identifier attr, expr_context ctx)
+ def visit_Attribute(self, node):
+ self.visit(node.value)
+ self._write('.')
+ self._write(node.attr)
+
+ # Subscript(expr value, slice slice, expr_context ctx)
+ def visit_Subscript(self, node):
+ self.visit(node.value)
+ self._write('[')
+
+ def _process_slice(node):
+ if isinstance(node, ast.Ellipsis):
+ self._write('...')
+ elif isinstance(node, ast.Slice):
+ if getattr(node, 'lower', 'None'):
+ self.visit(node.lower)
+ self._write(':')
+ if getattr(node, 'upper', None):
+ self.visit(node.upper)
+ if getattr(node, 'step', None):
+ self._write(':')
+ self.visit(node.step)
+ elif isinstance(node, ast.Index):
+ self.visit(node.value)
+ elif isinstance(node, ast.ExtSlice):
+ self.visit(node.dims[0])
+ for dim in node.dims[1:]:
+ self._write(', ')
+ self.visit(dim)
+ else:
+ raise NotImplemented('Slice type not implemented')
+ _process_slice(node.slice)
+ self._write(']')
+
+ # Name(identifier id, expr_context ctx)
+ def visit_Name(self, node):
+ self._write(node.id)
+
+ # List(expr* elts, expr_context ctx)
+ def visit_List(self, node):
+ self._write('[')
+ for elt in node.elts:
+ self.visit(elt)
+ self._write(', ')
+ self._write(']')
+
+ # Tuple(expr *elts, expr_context ctx)
+ def visit_Tuple(self, node):
+ self._write('(')
+ for elt in node.elts:
+ self.visit(elt)
+ self._write(', ')
+ self._write(')')
+
+
+class ASTTransformer(object):
+ """General purpose base class for AST transformations.
+
+ Every visitor method can be overridden to return an AST node that has been
+ altered or replaced in some way.
+ """
+
+ def visit(self, node):
+ if node is None:
+ return None
+
+ if type(node) is tuple:
+ return tuple([self.visit(n) for n in node])
+
+ visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
+ if visitor is not None:
+ return visitor(node)
+
+ if isinstance(node, ast.AST):
+ return self._clone(node)
+
+ return node
+
+ def _clone(self, node):
+ clone = copy_node(node)
+ for name in getattr(clone, '_attributes', ()):
+ try:
+ setattr(clone, 'name', getattr(node, name))
+ except AttributeError:
+ pass
+ for name in clone._fields:
+ try:
+ value = getattr(node, name)
+ except AttributeError:
+ pass
+ else:
+ if value is None:
+ pass
+ elif isinstance(value, list):
+ value = [self.visit(x) for x in value]
+ elif isinstance(value, tuple):
+ value = tuple(self.visit(x) for x in value)
+ else:
+ value = self.visit(value)
+ setattr(clone, name, value)
+ return clone
+
+ def _leave(self, node):
+ return node
+
+ visit_Module = _clone
+ visit_Interactive = _clone
+ visit_Expression = _clone
+ visit_Suite = _clone
+
+ visit_FunctionDef = _clone
+ visit_ClassDef = _clone
+ visit_Return = _clone
+ visit_Delete = _clone
+ visit_Assign = _clone
+ visit_AugAssign = _clone
+ visit_Print = _clone
+ visit_For = _clone
+ visit_While = _clone
+ visit_If = _clone
+ visit_With = _clone
+ visit_Raise = _clone
+ visit_TryExcept = _clone
+ visit_TryFinally = _clone
+ visit_Assert = _clone
+ visit_ExceptHandler = _clone
+
+ visit_Import = _clone
+ visit_ImportFrom = _clone
+ visit_Exec = _clone
+ visit_Global = _clone
+ visit_Expr = _clone
+
+ visit_BoolOp = _clone
+ visit_BinOp = _clone
+ visit_UnaryOp = _clone
+ visit_Lambda = _clone
+ visit_IfExp = _clone
+ visit_Dict = _clone
+ visit_ListComp = _clone
+ visit_GeneratorExp = _clone
+ visit_Yield = _clone
+ visit_Compare = _clone
+ visit_Call = _clone
+ visit_Repr = _clone
+
+ visit_Attribute = _clone
+ visit_Subscript = _clone
+ visit_Name = _clone
+ visit_List = _clone
+ visit_Tuple = _clone
+
+ visit_comprehension = _clone
+ visit_excepthandler = _clone
+ visit_arguments = _clone
+ visit_keyword = _clone
+ visit_alias = _clone
+
+ visit_Slice = _clone
+ visit_ExtSlice = _clone
+ visit_Index = _clone
+
+
+class NameLookupRewriteTransformer(ast.NodeTransformer):
+ def __init__(self, transform):
+ self.transform = transform
+ self.transformed = set()
+
+ def __call__(self, node):
+ self.visit(node)
+ return self.transformed
+
+ def visit_Name(self, node):
+ self.transformed.add(node.id)
+ return self.transform(node)
+
+
+class ItemLookupOnAttributeErrorTransformer(ast.NodeTransformer):
+ def __init__(self, transform):
+ self.transform = transform
+
+ def visit_Attribute(self, node):
+ self.generic_visit(node)
+
+ # Only apply transform for name values
+ if isinstance(node.value, ast.Name) and isinstance(node.ctx, ast.Load):
+ return self.transform(node)
+
+ return node
477 src/chameleon/benchmark.py
@@ -0,0 +1,477 @@
+import unittest
+import time
+import os
+import re
+
+re_amp = re.compile(r'&(?!([A-Za-z]+|#[0-9]+);)')
+
+BIGTABLE_ZPT = """\
+<table xmlns="http://www.w3.org/1999/xhtml"
+xmlns:tal="http://xml.zope.org/namespaces/tal">
+<tr tal:repeat="row python: options['table']">
+<td tal:repeat="c python: row.values()">
+<span tal:define="d python: c + 1"
+tal:attributes="class python: 'column-' + str(d)"
+tal:content="python: d" />
+</td>
+</tr>
+</table>"""
+
+MANY_STRINGS_ZPT = """\
+<table xmlns="http://www.w3.org/1999/xhtml"
+xmlns:tal="http://xml.zope.org/namespaces/tal">
+<tr tal:repeat="i python: xrange(1000)">
+<td tal:content="string: number ${i}" />
+</tr>
+</table>
+"""
+
+HELLO_WORLD_ZPT = """\
+<html xmlns="http://www.w3.org/1999/xhtml"
+xmlns:tal="http://xml.zope.org/namespaces/tal">
+<body>
+<h1>Hello, world!</h1>
+</body>
+</html>
+"""
+
+I18N_ZPT = """\
+<html xmlns="http://www.w3.org/1999/xhtml"
+xmlns:tal="http://xml.zope.org/namespaces/tal"
+xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+ <body>
+ <div tal:repeat="i python: xrange(10)">
+ <div i18n:translate="">
+ Hello world!
+ </div>
+ <div i18n:translate="hello_world">
+ Hello world!
+ </div>
+ <div i18n:translate="">
+ <sup>Hello world!</sup>
+ </div>
+ </div>
+ </body>
+</html>
+"""
+
+
+def benchmark(title):
+ def decorator(f):
+ def wrapper(*args):
+ print(
+ "==========================\n " \
+ "%s\n==========================" % \
+ title)
+ return f(*args)
+ return wrapper
+ return decorator
+
+
+def timing(func, *args, **kwargs):
+ t1 = t2 = time.time()
+ i = 0
+ while t2 - t1 < 3:
+ func(**kwargs)
+ func(**kwargs)
+ func(**kwargs)
+ func(**kwargs)
+ i += 4
+ t2 = time.time()
+ return float(10 * (t2 - t1)) / i
+
+
+START = 0
+END = 1
+TAG = 2
+
+
+def yield_tokens(table=None):
+ index = []
+ tag = index.append
+ _re_amp = re_amp
+ tag(START)
+ yield "<", "html", "", ">\n"
+ for r in table:
+ tag(START)
+ yield "<", "tr", "", ">\n"
+
+ for c in r.values():
+ d = c + 1
+ tag(START)
+ yield "<", "td", "", ">\n"
+
+ _tmp5 = d
+ if not isinstance(_tmp5, unicode):
+ _tmp5 = str(_tmp5)
+ if ('&' in _tmp5):
+ if (';' in _tmp5):
+ _tmp5 = _re_amp.sub('&amp;', _tmp5)
+ else:
+ _tmp5 = _tmp5.replace('&', '&amp;')
+ if ('<' in _tmp5):
+ _tmp5 = _tmp5.replace('<', '&lt;')
+ if ('>' in _tmp5):
+ _tmp5 = _tmp5.replace('>', '&gt;')
+ if ('"' in _tmp5):
+ _tmp5 = _tmp5.replace('"', '&quot;')
+ _tmp5 = "column-%s" % _tmp5
+
+ _tmp = d
+ if (_tmp.__class__ not in (str, unicode, int, float, )):
+ raise
+ if (_tmp is not None):
+ if not isinstance(_tmp, unicode):
+ _tmp = str(_tmp)
+ if ('&' in _tmp):
+ if (';' in _tmp):
+ _tmp = _re_amp.sub('&amp;', _tmp)
+ else:
+ _tmp = _tmp.replace('&', '&amp;')
+ if ('<' in _tmp):
+ _tmp = _tmp.replace('<', '&lt;')
+ if ('>' in _tmp):
+ _tmp = _tmp.replace('>', '&gt;')
+ tag(START)
+
+ t = ["classicism"]
+
+ yield "<", "span", " ", t[0], '="', _tmp5, '"', ">\n"
+ tag(END)
+ yield "</", "span", ">\n"
+ tag(END)
+ yield "</", "td", ">\n"
+ tag(END)
+ yield "</", "tr", ">\n"
+ tag(END)
+ yield "</", "html", ">\n"
+
+
+def yield_tokens_dict_version(**kwargs):
+ index = []
+ tag = index.append
+ _re_amp = re_amp
+ tag(START)
+ yield "<", "html", "", ">\n"
+
+ for r in kwargs['table']:
+ kwargs['r'] = r
+ tag(START)
+ yield "<", "tr", "", ">\n"
+
+ for c in kwargs['r'].values():
+ kwargs['d'] = c + 1
+ tag(START)
+ yield "<", "td", "", ">\n"
+
+ _tmp5 = kwargs['d']
+ if not isinstance(_tmp5, unicode):
+ _tmp5 = str(_tmp5)
+ if ('