Skip to content
This repository
Browse code

First commit.

  • Loading branch information...
commit 117e89ac95e26565264d906a88dc07e87974b3bb 0 parents
Malthe Borch authored February 21, 2011

Showing 256 changed files with 8,415 additions and 0 deletions. Show diff stats Hide diff stats

  1. 6  CHANGES.rst
  2. 5  COPYRIGHT.txt
  3. 132  LICENSE.txt
  4. 72  README.rst
  5. 485  distribute_setup.py
  6. 73  setup.py
  7. 1  src/chameleon/__init__.py
  8. 927  src/chameleon/astutil.py
  9. 477  src/chameleon/benchmark.py
  10. 210  src/chameleon/codegen.py
  11. 873  src/chameleon/compiler.py
  12. 43  src/chameleon/config.py
  13. 63  src/chameleon/exc.py
  14. 95  src/chameleon/i18n.py
  15. 86  src/chameleon/interfaces.py
  16. 115  src/chameleon/loader.py
  17. 21  src/chameleon/metal.py
  18. 9  src/chameleon/namespaces.py
  19. 176  src/chameleon/nodes.py
  20. 200  src/chameleon/parser.py
  21. 43  src/chameleon/program.py
  22. 378  src/chameleon/tal.py
  23. 515  src/chameleon/tales.py
  24. 290  src/chameleon/template.py
  25. 1  src/chameleon/tests/__init__.py
  26. 7  src/chameleon/tests/inputs/001-variable-scope.html
  27. 12  src/chameleon/tests/inputs/001-variable-scope.pt
  28. 4  src/chameleon/tests/inputs/001.xml
  29. 8  src/chameleon/tests/inputs/002-repeat-scope.pt
  30. 4  src/chameleon/tests/inputs/002.xml
  31. 16  src/chameleon/tests/inputs/003-content.pt
  32. 4  src/chameleon/tests/inputs/003.xml
  33. 11  src/chameleon/tests/inputs/004-attributes.pt
  34. 5  src/chameleon/tests/inputs/004.xml
  35. 10  src/chameleon/tests/inputs/005-default.pt
  36. 5  src/chameleon/tests/inputs/005.xml
  37. 6  src/chameleon/tests/inputs/006-attribute-interpolation.pt
  38. 5  src/chameleon/tests/inputs/006.xml
  39. 6  src/chameleon/tests/inputs/007-content-interpolation.pt
  40. 4  src/chameleon/tests/inputs/007.xml
  41. 8  src/chameleon/tests/inputs/008-builtins.pt
  42. 4  src/chameleon/tests/inputs/008.xml
  43. 5  src/chameleon/tests/inputs/009-literals.pt
  44. 4  src/chameleon/tests/inputs/009.xml
  45. 7  src/chameleon/tests/inputs/010-structure.pt
  46. 5  src/chameleon/tests/inputs/010.xml
  47. 9  src/chameleon/tests/inputs/011-messages.pt
  48. 5  src/chameleon/tests/inputs/011.xml
  49. 21  src/chameleon/tests/inputs/012-translation.pt
  50. 5  src/chameleon/tests/inputs/012.xml
  51. 11  src/chameleon/tests/inputs/013-repeat-nested.pt
  52. 5  src/chameleon/tests/inputs/013.xml
  53. 7  src/chameleon/tests/inputs/014-repeat-nested-similar.pt
  54. 5  src/chameleon/tests/inputs/014.xml
  55. 10  src/chameleon/tests/inputs/015-translation-nested.pt
  56. 5  src/chameleon/tests/inputs/015.xml
  57. 11  src/chameleon/tests/inputs/016-explicit-translation.pt
  58. 4  src/chameleon/tests/inputs/016.xml
  59. 12  src/chameleon/tests/inputs/017-omit-tag.pt
  60. 4  src/chameleon/tests/inputs/017.xml
  61. 13  src/chameleon/tests/inputs/018-translation-nested-dynamic.pt
  62. 4  src/chameleon/tests/inputs/018.xml
  63. 13  src/chameleon/tests/inputs/019-replace.pt
  64. 4  src/chameleon/tests/inputs/019.xml
  65. 5  src/chameleon/tests/inputs/020-on-error.pt
  66. 4  src/chameleon/tests/inputs/020.xml
  67. 16  src/chameleon/tests/inputs/021-translation-domain.pt
  68. 4  src/chameleon/tests/inputs/021.xml
  69. 13  src/chameleon/tests/inputs/022-switch.pt
  70. 4  src/chameleon/tests/inputs/022.xml
  71. 6  src/chameleon/tests/inputs/023-condition.pt
  72. 5  src/chameleon/tests/inputs/023.xml
  73. 16  src/chameleon/tests/inputs/024-namespace-elements.pt
  74. 6  src/chameleon/tests/inputs/024.xml
  75. 14  src/chameleon/tests/inputs/025-repeat-whitespace.pt
  76. 5  src/chameleon/tests/inputs/025.xml
  77. 14  src/chameleon/tests/inputs/026-repeat-variable.pt
  78. 5  src/chameleon/tests/inputs/026.xml
  79. 11  src/chameleon/tests/inputs/027-attribute-replacement.pt
  80. 5  src/chameleon/tests/inputs/027.xml
  81. 7  src/chameleon/tests/inputs/028-attribute-toggle.pt
  82. 5  src/chameleon/tests/inputs/028.xml
  83. 5  src/chameleon/tests/inputs/029-attribute-ordering.pt
  84. 5  src/chameleon/tests/inputs/029.xml
  85. 7  src/chameleon/tests/inputs/030-repeat-tuples.pt
  86. 5  src/chameleon/tests/inputs/030.xml
  87. 7  src/chameleon/tests/inputs/031-namespace-with-tal.pt
  88. 5  src/chameleon/tests/inputs/031.xml
  89. 19  src/chameleon/tests/inputs/032-master-template.pt
  90. 5  src/chameleon/tests/inputs/032.xml
  91. 1  src/chameleon/tests/inputs/033-use-macro-trivial.pt
  92. 5  src/chameleon/tests/inputs/033.xml
  93. 1  src/chameleon/tests/inputs/034-use-template-as-macro.pt
  94. 4  src/chameleon/tests/inputs/034.xml
  95. 5  src/chameleon/tests/inputs/035-use-macro-with-fill-slot.pt
  96. 4  src/chameleon/tests/inputs/035.xml
  97. 2  src/chameleon/tests/inputs/036-use-macro-inherits-dynamic-scope.pt
  98. 5  src/chameleon/tests/inputs/036.xml
  99. 5  src/chameleon/tests/inputs/037-use-macro-local-variable-scope.pt
  100. 6  src/chameleon/tests/inputs/037.xml
  101. 6  src/chameleon/tests/inputs/038-use-macro-globals.pt
  102. 6  src/chameleon/tests/inputs/038.xml
  103. 1  src/chameleon/tests/inputs/039-globals.pt
  104. 5  src/chameleon/tests/inputs/039.xml
  105. 20  src/chameleon/tests/inputs/040-macro-using-template-symbol.pt
  106. 5  src/chameleon/tests/inputs/040.xml
  107. 22  src/chameleon/tests/inputs/041-translate-nested-names.pt
  108. 5  src/chameleon/tests/inputs/041.xml
  109. 3  src/chameleon/tests/inputs/042-use-macro-fill-footer.pt
  110. 4  src/chameleon/tests/inputs/042.xml
  111. 19  src/chameleon/tests/inputs/043-macro-nested-dynamic-vars.pt
  112. 6  src/chameleon/tests/inputs/043.xml
  113. 5  src/chameleon/tests/inputs/044-tuple-define.pt
  114. 10  src/chameleon/tests/inputs/044.xml
  115. 6  src/chameleon/tests/inputs/045.xml
  116. 6  src/chameleon/tests/inputs/046.xml
  117. 5  src/chameleon/tests/inputs/047.xml
  118. 4  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  src/chameleon/tests/inputs/052.xml
  123. 6  src/chameleon/tests/inputs/053.xml
  124. 10  src/chameleon/tests/inputs/054.xml
  125. 5  src/chameleon/tests/inputs/055.xml
  126. 4  src/chameleon/tests/inputs/056.xml
  127. 4  src/chameleon/tests/inputs/057.xml
  128. 5  src/chameleon/tests/inputs/058.xml
  129. 10  src/chameleon/tests/inputs/059.xml
  130. 4  src/chameleon/tests/inputs/060.xml
  131. 4  src/chameleon/tests/inputs/061.xml
  132. 4  src/chameleon/tests/inputs/062.xml
  133. 4  src/chameleon/tests/inputs/063.xml
  134. 4  src/chameleon/tests/inputs/064.xml
  135. 5  src/chameleon/tests/inputs/065.xml
  136. 7  src/chameleon/tests/inputs/066.xml
  137. 4  src/chameleon/tests/inputs/067.xml
  138. 5  src/chameleon/tests/inputs/068.xml
  139. 5  src/chameleon/tests/inputs/069.xml
  140. 5  src/chameleon/tests/inputs/070.xml
  141. 5  src/chameleon/tests/inputs/071.xml
  142. 5  src/chameleon/tests/inputs/072.xml
  143. 5  src/chameleon/tests/inputs/073.xml
  144. 5  src/chameleon/tests/inputs/074.xml
  145. 5  src/chameleon/tests/inputs/075.xml
  146. 7  src/chameleon/tests/inputs/076.xml
  147. 5  src/chameleon/tests/inputs/077.xml
  148. 5  src/chameleon/tests/inputs/078.xml
  149. 5  src/chameleon/tests/inputs/079.xml
  150. 5  src/chameleon/tests/inputs/080.xml
  151. 7  src/chameleon/tests/inputs/081.xml
  152. 5  src/chameleon/tests/inputs/082.xml
  153. 5  src/chameleon/tests/inputs/083.xml
  154. 1  src/chameleon/tests/inputs/084.xml
  155. 6  src/chameleon/tests/inputs/085.xml
  156. 6  src/chameleon/tests/inputs/086.xml
  157. 6  src/chameleon/tests/inputs/087.xml
  158. 5  src/chameleon/tests/inputs/088.xml
  159. 5  src/chameleon/tests/inputs/089.xml
  160. 7  src/chameleon/tests/inputs/090.xml
  161. 7  src/chameleon/tests/inputs/091.xml
  162. 10  src/chameleon/tests/inputs/092.xml
  163. 5  src/chameleon/tests/inputs/093.xml
  164. 6  src/chameleon/tests/inputs/094.xml
  165. 6  src/chameleon/tests/inputs/095.xml
  166. 5  src/chameleon/tests/inputs/096.xml
  167. 8  src/chameleon/tests/inputs/097.xml
  168. 5  src/chameleon/tests/inputs/098.xml
  169. 5  src/chameleon/tests/inputs/099.xml
  170. 5  src/chameleon/tests/inputs/100.xml
  171. 5  src/chameleon/tests/inputs/101-unclosed-tags.html
  172. 5  src/chameleon/tests/inputs/101.xml
  173. 5  src/chameleon/tests/inputs/102-unquoted-attributes.html
  174. 5  src/chameleon/tests/inputs/102.xml
  175. 8  src/chameleon/tests/inputs/103-simple-attribute.html
  176. 4  src/chameleon/tests/inputs/103.xml
  177. 5  src/chameleon/tests/inputs/104.xml
  178. 5  src/chameleon/tests/inputs/105.xml
  179. 5  src/chameleon/tests/inputs/106.xml
  180. 5  src/chameleon/tests/inputs/107.xml
  181. 7  src/chameleon/tests/inputs/108.xml
  182. 5  src/chameleon/tests/inputs/109.xml
  183. 6  src/chameleon/tests/inputs/110.xml
  184. 5  src/chameleon/tests/inputs/111.xml
  185. 5  src/chameleon/tests/inputs/112.xml
  186. 5  src/chameleon/tests/inputs/113.xml
  187. 5  src/chameleon/tests/inputs/114.xml
  188. 6  src/chameleon/tests/inputs/115.xml
  189. 5  src/chameleon/tests/inputs/116.xml
  190. 5  src/chameleon/tests/inputs/117.xml
  191. 5  src/chameleon/tests/inputs/118.xml
  192. 4  src/chameleon/tests/inputs/119.xml
  193. 7  src/chameleon/tests/outputs/001.html
  194. 10  src/chameleon/tests/outputs/001.pt
  195. 13  src/chameleon/tests/outputs/002.pt
  196. 16  src/chameleon/tests/outputs/003.pt
  197. 11  src/chameleon/tests/outputs/004.pt
  198. 10  src/chameleon/tests/outputs/005.pt
  199. 6  src/chameleon/tests/outputs/006.pt
  200. 6  src/chameleon/tests/outputs/007.pt
  201. 8  src/chameleon/tests/outputs/008.pt
  202. 5  src/chameleon/tests/outputs/009.pt
  203. 7  src/chameleon/tests/outputs/010.pt
  204. 9  src/chameleon/tests/outputs/011-en.pt
  205. 9  src/chameleon/tests/outputs/011.pt
  206. 9  src/chameleon/tests/outputs/012-en.pt
  207. 9  src/chameleon/tests/outputs/012.pt
  208. 22  src/chameleon/tests/outputs/013.pt
  209. 12  src/chameleon/tests/outputs/014.pt
  210. 5  src/chameleon/tests/outputs/015-en.pt
  211. 5  src/chameleon/tests/outputs/015.pt
  212. 9  src/chameleon/tests/outputs/016-en.pt
  213. 9  src/chameleon/tests/outputs/016.pt
  214. 12  src/chameleon/tests/outputs/017.pt
  215. 4  src/chameleon/tests/outputs/018-en.pt
  216. 4  src/chameleon/tests/outputs/018.pt
  217. 13  src/chameleon/tests/outputs/019.pt
  218. 3  src/chameleon/tests/outputs/020.pt
  219. 12  src/chameleon/tests/outputs/021-en.pt
  220. 12  src/chameleon/tests/outputs/021.pt
  221. 13  src/chameleon/tests/outputs/022.pt
  222. 6  src/chameleon/tests/outputs/023.pt
  223. 14  src/chameleon/tests/outputs/024.pt
  224. 23  src/chameleon/tests/outputs/025.pt
  225. 18  src/chameleon/tests/outputs/026.pt
  226. 8  src/chameleon/tests/outputs/027.pt
  227. 7  src/chameleon/tests/outputs/028.pt
  228. 4  src/chameleon/tests/outputs/029.pt
  229. 10  src/chameleon/tests/outputs/030.pt
  230. 9  src/chameleon/tests/outputs/031.pt
  231. 15  src/chameleon/tests/outputs/032.pt
  232. 15  src/chameleon/tests/outputs/033.pt
  233. 15  src/chameleon/tests/outputs/034.pt
  234. 17  src/chameleon/tests/outputs/035.pt
  235. 15  src/chameleon/tests/outputs/036.pt
  236. 15  src/chameleon/tests/outputs/037.pt
  237. 6  src/chameleon/tests/outputs/038.pt
  238. 0  src/chameleon/tests/outputs/039.pt
  239. 17  src/chameleon/tests/outputs/040.pt
  240. 7  src/chameleon/tests/outputs/041.pt
  241. 15  src/chameleon/tests/outputs/042.pt
  242. 11  src/chameleon/tests/outputs/043.pt
  243. 5  src/chameleon/tests/outputs/044.pt
  244. 5  src/chameleon/tests/outputs/101.html
  245. 5  src/chameleon/tests/outputs/102.html
  246. 8  src/chameleon/tests/outputs/103.html
  247. 23  src/chameleon/tests/test_doctests.py
  248. 86  src/chameleon/tests/test_parser.py
  249. 163  src/chameleon/tests/test_sniffing.py
  250. 270  src/chameleon/tests/test_templates.py
  251. 36  src/chameleon/tests/test_tokenizer.py
  252. 135  src/chameleon/tokenize.py
  253. 151  src/chameleon/utils.py
  254. 1  src/chameleon/zpt/__init__.py
  255. 534  src/chameleon/zpt/program.py
  256. 132  src/chameleon/zpt/template.py
6  CHANGES.rst
Source Rendered
... ...
@@ -0,0 +1,6 @@
  1
+Changes
  2
+=======
  3
+
  4
+In next release...
  5
+
  6
+- Initial public release.
5  COPYRIGHT.txt
... ...
@@ -0,0 +1,5 @@
132  LICENSE.txt
... ...
@@ -0,0 +1,132 @@
  1
+The majority of the code in Chameleon is supplied under this license:
  2
+
  3
+  A copyright notice accompanies this license document that identifies
  4
+  the copyright holders.
  5
+
  6
+  Redistribution and use in source and binary forms, with or without
  7
+  modification, are permitted provided that the following conditions are
  8
+  met:
  9
+
  10
+  1.  Redistributions in source code must retain the accompanying
  11
+      copyright notice, this list of conditions, and the following
  12
+      disclaimer.
  13
+
  14
+  2.  Redistributions in binary form must reproduce the accompanying
  15
+      copyright notice, this list of conditions, and the following
  16
+      disclaimer in the documentation and/or other materials provided
  17
+      with the distribution.
  18
+
  19
+  3.  Names of the copyright holders must not be used to endorse or
  20
+      promote products derived from this software without prior
  21
+      written permission from the copyright holders.
  22
+
  23
+  4.  If any files are modified, you must cause the modified files to
  24
+      carry prominent notices stating that you changed the files and
  25
+      the date of any change.
  26
+
  27
+  Disclaimer
  28
+
  29
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
  30
+    ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  31
+    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  32
+    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  33
+    HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  34
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  35
+    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  36
+    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  37
+    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  38
+    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  39
+    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  40
+    SUCH DAMAGE.
  41
+
  42
+Portions of the code in Chameleon are supplied under the ZPL (headers
  43
+within individiual files indicate that these portions are licensed
  44
+under the ZPL):
  45
+
  46
+  Zope Public License (ZPL) Version 2.1
  47
+  -------------------------------------
  48
+
  49
+  A copyright notice accompanies this license document that
  50
+  identifies the copyright holders.
  51
+
  52
+  This license has been certified as open source. It has also
  53
+  been designated as GPL compatible by the Free Software
  54
+  Foundation (FSF).
  55
+
  56
+  Redistribution and use in source and binary forms, with or
  57
+  without modification, are permitted provided that the
  58
+  following conditions are met:
  59
+
  60
+  1. Redistributions in source code must retain the
  61
+     accompanying copyright notice, this list of conditions,
  62
+     and the following disclaimer.
  63
+
  64
+  2. Redistributions in binary form must reproduce the accompanying
  65
+     copyright notice, this list of conditions, and the
  66
+     following disclaimer in the documentation and/or other
  67
+     materials provided with the distribution.
  68
+
  69
+  3. Names of the copyright holders must not be used to
  70
+     endorse or promote products derived from this software
  71
+     without prior written permission from the copyright
  72
+     holders.
  73
+
  74
+  4. The right to distribute this software or to use it for
  75
+     any purpose does not give you the right to use
  76
+     Servicemarks (sm) or Trademarks (tm) of the copyright
  77
+     holders. Use of them is covered by separate agreement
  78
+     with the copyright holders.
  79
+
  80
+  5. If any files are modified, you must cause the modified
  81
+     files to carry prominent notices stating that you changed
  82
+     the files and the date of any change.
  83
+
  84
+  Disclaimer
  85
+
  86
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
  87
+    AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
  88
+    NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  89
+    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
  90
+    NO EVENT SHALL THE COPYRIGHT HOLDERS BE
  91
+    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  92
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  93
+    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  94
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  95
+    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  96
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
  97
+    OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  98
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  99
+    DAMAGE.
  100
+
  101
+Portions of the code in Chameleon are supplied under the BSD license
  102
+(headers within individiual files indicate that these portions are
  103
+licensed under this license):
  104
+
  105
+  All rights reserved.
  106
+
  107
+  Redistribution and use in source and binary forms, with or without
  108
+  modification, are permitted provided that the following conditions
  109
+  are met:
  110
+
  111
+   1. Redistributions of source code must retain the above copyright
  112
+      notice, this list of conditions and the following disclaimer.
  113
+   2. Redistributions in binary form must reproduce the above copyright
  114
+      notice, this list of conditions and the following disclaimer in
  115
+      the documentation and/or other materials provided with the
  116
+      distribution.
  117
+   3. The name of the author may not be used to endorse or promote
  118
+      products derived from this software without specific prior
  119
+      written permission.
  120
+
  121
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
  122
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  123
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  124
+  ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  125
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  126
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  127
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  128
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  129
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  130
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  131
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  132
+
72  README.rst
Source Rendered
... ...
@@ -0,0 +1,72 @@
  1
+Overview
  2
+========
  3
+
  4
+Chameleon is an HTML/XML [1]_ template language compiler.
  5
+
  6
+It includes a feature-complete engine for the Zope Page Templates
  7
+(ZPT) language.
  8
+
  9
+The software is released on PyPi. To download and install the latest
  10
+release::
  11
+
  12
+  $ easy-install -U Chameleon
  13
+
  14
+
  15
+What's New in 2.x
  16
+------------------
  17
+
  18
+The 2.x series is a complete rewrite of the library and supports both
  19
+Python 2.7+ and Python 3.1+ with a single source code.
  20
+
  21
+For most users it should be an easy upgrade, however note that at
  22
+present, there is no engine for the Genshi language.
  23
+
  24
+New parser
  25
+~~~~~~~~~~
  26
+
  27
+This series features a new parser, implemented in pure Python. It
  28
+parses both HTML and XML inputs (the previous parser relied on the
  29
+expat system library and was more strict about its input).
  30
+
  31
+Language support
  32
+~~~~~~~~~~~~~~~~
  33
+
  34
+The 2.x series supports a couple of new constructs:
  35
+
  36
+1) Support for the ``tal:on-error`` from the reference specification
  37
+has been added.
  38
+
  39
+2) Inspired by the Genshi language, a pair of new attributes has been
  40
+added: ``tal:switch`` and ``tal:case``, allowing flexible conditions.
  41
+
  42
+Expression engine
  43
+~~~~~~~~~~~~~~~~~
  44
+
  45
+The expression engine has been redesigned to make it easier to
  46
+understand and extend.
  47
+
  48
+The engine is built on the new ``ast`` module.
  49
+
  50
+Performance
  51
+~~~~~~~~~~~
  52
+
  53
+The compiler output has been optimized for complex templates. For most
  54
+applications, the engine should perform similarly to the 1.x
  55
+series.
  56
+
  57
+Very simple templates with tight loops (such as that of the "big
  58
+table" benchmark) will see a decrease in performance. The compiler is
  59
+optimized for dynamic variable scope and this lowers performance for
  60
+templates that require only a static scope.
  61
+
  62
+
  63
+License and Copyright
  64
+---------------------
  65
+
  66
+This software is made available as-is under a BSD-like license
  67
+[2]_ (see included copyright notice).
  68
+
  69
+.. [1] There is currently no support for unstructured documents.
  70
+
  71
+.. [2] Licensed under the `Repoze <http://repoze.org/license.html>`_
  72
+       license.
485  distribute_setup.py
... ...
@@ -0,0 +1,485 @@
  1
+#!python
  2
+"""Bootstrap distribute installation
  3
+
  4
+If you want to use setuptools in your package's setup.py, just include this
  5
+file in the same directory with it, and add this to the top of your setup.py::
  6
+
  7
+    from distribute_setup import use_setuptools
  8
+    use_setuptools()
  9
+
  10
+If you want to require a specific version of setuptools, set a download
  11
+mirror, or use an alternate download directory, you can do so by supplying
  12
+the appropriate options to ``use_setuptools()``.
  13
+
  14
+This file can also be run as a script to install or upgrade setuptools.
  15
+"""
  16
+import os
  17
+import sys
  18
+import time
  19
+import fnmatch
  20
+import tempfile
  21
+import tarfile
  22
+from distutils import log
  23
+
  24
+try:
  25
+    from site import USER_SITE
  26
+except ImportError:
  27
+    USER_SITE = None
  28
+
  29
+try:
  30
+    import subprocess
  31
+
  32
+    def _python_cmd(*args):
  33
+        args = (sys.executable,) + args
  34
+        return subprocess.call(args) == 0
  35
+
  36
+except ImportError:
  37
+    # will be used for python 2.3
  38
+    def _python_cmd(*args):
  39
+        args = (sys.executable,) + args
  40
+        # quoting arguments if windows
  41
+        if sys.platform == 'win32':
  42
+            def quote(arg):
  43
+                if ' ' in arg:
  44
+                    return '"%s"' % arg
  45
+                return arg
  46
+            args = [quote(arg) for arg in args]
  47
+        return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
  48
+
  49
+DEFAULT_VERSION = "0.6.14"
  50
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
  51
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
  52
+
  53
+SETUPTOOLS_PKG_INFO = """\
  54
+Metadata-Version: 1.0
  55
+Name: setuptools
  56
+Version: %s
  57
+Summary: xxxx
  58
+Home-page: xxx
  59
+Author: xxx
  60
+Author-email: xxx
  61
+License: xxx
  62
+Description: xxx
  63
+""" % SETUPTOOLS_FAKED_VERSION
  64
+
  65
+
  66
+def _install(tarball):
  67
+    # extracting the tarball
  68
+    tmpdir = tempfile.mkdtemp()
  69
+    log.warn('Extracting in %s', tmpdir)
  70
+    old_wd = os.getcwd()
  71
+    try:
  72
+        os.chdir(tmpdir)
  73
+        tar = tarfile.open(tarball)
  74
+        _extractall(tar)
  75
+        tar.close()
  76
+
  77
+        # going in the directory
  78
+        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
  79
+        os.chdir(subdir)
  80
+        log.warn('Now working in %s', subdir)
  81
+
  82
+        # installing
  83
+        log.warn('Installing Distribute')
  84
+        if not _python_cmd('setup.py', 'install'):
  85
+            log.warn('Something went wrong during the installation.')
  86
+            log.warn('See the error message above.')
  87
+    finally:
  88
+        os.chdir(old_wd)
  89
+
  90
+
  91
+def _build_egg(egg, tarball, to_dir):
  92
+    # extracting the tarball
  93
+    tmpdir = tempfile.mkdtemp()
  94
+    log.warn('Extracting in %s', tmpdir)
  95
+    old_wd = os.getcwd()
  96
+    try:
  97
+        os.chdir(tmpdir)
  98
+        tar = tarfile.open(tarball)
  99
+        _extractall(tar)
  100
+        tar.close()
  101
+
  102
+        # going in the directory
  103
+        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
  104
+        os.chdir(subdir)
  105
+        log.warn('Now working in %s', subdir)
  106
+
  107
+        # building an egg
  108
+        log.warn('Building a Distribute egg in %s', to_dir)
  109
+        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
  110
+
  111
+    finally:
  112
+        os.chdir(old_wd)
  113
+    # returning the result
  114
+    log.warn(egg)
  115
+    if not os.path.exists(egg):
  116
+        raise IOError('Could not build the egg.')
  117
+
  118
+
  119
+def _do_download(version, download_base, to_dir, download_delay):
  120
+    egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
  121
+                       % (version, sys.version_info[0], sys.version_info[1]))
  122
+    if not os.path.exists(egg):
  123
+        tarball = download_setuptools(version, download_base,
  124
+                                      to_dir, download_delay)
  125
+        _build_egg(egg, tarball, to_dir)
  126
+    sys.path.insert(0, egg)
  127
+    import setuptools
  128
+    setuptools.bootstrap_install_from = egg
  129
+
  130
+
  131
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
  132
+                   to_dir=os.curdir, download_delay=15, no_fake=True):
  133
+    # making sure we use the absolute path
  134
+    to_dir = os.path.abspath(to_dir)
  135
+    was_imported = 'pkg_resources' in sys.modules or \
  136
+        'setuptools' in sys.modules
  137
+    try:
  138
+        try:
  139
+            import pkg_resources
  140
+            if not hasattr(pkg_resources, '_distribute'):
  141
+                if not no_fake:
  142
+                    _fake_setuptools()
  143
+                raise ImportError
  144
+        except ImportError:
  145
+            return _do_download(version, download_base, to_dir, download_delay)
  146
+        try:
  147
+            pkg_resources.require("distribute>="+version)
  148
+            return
  149
+        except pkg_resources.VersionConflict:
  150
+            e = sys.exc_info()[1]
  151
+            if was_imported:
  152
+                sys.stderr.write(
  153
+                "The required version of distribute (>=%s) is not available,\n"
  154
+                "and can't be installed while this script is running. Please\n"
  155
+                "install a more recent version first, using\n"
  156
+                "'easy_install -U distribute'."
  157
+                "\n\n(Currently using %r)\n" % (version, e.args[0]))
  158
+                sys.exit(2)
  159
+            else:
  160
+                del pkg_resources, sys.modules['pkg_resources']    # reload ok
  161
+                return _do_download(version, download_base, to_dir,
  162
+                                    download_delay)
  163
+        except pkg_resources.DistributionNotFound:
  164
+            return _do_download(version, download_base, to_dir,
  165
+                                download_delay)
  166
+    finally:
  167
+        if not no_fake:
  168
+            _create_fake_setuptools_pkg_info(to_dir)
  169
+
  170
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
  171
+                        to_dir=os.curdir, delay=15):
  172
+    """Download distribute from a specified location and return its filename
  173
+
  174
+    `version` should be a valid distribute version number that is available
  175
+    as an egg for download under the `download_base` URL (which should end
  176
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
  177
+    `delay` is the number of seconds to pause before an actual download
  178
+    attempt.
  179
+    """
  180
+    # making sure we use the absolute path
  181
+    to_dir = os.path.abspath(to_dir)
  182
+    try:
  183
+        from urllib.request import urlopen
  184
+    except ImportError:
  185
+        from urllib2 import urlopen
  186
+    tgz_name = "distribute-%s.tar.gz" % version
  187
+    url = download_base + tgz_name
  188
+    saveto = os.path.join(to_dir, tgz_name)
  189
+    src = dst = None
  190
+    if not os.path.exists(saveto):  # Avoid repeated downloads
  191
+        try:
  192
+            log.warn("Downloading %s", url)
  193
+            src = urlopen(url)
  194
+            # Read/write all in one block, so we don't create a corrupt file
  195
+            # if the download is interrupted.
  196
+            data = src.read()
  197
+            dst = open(saveto, "wb")
  198
+            dst.write(data)
  199
+        finally:
  200
+            if src:
  201
+                src.close()
  202
+            if dst:
  203
+                dst.close()
  204
+    return os.path.realpath(saveto)
  205
+
  206
+def _no_sandbox(function):
  207
+    def __no_sandbox(*args, **kw):
  208
+        try:
  209
+            from setuptools.sandbox import DirectorySandbox
  210
+            if not hasattr(DirectorySandbox, '_old'):
  211
+                def violation(*args):
  212
+                    pass
  213
+                DirectorySandbox._old = DirectorySandbox._violation
  214
+                DirectorySandbox._violation = violation
  215
+                patched = True
  216
+            else:
  217
+                patched = False
  218
+        except ImportError:
  219
+            patched = False
  220
+
  221
+        try:
  222
+            return function(*args, **kw)
  223
+        finally:
  224
+            if patched:
  225
+                DirectorySandbox._violation = DirectorySandbox._old
  226
+                del DirectorySandbox._old
  227
+
  228
+    return __no_sandbox
  229
+
  230
+def _patch_file(path, content):
  231
+    """Will backup the file then patch it"""
  232
+    existing_content = open(path).read()
  233
+    if existing_content == content:
  234
+        # already patched
  235
+        log.warn('Already patched.')
  236
+        return False
  237
+    log.warn('Patching...')
  238
+    _rename_path(path)
  239
+    f = open(path, 'w')
  240
+    try:
  241
+        f.write(content)
  242
+    finally:
  243
+        f.close()
  244
+    return True
  245
+
  246
+_patch_file = _no_sandbox(_patch_file)
  247
+
  248
+def _same_content(path, content):
  249
+    return open(path).read() == content
  250
+
  251
+def _rename_path(path):
  252
+    new_name = path + '.OLD.%s' % time.time()
  253
+    log.warn('Renaming %s into %s', path, new_name)
  254
+    os.rename(path, new_name)
  255
+    return new_name
  256
+
  257
+def _remove_flat_installation(placeholder):
  258
+    if not os.path.isdir(placeholder):
  259
+        log.warn('Unkown installation at %s', placeholder)
  260
+        return False
  261
+    found = False
  262
+    for file in os.listdir(placeholder):
  263
+        if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
  264
+            found = True
  265
+            break
  266
+    if not found:
  267
+        log.warn('Could not locate setuptools*.egg-info')
  268
+        return
  269
+
  270
+    log.warn('Removing elements out of the way...')
  271
+    pkg_info = os.path.join(placeholder, file)
  272
+    if os.path.isdir(pkg_info):
  273
+        patched = _patch_egg_dir(pkg_info)
  274
+    else:
  275
+        patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
  276
+
  277
+    if not patched:
  278
+        log.warn('%s already patched.', pkg_info)
  279
+        return False
  280
+    # now let's move the files out of the way
  281
+    for element in ('setuptools', 'pkg_resources.py', 'site.py'):
  282
+        element = os.path.join(placeholder, element)
  283
+        if os.path.exists(element):
  284
+            _rename_path(element)
  285
+        else:
  286
+            log.warn('Could not find the %s element of the '
  287
+                     'Setuptools distribution', element)
  288
+    return True
  289
+
  290
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
  291
+
  292
+def _after_install(dist):
  293
+    log.warn('After install bootstrap.')
  294
+    placeholder = dist.get_command_obj('install').install_purelib
  295
+    _create_fake_setuptools_pkg_info(placeholder)
  296
+
  297
+def _create_fake_setuptools_pkg_info(placeholder):
  298
+    if not placeholder or not os.path.exists(placeholder):
  299
+        log.warn('Could not find the install location')
  300
+        return
  301
+    pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
  302
+    setuptools_file = 'setuptools-%s-py%s.egg-info' % \
  303
+            (SETUPTOOLS_FAKED_VERSION, pyver)
  304
+    pkg_info = os.path.join(placeholder, setuptools_file)
  305
+    if os.path.exists(pkg_info):
  306
+        log.warn('%s already exists', pkg_info)
  307
+        return
  308
+
  309
+    log.warn('Creating %s', pkg_info)
  310
+    f = open(pkg_info, 'w')
  311
+    try:
  312
+        f.write(SETUPTOOLS_PKG_INFO)
  313
+    finally:
  314
+        f.close()
  315
+
  316
+    pth_file = os.path.join(placeholder, 'setuptools.pth')
  317
+    log.warn('Creating %s', pth_file)
  318
+    f = open(pth_file, 'w')
  319
+    try:
  320
+        f.write(os.path.join(os.curdir, setuptools_file))
  321
+    finally:
  322
+        f.close()
  323
+
  324
+_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
  325
+
  326
+def _patch_egg_dir(path):
  327
+    # let's check if it's already patched
  328
+    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
  329
+    if os.path.exists(pkg_info):
  330
+        if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
  331
+            log.warn('%s already patched.', pkg_info)
  332
+            return False
  333
+    _rename_path(path)
  334
+    os.mkdir(path)
  335
+    os.mkdir(os.path.join(path, 'EGG-INFO'))
  336
+    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
  337
+    f = open(pkg_info, 'w')
  338
+    try:
  339
+        f.write(SETUPTOOLS_PKG_INFO)
  340
+    finally:
  341
+        f.close()
  342
+    return True
  343
+
  344
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
  345
+
  346
+def _before_install():
  347
+    log.warn('Before install bootstrap.')
  348
+    _fake_setuptools()
  349
+
  350
+
  351
+def _under_prefix(location):
  352
+    if 'install' not in sys.argv:
  353
+        return True
  354
+    args = sys.argv[sys.argv.index('install')+1:]
  355
+    for index, arg in enumerate(args):
  356
+        for option in ('--root', '--prefix'):
  357
+            if arg.startswith('%s=' % option):
  358
+                top_dir = arg.split('root=')[-1]
  359
+                return location.startswith(top_dir)
  360
+            elif arg == option:
  361
+                if len(args) > index:
  362
+                    top_dir = args[index+1]
  363
+                    return location.startswith(top_dir)
  364
+        if arg == '--user' and USER_SITE is not None:
  365
+            return location.startswith(USER_SITE)
  366
+    return True
  367
+
  368
+
  369
+def _fake_setuptools():
  370
+    log.warn('Scanning installed packages')
  371
+    try:
  372
+        import pkg_resources
  373
+    except ImportError:
  374
+        # we're cool
  375
+        log.warn('Setuptools or Distribute does not seem to be installed.')
  376
+        return
  377
+    ws = pkg_resources.working_set
  378
+    try:
  379
+        setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
  380
+                                  replacement=False))
  381
+    except TypeError:
  382
+        # old distribute API
  383
+        setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
  384
+
  385
+    if setuptools_dist is None:
  386
+        log.warn('No setuptools distribution found')
  387
+        return
  388
+    # detecting if it was already faked
  389
+    setuptools_location = setuptools_dist.location
  390
+    log.warn('Setuptools installation detected at %s', setuptools_location)
  391
+
  392
+    # if --root or --preix was provided, and if
  393
+    # setuptools is not located in them, we don't patch it
  394
+    if not _under_prefix(setuptools_location):
  395
+        log.warn('Not patching, --root or --prefix is installing Distribute'
  396
+                 ' in another location')
  397
+        return
  398
+
  399
+    # let's see if its an egg
  400
+    if not setuptools_location.endswith('.egg'):
  401
+        log.warn('Non-egg installation')
  402
+        res = _remove_flat_installation(setuptools_location)
  403
+        if not res:
  404
+            return
  405
+    else:
  406
+        log.warn('Egg installation')
  407
+        pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
  408
+        if (os.path.exists(pkg_info) and
  409
+            _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
  410
+            log.warn('Already patched.')
  411
+            return
  412
+        log.warn('Patching...')
  413
+        # let's create a fake egg replacing setuptools one
  414
+        res = _patch_egg_dir(setuptools_location)
  415
+        if not res:
  416
+            return
  417
+    log.warn('Patched done.')
  418
+    _relaunch()
  419
+
  420
+
  421
+def _relaunch():
  422
+    log.warn('Relaunching...')
  423
+    # we have to relaunch the process
  424
+    # pip marker to avoid a relaunch bug
  425
+    if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
  426
+        sys.argv[0] = 'setup.py'
  427
+    args = [sys.executable] + sys.argv
  428
+    sys.exit(subprocess.call(args))
  429
+
  430
+
  431
+def _extractall(self, path=".", members=None):
  432
+    """Extract all members from the archive to the current working
  433
+       directory and set owner, modification time and permissions on
  434
+       directories afterwards. `path' specifies a different directory
  435
+       to extract to. `members' is optional and must be a subset of the
  436
+       list returned by getmembers().
  437
+    """
  438
+    import copy
  439
+    import operator
  440
+    from tarfile import ExtractError
  441
+    directories = []
  442
+
  443
+    if members is None:
  444
+        members = self
  445
+
  446
+    for tarinfo in members:
  447
+        if tarinfo.isdir():
  448
+            # Extract directories with a safe mode.
  449
+            directories.append(tarinfo)
  450
+            tarinfo = copy.copy(tarinfo)
  451
+            tarinfo.mode = 448 # decimal for oct 0700
  452
+        self.extract(tarinfo, path)
  453
+
  454
+    # Reverse sort directories.
  455
+    if sys.version_info < (2, 4):
  456
+        def sorter(dir1, dir2):
  457
+            return cmp(dir1.name, dir2.name)
  458
+        directories.sort(sorter)
  459
+        directories.reverse()
  460
+    else:
  461
+        directories.sort(key=operator.attrgetter('name'), reverse=True)
  462
+
  463
+    # Set correct owner, mtime and filemode on directories.
  464
+    for tarinfo in directories:
  465
+        dirpath = os.path.join(path, tarinfo.name)
  466
+        try:
  467
+            self.chown(tarinfo, dirpath)
  468
+            self.utime(tarinfo, dirpath)
  469
+            self.chmod(tarinfo, dirpath)
  470
+        except ExtractError:
  471
+            e = sys.exc_info()[1]
  472
+            if self.errorlevel > 1:
  473
+                raise
  474
+            else:
  475
+                self._dbg(1, "tarfile: %s" % e)
  476
+
  477
+
  478
+def main(argv, version=DEFAULT_VERSION):
  479
+    """Install or upgrade setuptools and EasyInstall"""
  480
+    tarball = download_setuptools()
  481
+    _install(tarball)
  482
+
  483
+
  484
+if __name__ == '__main__':
  485
+    main(sys.argv[1:])
73  setup.py
... ...
@@ -0,0 +1,73 @@
  1
+__version__ = '2.0-dev'
  2
+
  3
+import os
  4
+import sys
  5
+
  6
+from distribute_setup import use_setuptools
  7
+use_setuptools()
  8
+
  9
+from setuptools import setup, find_packages
  10
+from setuptools.command.test import test
  11
+
  12
+here = os.path.abspath(os.path.dirname(__file__))
  13
+README = open(os.path.join(here, 'README.rst')).read()
  14
+CHANGES = open(os.path.join(here, 'CHANGES.rst')).read()
  15
+
  16
+install_requires = []
  17
+
  18
+version = sys.version_info[:3]
  19
+if version < (2, 7, 0):
  20
+    install_requires.append("ordereddict")
  21
+
  22
+class Benchmark(test):
  23
+    description = "Run benchmarks"
  24
+    user_options = []
  25
+    test_suite = None
  26
+
  27
+    def initialize_options(self):
  28
+        """init options"""
  29
+        pass
  30
+
  31
+    def finalize_options(self):
  32
+        """finalize options"""
  33
+
  34
+        self.distribution.tests_require = [
  35
+            'zope.pagetemplate',
  36
+            'zope.component',
  37
+            'zope.i18n',
  38
+            'zope.testing']
  39
+
  40
+    def run(self):
  41
+        test.run(self)
  42
+        self.with_project_on_sys_path(self.run_benchmark)
  43
+
  44
+    def run_benchmark(self):
  45
+        from chameleon import benchmark
  46
+        print("running benchmark...")
  47
+
  48
+        benchmark.start()
  49
+
  50
+setup(
  51
+    name="Chameleon",
  52
+    version=__version__,
  53
+    description="Fast XML template engine for Python.",
  54
+    long_description="\n\n".join((README, CHANGES)),
  55
+    classifiers=[
  56
+       "Development Status :: 3 - Alpha",
  57
+       "Intended Audience :: Developers",
  58
+       "Programming Language :: Python",
  59
+      ],
  60
+    author="Malthe Borch",
  61
+    author_email="mborch@gmail.com",
  62
+    license='BSD-like (http://repoze.org/license.html)',
  63
+    packages=find_packages('src'),
  64
+    package_dir = {'': 'src'},
  65
+    include_package_data=True,
  66
+    install_requires=install_requires,
  67
+    zip_safe=False,
  68
+    test_suite="chameleon.tests",
  69
+    cmdclass={
  70
+        'benchmark': Benchmark,
  71
+        }
  72
+    )
  73
+
1  src/chameleon/__init__.py
... ...
@@ -0,0 +1 @@
  1
+
927  src/chameleon/astutil.py
... ...
@@ -0,0 +1,927 @@
  1
+# -*- coding: utf-8 -*-
  2
+#
  3
+# Copyright (C) 2008-2009 Edgewall Software
  4
+# All rights reserved.
  5
+#
  6
+# This software is licensed as described in the file COPYING, which
  7
+# you should have received as part of this distribution. The terms
  8
+# are also available at http://genshi.edgewall.org/wiki/License.
  9
+#
  10
+# This software consists of voluntary contributions made by many
  11
+# individuals. For the exact contribution history, see the revision
  12
+# history and logs, available at http://genshi.edgewall.org/log/.
  13
+
  14
+"""Support classes for generating code from abstract syntax trees."""
  15
+
  16
+import ast
  17
+
  18
+from copy import copy as copy_node
  19
+
  20
+
  21
+__docformat__ = 'restructuredtext en'
  22
+
  23
+
  24
+def parse(source, mode='eval'):
  25
+    return compile(source, '', mode, ast.PyCF_ONLY_AST)
  26
+
  27
+
  28
+def load(name):
  29
+    return ast.Name(name, ast.Load())
  30
+
  31
+
  32
+def store(name):
  33
+    return ast.Name(name, ast.Store())
  34
+
  35
+
  36
+def param(name):
  37
+    return ast.Name(name, ast.Param())
  38
+
  39
+
  40
+def delete(name):
  41
+    return ast.Name(name, ast.Del())
  42
+
  43
+
  44
+def subscript(name, target, ctx):
  45
+    return ast.Subscript(
  46
+        target,
  47
+        ast.Index(ast.Str(name)),
  48
+        ctx,
  49
+        )
  50
+
  51
+
  52
+def walk_names(target, mode):
  53
+    for node in ast.walk(target):
  54
+        if isinstance(node, ast.Name) and \
  55
+               isinstance(node.ctx, mode):
  56
+            yield node.id
  57
+
  58
+
  59
+def copy(source, target):
  60
+    target.__class__ = source.__class__
  61
+    target.__dict__ = source.__dict__
  62
+
  63
+
  64
+def swap(root, replacement, name):
  65
+    for node in ast.walk(root):
  66
+        if (isinstance(node, ast.Name) and
  67
+            isinstance(node.ctx, ast.Load) and
  68
+            node.id == name):
  69
+            copy(replacement, node)
  70
+
  71
+
  72
+class Node(ast.AST):
  73
+    """AST baseclass that gives us a convenient initialization
  74
+    method. We explicitly declare and use the ``_fields`` attribute."""
  75
+
  76
+    _fields = ()
  77
+
  78
+    def __init__(self, *args, **kwargs):
  79
+        assert isinstance(self._fields, tuple)
  80
+        self.__dict__.update(kwargs)
  81
+        for name, value in zip(self._fields, args):
  82
+            setattr(self, name, value)
  83
+
  84
+    def __repr__(self):
  85
+        """Poor man's single-line pretty printer."""
  86
+
  87
+        name = type(self).__name__
  88
+        return '<%s%s at %x>' % (
  89
+            name,
  90
+            "".join(" %s=%r" % (name, getattr(self, name, "\"?\""))
  91
+                        for name in self._fields),
  92
+            id(self)
  93
+            )
  94
+
  95
+
  96
+class Builtin(Node, ast.Name):
  97
+    """Represents a Python builtin.
  98
+
  99
+    Used when a builtin is used internally by the compiler, to avoid
  100
+    clashing with a user assignment (e.g. ``help`` is a builtin, but
  101
+    also commonly assigned in templates).
  102
+    """
  103
+
  104
+    _fields = "id", "ctx"
  105
+
  106
+    ctx = ast.Load()
  107
+
  108
+
  109
+class Undefined(Node, ast.Expr):
  110
+    _fields = "id",
  111
+
  112
+
  113
+class Symbol(Node, ast.expr):
  114
+    """Represents an importable symbol."""
  115
+
  116
+    _fields = "value",
  117
+
  118
+
  119
+class Static(Node, ast.expr):
  120
+    """Represents a static value."""
  121
+
  122
+    _fields = "value", "name"
  123
+
  124
+    name = None
  125
+
  126
+
  127
+class Comment(Node, ast.stmt):
  128
+    _fields = "text", "space", "stmt"
  129
+
  130
+    stmt = None
  131
+    space = ""
  132
+
  133
+
  134
+class ASTCodeGenerator(object):
  135
+    """General purpose base class for AST transformations.
  136
+
  137
+    Every visitor method can be overridden to return an AST node that has been
  138
+    altered or replaced in some way.
  139
+    """
  140
+
  141
+    def __init__(self, tree):
  142
+        self.lines_info = []
  143
+        self.line_info = None
  144
+        self.lines = []
  145
+        self.line = None
  146
+        self.last = None
  147
+        self.indent = 0
  148
+        self.blame_stack = []
  149
+        self.visit(tree)
  150
+
  151
+        if self.line.strip():
  152
+            self._new_line()
  153
+
  154
+        self.line = None
  155
+        self.line_info = None
  156
+
  157
+        # strip trivial lines
  158
+        self.code = "\n".join(
  159
+            line.strip() and line or ""
  160
+            for line in self.lines
  161
+            )
  162
+
  163
+    def _change_indent(self, delta):
  164
+        self.indent += delta
  165
+
  166
+    def _new_line(self):
  167
+        if self.line is not None:
  168
+            self.lines.append(self.line)
  169
+            self.lines_info.append(self.line_info)
  170
+        self.line = ' ' * 4 * self.indent
  171
+        if len(self.blame_stack) == 0:
  172
+            self.line_info = []
  173
+            self.last = None
  174
+        else:
  175
+            self.line_info = [(0, self.blame_stack[-1],)]
  176
+            self.last = self.blame_stack[-1]
  177
+
  178
+    def _write(self, s):
  179
+        if len(s) == 0:
  180
+            return
  181
+        if len(self.blame_stack) == 0:
  182
+            if self.last is not None:
  183
+                self.last = None
  184
+                self.line_info.append((len(self.line), self.last))
  185
+        else:
  186
+            if self.last != self.blame_stack[-1]:
  187
+                self.last = self.blame_stack[-1]
  188
+                self.line_info.append((len(self.line), self.last))
  189
+        self.line += s
  190
+
  191
+    def visit(self, node):
  192
+        if node is None:
  193
+            return None
  194
+        if type(node) is tuple:
  195
+            return tuple([self.visit(n) for n in node])
  196
+        try:
  197
+            self.blame_stack.append((node.lineno, node.col_offset,))
  198
+            info = True
  199
+        except AttributeError:
  200
+            info = False
  201
+        visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
  202
+        if visitor is None:
  203
+            raise Exception('No handler for ``%s`` (%s).' % (
  204
+                node.__class__.__name__, repr(node)))
  205
+        ret = visitor(node)
  206
+        if info:
  207
+            self.blame_stack.pop()
  208
+        return ret
  209
+
  210
+    def visit_Module(self, node):
  211
+        for n in node.body:
  212
+            self.visit(n)
  213
+    visit_Interactive = visit_Module
  214
+    visit_Suite = visit_Module
  215
+
  216
+    def visit_Expression(self, node):
  217
+        return self.visit(node.body)
  218
+
  219
+    # arguments = (expr* args, identifier? vararg,
  220
+    #              identifier? kwarg, expr* defaults)
  221
+    def visit_arguments(self, node):
  222
+        first = True
  223
+        no_default_count = len(node.args) - len(node.defaults)
  224
+        for i, arg in enumerate(node.args):
  225
+            if not first:
  226
+                self._write(', ')
  227
+            else:
  228
+                first = False
  229
+            self.visit(arg)
  230
+            if i >= no_default_count:
  231
+                self._write('=')
  232
+                self.visit(node.defaults[i - no_default_count])
  233
+        if getattr(node, 'vararg', None):
  234
+            if not first:
  235
+                self._write(', ')
  236
+            else:
  237
+                first = False
  238
+            self._write('*' + node.vararg)
  239
+        if getattr(node, 'kwarg', None):
  240
+            if not first:
  241
+                self._write(', ')
  242
+            else:
  243
+                first = False
  244
+            self._write('**' + node.kwarg)
  245
+
  246
+    # FunctionDef(identifier name, arguments args,
  247
+    #                           stmt* body, expr* decorators)
  248
+    def visit_FunctionDef(self, node):
  249
+        self._new_line()
  250
+        for decorator in getattr(node, 'decorator_list', ()):
  251
+            self._new_line()
  252
+            self._write('@')
  253
+            self.visit(decorator)
  254
+        self._new_line()
  255
+        self._write('def ' + node.name + '(')
  256
+        self.visit(node.args)
  257
+        self._write('):')
  258
+        self._change_indent(1)
  259
+        for statement in node.body:
  260
+            self.visit(statement)
  261
+        self._change_indent(-1)
  262
+
  263
+    # ClassDef(identifier name, expr* bases, stmt* body)
  264
+    def visit_ClassDef(self, node):
  265
+        self._new_line()
  266
+        self._write('class ' + node.name)
  267
+        if node.bases:
  268
+            self._write('(')
  269
+            self.visit(node.bases[0])
  270
+            for base in node.bases[1:]:
  271
+                self._write(', ')
  272
+                self.visit(base)
  273
+            self._write(')')
  274
+        self._write(':')
  275
+        self._change_indent(1)
  276
+        for statement in node.body:
  277
+            self.visit(statement)
  278
+        self._change_indent(-1)
  279
+
  280
+    # Return(expr? value)
  281
+    def visit_Return(self, node):
  282
+        self._new_line()
  283
+        self._write('return')
  284
+        if getattr(node, 'value', None):
  285
+            self._write(' ')
  286
+            self.visit(node.value)
  287
+
  288
+    # Delete(expr* targets)
  289
+    def visit_Delete(self, node):
  290
+        self._new_line()