bee_python 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (503) hide show
  1. data/LICENSE +202 -0
  2. data/build/README +23 -0
  3. data/egg/egg/build.yml +70 -0
  4. data/egg/egg/ez_setup.py +280 -0
  5. data/egg/egg/script.py +12 -0
  6. data/egg/egg/setup.erb +11 -0
  7. data/egg/egg/suite.py +37 -0
  8. data/egg/egg/test.py +31 -0
  9. data/egg/egg.yml +69 -0
  10. data/egg/http/build.erb +53 -0
  11. data/egg/http/server.py +34 -0
  12. data/egg/http.yml +50 -0
  13. data/egg/mysql/mysql.py +43 -0
  14. data/egg/mysql.yml +44 -0
  15. data/egg/project/build.erb +77 -0
  16. data/egg/project/script.py +42 -0
  17. data/egg/project/test.py +31 -0
  18. data/egg/project.yml +59 -0
  19. data/egg/script/build.erb +35 -0
  20. data/egg/script/script.py +42 -0
  21. data/egg/script.yml +50 -0
  22. data/egg/soap/build.erb +52 -0
  23. data/egg/soap/client.py +18 -0
  24. data/egg/soap/server.py +30 -0
  25. data/egg/soap.yml +58 -0
  26. data/egg/source/source.py +42 -0
  27. data/egg/source.yml +47 -0
  28. data/egg/test/test.py +28 -0
  29. data/egg/test.yml +44 -0
  30. data/egg/xmlrpc/build.erb +52 -0
  31. data/egg/xmlrpc/client.py +22 -0
  32. data/egg/xmlrpc/server.py +24 -0
  33. data/egg/xmlrpc.yml +51 -0
  34. data/lib/bee_task_python.rb +390 -0
  35. data/test/build.yml +16 -0
  36. data/test/tc_bee_task_python.rb +27 -0
  37. data/test/test_build.rb +26 -0
  38. data/test/test_build_listener.rb +62 -0
  39. data/test/ts_bee_python.rb +26 -0
  40. data/tools/common/__init__.py +5 -0
  41. data/tools/common/common/__init__.py +140 -0
  42. data/tools/common/common/__pkginfo__.py +43 -0
  43. data/tools/common/common/adbh.py +35 -0
  44. data/tools/common/common/cache.py +114 -0
  45. data/tools/common/common/changelog.py +234 -0
  46. data/tools/common/common/clcommands.py +181 -0
  47. data/tools/common/common/cli.py +212 -0
  48. data/tools/common/common/compat.py +328 -0
  49. data/tools/common/common/configuration.py +1087 -0
  50. data/tools/common/common/contexts.py +58 -0
  51. data/tools/common/common/corbautils.py +117 -0
  52. data/tools/common/common/daemon.py +171 -0
  53. data/tools/common/common/date.py +279 -0
  54. data/tools/common/common/db.py +49 -0
  55. data/tools/common/common/dbf.py +229 -0
  56. data/tools/common/common/debugger.py +208 -0
  57. data/tools/common/common/decorators.py +190 -0
  58. data/tools/common/common/deprecation.py +118 -0
  59. data/tools/common/common/fileutils.py +409 -0
  60. data/tools/common/common/graph.py +259 -0
  61. data/tools/common/common/html.py +142 -0
  62. data/tools/common/common/interface.py +76 -0
  63. data/tools/common/common/logging_ext.py +166 -0
  64. data/tools/common/common/modutils.py +670 -0
  65. data/tools/common/common/optik_ext.py +383 -0
  66. data/tools/common/common/optparser.py +92 -0
  67. data/tools/common/common/pdf_ext.py +111 -0
  68. data/tools/common/common/proc.py +276 -0
  69. data/tools/common/common/pyro_ext.py +146 -0
  70. data/tools/common/common/pytest.py +754 -0
  71. data/tools/common/common/shellutils.py +383 -0
  72. data/tools/common/common/sphinx_ext.py +87 -0
  73. data/tools/common/common/sphinxutils.py +122 -0
  74. data/tools/common/common/sqlgen.py +31 -0
  75. data/tools/common/common/table.py +930 -0
  76. data/tools/common/common/tasksqueue.py +97 -0
  77. data/tools/common/common/test/__init__.py +1 -0
  78. data/tools/common/common/test/data/ChangeLog +184 -0
  79. data/tools/common/common/test/data/MyPyPa-0.1.0-py2.5.egg +0 -0
  80. data/tools/common/common/test/data/__init__.py +1 -0
  81. data/tools/common/common/test/data/content_differ_dir/NOTHING +0 -0
  82. data/tools/common/common/test/data/content_differ_dir/README +1 -0
  83. data/tools/common/common/test/data/content_differ_dir/subdir/coin +1 -0
  84. data/tools/common/common/test/data/content_differ_dir/subdir/toto.txt +53 -0
  85. data/tools/common/common/test/data/file_differ_dir/NOTHING +0 -0
  86. data/tools/common/common/test/data/file_differ_dir/README +1 -0
  87. data/tools/common/common/test/data/file_differ_dir/subdir/toto.txt +53 -0
  88. data/tools/common/common/test/data/file_differ_dir/subdirtwo/Hello +0 -0
  89. data/tools/common/common/test/data/find_test/__init__.py +0 -0
  90. data/tools/common/common/test/data/find_test/foo.txt +0 -0
  91. data/tools/common/common/test/data/find_test/module.py +0 -0
  92. data/tools/common/common/test/data/find_test/module2.py +0 -0
  93. data/tools/common/common/test/data/find_test/newlines.txt +0 -0
  94. data/tools/common/common/test/data/find_test/noendingnewline.py +0 -0
  95. data/tools/common/common/test/data/find_test/nonregr.py +0 -0
  96. data/tools/common/common/test/data/find_test/normal_file.txt +0 -0
  97. data/tools/common/common/test/data/find_test/spam.txt +0 -0
  98. data/tools/common/common/test/data/find_test/sub/doc.txt +0 -0
  99. data/tools/common/common/test/data/find_test/sub/momo.py +0 -0
  100. data/tools/common/common/test/data/find_test/test.ini +0 -0
  101. data/tools/common/common/test/data/find_test/test1.msg +0 -0
  102. data/tools/common/common/test/data/find_test/test2.msg +0 -0
  103. data/tools/common/common/test/data/find_test/write_protected_file.txt +0 -0
  104. data/tools/common/common/test/data/foo.txt +9 -0
  105. data/tools/common/common/test/data/module.py +88 -0
  106. data/tools/common/common/test/data/module2.py +77 -0
  107. data/tools/common/common/test/data/newlines.txt +3 -0
  108. data/tools/common/common/test/data/noendingnewline.py +36 -0
  109. data/tools/common/common/test/data/nonregr.py +14 -0
  110. data/tools/common/common/test/data/normal_file.txt +0 -0
  111. data/tools/common/common/test/data/reference_dir/NOTHING +0 -0
  112. data/tools/common/common/test/data/reference_dir/README +1 -0
  113. data/tools/common/common/test/data/reference_dir/subdir/coin +1 -0
  114. data/tools/common/common/test/data/reference_dir/subdir/toto.txt +53 -0
  115. data/tools/common/common/test/data/same_dir/NOTHING +0 -0
  116. data/tools/common/common/test/data/same_dir/README +1 -0
  117. data/tools/common/common/test/data/same_dir/subdir/coin +1 -0
  118. data/tools/common/common/test/data/same_dir/subdir/toto.txt +53 -0
  119. data/tools/common/common/test/data/spam.txt +9 -0
  120. data/tools/common/common/test/data/sub/doc.txt +1 -0
  121. data/tools/common/common/test/data/sub/momo.py +1 -0
  122. data/tools/common/common/test/data/subdir_differ_dir/NOTHING +0 -0
  123. data/tools/common/common/test/data/subdir_differ_dir/README +1 -0
  124. data/tools/common/common/test/data/subdir_differ_dir/subdir/coin +1 -0
  125. data/tools/common/common/test/data/subdir_differ_dir/subdir/toto.txt +53 -0
  126. data/tools/common/common/test/data/test.ini +20 -0
  127. data/tools/common/common/test/data/test1.msg +30 -0
  128. data/tools/common/common/test/data/test2.msg +42 -0
  129. data/tools/common/common/test/data/write_protected_file.txt +0 -0
  130. data/tools/common/common/test/foomod.py +17 -0
  131. data/tools/common/common/test/unittest_cache.py +129 -0
  132. data/tools/common/common/test/unittest_changelog.py +37 -0
  133. data/tools/common/common/test/unittest_compat.py +239 -0
  134. data/tools/common/common/test/unittest_configuration.py +348 -0
  135. data/tools/common/common/test/unittest_date.py +154 -0
  136. data/tools/common/common/test/unittest_decorators.py +62 -0
  137. data/tools/common/common/test/unittest_deprecation.py +76 -0
  138. data/tools/common/common/test/unittest_fileutils.py +133 -0
  139. data/tools/common/common/test/unittest_graph.py +50 -0
  140. data/tools/common/common/test/unittest_html.py +76 -0
  141. data/tools/common/common/test/unittest_interface.py +87 -0
  142. data/tools/common/common/test/unittest_modutils.py +244 -0
  143. data/tools/common/common/test/unittest_pytest.py +50 -0
  144. data/tools/common/common/test/unittest_shellutils.py +248 -0
  145. data/tools/common/common/test/unittest_table.py +448 -0
  146. data/tools/common/common/test/unittest_taskqueue.py +71 -0
  147. data/tools/common/common/test/unittest_testlib.py +956 -0
  148. data/tools/common/common/test/unittest_textutils.py +247 -0
  149. data/tools/common/common/test/unittest_tree.py +248 -0
  150. data/tools/common/common/test/unittest_umessage.py +55 -0
  151. data/tools/common/common/test/unittest_ureports_html.py +64 -0
  152. data/tools/common/common/test/unittest_ureports_text.py +105 -0
  153. data/tools/common/common/test/unittest_xmlutils.py +75 -0
  154. data/tools/common/common/test/utils.py +87 -0
  155. data/tools/common/common/testlib.py +1927 -0
  156. data/tools/common/common/textutils.py +476 -0
  157. data/tools/common/common/tree.py +372 -0
  158. data/tools/common/common/umessage.py +161 -0
  159. data/tools/common/common/ureports/__init__.py +174 -0
  160. data/tools/common/common/ureports/docbook_writer.py +139 -0
  161. data/tools/common/common/ureports/html_writer.py +131 -0
  162. data/tools/common/common/ureports/nodes.py +201 -0
  163. data/tools/common/common/ureports/text_writer.py +140 -0
  164. data/tools/common/common/vcgutils.py +216 -0
  165. data/tools/common/common/visitor.py +107 -0
  166. data/tools/common/common/xmlrpcutils.py +136 -0
  167. data/tools/common/common/xmlutils.py +61 -0
  168. data/tools/compile/compile.py +16 -0
  169. data/tools/coverage/coverage.py +602 -0
  170. data/tools/epydoc/__init__.py +227 -0
  171. data/tools/epydoc/__init__.pyc +0 -0
  172. data/tools/epydoc/apidoc.py +2203 -0
  173. data/tools/epydoc/apidoc.pyc +0 -0
  174. data/tools/epydoc/checker.py +349 -0
  175. data/tools/epydoc/checker.pyc +0 -0
  176. data/tools/epydoc/cli.py +1470 -0
  177. data/tools/epydoc/cli.pyc +0 -0
  178. data/tools/epydoc/compat.py +250 -0
  179. data/tools/epydoc/compat.pyc +0 -0
  180. data/tools/epydoc/docbuilder.py +1358 -0
  181. data/tools/epydoc/docbuilder.pyc +0 -0
  182. data/tools/epydoc/docintrospecter.py +1056 -0
  183. data/tools/epydoc/docintrospecter.pyc +0 -0
  184. data/tools/epydoc/docparser.py +2113 -0
  185. data/tools/epydoc/docparser.pyc +0 -0
  186. data/tools/epydoc/docstringparser.py +1111 -0
  187. data/tools/epydoc/docstringparser.pyc +0 -0
  188. data/tools/epydoc/docwriter/__init__.py +12 -0
  189. data/tools/epydoc/docwriter/__init__.pyc +0 -0
  190. data/tools/epydoc/docwriter/dotgraph.py +1351 -0
  191. data/tools/epydoc/docwriter/dotgraph.pyc +0 -0
  192. data/tools/epydoc/docwriter/html.py +3491 -0
  193. data/tools/epydoc/docwriter/html.pyc +0 -0
  194. data/tools/epydoc/docwriter/html_colorize.py +909 -0
  195. data/tools/epydoc/docwriter/html_colorize.pyc +0 -0
  196. data/tools/epydoc/docwriter/html_css.py +550 -0
  197. data/tools/epydoc/docwriter/html_css.pyc +0 -0
  198. data/tools/epydoc/docwriter/html_help.py +190 -0
  199. data/tools/epydoc/docwriter/html_help.pyc +0 -0
  200. data/tools/epydoc/docwriter/latex.py +1187 -0
  201. data/tools/epydoc/docwriter/latex.pyc +0 -0
  202. data/tools/epydoc/docwriter/plaintext.py +276 -0
  203. data/tools/epydoc/docwriter/plaintext.pyc +0 -0
  204. data/tools/epydoc/docwriter/xlink.py +505 -0
  205. data/tools/epydoc/docwriter/xlink.pyc +0 -0
  206. data/tools/epydoc/gui.py +1148 -0
  207. data/tools/epydoc/gui.pyc +0 -0
  208. data/tools/epydoc/log.py +204 -0
  209. data/tools/epydoc/log.pyc +0 -0
  210. data/tools/epydoc/markup/__init__.py +623 -0
  211. data/tools/epydoc/markup/__init__.pyc +0 -0
  212. data/tools/epydoc/markup/doctest.py +311 -0
  213. data/tools/epydoc/markup/doctest.pyc +0 -0
  214. data/tools/epydoc/markup/epytext.py +2116 -0
  215. data/tools/epydoc/markup/epytext.pyc +0 -0
  216. data/tools/epydoc/markup/javadoc.py +250 -0
  217. data/tools/epydoc/markup/javadoc.pyc +0 -0
  218. data/tools/epydoc/markup/plaintext.py +78 -0
  219. data/tools/epydoc/markup/plaintext.pyc +0 -0
  220. data/tools/epydoc/markup/pyval_repr.py +532 -0
  221. data/tools/epydoc/markup/pyval_repr.pyc +0 -0
  222. data/tools/epydoc/markup/restructuredtext.py +906 -0
  223. data/tools/epydoc/markup/restructuredtext.pyc +0 -0
  224. data/tools/epydoc/test/__init__.py +97 -0
  225. data/tools/epydoc/test/__init__.pyc +0 -0
  226. data/tools/epydoc/test/util.py +226 -0
  227. data/tools/epydoc/test/util.pyc +0 -0
  228. data/tools/epydoc/util.py +289 -0
  229. data/tools/epydoc/util.pyc +0 -0
  230. data/tools/logilab/logilab/__init__.py +5 -0
  231. data/tools/logilab/logilab/astng/__init__.py +82 -0
  232. data/tools/logilab/logilab/astng/__pkginfo__.py +76 -0
  233. data/tools/logilab/logilab/astng/_exceptions.py +64 -0
  234. data/tools/logilab/logilab/astng/_nodes_ast.py +667 -0
  235. data/tools/logilab/logilab/astng/_nodes_compiler.py +758 -0
  236. data/tools/logilab/logilab/astng/bases.py +608 -0
  237. data/tools/logilab/logilab/astng/builder.py +239 -0
  238. data/tools/logilab/logilab/astng/inference.py +426 -0
  239. data/tools/logilab/logilab/astng/inspector.py +289 -0
  240. data/tools/logilab/logilab/astng/manager.py +421 -0
  241. data/tools/logilab/logilab/astng/mixins.py +165 -0
  242. data/tools/logilab/logilab/astng/node_classes.py +848 -0
  243. data/tools/logilab/logilab/astng/nodes.py +85 -0
  244. data/tools/logilab/logilab/astng/nodes_as_string.py +389 -0
  245. data/tools/logilab/logilab/astng/patchcomptransformer.py +159 -0
  246. data/tools/logilab/logilab/astng/protocols.py +333 -0
  247. data/tools/logilab/logilab/astng/raw_building.py +212 -0
  248. data/tools/logilab/logilab/astng/rebuilder.py +307 -0
  249. data/tools/logilab/logilab/astng/scoped_nodes.py +951 -0
  250. data/tools/logilab/logilab/astng/test/__init__.py +19 -0
  251. data/tools/logilab/logilab/astng/test/data/MyPyPa-0.1.0-py2.5.egg +0 -0
  252. data/tools/logilab/logilab/astng/test/data/MyPyPa-0.1.0-py2.5.zip +0 -0
  253. data/tools/logilab/logilab/astng/test/data/SSL1/Connection1.py +33 -0
  254. data/tools/logilab/logilab/astng/test/data/SSL1/__init__.py +20 -0
  255. data/tools/logilab/logilab/astng/test/data/__init__.py +20 -0
  256. data/tools/logilab/logilab/astng/test/data/all.py +29 -0
  257. data/tools/logilab/logilab/astng/test/data/appl/__init__.py +23 -0
  258. data/tools/logilab/logilab/astng/test/data/appl/myConnection.py +30 -0
  259. data/tools/logilab/logilab/astng/test/data/format.py +34 -0
  260. data/tools/logilab/logilab/astng/test/data/module.py +90 -0
  261. data/tools/logilab/logilab/astng/test/data/module2.py +112 -0
  262. data/tools/logilab/logilab/astng/test/data/noendingnewline.py +57 -0
  263. data/tools/logilab/logilab/astng/test/data/nonregr.py +76 -0
  264. data/tools/logilab/logilab/astng/test/data/notall.py +28 -0
  265. data/tools/logilab/logilab/astng/test/data2/__init__.py +20 -0
  266. data/tools/logilab/logilab/astng/test/data2/clientmodule_test.py +51 -0
  267. data/tools/logilab/logilab/astng/test/data2/suppliermodule_test.py +32 -0
  268. data/tools/logilab/logilab/astng/test/regrtest.py +135 -0
  269. data/tools/logilab/logilab/astng/test/regrtest_data/absimport.py +22 -0
  270. data/tools/logilab/logilab/astng/test/regrtest_data/descriptor_crash.py +31 -0
  271. data/tools/logilab/logilab/astng/test/regrtest_data/import_package_subpackage_module.py +68 -0
  272. data/tools/logilab/logilab/astng/test/regrtest_data/package/__init__.py +24 -0
  273. data/tools/logilab/logilab/astng/test/regrtest_data/package/subpackage/__init__.py +20 -0
  274. data/tools/logilab/logilab/astng/test/regrtest_data/package/subpackage/module.py +20 -0
  275. data/tools/logilab/logilab/astng/test/unittest_builder.py +684 -0
  276. data/tools/logilab/logilab/astng/test/unittest_inference.py +1112 -0
  277. data/tools/logilab/logilab/astng/test/unittest_inspector.py +105 -0
  278. data/tools/logilab/logilab/astng/test/unittest_lookup.py +302 -0
  279. data/tools/logilab/logilab/astng/test/unittest_manager.py +98 -0
  280. data/tools/logilab/logilab/astng/test/unittest_nodes.py +302 -0
  281. data/tools/logilab/logilab/astng/test/unittest_scoped_nodes.py +501 -0
  282. data/tools/logilab/logilab/astng/test/unittest_utils.py +104 -0
  283. data/tools/logilab/logilab/astng/utils.py +342 -0
  284. data/tools/logilab/logilab/common/__init__.py +140 -0
  285. data/tools/logilab/logilab/common/__pkginfo__.py +43 -0
  286. data/tools/logilab/logilab/common/adbh.py +35 -0
  287. data/tools/logilab/logilab/common/cache.py +114 -0
  288. data/tools/logilab/logilab/common/changelog.py +234 -0
  289. data/tools/logilab/logilab/common/clcommands.py +181 -0
  290. data/tools/logilab/logilab/common/cli.py +212 -0
  291. data/tools/logilab/logilab/common/compat.py +328 -0
  292. data/tools/logilab/logilab/common/configuration.py +1087 -0
  293. data/tools/logilab/logilab/common/contexts.py +58 -0
  294. data/tools/logilab/logilab/common/corbautils.py +117 -0
  295. data/tools/logilab/logilab/common/daemon.py +171 -0
  296. data/tools/logilab/logilab/common/date.py +279 -0
  297. data/tools/logilab/logilab/common/db.py +49 -0
  298. data/tools/logilab/logilab/common/dbf.py +229 -0
  299. data/tools/logilab/logilab/common/debugger.py +208 -0
  300. data/tools/logilab/logilab/common/decorators.py +190 -0
  301. data/tools/logilab/logilab/common/deprecation.py +118 -0
  302. data/tools/logilab/logilab/common/fileutils.py +409 -0
  303. data/tools/logilab/logilab/common/graph.py +259 -0
  304. data/tools/logilab/logilab/common/html.py +142 -0
  305. data/tools/logilab/logilab/common/interface.py +76 -0
  306. data/tools/logilab/logilab/common/logging_ext.py +166 -0
  307. data/tools/logilab/logilab/common/modutils.py +670 -0
  308. data/tools/logilab/logilab/common/optik_ext.py +383 -0
  309. data/tools/logilab/logilab/common/optparser.py +92 -0
  310. data/tools/logilab/logilab/common/pdf_ext.py +111 -0
  311. data/tools/logilab/logilab/common/proc.py +276 -0
  312. data/tools/logilab/logilab/common/pyro_ext.py +146 -0
  313. data/tools/logilab/logilab/common/pytest.py +754 -0
  314. data/tools/logilab/logilab/common/shellutils.py +383 -0
  315. data/tools/logilab/logilab/common/sphinx_ext.py +87 -0
  316. data/tools/logilab/logilab/common/sphinxutils.py +122 -0
  317. data/tools/logilab/logilab/common/sqlgen.py +31 -0
  318. data/tools/logilab/logilab/common/table.py +930 -0
  319. data/tools/logilab/logilab/common/tasksqueue.py +97 -0
  320. data/tools/logilab/logilab/common/test/__init__.py +1 -0
  321. data/tools/logilab/logilab/common/test/data/ChangeLog +184 -0
  322. data/tools/logilab/logilab/common/test/data/MyPyPa-0.1.0-py2.5.egg +0 -0
  323. data/tools/logilab/logilab/common/test/data/__init__.py +1 -0
  324. data/tools/logilab/logilab/common/test/data/content_differ_dir/NOTHING +0 -0
  325. data/tools/logilab/logilab/common/test/data/content_differ_dir/README +1 -0
  326. data/tools/logilab/logilab/common/test/data/content_differ_dir/subdir/coin +1 -0
  327. data/tools/logilab/logilab/common/test/data/content_differ_dir/subdir/toto.txt +53 -0
  328. data/tools/logilab/logilab/common/test/data/file_differ_dir/NOTHING +0 -0
  329. data/tools/logilab/logilab/common/test/data/file_differ_dir/README +1 -0
  330. data/tools/logilab/logilab/common/test/data/file_differ_dir/subdir/toto.txt +53 -0
  331. data/tools/logilab/logilab/common/test/data/file_differ_dir/subdirtwo/Hello +0 -0
  332. data/tools/logilab/logilab/common/test/data/find_test/__init__.py +0 -0
  333. data/tools/logilab/logilab/common/test/data/find_test/foo.txt +0 -0
  334. data/tools/logilab/logilab/common/test/data/find_test/module.py +0 -0
  335. data/tools/logilab/logilab/common/test/data/find_test/module2.py +0 -0
  336. data/tools/logilab/logilab/common/test/data/find_test/newlines.txt +0 -0
  337. data/tools/logilab/logilab/common/test/data/find_test/noendingnewline.py +0 -0
  338. data/tools/logilab/logilab/common/test/data/find_test/nonregr.py +0 -0
  339. data/tools/logilab/logilab/common/test/data/find_test/normal_file.txt +0 -0
  340. data/tools/logilab/logilab/common/test/data/find_test/spam.txt +0 -0
  341. data/tools/logilab/logilab/common/test/data/find_test/sub/doc.txt +0 -0
  342. data/tools/logilab/logilab/common/test/data/find_test/sub/momo.py +0 -0
  343. data/tools/logilab/logilab/common/test/data/find_test/test.ini +0 -0
  344. data/tools/logilab/logilab/common/test/data/find_test/test1.msg +0 -0
  345. data/tools/logilab/logilab/common/test/data/find_test/test2.msg +0 -0
  346. data/tools/logilab/logilab/common/test/data/find_test/write_protected_file.txt +0 -0
  347. data/tools/logilab/logilab/common/test/data/foo.txt +9 -0
  348. data/tools/logilab/logilab/common/test/data/module.py +88 -0
  349. data/tools/logilab/logilab/common/test/data/module2.py +77 -0
  350. data/tools/logilab/logilab/common/test/data/newlines.txt +3 -0
  351. data/tools/logilab/logilab/common/test/data/noendingnewline.py +36 -0
  352. data/tools/logilab/logilab/common/test/data/nonregr.py +14 -0
  353. data/tools/logilab/logilab/common/test/data/normal_file.txt +0 -0
  354. data/tools/logilab/logilab/common/test/data/reference_dir/NOTHING +0 -0
  355. data/tools/logilab/logilab/common/test/data/reference_dir/README +1 -0
  356. data/tools/logilab/logilab/common/test/data/reference_dir/subdir/coin +1 -0
  357. data/tools/logilab/logilab/common/test/data/reference_dir/subdir/toto.txt +53 -0
  358. data/tools/logilab/logilab/common/test/data/same_dir/NOTHING +0 -0
  359. data/tools/logilab/logilab/common/test/data/same_dir/README +1 -0
  360. data/tools/logilab/logilab/common/test/data/same_dir/subdir/coin +1 -0
  361. data/tools/logilab/logilab/common/test/data/same_dir/subdir/toto.txt +53 -0
  362. data/tools/logilab/logilab/common/test/data/spam.txt +9 -0
  363. data/tools/logilab/logilab/common/test/data/sub/doc.txt +1 -0
  364. data/tools/logilab/logilab/common/test/data/sub/momo.py +1 -0
  365. data/tools/logilab/logilab/common/test/data/subdir_differ_dir/NOTHING +0 -0
  366. data/tools/logilab/logilab/common/test/data/subdir_differ_dir/README +1 -0
  367. data/tools/logilab/logilab/common/test/data/subdir_differ_dir/subdir/coin +1 -0
  368. data/tools/logilab/logilab/common/test/data/subdir_differ_dir/subdir/toto.txt +53 -0
  369. data/tools/logilab/logilab/common/test/data/test.ini +20 -0
  370. data/tools/logilab/logilab/common/test/data/test1.msg +30 -0
  371. data/tools/logilab/logilab/common/test/data/test2.msg +42 -0
  372. data/tools/logilab/logilab/common/test/data/write_protected_file.txt +0 -0
  373. data/tools/logilab/logilab/common/test/foomod.py +17 -0
  374. data/tools/logilab/logilab/common/test/unittest_cache.py +129 -0
  375. data/tools/logilab/logilab/common/test/unittest_changelog.py +37 -0
  376. data/tools/logilab/logilab/common/test/unittest_compat.py +239 -0
  377. data/tools/logilab/logilab/common/test/unittest_configuration.py +348 -0
  378. data/tools/logilab/logilab/common/test/unittest_date.py +154 -0
  379. data/tools/logilab/logilab/common/test/unittest_decorators.py +62 -0
  380. data/tools/logilab/logilab/common/test/unittest_deprecation.py +76 -0
  381. data/tools/logilab/logilab/common/test/unittest_fileutils.py +133 -0
  382. data/tools/logilab/logilab/common/test/unittest_graph.py +50 -0
  383. data/tools/logilab/logilab/common/test/unittest_html.py +76 -0
  384. data/tools/logilab/logilab/common/test/unittest_interface.py +87 -0
  385. data/tools/logilab/logilab/common/test/unittest_modutils.py +244 -0
  386. data/tools/logilab/logilab/common/test/unittest_pytest.py +50 -0
  387. data/tools/logilab/logilab/common/test/unittest_shellutils.py +248 -0
  388. data/tools/logilab/logilab/common/test/unittest_table.py +448 -0
  389. data/tools/logilab/logilab/common/test/unittest_taskqueue.py +71 -0
  390. data/tools/logilab/logilab/common/test/unittest_testlib.py +956 -0
  391. data/tools/logilab/logilab/common/test/unittest_textutils.py +247 -0
  392. data/tools/logilab/logilab/common/test/unittest_tree.py +248 -0
  393. data/tools/logilab/logilab/common/test/unittest_umessage.py +55 -0
  394. data/tools/logilab/logilab/common/test/unittest_ureports_html.py +64 -0
  395. data/tools/logilab/logilab/common/test/unittest_ureports_text.py +105 -0
  396. data/tools/logilab/logilab/common/test/unittest_xmlutils.py +75 -0
  397. data/tools/logilab/logilab/common/test/utils.py +87 -0
  398. data/tools/logilab/logilab/common/testlib.py +1927 -0
  399. data/tools/logilab/logilab/common/textutils.py +476 -0
  400. data/tools/logilab/logilab/common/tree.py +372 -0
  401. data/tools/logilab/logilab/common/umessage.py +161 -0
  402. data/tools/logilab/logilab/common/ureports/__init__.py +174 -0
  403. data/tools/logilab/logilab/common/ureports/docbook_writer.py +139 -0
  404. data/tools/logilab/logilab/common/ureports/html_writer.py +131 -0
  405. data/tools/logilab/logilab/common/ureports/nodes.py +201 -0
  406. data/tools/logilab/logilab/common/ureports/text_writer.py +140 -0
  407. data/tools/logilab/logilab/common/vcgutils.py +216 -0
  408. data/tools/logilab/logilab/common/visitor.py +107 -0
  409. data/tools/logilab/logilab/common/xmlrpcutils.py +136 -0
  410. data/tools/logilab/logilab/common/xmlutils.py +61 -0
  411. data/tools/pychecker/COPYRIGHT +31 -0
  412. data/tools/pychecker/ChangeLog +349 -0
  413. data/tools/pychecker/CodeChecks.py +1969 -0
  414. data/tools/pychecker/CodeChecks.pyc +0 -0
  415. data/tools/pychecker/CodeChecks.pyo +0 -0
  416. data/tools/pychecker/Config.py +475 -0
  417. data/tools/pychecker/Config.pyc +0 -0
  418. data/tools/pychecker/Config.pyo +0 -0
  419. data/tools/pychecker/KNOWN_BUGS +100 -0
  420. data/tools/pychecker/MAINTAINERS +81 -0
  421. data/tools/pychecker/NEWS +406 -0
  422. data/tools/pychecker/OP.py +131 -0
  423. data/tools/pychecker/OP.pyc +0 -0
  424. data/tools/pychecker/OP.pyo +0 -0
  425. data/tools/pychecker/OptionTypes.py +117 -0
  426. data/tools/pychecker/OptionTypes.pyc +0 -0
  427. data/tools/pychecker/OptionTypes.pyo +0 -0
  428. data/tools/pychecker/README +152 -0
  429. data/tools/pychecker/Stack.py +115 -0
  430. data/tools/pychecker/Stack.pyc +0 -0
  431. data/tools/pychecker/Stack.pyo +0 -0
  432. data/tools/pychecker/TODO +101 -0
  433. data/tools/pychecker/VERSION +1 -0
  434. data/tools/pychecker/Warning.py +50 -0
  435. data/tools/pychecker/Warning.pyc +0 -0
  436. data/tools/pychecker/Warning.pyo +0 -0
  437. data/tools/pychecker/__init__.py +17 -0
  438. data/tools/pychecker/__init__.pyc +0 -0
  439. data/tools/pychecker/__init__.pyo +0 -0
  440. data/tools/pychecker/checker.py +961 -0
  441. data/tools/pychecker/checker.pyc +0 -0
  442. data/tools/pychecker/checker.pyo +0 -0
  443. data/tools/pychecker/function.py +159 -0
  444. data/tools/pychecker/function.pyc +0 -0
  445. data/tools/pychecker/function.pyo +0 -0
  446. data/tools/pychecker/msgs.py +175 -0
  447. data/tools/pychecker/msgs.pyc +0 -0
  448. data/tools/pychecker/msgs.pyo +0 -0
  449. data/tools/pychecker/options.py +275 -0
  450. data/tools/pychecker/options.pyc +0 -0
  451. data/tools/pychecker/options.pyo +0 -0
  452. data/tools/pychecker/pcmodules.py +19 -0
  453. data/tools/pychecker/pcmodules.pyc +0 -0
  454. data/tools/pychecker/pcmodules.pyo +0 -0
  455. data/tools/pychecker/printer.py +47 -0
  456. data/tools/pychecker/printer.pyc +0 -0
  457. data/tools/pychecker/printer.pyo +0 -0
  458. data/tools/pychecker/python.py +427 -0
  459. data/tools/pychecker/python.pyc +0 -0
  460. data/tools/pychecker/python.pyo +0 -0
  461. data/tools/pychecker/utils.py +102 -0
  462. data/tools/pychecker/utils.pyc +0 -0
  463. data/tools/pychecker/utils.pyo +0 -0
  464. data/tools/pychecker/warn.py +778 -0
  465. data/tools/pychecker/warn.pyc +0 -0
  466. data/tools/pychecker/warn.pyo +0 -0
  467. data/tools/pylint2/pylint/__init__.py +16 -0
  468. data/tools/pylint2/pylint/__pkginfo__.py +67 -0
  469. data/tools/pylint2/pylint/checkers/__init__.py +155 -0
  470. data/tools/pylint2/pylint/checkers/base.py +749 -0
  471. data/tools/pylint2/pylint/checkers/classes.py +527 -0
  472. data/tools/pylint2/pylint/checkers/design_analysis.py +344 -0
  473. data/tools/pylint2/pylint/checkers/exceptions.py +183 -0
  474. data/tools/pylint2/pylint/checkers/format.py +367 -0
  475. data/tools/pylint2/pylint/checkers/imports.py +379 -0
  476. data/tools/pylint2/pylint/checkers/logging.py +98 -0
  477. data/tools/pylint2/pylint/checkers/misc.py +128 -0
  478. data/tools/pylint2/pylint/checkers/newstyle.py +107 -0
  479. data/tools/pylint2/pylint/checkers/raw_metrics.py +125 -0
  480. data/tools/pylint2/pylint/checkers/similar.py +333 -0
  481. data/tools/pylint2/pylint/checkers/string_format.py +239 -0
  482. data/tools/pylint2/pylint/checkers/typecheck.py +364 -0
  483. data/tools/pylint2/pylint/checkers/utils.py +208 -0
  484. data/tools/pylint2/pylint/checkers/variables.py +498 -0
  485. data/tools/pylint2/pylint/config.py +149 -0
  486. data/tools/pylint2/pylint/epylint.py +149 -0
  487. data/tools/pylint2/pylint/gui.py +433 -0
  488. data/tools/pylint2/pylint/interfaces.py +98 -0
  489. data/tools/pylint2/pylint/lint.py +914 -0
  490. data/tools/pylint2/pylint/pyreverse/__init__.py +5 -0
  491. data/tools/pylint2/pylint/pyreverse/diadefslib.py +229 -0
  492. data/tools/pylint2/pylint/pyreverse/diagrams.py +247 -0
  493. data/tools/pylint2/pylint/pyreverse/main.py +123 -0
  494. data/tools/pylint2/pylint/pyreverse/utils.py +131 -0
  495. data/tools/pylint2/pylint/pyreverse/writer.py +196 -0
  496. data/tools/pylint2/pylint/reporters/__init__.py +67 -0
  497. data/tools/pylint2/pylint/reporters/guireporter.py +36 -0
  498. data/tools/pylint2/pylint/reporters/html.py +69 -0
  499. data/tools/pylint2/pylint/reporters/text.py +156 -0
  500. data/tools/pylint2/pylint/utils.py +518 -0
  501. data/tools/pylint2/pylint.py +16 -0
  502. data/tools/test/suite.py +35 -0
  503. metadata +566 -0
@@ -0,0 +1,3491 @@
1
+ #
2
+ # epydoc -- HTML output generator
3
+ # Edward Loper
4
+ #
5
+ # Created [01/30/01 05:18 PM]
6
+ # $Id: html.py,v 1.1 2009/11/26 13:20:44 casa Exp $
7
+ #
8
+
9
+ """
10
+ The HTML output generator for epydoc. The main interface provided by
11
+ this module is the L{HTMLWriter} class.
12
+
13
+ @todo: Add a cache to L{HTMLWriter.url()}?
14
+ """
15
+ __docformat__ = 'epytext en'
16
+
17
+ import re, os, sys, codecs, sre_constants, pprint, base64
18
+ import urllib
19
+ import __builtin__
20
+ from epydoc.apidoc import *
21
+ import epydoc.docstringparser
22
+ import time, epydoc, epydoc.markup, epydoc.markup.epytext
23
+ from epydoc.docwriter.html_colorize import PythonSourceColorizer
24
+ from epydoc.docwriter import html_colorize
25
+ from epydoc.docwriter.html_css import STYLESHEETS
26
+ from epydoc.docwriter.html_help import HTML_HELP
27
+ from epydoc.docwriter.dotgraph import *
28
+ from epydoc import log
29
+ from epydoc.util import plaintext_to_html, is_src_filename
30
+ from epydoc.compat import * # Backwards compatibility
31
+
32
+ ######################################################################
33
+ ## Template Compiler
34
+ ######################################################################
35
+ # The compile_template() method defined in this section is used to
36
+ # define several of HTMLWriter's methods.
37
+
38
+ def compile_template(docstring, template_string,
39
+ output_function='out', debug=epydoc.DEBUG):
40
+ """
41
+ Given a template string containing inline python source code,
42
+ return a python function that will fill in the template, and
43
+ output the result. The signature for this function is taken from
44
+ the first line of C{docstring}. Output is generated by making
45
+ repeated calls to the output function with the given name (which
46
+ is typically one of the function's parameters).
47
+
48
+ The templating language used by this function passes through all
49
+ text as-is, with three exceptions:
50
+
51
+ - If every line in the template string is indented by at least
52
+ M{x} spaces, then the first M{x} spaces are stripped from each
53
+ line.
54
+
55
+ - Any line that begins with '>>>' (with no indentation)
56
+ should contain python code, and will be inserted as-is into
57
+ the template-filling function. If the line begins a control
58
+ block (such as 'if' or 'for'), then the control block will
59
+ be closed by the first '>>>'-marked line whose indentation is
60
+ less than or equal to the line's own indentation (including
61
+ lines that only contain comments.)
62
+
63
+ - In any other line, any expression between two '$' signs will
64
+ be evaluated and inserted into the line (using C{str()} to
65
+ convert the result to a string).
66
+
67
+ Here is a simple example:
68
+
69
+ >>> TEMPLATE = '''
70
+ ... <book>
71
+ ... <title>$book.title$</title>
72
+ ... <pages>$book.count_pages()$</pages>
73
+ ... >>> for chapter in book.chapters:
74
+ ... <chaptername>$chapter.name$</chaptername>
75
+ ... >>> #endfor
76
+ ... </book>
77
+ >>> write_book = compile_template('write_book(out, book)', TEMPLATE)
78
+
79
+ @newfield acknowledgements: Acknowledgements
80
+ @acknowledgements: The syntax used by C{compile_template} is
81
+ loosely based on Cheetah.
82
+ """
83
+ # Extract signature from the docstring:
84
+ signature = docstring.lstrip().split('\n',1)[0].strip()
85
+ func_name = signature.split('(',1)[0].strip()
86
+
87
+ # Regexp to search for inline substitutions:
88
+ INLINE = re.compile(r'\$([^\$]+)\$')
89
+ # Regexp to search for python statements in the template:
90
+ COMMAND = re.compile(r'(^>>>.*)\n?', re.MULTILINE)
91
+
92
+ # Strip indentation from the template.
93
+ template_string = strip_indent(template_string)
94
+
95
+ # If we're debugging, then we'll store the generated function,
96
+ # so we can print it along with any tracebacks that depend on it.
97
+ if debug:
98
+ signature = re.sub(r'\)\s*$', ', __debug=__debug)', signature)
99
+
100
+ # Funciton declaration line
101
+ pysrc_lines = ['def %s:' % signature]
102
+ indents = [-1]
103
+
104
+ if debug:
105
+ pysrc_lines.append(' try:')
106
+ indents.append(-1)
107
+
108
+ commands = COMMAND.split(template_string.strip()+'\n')
109
+ for i, command in enumerate(commands):
110
+ if command == '': continue
111
+
112
+ # String literal segment:
113
+ if i%2 == 0:
114
+ pieces = INLINE.split(command)
115
+ for j, piece in enumerate(pieces):
116
+ if j%2 == 0:
117
+ # String piece
118
+ pysrc_lines.append(' '*len(indents)+
119
+ '%s(%r)' % (output_function, piece))
120
+ else:
121
+ # Variable piece
122
+ pysrc_lines.append(' '*len(indents)+
123
+ '%s(unicode(%s))' % (output_function, piece))
124
+
125
+ # Python command:
126
+ else:
127
+ srcline = command[3:].lstrip()
128
+ # Update indentation
129
+ indent = len(command)-len(srcline)
130
+ while indent <= indents[-1]: indents.pop()
131
+ # Add on the line.
132
+ srcline = srcline.rstrip()
133
+ pysrc_lines.append(' '*len(indents)+srcline)
134
+ if srcline.endswith(':'):
135
+ indents.append(indent)
136
+
137
+ if debug:
138
+ pysrc_lines.append(' except Exception,e:')
139
+ pysrc_lines.append(' pysrc, func_name = __debug ')
140
+ pysrc_lines.append(' lineno = sys.exc_info()[2].tb_lineno')
141
+ pysrc_lines.append(' print ("Exception in template %s() on "')
142
+ pysrc_lines.append(' "line %d:" % (func_name, lineno))')
143
+ pysrc_lines.append(' print pysrc[lineno-1]')
144
+ pysrc_lines.append(' raise')
145
+
146
+ pysrc = '\n'.join(pysrc_lines)+'\n'
147
+ #log.debug(pysrc)
148
+ if debug: localdict = {'__debug': (pysrc_lines, func_name)}
149
+ else: localdict = {}
150
+ try: exec pysrc in globals(), localdict
151
+ except SyntaxError:
152
+ log.error('Error in script:\n' + pysrc + '\n')
153
+ raise
154
+ template_func = localdict[func_name]
155
+ template_func.__doc__ = docstring
156
+ return template_func
157
+
158
+ def strip_indent(s):
159
+ """
160
+ Given a multiline string C{s}, find the minimum indentation for
161
+ all non-blank lines, and return a new string formed by stripping
162
+ that amount of indentation from all lines in C{s}.
163
+ """
164
+ # Strip indentation from the template.
165
+ minindent = sys.maxint
166
+ lines = s.split('\n')
167
+ for line in lines:
168
+ stripline = line.lstrip()
169
+ if stripline:
170
+ minindent = min(minindent, len(line)-len(stripline))
171
+ return '\n'.join([l[minindent:] for l in lines])
172
+
173
+ ######################################################################
174
+ ## HTML Writer
175
+ ######################################################################
176
+
177
+ class HTMLWriter:
178
+ #////////////////////////////////////////////////////////////
179
+ # Table of Contents
180
+ #////////////////////////////////////////////////////////////
181
+ #
182
+ # 1. Interface Methods
183
+ #
184
+ # 2. Page Generation -- write complete web page files
185
+ # 2.1. Module Pages
186
+ # 2.2. Class Pages
187
+ # 2.3. Trees Page
188
+ # 2.4. Indices Page
189
+ # 2.5. Help Page
190
+ # 2.6. Frames-based table of contents pages
191
+ # 2.7. Homepage (index.html)
192
+ # 2.8. CSS Stylesheet
193
+ # 2.9. Javascript file
194
+ # 2.10. Graphs
195
+ # 2.11. Images
196
+ #
197
+ # 3. Page Element Generation -- write pieces of a web page file
198
+ # 3.1. Page Header
199
+ # 3.2. Page Footer
200
+ # 3.3. Navigation Bar
201
+ # 3.4. Breadcrumbs
202
+ # 3.5. Summary Tables
203
+ #
204
+ # 4. Helper functions
205
+
206
+ def __init__(self, docindex, **kwargs):
207
+ """
208
+ Construct a new HTML writer, using the given documentation
209
+ index.
210
+
211
+ @param docindex: The documentation index.
212
+
213
+ @type prj_name: C{string}
214
+ @keyword prj_name: The name of the project. Defaults to
215
+ none.
216
+ @type prj_url: C{string}
217
+ @keyword prj_url: The target for the project hopeage link on
218
+ the navigation bar. If C{prj_url} is not specified,
219
+ then no hyperlink is created.
220
+ @type prj_link: C{string}
221
+ @keyword prj_link: The label for the project link on the
222
+ navigation bar. This link can contain arbitrary HTML
223
+ code (e.g. images). By default, a label is constructed
224
+ from C{prj_name}.
225
+ @type top_page: C{string}
226
+ @keyword top_page: The top page for the documentation. This
227
+ is the default page shown main frame, when frames are
228
+ enabled. C{top} can be a URL, the name of a
229
+ module, the name of a class, or one of the special
230
+ strings C{"trees.html"}, C{"indices.html"}, or
231
+ C{"help.html"}. By default, the top-level package or
232
+ module is used, if there is one; otherwise, C{"trees"}
233
+ is used.
234
+ @type css: C{string}
235
+ @keyword css: The CSS stylesheet file. If C{css} is a file
236
+ name, then the specified file's conents will be used.
237
+ Otherwise, if C{css} is the name of a CSS stylesheet in
238
+ L{epydoc.docwriter.html_css}, then that stylesheet will
239
+ be used. Otherwise, an error is reported. If no stylesheet
240
+ is specified, then the default stylesheet is used.
241
+ @type help_file: C{string}
242
+ @keyword help_file: The name of the help file. If no help file is
243
+ specified, then the default help file will be used.
244
+ @type show_private: C{boolean}
245
+ @keyword show_private: Whether to create documentation for
246
+ private objects. By default, private objects are documented.
247
+ @type show_frames: C{boolean})
248
+ @keyword show_frames: Whether to create a frames-based table of
249
+ contents. By default, it is produced.
250
+ @type show_imports: C{boolean}
251
+ @keyword show_imports: Whether or not to display lists of
252
+ imported functions and classes. By default, they are
253
+ not shown.
254
+ @type variable_maxlines: C{int}
255
+ @keyword variable_maxlines: The maximum number of lines that
256
+ should be displayed for the value of a variable in the
257
+ variable details section. By default, 8 lines are
258
+ displayed.
259
+ @type variable_linelength: C{int}
260
+ @keyword variable_linelength: The maximum line length used for
261
+ displaying the values of variables in the variable
262
+ details sections. If a line is longer than this length,
263
+ then it will be wrapped to the next line. The default
264
+ line length is 70 characters.
265
+ @type variable_summary_linelength: C{int}
266
+ @keyword variable_summary_linelength: The maximum line length
267
+ used for displaying the values of variables in the summary
268
+ section. If a line is longer than this length, then it
269
+ will be truncated. The default is 40 characters.
270
+ @type variable_tooltip_linelength: C{int}
271
+ @keyword variable_tooltip_linelength: The maximum line length
272
+ used for tooltips for the values of variables. If a
273
+ line is longer than this length, then it will be
274
+ truncated. The default is 600 characters.
275
+ @type property_function_linelength: C{int}
276
+ @keyword property_function_linelength: The maximum line length
277
+ used to dispaly property functions (C{fget}, C{fset}, and
278
+ C{fdel}) that contain something other than a function
279
+ object. The default length is 40 characters.
280
+ @type inheritance: C{string}
281
+ @keyword inheritance: How inherited objects should be displayed.
282
+ If C{inheritance='grouped'}, then inherited objects are
283
+ gathered into groups; if C{inheritance='listed'}, then
284
+ inherited objects are listed in a short list at the
285
+ end of their group; if C{inheritance='included'}, then
286
+ inherited objects are mixed in with non-inherited
287
+ objects. The default is 'grouped'.
288
+ @type include_source_code: C{boolean}
289
+ @keyword include_source_code: If true, then generate colorized
290
+ source code files for each python module.
291
+ @type include_log: C{boolean}
292
+ @keyword include_log: If true, the the footer will include an
293
+ href to the page 'epydoc-log.html'.
294
+ @type src_code_tab_width: C{int}
295
+ @keyword src_code_tab_width: Number of spaces to replace each tab
296
+ with in source code listings.
297
+ """
298
+ self.docindex = docindex
299
+
300
+ # Process keyword arguments.
301
+ self._show_private = kwargs.get('show_private', 1)
302
+ """Should private docs be included?"""
303
+
304
+ self._prj_name = kwargs.get('prj_name', None)
305
+ """The project's name (for the project link in the navbar)"""
306
+
307
+ self._prj_url = kwargs.get('prj_url', None)
308
+ """URL for the project link in the navbar"""
309
+
310
+ self._prj_link = kwargs.get('prj_link', None)
311
+ """HTML code for the project link in the navbar"""
312
+
313
+ self._top_page = kwargs.get('top_page', None)
314
+ """The 'main' page"""
315
+
316
+ self._css = kwargs.get('css')
317
+ """CSS stylesheet to use"""
318
+
319
+ self._helpfile = kwargs.get('help_file', None)
320
+ """Filename of file to extract help contents from"""
321
+
322
+ self._frames_index = kwargs.get('show_frames', 1)
323
+ """Should a frames index be created?"""
324
+
325
+ self._show_imports = kwargs.get('show_imports', False)
326
+ """Should imports be listed?"""
327
+
328
+ self._propfunc_linelen = kwargs.get('property_function_linelength', 40)
329
+ """[XXX] Not used!"""
330
+
331
+ self._variable_maxlines = kwargs.get('variable_maxlines', 8)
332
+ """Max lines for variable values"""
333
+
334
+ self._variable_linelen = kwargs.get('variable_linelength', 70)
335
+ """Max line length for variable values"""
336
+
337
+ self._variable_summary_linelen = \
338
+ kwargs.get('variable_summary_linelength', 65)
339
+ """Max length for variable value summaries"""
340
+
341
+ self._variable_tooltip_linelen = \
342
+ kwargs.get('variable_tooltip_linelength', 600)
343
+ """Max length for variable tooltips"""
344
+
345
+ self._inheritance = kwargs.get('inheritance', 'listed')
346
+ """How should inheritance be displayed? 'listed', 'included',
347
+ or 'grouped'"""
348
+
349
+ self._incl_sourcecode = kwargs.get('include_source_code', True)
350
+ """Should pages be generated for source code of modules?"""
351
+
352
+ self._mark_docstrings = kwargs.get('mark_docstrings', False)
353
+ """Wrap <span class='docstring'>...</span> around docstrings?"""
354
+
355
+ self._graph_types = kwargs.get('graphs', ()) or ()
356
+ """Graphs that we should include in our output."""
357
+
358
+ self._include_log = kwargs.get('include_log', False)
359
+ """Are we generating an HTML log page?"""
360
+
361
+ self._src_code_tab_width = kwargs.get('src_code_tab_width', 8)
362
+ """Number of spaces to replace each tab with in source code
363
+ listings."""
364
+
365
+ self._callgraph_cache = {}
366
+ """Map the callgraph L{uid<DotGraph.uid>} to their HTML
367
+ representation."""
368
+
369
+ self._redundant_details = kwargs.get('redundant_details', False)
370
+ """If true, then include objects in the details list even if all
371
+ info about them is already provided by the summary table."""
372
+
373
+ # For use with select_variables():
374
+ if self._show_private:
375
+ self._public_filter = None
376
+ else:
377
+ self._public_filter = True
378
+
379
+ # Make sure inheritance has a sane value.
380
+ if self._inheritance not in ('listed', 'included', 'grouped'):
381
+ raise ValueError, 'Bad value for inheritance'
382
+
383
+ # Create the project homepage link, if it was not specified.
384
+ if (self._prj_name or self._prj_url) and not self._prj_link:
385
+ self._prj_link = plaintext_to_html(self._prj_name or
386
+ 'Project Homepage')
387
+
388
+ # Add a hyperlink to _prj_url, if _prj_link doesn't already
389
+ # contain any hyperlinks.
390
+ if (self._prj_link and self._prj_url and
391
+ not re.search(r'<a[^>]*\shref', self._prj_link)):
392
+ self._prj_link = ('<a class="navbar" target="_top" href="'+
393
+ self._prj_url+'">'+self._prj_link+'</a>')
394
+
395
+ # Precompute lists & sets of APIDoc objects that we're
396
+ # interested in.
397
+ self.valdocs = valdocs = sorted(docindex.reachable_valdocs(
398
+ imports=False, packages=False, bases=False, submodules=False,
399
+ subclasses=False, private=self._show_private))
400
+ self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)]
401
+ """The list of L{ModuleDoc}s for the documented modules."""
402
+ self.module_set = set(self.module_list)
403
+ """The set of L{ModuleDoc}s for the documented modules."""
404
+ self.class_list = [d for d in valdocs if isinstance(d, ClassDoc)]
405
+ """The list of L{ClassDoc}s for the documented classes."""
406
+ self.class_set = set(self.class_list)
407
+ """The set of L{ClassDoc}s for the documented classes."""
408
+ self.routine_list = [d for d in valdocs if isinstance(d, RoutineDoc)]
409
+ """The list of L{RoutineDoc}s for the documented routines."""
410
+ self.indexed_docs = []
411
+ """The list of L{APIDoc}s for variables and values that should
412
+ be included in the index."""
413
+
414
+ # URL for 'trees' page
415
+ if self.module_list: self._trees_url = 'module-tree.html'
416
+ else: self._trees_url = 'class-tree.html'
417
+
418
+ # Construct the value for self.indexed_docs.
419
+ self.indexed_docs += [d for d in valdocs
420
+ if not isinstance(d, GenericValueDoc)]
421
+ for doc in valdocs:
422
+ if isinstance(doc, NamespaceDoc):
423
+ # add any vars with generic values; but don't include
424
+ # inherited vars.
425
+ self.indexed_docs += [d for d in doc.variables.values() if
426
+ isinstance(d.value, GenericValueDoc)
427
+ and d.container == doc]
428
+ self.indexed_docs.sort()
429
+
430
+ # Figure out the url for the top page.
431
+ self._top_page_url = self._find_top_page(self._top_page)
432
+
433
+ # Decide whether or not to split the identifier index.
434
+ self._split_ident_index = (len(self.indexed_docs) >=
435
+ self.SPLIT_IDENT_INDEX_SIZE)
436
+
437
+ # Figure out how many output files there will be (for progress
438
+ # reporting).
439
+ self.modules_with_sourcecode = set()
440
+ for doc in self.module_list:
441
+ if isinstance(doc, ModuleDoc) and is_src_filename(doc.filename):
442
+ self.modules_with_sourcecode.add(doc)
443
+ self._num_files = (len(self.class_list) + len(self.module_list) +
444
+ 10 + len(self.METADATA_INDICES))
445
+ if self._frames_index:
446
+ self._num_files += len(self.module_list) + 3
447
+
448
+ if self._incl_sourcecode:
449
+ self._num_files += len(self.modules_with_sourcecode)
450
+ if self._split_ident_index:
451
+ self._num_files += len(self.LETTERS)
452
+
453
+ def _find_top_page(self, pagename):
454
+ """
455
+ Find the top page for the API documentation. This page is
456
+ used as the default page shown in the main frame, when frames
457
+ are used. When frames are not used, this page is copied to
458
+ C{index.html}.
459
+
460
+ @param pagename: The name of the page, as specified by the
461
+ keyword argument C{top} to the constructor.
462
+ @type pagename: C{string}
463
+ @return: The URL of the top page.
464
+ @rtype: C{string}
465
+ """
466
+ # If a page name was specified, then we need to figure out
467
+ # what it points to.
468
+ if pagename:
469
+ # If it's a URL, then use it directly.
470
+ if pagename.lower().startswith('http:'):
471
+ return pagename
472
+
473
+ # If it's an object, then use that object's page.
474
+ try:
475
+ doc = self.docindex.get_valdoc(pagename)
476
+ return self.url(doc)
477
+ except:
478
+ pass
479
+
480
+ # Otherwise, give up.
481
+ log.warning('Could not find top page %r; using %s '
482
+ 'instead' % (pagename, self._trees_url))
483
+ return self._trees_url
484
+
485
+ # If no page name was specified, then try to choose one
486
+ # automatically.
487
+ else:
488
+ root = [val_doc for val_doc in self.docindex.root
489
+ if isinstance(val_doc, (ClassDoc, ModuleDoc))]
490
+ if len(root) == 0:
491
+ # No docs?? Try the trees page.
492
+ return self._trees_url
493
+ elif len(root) == 1:
494
+ # One item in the root; use that.
495
+ return self.url(root[0])
496
+ else:
497
+ # Multiple root items; if they're all in one package,
498
+ # then use that. Otherwise, use self._trees_url
499
+ root = sorted(root, key=lambda v:len(v.canonical_name))
500
+ top = root[0]
501
+ for doc in root[1:]:
502
+ if not top.canonical_name.dominates(doc.canonical_name):
503
+ return self._trees_url
504
+ else:
505
+ return self.url(top)
506
+
507
+ #////////////////////////////////////////////////////////////
508
+ #{ 1. Interface Methods
509
+ #////////////////////////////////////////////////////////////
510
+
511
+ def write(self, directory=None):
512
+ """
513
+ Write the documentation to the given directory.
514
+
515
+ @type directory: C{string}
516
+ @param directory: The directory to which output should be
517
+ written. If no directory is specified, output will be
518
+ written to the current directory. If the directory does
519
+ not exist, it will be created.
520
+ @rtype: C{None}
521
+ @raise OSError: If C{directory} cannot be created.
522
+ @raise OSError: If any file cannot be created or written to.
523
+ """
524
+ # For progress reporting:
525
+ self._files_written = 0.
526
+
527
+ # Set the default values for ValueDoc formatted representations.
528
+ orig_valdoc_defaults = (ValueDoc.SUMMARY_REPR_LINELEN,
529
+ ValueDoc.REPR_LINELEN,
530
+ ValueDoc.REPR_MAXLINES)
531
+ ValueDoc.SUMMARY_REPR_LINELEN = self._variable_summary_linelen
532
+ ValueDoc.REPR_LINELEN = self._variable_linelen
533
+ ValueDoc.REPR_MAXLINES = self._variable_maxlines
534
+
535
+ # Use an image for the crarr symbol.
536
+ from epydoc.markup.epytext import ParsedEpytextDocstring
537
+ orig_crarr_html = ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr']
538
+ ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = (
539
+ r'<span class="variable-linewrap">'
540
+ r'<img src="crarr.png" alt="\" /></span>')
541
+
542
+ # Keep track of failed xrefs, and report them at the end.
543
+ self._failed_xrefs = {}
544
+
545
+ # Create destination directories, if necessary
546
+ if not directory: directory = os.curdir
547
+ self._mkdir(directory)
548
+ self._directory = directory
549
+
550
+ # Write the CSS file.
551
+ self._files_written += 1
552
+ log.progress(self._files_written/self._num_files, 'epydoc.css')
553
+ self.write_css(directory, self._css)
554
+
555
+ # Write the Javascript file.
556
+ self._files_written += 1
557
+ log.progress(self._files_written/self._num_files, 'epydoc.js')
558
+ self.write_javascript(directory)
559
+
560
+ # Write images
561
+ self.write_images(directory)
562
+
563
+ # Build the indices.
564
+ indices = {'ident': self.build_identifier_index(),
565
+ 'term': self.build_term_index()}
566
+ for (name, label, label2) in self.METADATA_INDICES:
567
+ indices[name] = self.build_metadata_index(name)
568
+
569
+ # Write the identifier index. If requested, split it into
570
+ # separate pages for each letter.
571
+ ident_by_letter = self._group_by_letter(indices['ident'])
572
+ if not self._split_ident_index:
573
+ self._write(self.write_link_index, directory,
574
+ 'identifier-index.html', indices,
575
+ 'Identifier Index', 'identifier-index.html',
576
+ ident_by_letter)
577
+ else:
578
+ # Write a page for each section.
579
+ for letter in self.LETTERS:
580
+ filename = 'identifier-index-%s.html' % letter
581
+ self._write(self.write_link_index, directory, filename,
582
+ indices, 'Identifier Index', filename,
583
+ ident_by_letter, [letter],
584
+ 'identifier-index-%s.html')
585
+ # Use the first non-empty section as the main index page.
586
+ for letter in self.LETTERS:
587
+ if letter in ident_by_letter:
588
+ filename = 'identifier-index.html'
589
+ self._write(self.write_link_index, directory, filename,
590
+ indices, 'Identifier Index', filename,
591
+ ident_by_letter, [letter],
592
+ 'identifier-index-%s.html')
593
+ break
594
+
595
+ # Write the term index.
596
+ if indices['term']:
597
+ term_by_letter = self._group_by_letter(indices['term'])
598
+ self._write(self.write_link_index, directory, 'term-index.html',
599
+ indices, 'Term Definition Index',
600
+ 'term-index.html', term_by_letter)
601
+ else:
602
+ self._files_written += 1 # (skipped)
603
+
604
+ # Write the metadata indices.
605
+ for (name, label, label2) in self.METADATA_INDICES:
606
+ if indices[name]:
607
+ self._write(self.write_metadata_index, directory,
608
+ '%s-index.html' % name, indices, name,
609
+ label, label2)
610
+ else:
611
+ self._files_written += 1 # (skipped)
612
+
613
+ # Write the trees file (package & class hierarchies)
614
+ if self.module_list:
615
+ self._write(self.write_module_tree, directory, 'module-tree.html')
616
+ else:
617
+ self._files_written += 1 # (skipped)
618
+ if self.class_list:
619
+ self._write(self.write_class_tree, directory, 'class-tree.html')
620
+ else:
621
+ self._files_written += 1 # (skipped)
622
+
623
+ # Write the help file.
624
+ self._write(self.write_help, directory,'help.html')
625
+
626
+ # Write the frames-based table of contents.
627
+ if self._frames_index:
628
+ self._write(self.write_frames_index, directory, 'frames.html')
629
+ self._write(self.write_toc, directory, 'toc.html')
630
+ self._write(self.write_project_toc, directory, 'toc-everything.html')
631
+ for doc in self.module_list:
632
+ filename = 'toc-%s' % urllib.unquote(self.url(doc))
633
+ self._write(self.write_module_toc, directory, filename, doc)
634
+
635
+ # Write the object documentation.
636
+ for doc in self.module_list:
637
+ filename = urllib.unquote(self.url(doc))
638
+ self._write(self.write_module, directory, filename, doc)
639
+ for doc in self.class_list:
640
+ filename = urllib.unquote(self.url(doc))
641
+ self._write(self.write_class, directory, filename, doc)
642
+
643
+ # Write source code files.
644
+ if self._incl_sourcecode:
645
+ # Build a map from short names to APIDocs, used when
646
+ # linking names in the source code.
647
+ name_to_docs = {}
648
+ for api_doc in self.indexed_docs:
649
+ if (api_doc.canonical_name is not None and
650
+ self.url(api_doc) is not None):
651
+ name = api_doc.canonical_name[-1]
652
+ name_to_docs.setdefault(name, []).append(api_doc)
653
+ # Sort each entry of the name_to_docs list.
654
+ for doc_list in name_to_docs.values():
655
+ doc_list.sort()
656
+ # Write the source code for each module.
657
+ for doc in self.modules_with_sourcecode:
658
+ filename = urllib.unquote(self.pysrc_url(doc))
659
+ self._write(self.write_sourcecode, directory, filename, doc,
660
+ name_to_docs)
661
+
662
+ # Write the auto-redirect page.
663
+ self._write(self.write_redirect_page, directory, 'redirect.html')
664
+
665
+ # Write the mapping object name -> URL
666
+ self._write(self.write_api_list, directory, 'api-objects.txt')
667
+
668
+ # Write the index.html files.
669
+ # (this must be done last, since it might copy another file)
670
+ self._files_written += 1
671
+ log.progress(self._files_written/self._num_files, 'index.html')
672
+ self.write_homepage(directory)
673
+
674
+ # Don't report references to builtins as missing
675
+ for k in self._failed_xrefs.keys(): # have a copy of keys
676
+ if hasattr(__builtin__, k):
677
+ del self._failed_xrefs[k]
678
+
679
+ # Report any failed crossreferences
680
+ if self._failed_xrefs:
681
+ estr = 'Failed identifier crossreference targets:\n'
682
+ failed_identifiers = self._failed_xrefs.keys()
683
+ failed_identifiers.sort()
684
+ for identifier in failed_identifiers:
685
+ names = self._failed_xrefs[identifier].keys()
686
+ names.sort()
687
+ estr += '- %s' % identifier
688
+ estr += '\n'
689
+ for name in names:
690
+ estr += ' (from %s)\n' % name
691
+ log.docstring_warning(estr)
692
+
693
+ # [xx] testing:
694
+ if self._num_files != int(self._files_written):
695
+ log.debug("Expected to write %d files, but actually "
696
+ "wrote %d files" %
697
+ (self._num_files, int(self._files_written)))
698
+
699
+ # Restore defaults that we changed.
700
+ (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN,
701
+ ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults
702
+ ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = orig_crarr_html
703
+
704
+ def _write(self, write_func, directory, filename, *args):
705
+ # Display our progress.
706
+ self._files_written += 1
707
+ log.progress(self._files_written/self._num_files, filename)
708
+
709
+ path = os.path.join(directory, filename)
710
+ f = codecs.open(path, 'w', 'ascii', errors='xmlcharrefreplace')
711
+ write_func(f.write, *args)
712
+ f.close()
713
+
714
+ def _mkdir(self, directory):
715
+ """
716
+ If the given directory does not exist, then attempt to create it.
717
+ @rtype: C{None}
718
+ """
719
+ if not os.path.isdir(directory):
720
+ if os.path.exists(directory):
721
+ raise OSError('%r is not a directory' % directory)
722
+ os.mkdir(directory)
723
+
724
+ #////////////////////////////////////////////////////////////
725
+ #{ 2.1. Module Pages
726
+ #////////////////////////////////////////////////////////////
727
+
728
+ def write_module(self, out, doc):
729
+ """
730
+ Write an HTML page containing the API documentation for the
731
+ given module to C{out}.
732
+
733
+ @param doc: A L{ModuleDoc} containing the API documentation
734
+ for the module that should be described.
735
+ """
736
+ longname = doc.canonical_name
737
+ shortname = doc.canonical_name[-1]
738
+
739
+ # Write the page header (incl. navigation bar & breadcrumbs)
740
+ self.write_header(out, str(longname))
741
+ self.write_navbar(out, doc)
742
+ self.write_breadcrumbs(out, doc, self.url(doc))
743
+
744
+ # Write the name of the module we're describing.
745
+ if doc.is_package is True: typ = 'Package'
746
+ else: typ = 'Module'
747
+ if longname[0].startswith('script-'):
748
+ shortname = str(longname)[7:]
749
+ typ = 'Script'
750
+ out('<!-- ==================== %s ' % typ.upper() +
751
+ 'DESCRIPTION ==================== -->\n')
752
+ out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
753
+ out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
754
+
755
+ # If the module has a description, then list it.
756
+ if doc.descr not in (None, UNKNOWN):
757
+ out(self.descr(doc, 2)+'\n\n')
758
+
759
+ # Write any standarad metadata (todo, author, etc.)
760
+ if doc.metadata is not UNKNOWN and doc.metadata:
761
+ out('<hr />\n')
762
+ self.write_standard_fields(out, doc)
763
+
764
+ # If it's a package, then list the modules it contains.
765
+ if doc.is_package is True:
766
+ self.write_module_list(out, doc)
767
+
768
+ # Write summary tables describing the variables that the
769
+ # module defines.
770
+ self.write_summary_table(out, "Classes", doc, "class")
771
+ self.write_summary_table(out, "Functions", doc, "function")
772
+ self.write_summary_table(out, "Variables", doc, "other")
773
+
774
+ # Write a list of all imported objects.
775
+ if self._show_imports:
776
+ self.write_imports(out, doc)
777
+
778
+ # Write detailed descriptions of functions & variables defined
779
+ # in this module.
780
+ self.write_details_list(out, "Function Details", doc, "function")
781
+ self.write_details_list(out, "Variables Details", doc, "other")
782
+
783
+ # Write the page footer (including navigation bar)
784
+ self.write_navbar(out, doc)
785
+ self.write_footer(out)
786
+
787
+ #////////////////////////////////////////////////////////////
788
+ #{ 2.??. Source Code Pages
789
+ #////////////////////////////////////////////////////////////
790
+
791
+ def write_sourcecode(self, out, doc, name_to_docs):
792
+ #t0 = time.time()
793
+
794
+ filename = doc.filename
795
+ name = str(doc.canonical_name)
796
+
797
+ # Header
798
+ self.write_header(out, name)
799
+ self.write_navbar(out, doc)
800
+ self.write_breadcrumbs(out, doc, self.pysrc_url(doc))
801
+
802
+ # Source code listing
803
+ out('<h1 class="epydoc">Source Code for %s</h1>\n' %
804
+ self.href(doc, label='%s %s' % (self.doc_kind(doc), name)))
805
+ out('<pre class="py-src">\n')
806
+ out(PythonSourceColorizer(filename, name, self.docindex,
807
+ self.url, name_to_docs,
808
+ self._src_code_tab_width).colorize())
809
+ out('</pre>\n<br />\n')
810
+
811
+ # Footer
812
+ self.write_navbar(out, doc)
813
+ self.write_footer(out)
814
+
815
+ #log.debug('[%6.2f sec] Wrote pysrc for %s' %
816
+ # (time.time()-t0, name))
817
+
818
+ #////////////////////////////////////////////////////////////
819
+ #{ 2.2. Class Pages
820
+ #////////////////////////////////////////////////////////////
821
+
822
+ def write_class(self, out, doc):
823
+ """
824
+ Write an HTML page containing the API documentation for the
825
+ given class to C{out}.
826
+
827
+ @param doc: A L{ClassDoc} containing the API documentation
828
+ for the class that should be described.
829
+ """
830
+ longname = doc.canonical_name
831
+ shortname = doc.canonical_name[-1]
832
+
833
+ # Write the page header (incl. navigation bar & breadcrumbs)
834
+ self.write_header(out, str(longname))
835
+ self.write_navbar(out, doc)
836
+ self.write_breadcrumbs(out, doc, self.url(doc))
837
+
838
+ # Write the name of the class we're describing.
839
+ if doc.is_type(): typ = 'Type'
840
+ elif doc.is_exception(): typ = 'Exception'
841
+ else: typ = 'Class'
842
+ out('<!-- ==================== %s ' % typ.upper() +
843
+ 'DESCRIPTION ==================== -->\n')
844
+ out('<h1 class="epydoc">%s %s</h1>' % (typ, shortname))
845
+ out('<p class="nomargin-top">%s</p>\n' % self.pysrc_link(doc))
846
+
847
+ if ((doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0) or
848
+ (doc.subclasses not in (UNKNOWN,None) and len(doc.subclasses)>0)):
849
+ # Display bases graphically, if requested.
850
+ if 'umlclasstree' in self._graph_types:
851
+ self.write_class_tree_graph(out, doc, uml_class_tree_graph)
852
+ elif 'classtree' in self._graph_types:
853
+ self.write_class_tree_graph(out, doc, class_tree_graph)
854
+
855
+ # Otherwise, use ascii-art.
856
+ else:
857
+ # Write the base class tree.
858
+ if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0:
859
+ out('<pre class="base-tree">\n%s</pre>\n\n' %
860
+ self.base_tree(doc))
861
+
862
+ # Write the known subclasses
863
+ if (doc.subclasses not in (UNKNOWN, None) and
864
+ len(doc.subclasses) > 0):
865
+ out('<dl><dt>Known Subclasses:</dt>\n<dd>\n ')
866
+ out(' <ul class="subclass-list">\n')
867
+ for i, subclass in enumerate(doc.subclasses):
868
+ href = self.href(subclass, context=doc)
869
+ if self._val_is_public(subclass): css = ''
870
+ else: css = ' class="private"'
871
+ if i > 0: href = ', '+href
872
+ out('<li%s>%s</li>' % (css, href))
873
+ out(' </ul>\n')
874
+ out('</dd></dl>\n\n')
875
+
876
+ out('<hr />\n')
877
+
878
+ # If the class has a description, then list it.
879
+ if doc.descr not in (None, UNKNOWN):
880
+ out(self.descr(doc, 2)+'\n\n')
881
+
882
+ # Write any standarad metadata (todo, author, etc.)
883
+ if doc.metadata is not UNKNOWN and doc.metadata:
884
+ out('<hr />\n')
885
+ self.write_standard_fields(out, doc)
886
+
887
+ # Write summary tables describing the variables that the
888
+ # class defines.
889
+ self.write_summary_table(out, "Nested Classes", doc, "class")
890
+ self.write_summary_table(out, "Instance Methods", doc,
891
+ "instancemethod")
892
+ self.write_summary_table(out, "Class Methods", doc, "classmethod")
893
+ self.write_summary_table(out, "Static Methods", doc, "staticmethod")
894
+ self.write_summary_table(out, "Class Variables", doc,
895
+ "classvariable")
896
+ self.write_summary_table(out, "Instance Variables", doc,
897
+ "instancevariable")
898
+ self.write_summary_table(out, "Properties", doc, "property")
899
+
900
+ # Write a list of all imported objects.
901
+ if self._show_imports:
902
+ self.write_imports(out, doc)
903
+
904
+ # Write detailed descriptions of functions & variables defined
905
+ # in this class.
906
+ # [xx] why group methods into one section but split vars into two?
907
+ # seems like we should either group in both cases or split in both
908
+ # cases.
909
+ self.write_details_list(out, "Method Details", doc, "method")
910
+ self.write_details_list(out, "Class Variable Details", doc,
911
+ "classvariable")
912
+ self.write_details_list(out, "Instance Variable Details", doc,
913
+ "instancevariable")
914
+ self.write_details_list(out, "Property Details", doc, "property")
915
+
916
+ # Write the page footer (including navigation bar)
917
+ self.write_navbar(out, doc)
918
+ self.write_footer(out)
919
+
920
+ def write_class_tree_graph(self, out, doc, graphmaker):
921
+ """
922
+ Write HTML code for a class tree graph of C{doc} (a classdoc),
923
+ using C{graphmaker} to draw the actual graph. C{graphmaker}
924
+ should be L{class_tree_graph()}, or L{uml_class_tree_graph()},
925
+ or any other function with a compatible signature.
926
+
927
+ If the given class has any private sublcasses (including
928
+ recursive subclasses), then two graph images will be generated
929
+ -- one to display when private values are shown, and the other
930
+ to display when private values are hidden.
931
+ """
932
+ linker = _HTMLDocstringLinker(self, doc)
933
+ private_subcls = self._private_subclasses(doc)
934
+ if private_subcls:
935
+ out('<center>\n'
936
+ ' <div class="private">%s</div>\n'
937
+ ' <div class="public" style="display:none">%s</div>\n'
938
+ '</center>\n' %
939
+ (self.render_graph(graphmaker(doc, linker, doc)),
940
+ self.render_graph(graphmaker(doc, linker, doc,
941
+ exclude=private_subcls))))
942
+ else:
943
+ out('<center>\n%s\n</center>\n' %
944
+ self.render_graph(graphmaker(doc, linker, doc)))
945
+
946
+ #////////////////////////////////////////////////////////////
947
+ #{ 2.3. Trees pages
948
+ #////////////////////////////////////////////////////////////
949
+
950
+ def write_module_tree(self, out):
951
+ # Header material
952
+ self.write_treepage_header(out, 'Module Hierarchy', 'module-tree.html')
953
+ out('<h1 class="epydoc">Module Hierarchy</h1>\n')
954
+
955
+ # Write entries for all top-level modules/packages.
956
+ out('<ul class="nomargin-top">\n')
957
+ for doc in self.module_list:
958
+ if (doc.package in (None, UNKNOWN) or
959
+ doc.package not in self.module_set):
960
+ self.write_module_tree_item(out, doc)
961
+ out('</ul>\n')
962
+
963
+ # Footer material
964
+ self.write_navbar(out, 'trees')
965
+ self.write_footer(out)
966
+
967
+ def write_class_tree(self, out):
968
+ """
969
+ Write HTML code for a nested list showing the base/subclass
970
+ relationships between all documented classes. Each element of
971
+ the top-level list is a class with no (documented) bases; and
972
+ under each class is listed all of its subclasses. Note that
973
+ in the case of multiple inheritance, a class may appear
974
+ multiple times.
975
+
976
+ @todo: For multiple inheritance, don't repeat subclasses the
977
+ second time a class is mentioned; instead, link to the
978
+ first mention.
979
+ """
980
+ # [XX] backref for multiple inheritance?
981
+ # Header material
982
+ self.write_treepage_header(out, 'Class Hierarchy', 'class-tree.html')
983
+ out('<h1 class="epydoc">Class Hierarchy</h1>\n')
984
+
985
+ # Build a set containing all classes that we should list.
986
+ # This includes everything in class_list, plus any of those
987
+ # class' bases, but not undocumented subclasses.
988
+ class_set = self.class_set.copy()
989
+ for doc in self.class_list:
990
+ if doc.bases != UNKNOWN:
991
+ for base in doc.bases:
992
+ if base not in class_set:
993
+ if isinstance(base, ClassDoc):
994
+ class_set.update(base.mro())
995
+ else:
996
+ # [XX] need to deal with this -- how?
997
+ pass
998
+ #class_set.add(base)
999
+
1000
+ out('<ul class="nomargin-top">\n')
1001
+ for doc in sorted(class_set, key=lambda c:c.canonical_name[-1]):
1002
+ if doc.bases != UNKNOWN and len(doc.bases)==0:
1003
+ self.write_class_tree_item(out, doc, class_set)
1004
+ out('</ul>\n')
1005
+
1006
+ # Footer material
1007
+ self.write_navbar(out, 'trees')
1008
+ self.write_footer(out)
1009
+
1010
+ def write_treepage_header(self, out, title, url):
1011
+ # Header material.
1012
+ self.write_header(out, title)
1013
+ self.write_navbar(out, 'trees')
1014
+ self.write_breadcrumbs(out, 'trees', url)
1015
+ if self.class_list and self.module_list:
1016
+ out('<center><b>\n')
1017
+ out(' [ <a href="module-tree.html">Module Hierarchy</a>\n')
1018
+ out(' | <a href="class-tree.html">Class Hierarchy</a> ]\n')
1019
+ out('</b></center><br />\n')
1020
+
1021
+
1022
+ #////////////////////////////////////////////////////////////
1023
+ #{ 2.4. Index pages
1024
+ #////////////////////////////////////////////////////////////
1025
+
1026
+ SPLIT_IDENT_INDEX_SIZE = 3000
1027
+ """If the identifier index has more than this number of entries,
1028
+ then it will be split into separate pages, one for each
1029
+ alphabetical section."""
1030
+
1031
+ LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_'
1032
+ """The alphabetical sections that are used for link index pages."""
1033
+
1034
+ def write_link_index(self, out, indices, title, url, index_by_section,
1035
+ sections=LETTERS, section_url='#%s'):
1036
+
1037
+ # Header
1038
+ self.write_indexpage_header(out, indices, title, url)
1039
+
1040
+ # Index title & links to alphabetical sections.
1041
+ out('<table border="0" width="100%">\n'
1042
+ '<tr valign="bottom"><td>\n')
1043
+ out('<h1 class="epydoc">%s</h1>\n</td><td>\n[\n' % title)
1044
+ for sec in self.LETTERS:
1045
+ if sec in index_by_section:
1046
+ out(' <a href="%s">%s</a>\n' % (section_url % sec, sec))
1047
+ else:
1048
+ out(' %s\n' % sec)
1049
+ out(']\n')
1050
+ out('</td></table>\n')
1051
+
1052
+ # Alphabetical sections.
1053
+ sections = [s for s in sections if s in index_by_section]
1054
+ if sections:
1055
+ out('<table border="0" width="100%">\n')
1056
+ for section in sorted(sections):
1057
+ out('<tr valign="top"><td valign="top" width="1%">')
1058
+ out('<h2 class="epydoc"><a name="%s">%s</a></h2></td>\n' %
1059
+ (section, section))
1060
+ out('<td valign="top">\n')
1061
+ self.write_index_section(out, index_by_section[section], True)
1062
+ out('</td></tr>\n')
1063
+ out('</table>\n<br />')
1064
+
1065
+ # Footer material.
1066
+ out('<br />')
1067
+ self.write_navbar(out, 'indices')
1068
+ self.write_footer(out)
1069
+
1070
+
1071
+ def write_metadata_index(self, out, indices, field, title, typ):
1072
+ """
1073
+ Write an HTML page containing a metadata index.
1074
+ """
1075
+ index = indices[field]
1076
+
1077
+ # Header material.
1078
+ self.write_indexpage_header(out, indices, title,
1079
+ '%s-index.html' % field)
1080
+
1081
+ # Page title.
1082
+ out('<h1 class="epydoc"><a name="%s">%s</a></h1>\n<br />\n' %
1083
+ (field, title))
1084
+
1085
+ # Index (one section per arg)
1086
+ for arg in sorted(index):
1087
+ # Write a section title.
1088
+ if arg is not None:
1089
+ if len([1 for (doc, descrs) in index[arg] if
1090
+ not self._doc_or_ancestor_is_private(doc)]) == 0:
1091
+ out('<div class="private">')
1092
+ else:
1093
+ out('<div>')
1094
+ self.write_table_header(out, 'metadata-index', arg)
1095
+ out('</table>')
1096
+ # List every descr for this arg.
1097
+ for (doc, descrs) in index[arg]:
1098
+ if self._doc_or_ancestor_is_private(doc):
1099
+ out('<div class="private">\n')
1100
+ else:
1101
+ out('<div>\n')
1102
+ out('<table width="100%" class="metadata-index" '
1103
+ 'bgcolor="#e0e0e0"><tr><td class="metadata-index">')
1104
+ out('<b>%s in %s</b>' %
1105
+ (typ, self.href(doc, label=doc.canonical_name)))
1106
+ out(' <ul class="nomargin">\n')
1107
+ for descr in descrs:
1108
+ out(' <li>%s</li>\n' %
1109
+ self.docstring_to_html(descr,doc,4))
1110
+ out(' </ul>\n')
1111
+ out('</table></div>\n')
1112
+
1113
+ # Footer material.
1114
+ out('<br />')
1115
+ self.write_navbar(out, 'indices')
1116
+ self.write_footer(out)
1117
+
1118
+ def write_indexpage_header(self, out, indices, title, url):
1119
+ """
1120
+ A helper for the index page generation functions, which
1121
+ generates a header that can be used to navigate between the
1122
+ different indices.
1123
+ """
1124
+ self.write_header(out, title)
1125
+ self.write_navbar(out, 'indices')
1126
+ self.write_breadcrumbs(out, 'indices', url)
1127
+
1128
+ if (indices['term'] or
1129
+ [1 for (name,l,l2) in self.METADATA_INDICES if indices[name]]):
1130
+ out('<center><b>[\n')
1131
+ out(' <a href="identifier-index.html">Identifiers</a>\n')
1132
+ if indices['term']:
1133
+ out('| <a href="term-index.html">Term Definitions</a>\n')
1134
+ for (name, label, label2) in self.METADATA_INDICES:
1135
+ if indices[name]:
1136
+ out('| <a href="%s-index.html">%s</a>\n' %
1137
+ (name, label2))
1138
+ out(']</b></center><br />\n')
1139
+
1140
+ def write_index_section(self, out, items, add_blankline=False):
1141
+ out('<table class="link-index" width="100%" border="1">\n')
1142
+ num_rows = (len(items)+2)/3
1143
+ for row in range(num_rows):
1144
+ out('<tr>\n')
1145
+ for col in range(3):
1146
+ out('<td width="33%" class="link-index">')
1147
+ i = col*num_rows+row
1148
+ if i < len(items):
1149
+ name, url, container = items[col*num_rows+row]
1150
+ out('<a href="%s">%s</a>' % (url, name))
1151
+ if container is not None:
1152
+ out('<br />\n')
1153
+ if isinstance(container, ModuleDoc):
1154
+ label = container.canonical_name
1155
+ else:
1156
+ label = container.canonical_name[-1]
1157
+ out('<span class="index-where">(in&nbsp;%s)'
1158
+ '</span>' % self.href(container, label))
1159
+ else:
1160
+ out('&nbsp;')
1161
+ out('</td>\n')
1162
+ out('</tr>\n')
1163
+ if add_blankline and num_rows == 1:
1164
+ blank_cell = '<td class="link-index">&nbsp;</td>'
1165
+ out('<tr>'+3*blank_cell+'</tr>\n')
1166
+ out('</table>\n')
1167
+
1168
+ #////////////////////////////////////////////////////////////
1169
+ #{ 2.5. Help Page
1170
+ #////////////////////////////////////////////////////////////
1171
+
1172
+ def write_help(self, out):
1173
+ """
1174
+ Write an HTML help file to the given stream. If
1175
+ C{self._helpfile} contains a help file, then use it;
1176
+ otherwise, use the default helpfile from
1177
+ L{epydoc.docwriter.html_help}.
1178
+ """
1179
+ # todo: optionally parse .rst etc help files?
1180
+
1181
+ # Get the contents of the help file.
1182
+ if self._helpfile:
1183
+ if os.path.exists(self._helpfile):
1184
+ try: help = open(self._helpfile).read()
1185
+ except: raise IOError("Can't open help file: %r" %
1186
+ self._helpfile)
1187
+ else:
1188
+ raise IOError("Can't find help file: %r" % self._helpfile)
1189
+ else:
1190
+ if self._prj_name: thisprj = self._prj_name
1191
+ else: thisprj = 'this project'
1192
+ help = HTML_HELP % {'this_project':thisprj}
1193
+
1194
+ # Insert the help contents into a webpage.
1195
+ self.write_header(out, 'Help')
1196
+ self.write_navbar(out, 'help')
1197
+ self.write_breadcrumbs(out, 'help', 'help.html')
1198
+ out(help)
1199
+ self.write_navbar(out, 'help')
1200
+ self.write_footer(out)
1201
+
1202
+ #////////////////////////////////////////////////////////////
1203
+ #{ 2.6. Frames-based Table of Contents
1204
+ #////////////////////////////////////////////////////////////
1205
+
1206
+ write_frames_index = compile_template(
1207
+ """
1208
+ write_frames_index(self, out)
1209
+
1210
+ Write the frames index file for the frames-based table of
1211
+ contents to the given streams.
1212
+ """,
1213
+ # /------------------------- Template -------------------------\
1214
+ '''
1215
+ <?xml version="1.0" encoding="iso-8859-1"?>
1216
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
1217
+ "DTD/xhtml1-frameset.dtd">
1218
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1219
+ <head>
1220
+ <title> $self._prj_name or "API Documentation"$ </title>
1221
+ </head>
1222
+ <frameset cols="20%,80%">
1223
+ <frameset rows="30%,70%">
1224
+ <frame src="toc.html" name="moduleListFrame"
1225
+ id="moduleListFrame" />
1226
+ <frame src="toc-everything.html" name="moduleFrame"
1227
+ id="moduleFrame" />
1228
+ </frameset>
1229
+ <frame src="$self._top_page_url$" name="mainFrame" id="mainFrame" />
1230
+ </frameset>
1231
+ </html>
1232
+ ''')
1233
+ # \------------------------------------------------------------/
1234
+
1235
+ write_toc = compile_template(
1236
+ """
1237
+ write_toc(self, out)
1238
+ """,
1239
+ # /------------------------- Template -------------------------\
1240
+ '''
1241
+ >>> self.write_header(out, "Table of Contents")
1242
+ <h1 class="toc">Table&nbsp;of&nbsp;Contents</h1>
1243
+ <hr />
1244
+ <a target="moduleFrame" href="toc-everything.html">Everything</a>
1245
+ <br />
1246
+ >>> self.write_toc_section(out, "Modules", self.module_list)
1247
+ <hr />
1248
+ >>> if self._show_private:
1249
+ $self.PRIVATE_LINK$
1250
+ >>> #endif
1251
+ >>> self.write_footer(out, short=True)
1252
+ ''')
1253
+ # \------------------------------------------------------------/
1254
+
1255
+ def write_toc_section(self, out, name, docs, fullname=True):
1256
+ if not docs: return
1257
+
1258
+ # Assign names to each item, and sort by name.
1259
+ if fullname:
1260
+ docs = [(str(d.canonical_name), d) for d in docs]
1261
+ else:
1262
+ docs = [(str(d.canonical_name[-1]), d) for d in docs]
1263
+ docs.sort()
1264
+
1265
+ out(' <h2 class="toc">%s</h2>\n' % name)
1266
+ for label, doc in docs:
1267
+ doc_url = self.url(doc)
1268
+ toc_url = 'toc-%s' % doc_url
1269
+ is_private = self._doc_or_ancestor_is_private(doc)
1270
+ if is_private:
1271
+ if not self._show_private: continue
1272
+ out(' <div class="private">\n')
1273
+
1274
+ if isinstance(doc, ModuleDoc):
1275
+ out(' <a target="moduleFrame" href="%s"\n'
1276
+ ' onclick="setFrame(\'%s\',\'%s\');"'
1277
+ ' >%s</a><br />' % (toc_url, toc_url, doc_url, label))
1278
+ else:
1279
+ out(' <a target="mainFrame" href="%s"\n'
1280
+ ' >%s</a><br />' % (doc_url, label))
1281
+ if is_private:
1282
+ out(' </div>\n')
1283
+
1284
+ def write_project_toc(self, out):
1285
+ self.write_header(out, "Everything")
1286
+ out('<h1 class="toc">Everything</h1>\n')
1287
+ out('<hr />\n')
1288
+
1289
+ # List the classes.
1290
+ self.write_toc_section(out, "All Classes", self.class_list)
1291
+
1292
+ # List the functions.
1293
+ funcs = [d for d in self.routine_list
1294
+ if not isinstance(self.docindex.container(d),
1295
+ (ClassDoc, types.NoneType))]
1296
+ self.write_toc_section(out, "All Functions", funcs)
1297
+
1298
+ # List the variables.
1299
+ vars = []
1300
+ for doc in self.module_list:
1301
+ vars += doc.select_variables(value_type='other',
1302
+ imported=False,
1303
+ public=self._public_filter)
1304
+ self.write_toc_section(out, "All Variables", vars)
1305
+
1306
+ # Footer material.
1307
+ out('<hr />\n')
1308
+ if self._show_private:
1309
+ out(self.PRIVATE_LINK+'\n')
1310
+ self.write_footer(out, short=True)
1311
+
1312
+ def write_module_toc(self, out, doc):
1313
+ """
1314
+ Write an HTML page containing the table of contents page for
1315
+ the given module to the given streams. This page lists the
1316
+ modules, classes, exceptions, functions, and variables defined
1317
+ by the module.
1318
+ """
1319
+ name = doc.canonical_name[-1]
1320
+ self.write_header(out, name)
1321
+ out('<h1 class="toc">Module %s</h1>\n' % name)
1322
+ out('<hr />\n')
1323
+
1324
+
1325
+ # List the classes.
1326
+ classes = doc.select_variables(value_type='class', imported=False,
1327
+ public=self._public_filter)
1328
+ self.write_toc_section(out, "Classes", classes, fullname=False)
1329
+
1330
+ # List the functions.
1331
+ funcs = doc.select_variables(value_type='function', imported=False,
1332
+ public=self._public_filter)
1333
+ self.write_toc_section(out, "Functions", funcs, fullname=False)
1334
+
1335
+ # List the variables.
1336
+ variables = doc.select_variables(value_type='other', imported=False,
1337
+ public=self._public_filter)
1338
+ self.write_toc_section(out, "Variables", variables, fullname=False)
1339
+
1340
+ # Footer material.
1341
+ out('<hr />\n')
1342
+ if self._show_private:
1343
+ out(self.PRIVATE_LINK+'\n')
1344
+ self.write_footer(out, short=True)
1345
+
1346
+ #////////////////////////////////////////////////////////////
1347
+ #{ 2.7. Project homepage (index.html)
1348
+ #////////////////////////////////////////////////////////////
1349
+
1350
+ def write_homepage(self, directory):
1351
+ """
1352
+ Write an C{index.html} file in the given directory. The
1353
+ contents of this file are copied or linked from an existing
1354
+ page, so this method must be called after all pages have been
1355
+ written. The page used is determined by L{_frames_index} and
1356
+ L{_top_page}:
1357
+ - If L{_frames_index} is true, then C{frames.html} is
1358
+ copied.
1359
+ - Otherwise, the page specified by L{_top_page} is
1360
+ copied.
1361
+ """
1362
+ filename = os.path.join(directory, 'index.html')
1363
+ if self._frames_index: top = 'frames.html'
1364
+ else: top = self._top_page_url
1365
+
1366
+ # Copy the non-frames index file from top, if it's internal.
1367
+ if top[:5] != 'http:' and '/' not in top:
1368
+ try:
1369
+ # Read top into `s`.
1370
+ topfile = os.path.join(directory, top)
1371
+ s = open(topfile, 'r').read()
1372
+
1373
+ # Write the output file.
1374
+ open(filename, 'w').write(s)
1375
+ return
1376
+ except:
1377
+ log.error('Warning: error copying index; '
1378
+ 'using a redirect page')
1379
+
1380
+ # Use a redirect if top is external, or if we faild to copy.
1381
+ name = self._prj_name or 'this project'
1382
+ f = open(filename, 'w')
1383
+ self.write_redirect_index(f.write, top, name)
1384
+ f.close()
1385
+
1386
+ write_redirect_index = compile_template(
1387
+ """
1388
+ write_redirect_index(self, out, top, name)
1389
+ """,
1390
+ # /------------------------- Template -------------------------\
1391
+ '''
1392
+ <?xml version="1.0" encoding="iso-8859-1"?>
1393
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
1394
+ "DTD/xhtml1-strict.dtd">
1395
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1396
+ <head>
1397
+ <title> Redirect </title>
1398
+ <meta http-equiv="refresh" content="1;url=$top$" />
1399
+ <link rel="stylesheet" href="epydoc.css" type="text/css"></link>
1400
+ </head>
1401
+ <body>
1402
+ <p>Redirecting to the API documentation for
1403
+ <a href="$top$">$self._prj_name or "this project"$</a>...</p>
1404
+ </body>
1405
+ </html>
1406
+ ''')
1407
+ # \------------------------------------------------------------/
1408
+
1409
+ #////////////////////////////////////////////////////////////
1410
+ #{ 2.8. Stylesheet (epydoc.css)
1411
+ #////////////////////////////////////////////////////////////
1412
+
1413
+ def write_css(self, directory, cssname):
1414
+ """
1415
+ Write the CSS stylesheet in the given directory. If
1416
+ C{cssname} contains a stylesheet file or name (from
1417
+ L{epydoc.docwriter.html_css}), then use that stylesheet;
1418
+ otherwise, use the default stylesheet.
1419
+
1420
+ @rtype: C{None}
1421
+ """
1422
+ filename = os.path.join(directory, 'epydoc.css')
1423
+
1424
+ # Get the contents for the stylesheet file.
1425
+ if cssname is None:
1426
+ css = STYLESHEETS['default'][0]
1427
+ else:
1428
+ if os.path.exists(cssname):
1429
+ try: css = open(cssname).read()
1430
+ except: raise IOError("Can't open CSS file: %r" % cssname)
1431
+ elif cssname in STYLESHEETS:
1432
+ css = STYLESHEETS[cssname][0]
1433
+ else:
1434
+ raise IOError("Can't find CSS file: %r" % cssname)
1435
+
1436
+ # Write the stylesheet.
1437
+ cssfile = open(filename, 'w')
1438
+ cssfile.write(css)
1439
+ cssfile.close()
1440
+
1441
+ #////////////////////////////////////////////////////////////
1442
+ #{ 2.9. Javascript (epydoc.js)
1443
+ #////////////////////////////////////////////////////////////
1444
+
1445
+ def write_javascript(self, directory):
1446
+ jsfile = open(os.path.join(directory, 'epydoc.js'), 'w')
1447
+ print >> jsfile, self.TOGGLE_PRIVATE_JS
1448
+ print >> jsfile, self.SHOW_PRIVATE_JS
1449
+ print >> jsfile, self.GET_COOKIE_JS
1450
+ print >> jsfile, self.SET_FRAME_JS
1451
+ print >> jsfile, self.HIDE_PRIVATE_JS
1452
+ print >> jsfile, self.TOGGLE_CALLGRAPH_JS
1453
+ print >> jsfile, html_colorize.PYSRC_JAVASCRIPTS
1454
+ print >> jsfile, self.GET_ANCHOR_JS
1455
+ print >> jsfile, self.REDIRECT_URL_JS
1456
+ jsfile.close()
1457
+
1458
+ #: A javascript that is used to show or hide the API documentation
1459
+ #: for private objects. In order for this to work correctly, all
1460
+ #: documentation for private objects should be enclosed in
1461
+ #: C{<div class="private">...</div>} elements.
1462
+ TOGGLE_PRIVATE_JS = '''
1463
+ function toggle_private() {
1464
+ // Search for any private/public links on this page. Store
1465
+ // their old text in "cmd," so we will know what action to
1466
+ // take; and change their text to the opposite action.
1467
+ var cmd = "?";
1468
+ var elts = document.getElementsByTagName("a");
1469
+ for(var i=0; i<elts.length; i++) {
1470
+ if (elts[i].className == "privatelink") {
1471
+ cmd = elts[i].innerHTML;
1472
+ elts[i].innerHTML = ((cmd && cmd.substr(0,4)=="show")?
1473
+ "hide&nbsp;private":"show&nbsp;private");
1474
+ }
1475
+ }
1476
+ // Update all DIVs containing private objects.
1477
+ var elts = document.getElementsByTagName("div");
1478
+ for(var i=0; i<elts.length; i++) {
1479
+ if (elts[i].className == "private") {
1480
+ elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
1481
+ }
1482
+ else if (elts[i].className == "public") {
1483
+ elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"block":"none");
1484
+ }
1485
+ }
1486
+ // Update all table rows containing private objects. Note, we
1487
+ // use "" instead of "block" becaue IE & firefox disagree on what
1488
+ // this should be (block vs table-row), and "" just gives the
1489
+ // default for both browsers.
1490
+ var elts = document.getElementsByTagName("tr");
1491
+ for(var i=0; i<elts.length; i++) {
1492
+ if (elts[i].className == "private") {
1493
+ elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"");
1494
+ }
1495
+ }
1496
+ // Update all list items containing private objects.
1497
+ var elts = document.getElementsByTagName("li");
1498
+ for(var i=0; i<elts.length; i++) {
1499
+ if (elts[i].className == "private") {
1500
+ elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?
1501
+ "none":"");
1502
+ }
1503
+ }
1504
+ // Update all list items containing private objects.
1505
+ var elts = document.getElementsByTagName("ul");
1506
+ for(var i=0; i<elts.length; i++) {
1507
+ if (elts[i].className == "private") {
1508
+ elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
1509
+ }
1510
+ }
1511
+ // Set a cookie to remember the current option.
1512
+ document.cookie = "EpydocPrivate="+cmd;
1513
+ }
1514
+ '''.strip()
1515
+
1516
+ #: A javascript that is used to read the value of a cookie. This
1517
+ #: is used to remember whether private variables should be shown or
1518
+ #: hidden.
1519
+ GET_COOKIE_JS = '''
1520
+ function getCookie(name) {
1521
+ var dc = document.cookie;
1522
+ var prefix = name + "=";
1523
+ var begin = dc.indexOf("; " + prefix);
1524
+ if (begin == -1) {
1525
+ begin = dc.indexOf(prefix);
1526
+ if (begin != 0) return null;
1527
+ } else
1528
+ { begin += 2; }
1529
+ var end = document.cookie.indexOf(";", begin);
1530
+ if (end == -1)
1531
+ { end = dc.length; }
1532
+ return unescape(dc.substring(begin + prefix.length, end));
1533
+ }
1534
+ '''.strip()
1535
+
1536
+ #: A javascript that is used to set the contents of two frames at
1537
+ #: once. This is used by the project table-of-contents frame to
1538
+ #: set both the module table-of-contents frame and the main frame
1539
+ #: when the user clicks on a module.
1540
+ SET_FRAME_JS = '''
1541
+ function setFrame(url1, url2) {
1542
+ parent.frames[1].location.href = url1;
1543
+ parent.frames[2].location.href = url2;
1544
+ }
1545
+ '''.strip()
1546
+
1547
+ #: A javascript that is used to hide private variables, unless
1548
+ #: either: (a) the cookie says not to; or (b) we appear to be
1549
+ #: linking to a private variable.
1550
+ HIDE_PRIVATE_JS = '''
1551
+ function checkCookie() {
1552
+ var cmd=getCookie("EpydocPrivate");
1553
+ if (cmd && cmd.substr(0,4)!="show" && location.href.indexOf("#_") < 0)
1554
+ toggle_private();
1555
+ }
1556
+ '''.strip()
1557
+
1558
+ TOGGLE_CALLGRAPH_JS = '''
1559
+ function toggleCallGraph(id) {
1560
+ var elt = document.getElementById(id);
1561
+ if (elt.style.display == "none")
1562
+ elt.style.display = "block";
1563
+ else
1564
+ elt.style.display = "none";
1565
+ }
1566
+ '''.strip()
1567
+
1568
+ SHOW_PRIVATE_JS = '''
1569
+ function show_private() {
1570
+ var elts = document.getElementsByTagName("a");
1571
+ for(var i=0; i<elts.length; i++) {
1572
+ if (elts[i].className == "privatelink") {
1573
+ cmd = elts[i].innerHTML;
1574
+ if (cmd && cmd.substr(0,4)=="show")
1575
+ toggle_private();
1576
+ }
1577
+ }
1578
+ }
1579
+ '''.strip()
1580
+
1581
+ GET_ANCHOR_JS = '''
1582
+ function get_anchor() {
1583
+ var href = location.href;
1584
+ var start = href.indexOf("#")+1;
1585
+ if ((start != 0) && (start != href.length))
1586
+ return href.substring(start, href.length);
1587
+ }
1588
+ '''.strip()
1589
+
1590
+ #: A javascript that is used to implement the auto-redirect page.
1591
+ #: When the user visits <redirect.html#dotted.name>, they will
1592
+ #: automatically get redirected to the page for the object with
1593
+ #: the given fully-qualified dotted name. E.g., for epydoc,
1594
+ #: <redirect.html#epydoc.apidoc.UNKNOWN> redirects the user to
1595
+ #: <epydoc.apidoc-module.html#UNKNOWN>.
1596
+ REDIRECT_URL_JS = '''
1597
+ function redirect_url(dottedName) {
1598
+ // Scan through each element of the "pages" list, and check
1599
+ // if "name" matches with any of them.
1600
+ for (var i=0; i<pages.length; i++) {
1601
+
1602
+ // Each page has the form "<pagename>-m" or "<pagename>-c";
1603
+ // extract the <pagename> portion & compare it to dottedName.
1604
+ var pagename = pages[i].substring(0, pages[i].length-2);
1605
+ if (pagename == dottedName.substring(0,pagename.length)) {
1606
+
1607
+ // We\'ve found a page that matches `dottedName`;
1608
+ // construct its URL, using leftover `dottedName`
1609
+ // content to form an anchor.
1610
+ var pagetype = pages[i].charAt(pages[i].length-1);
1611
+ var url = pagename + ((pagetype=="m")?"-module.html":
1612
+ "-class.html");
1613
+ if (dottedName.length > pagename.length)
1614
+ url += "#" + dottedName.substring(pagename.length+1,
1615
+ dottedName.length);
1616
+ return url;
1617
+ }
1618
+ }
1619
+ }
1620
+ '''.strip()
1621
+
1622
+
1623
+ #////////////////////////////////////////////////////////////
1624
+ #{ 2.10. Graphs
1625
+ #////////////////////////////////////////////////////////////
1626
+
1627
+ def render_graph(self, graph):
1628
+ if graph is None: return ''
1629
+ graph.caption = graph.title = None
1630
+ image_url = '%s.gif' % graph.uid
1631
+ image_file = os.path.join(self._directory, image_url)
1632
+ return graph.to_html(image_file, image_url)
1633
+
1634
+ RE_CALLGRAPH_ID = re.compile(r"""["'](.+-div)['"]""")
1635
+
1636
+ def render_callgraph(self, callgraph, token=""):
1637
+ """Render the HTML chunk of a callgraph.
1638
+
1639
+ If C{callgraph} is a string, use the L{_callgraph_cache} to return
1640
+ a pre-rendered HTML chunk. This mostly avoids to run C{dot} twice for
1641
+ the same callgraph. Else, run the graph and store its HTML output in
1642
+ the cache.
1643
+
1644
+ @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
1645
+ @type callgraph: L{DotGraph} or C{str}
1646
+ @param token: A string that can be used to make the C{<div>} id
1647
+ unambiguous, if the callgraph is used more than once in a page.
1648
+ @type token: C{str}
1649
+ @return: The HTML representation of the graph.
1650
+ @rtype: C{str}
1651
+ """
1652
+ if callgraph is None: return ""
1653
+
1654
+ if isinstance(callgraph, basestring):
1655
+ uid = callgraph
1656
+ rv = self._callgraph_cache.get(callgraph, "")
1657
+
1658
+ else:
1659
+ uid = callgraph.uid
1660
+ graph_html = self.render_graph(callgraph)
1661
+ if graph_html == '':
1662
+ rv = ""
1663
+ else:
1664
+ rv = ('<div style="display:none" id="%%s-div"><center>\n'
1665
+ '<table border="0" cellpadding="0" cellspacing="0">\n'
1666
+ ' <tr><td>%s</td></tr>\n'
1667
+ ' <tr><th>Call Graph</th></tr>\n'
1668
+ '</table><br />\n</center></div>\n' % graph_html)
1669
+
1670
+ # Store in the cache the complete HTML chunk without the
1671
+ # div id, which may be made unambiguous by the token
1672
+ self._callgraph_cache[uid] = rv
1673
+
1674
+ # Mangle with the graph
1675
+ if rv: rv = rv % (uid + token)
1676
+ return rv
1677
+
1678
+ def callgraph_link(self, callgraph, token=""):
1679
+ """Render the HTML chunk of a callgraph link.
1680
+
1681
+ The link can toggles the visibility of the callgraph rendered using
1682
+ L{render_callgraph} with matching parameters.
1683
+
1684
+ @param callgraph: The graph to render or its L{uid<DotGraph.uid>}.
1685
+ @type callgraph: L{DotGraph} or C{str}
1686
+ @param token: A string that can be used to make the C{<div>} id
1687
+ unambiguous, if the callgraph is used more than once in a page.
1688
+ @type token: C{str}
1689
+ @return: The HTML representation of the graph link.
1690
+ @rtype: C{str}
1691
+ """
1692
+ # Use class=codelink, to match style w/ the source code link.
1693
+ if callgraph is None: return ''
1694
+
1695
+ if isinstance(callgraph, basestring):
1696
+ uid = callgraph
1697
+ else:
1698
+ uid = callgraph.uid
1699
+
1700
+ return ('<br /><span class="codelink"><a href="javascript:void(0);" '
1701
+ 'onclick="toggleCallGraph(\'%s-div\');return false;">'
1702
+ 'call&nbsp;graph</a></span>&nbsp;' % (uid + token))
1703
+
1704
+ #////////////////////////////////////////////////////////////
1705
+ #{ 2.11. Images
1706
+ #////////////////////////////////////////////////////////////
1707
+
1708
+ IMAGES = {'crarr.png': # Carriage-return arrow, used for LINEWRAP.
1709
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAKCAMAAABlokWQAAAALHRFWHRD'
1710
+ 'cmVhdGlvbiBUaW1lAFR1\nZSAyMiBBdWcgMjAwNiAwMDo0MzoxMCAtMD'
1711
+ 'UwMGAMEFgAAAAHdElNRQfWCBYFASkQ033WAAAACXBI\nWXMAAB7CAAAe'
1712
+ 'wgFu0HU+AAAABGdBTUEAALGPC/xhBQAAAEVQTFRF////zcOw18/AgGY0'
1713
+ 'c1cg4dvQ\ninJEYEAAYkME3NXI6eTcloFYe2Asr5+AbE4Uh29A9fPwqp'
1714
+ 'l4ZEUI8O3onopk0Ma0lH5U1nfFdgAA\nAAF0Uk5TAEDm2GYAAABNSURB'
1715
+ 'VHjaY2BAAbzsvDAmK5oIlxgfioiwCAe7KJKIgKAQOzsLLwTwA0VY\n+d'
1716
+ 'iRAT8T0AxuIIMHqoaXCWIPGzsHJ6orGJiYWRjQASOcBQAocgMSPKMTIg'
1717
+ 'AAAABJRU5ErkJggg==\n',
1718
+ }
1719
+
1720
+ def write_images(self, directory):
1721
+ for (name, data) in self.IMAGES.items():
1722
+ f = open(os.path.join(directory, name), 'wb')
1723
+ f.write(base64.decodestring(data))
1724
+ f.close()
1725
+
1726
+ #////////////////////////////////////////////////////////////
1727
+ #{ 3.1. Page Header
1728
+ #////////////////////////////////////////////////////////////
1729
+
1730
+ write_header = compile_template(
1731
+ """
1732
+ write_header(self, out, title)
1733
+
1734
+ Generate HTML code for the standard page header, and write it
1735
+ to C{out}. C{title} is a string containing the page title.
1736
+ It should be appropriately escaped/encoded.
1737
+ """,
1738
+ # /------------------------- Template -------------------------\
1739
+ '''
1740
+ <?xml version="1.0" encoding="ascii"?>
1741
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1742
+ "DTD/xhtml1-transitional.dtd">
1743
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
1744
+ <head>
1745
+ <title>$title$</title>
1746
+ <link rel="stylesheet" href="epydoc.css" type="text/css" />
1747
+ <script type="text/javascript" src="epydoc.js"></script>
1748
+ </head>
1749
+
1750
+ <body bgcolor="white" text="black" link="blue" vlink="#204080"
1751
+ alink="#204080">
1752
+ ''')
1753
+ # \------------------------------------------------------------/
1754
+
1755
+ #////////////////////////////////////////////////////////////
1756
+ #{ 3.2. Page Footer
1757
+ #////////////////////////////////////////////////////////////
1758
+
1759
+ write_footer = compile_template(
1760
+ """
1761
+ write_footer(self, out, short=False)
1762
+
1763
+ Generate HTML code for the standard page footer, and write it
1764
+ to C{out}.
1765
+ """,
1766
+ # /------------------------- Template -------------------------\
1767
+ '''
1768
+ >>> if not short:
1769
+ <table border="0" cellpadding="0" cellspacing="0" width="100%%">
1770
+ <tr>
1771
+ <td align="left" class="footer">
1772
+ >>> if self._include_log:
1773
+ <a href="epydoc-log.html">Generated by Epydoc
1774
+ $epydoc.__version__$ on $time.asctime()$</a>
1775
+ >>> else:
1776
+ Generated by Epydoc $epydoc.__version__$ on $time.asctime()$
1777
+ >>> #endif
1778
+ </td>
1779
+ <td align="right" class="footer">
1780
+ <a target="mainFrame" href="http://epydoc.sourceforge.net"
1781
+ >http://epydoc.sourceforge.net</a>
1782
+ </td>
1783
+ </tr>
1784
+ </table>
1785
+ >>> #endif
1786
+
1787
+ <script type="text/javascript">
1788
+ <!--
1789
+ // Private objects are initially displayed (because if
1790
+ // javascript is turned off then we want them to be
1791
+ // visible); but by default, we want to hide them. So hide
1792
+ // them unless we have a cookie that says to show them.
1793
+ checkCookie();
1794
+ // -->
1795
+ </script>
1796
+ </body>
1797
+ </html>
1798
+ ''')
1799
+ # \------------------------------------------------------------/
1800
+
1801
+ #////////////////////////////////////////////////////////////
1802
+ #{ 3.3. Navigation Bar
1803
+ #////////////////////////////////////////////////////////////
1804
+
1805
+ write_navbar = compile_template(
1806
+ """
1807
+ write_navbar(self, out, context)
1808
+
1809
+ Generate HTML code for the navigation bar, and write it to
1810
+ C{out}. The navigation bar typically looks like::
1811
+
1812
+ [ Home Trees Index Help Project ]
1813
+
1814
+ @param context: A value indicating what page we're generating
1815
+ a navigation bar for. If we're generating an API
1816
+ documentation page for an object, then C{context} is a
1817
+ L{ValueDoc} containing the documentation for that object;
1818
+ otherwise, C{context} is a string name for the page. The
1819
+ following string names are recognized: C{'tree'}, C{'index'},
1820
+ and C{'help'}.
1821
+ """,
1822
+ # /------------------------- Template -------------------------\
1823
+ '''
1824
+ <!-- ==================== NAVIGATION BAR ==================== -->
1825
+ <table class="navbar" border="0" width="100%" cellpadding="0"
1826
+ bgcolor="#a0c0ff" cellspacing="0">
1827
+ <tr valign="middle">
1828
+ >>> if self._top_page_url not in (self._trees_url, "identifier-index.html", "help.html"):
1829
+ <!-- Home link -->
1830
+ >>> if (isinstance(context, ValueDoc) and
1831
+ >>> self._top_page_url == self.url(context.canonical_name)):
1832
+ <th bgcolor="#70b0f0" class="navbar-select"
1833
+ >&nbsp;&nbsp;&nbsp;Home&nbsp;&nbsp;&nbsp;</th>
1834
+ >>> else:
1835
+ <th>&nbsp;&nbsp;&nbsp;<a
1836
+ href="$self._top_page_url$">Home</a>&nbsp;&nbsp;&nbsp;</th>
1837
+ >>> #endif
1838
+
1839
+ <!-- Tree link -->
1840
+ >>> if context == "trees":
1841
+ <th bgcolor="#70b0f0" class="navbar-select"
1842
+ >&nbsp;&nbsp;&nbsp;Trees&nbsp;&nbsp;&nbsp;</th>
1843
+ >>> else:
1844
+ <th>&nbsp;&nbsp;&nbsp;<a
1845
+ href="$self._trees_url$">Trees</a>&nbsp;&nbsp;&nbsp;</th>
1846
+ >>> #endif
1847
+
1848
+ <!-- Index link -->
1849
+ >>> if context == "indices":
1850
+ <th bgcolor="#70b0f0" class="navbar-select"
1851
+ >&nbsp;&nbsp;&nbsp;Indices&nbsp;&nbsp;&nbsp;</th>
1852
+ >>> else:
1853
+ <th>&nbsp;&nbsp;&nbsp;<a
1854
+ href="identifier-index.html">Indices</a>&nbsp;&nbsp;&nbsp;</th>
1855
+ >>> #endif
1856
+
1857
+ <!-- Help link -->
1858
+ >>> if context == "help":
1859
+ <th bgcolor="#70b0f0" class="navbar-select"
1860
+ >&nbsp;&nbsp;&nbsp;Help&nbsp;&nbsp;&nbsp;</th>
1861
+ >>> else:
1862
+ <th>&nbsp;&nbsp;&nbsp;<a
1863
+ href="help.html">Help</a>&nbsp;&nbsp;&nbsp;</th>
1864
+ >>> #endif
1865
+
1866
+ >>> if self._prj_link:
1867
+ <!-- Project homepage -->
1868
+ <th class="navbar" align="right" width="100%">
1869
+ <table border="0" cellpadding="0" cellspacing="0">
1870
+ <tr><th class="navbar" align="center"
1871
+ >$self._prj_link.strip()$</th>
1872
+ </tr></table></th>
1873
+ >>> else:
1874
+ <th class="navbar" width="100%"></th>
1875
+ >>> #endif
1876
+ </tr>
1877
+ </table>
1878
+ ''')
1879
+ # \------------------------------------------------------------/
1880
+
1881
+ #////////////////////////////////////////////////////////////
1882
+ #{ 3.4. Breadcrumbs
1883
+ #////////////////////////////////////////////////////////////
1884
+
1885
+ write_breadcrumbs = compile_template(
1886
+ """
1887
+ write_breadcrumbs(self, out, context, context_url)
1888
+
1889
+ Generate HTML for the breadcrumbs line, and write it to
1890
+ C{out}. The breadcrumbs line is an invisible table with a
1891
+ list of pointers to the current object's ancestors on the
1892
+ left; and the show/hide private selector and the
1893
+ frames/noframes selector on the right.
1894
+
1895
+ @param context: The API documentation for the object whose
1896
+ breadcrumbs we should generate.
1897
+ @type context: L{ValueDoc}
1898
+ """,
1899
+ # /------------------------- Template -------------------------\
1900
+ '''
1901
+ <table width="100%" cellpadding="0" cellspacing="0">
1902
+ <tr valign="top">
1903
+ >>> if isinstance(context, APIDoc):
1904
+ <td width="100%">
1905
+ <span class="breadcrumbs">
1906
+ >>> crumbs = self.breadcrumbs(context)
1907
+ >>> for crumb in crumbs[:-1]:
1908
+ $crumb$ ::
1909
+ >>> #endfor
1910
+ $crumbs[-1]$
1911
+ </span>
1912
+ </td>
1913
+ >>> else:
1914
+ <td width="100%">&nbsp;</td>
1915
+ >>> #endif
1916
+ <td>
1917
+ <table cellpadding="0" cellspacing="0">
1918
+ <!-- hide/show private -->
1919
+ >>> if self._show_private:
1920
+ <tr><td align="right">$self.PRIVATE_LINK$</td></tr>
1921
+ >>> #endif
1922
+ >>> if self._frames_index:
1923
+ <tr><td align="right"><span class="options"
1924
+ >[<a href="frames.html" target="_top">frames</a
1925
+ >]&nbsp;|&nbsp;<a href="$context_url$"
1926
+ target="_top">no&nbsp;frames</a>]</span></td></tr>
1927
+ >>> #endif
1928
+ </table>
1929
+ </td>
1930
+ </tr>
1931
+ </table>
1932
+ ''')
1933
+ # \------------------------------------------------------------/
1934
+
1935
+ def breadcrumbs(self, doc):
1936
+ crumbs = [self._crumb(doc)]
1937
+
1938
+ # Generate the crumbs for uid's ancestors.
1939
+ while True:
1940
+ container = self.docindex.container(doc)
1941
+ assert doc != container, 'object is its own container?'
1942
+ if container is None:
1943
+ if doc.canonical_name is UNKNOWN:
1944
+ return ['??']+crumbs
1945
+ elif isinstance(doc, ModuleDoc):
1946
+ return ['Package&nbsp;%s' % ident
1947
+ for ident in doc.canonical_name[:-1]]+crumbs
1948
+ else:
1949
+ return list(doc.canonical_name)+crumbs
1950
+ else:
1951
+ label = self._crumb(container)
1952
+ name = container.canonical_name
1953
+ crumbs.insert(0, self.href(container, label)) # [xx] code=0??
1954
+ doc = container
1955
+
1956
+ def _crumb(self, doc):
1957
+ if (len(doc.canonical_name)==1 and
1958
+ doc.canonical_name[0].startswith('script-')):
1959
+ return 'Script&nbsp;%s' % doc.canonical_name[0][7:]
1960
+ return '%s&nbsp;%s' % (self.doc_kind(doc), doc.canonical_name[-1])
1961
+
1962
+ #////////////////////////////////////////////////////////////
1963
+ #{ 3.5. Summary Tables
1964
+ #////////////////////////////////////////////////////////////
1965
+
1966
+ def write_summary_table(self, out, heading, doc, value_type):
1967
+ """
1968
+ Generate HTML code for a summary table, and write it to
1969
+ C{out}. A summary table is a table that includes a one-row
1970
+ description for each variable (of a given type) in a module
1971
+ or class.
1972
+
1973
+ @param heading: The heading for the summary table; typically,
1974
+ this indicates what kind of value the table describes
1975
+ (e.g., functions or classes).
1976
+ @param doc: A L{ValueDoc} object containing the API
1977
+ documentation for the module or class whose variables
1978
+ we should summarize.
1979
+ @param value_type: A string indicating what type of value
1980
+ should be listed in this summary table. This value
1981
+ is passed on to C{doc}'s C{select_variables()} method.
1982
+ """
1983
+ # inh_var_groups is a dictionary used to hold "inheritance
1984
+ # pseudo-groups", which are created when inheritance is
1985
+ # 'grouped'. It maps each base to a list of vars inherited
1986
+ # from that base.
1987
+ grouped_inh_vars = {}
1988
+
1989
+ # Divide all public variables of the given type into groups.
1990
+ groups = [(plaintext_to_html(group_name),
1991
+ doc.select_variables(group=group_name, imported=False,
1992
+ value_type=value_type,
1993
+ public=self._public_filter))
1994
+ for group_name in doc.group_names()]
1995
+
1996
+ # Discard any empty groups; and return if they're all empty.
1997
+ groups = [(g,vars) for (g,vars) in groups if vars]
1998
+ if not groups: return
1999
+
2000
+ # Write a header
2001
+ self.write_table_header(out, "summary", heading)
2002
+
2003
+ # Write a section for each group.
2004
+ for name, var_docs in groups:
2005
+ self.write_summary_group(out, doc, name,
2006
+ var_docs, grouped_inh_vars)
2007
+
2008
+ # Write a section for each inheritance pseudo-group (used if
2009
+ # inheritance=='grouped')
2010
+ if grouped_inh_vars:
2011
+ for base in doc.mro():
2012
+ if base in grouped_inh_vars:
2013
+ hdr = 'Inherited from %s' % self.href(base, context=doc)
2014
+ tr_class = ''
2015
+ if len([v for v in grouped_inh_vars[base]
2016
+ if v.is_public]) == 0:
2017
+ tr_class = ' class="private"'
2018
+ self.write_group_header(out, hdr, tr_class)
2019
+ for var_doc in grouped_inh_vars[base]:
2020
+ self.write_summary_line(out, var_doc, doc)
2021
+
2022
+ # Write a footer for the table.
2023
+ out(self.TABLE_FOOTER)
2024
+
2025
+ def write_summary_group(self, out, doc, name, var_docs, grouped_inh_vars):
2026
+ # Split up the var_docs list, according to the way each var
2027
+ # should be displayed:
2028
+ # - listed_inh_vars -- for listed inherited variables.
2029
+ # - grouped_inh_vars -- for grouped inherited variables.
2030
+ # - normal_vars -- for all other variables.
2031
+ listed_inh_vars = {}
2032
+ normal_vars = []
2033
+ for var_doc in var_docs:
2034
+ if var_doc.container != doc:
2035
+ base = var_doc.container
2036
+ if not isinstance(base, ClassDoc):
2037
+ # This *should* never happen:
2038
+ log.warning("%s's container is not a class!" % var_doc)
2039
+ normal_vars.append(var_doc)
2040
+ elif (base not in self.class_set or
2041
+ self._inheritance == 'listed'):
2042
+ listed_inh_vars.setdefault(base,[]).append(var_doc)
2043
+ elif self._inheritance == 'grouped':
2044
+ grouped_inh_vars.setdefault(base,[]).append(var_doc)
2045
+ else:
2046
+ normal_vars.append(var_doc)
2047
+ else:
2048
+ normal_vars.append(var_doc)
2049
+
2050
+ # Write a header for the group.
2051
+ if name != '':
2052
+ tr_class = ''
2053
+ if len([v for v in var_docs if v.is_public]) == 0:
2054
+ tr_class = ' class="private"'
2055
+ self.write_group_header(out, name, tr_class)
2056
+
2057
+ # Write a line for each normal var:
2058
+ for var_doc in normal_vars:
2059
+ self.write_summary_line(out, var_doc, doc)
2060
+ # Write a subsection for inherited vars:
2061
+ if listed_inh_vars:
2062
+ self.write_inheritance_list(out, doc, listed_inh_vars)
2063
+
2064
+ def write_inheritance_list(self, out, doc, listed_inh_vars):
2065
+ out(' <tr>\n <td colspan="2" class="summary">\n')
2066
+ for base in doc.mro():
2067
+ if base not in listed_inh_vars: continue
2068
+ public_vars = [v for v in listed_inh_vars[base]
2069
+ if v.is_public]
2070
+ private_vars = [v for v in listed_inh_vars[base]
2071
+ if not v.is_public]
2072
+ if public_vars:
2073
+ out(' <p class="indent-wrapped-lines">'
2074
+ '<b>Inherited from <code>%s</code></b>:\n' %
2075
+ self.href(base, context=doc))
2076
+ self.write_var_list(out, public_vars)
2077
+ out(' </p>\n')
2078
+ if private_vars and self._show_private:
2079
+ out(' <div class="private">')
2080
+ out(' <p class="indent-wrapped-lines">'
2081
+ '<b>Inherited from <code>%s</code></b> (private):\n' %
2082
+ self.href(base, context=doc))
2083
+ self.write_var_list(out, private_vars)
2084
+ out(' </p></div>\n')
2085
+ out(' </td>\n </tr>\n')
2086
+
2087
+ def write_var_list(self, out, vardocs):
2088
+ out(' ')
2089
+ out(',\n '.join(['<code>%s</code>' % self.href(v,v.name)
2090
+ for v in vardocs])+'\n')
2091
+
2092
+ def write_summary_line(self, out, var_doc, container):
2093
+ """
2094
+ Generate HTML code for a single line of a summary table, and
2095
+ write it to C{out}. See L{write_summary_table} for more
2096
+ information.
2097
+
2098
+ @param var_doc: The API documentation for the variable that
2099
+ should be described by this line of the summary table.
2100
+ @param container: The API documentation for the class or
2101
+ module whose summary table we're writing.
2102
+ """
2103
+ pysrc_link = None
2104
+ callgraph = None
2105
+
2106
+ # If it's a private variable, then mark its <tr>.
2107
+ if var_doc.is_public: tr_class = ''
2108
+ else: tr_class = ' class="private"'
2109
+
2110
+ # Decide an anchor or a link is to be generated.
2111
+ link_name = self._redundant_details or var_doc.is_detailed()
2112
+ anchor = not link_name
2113
+
2114
+ # Construct the HTML code for the type (cell 1) & description
2115
+ # (cell 2).
2116
+ if isinstance(var_doc.value, RoutineDoc):
2117
+ typ = self.return_type(var_doc, indent=6)
2118
+ description = self.function_signature(var_doc, is_summary=True,
2119
+ link_name=link_name, anchor=anchor)
2120
+ pysrc_link = self.pysrc_link(var_doc.value)
2121
+
2122
+ # Perpare the call-graph, if requested
2123
+ if 'callgraph' in self._graph_types:
2124
+ linker = _HTMLDocstringLinker(self, var_doc.value)
2125
+ callgraph = call_graph([var_doc.value], self.docindex,
2126
+ linker, var_doc, add_callers=True,
2127
+ add_callees=True)
2128
+ if callgraph and callgraph.nodes:
2129
+ var_doc.value.callgraph_uid = callgraph.uid
2130
+ else:
2131
+ callgraph = None
2132
+ else:
2133
+ typ = self.type_descr(var_doc, indent=6)
2134
+ description = self.summary_name(var_doc,
2135
+ link_name=link_name, anchor=anchor)
2136
+ if isinstance(var_doc.value, GenericValueDoc):
2137
+ # The summary max length has been chosen setting
2138
+ # L{ValueDoc.SUMMARY_REPR_LINELEN} in the constructor
2139
+ max_len=self._variable_summary_linelen-3-len(var_doc.name)
2140
+ val_repr = var_doc.value.summary_pyval_repr(max_len)
2141
+ tooltip = self.variable_tooltip(var_doc)
2142
+ description += (' = <code%s>%s</code>' %
2143
+ (tooltip, val_repr.to_html(None)))
2144
+
2145
+ # Add the summary to the description (if there is one).
2146
+ summary = self.summary(var_doc, indent=6)
2147
+ if summary: description += '<br />\n %s' % summary
2148
+
2149
+ # If it's inherited, then add a note to the description.
2150
+ if var_doc.container != container and self._inheritance=="included":
2151
+ description += ("\n <em>(Inherited from " +
2152
+ self.href(var_doc.container) + ")</em>")
2153
+
2154
+ # Write the summary line.
2155
+ self._write_summary_line(out, typ, description, tr_class, pysrc_link,
2156
+ callgraph)
2157
+
2158
+ _write_summary_line = compile_template(
2159
+ "_write_summary_line(self, out, typ, description, tr_class, "
2160
+ "pysrc_link, callgraph)",
2161
+ # /------------------------- Template -------------------------\
2162
+ '''
2163
+ <tr$tr_class$>
2164
+ <td width="15%" align="right" valign="top" class="summary">
2165
+ <span class="summary-type">$typ or "&nbsp;"$</span>
2166
+ </td><td class="summary">
2167
+ >>> if pysrc_link is not None or callgraph is not None:
2168
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
2169
+ <tr>
2170
+ <td>$description$</td>
2171
+ <td align="right" valign="top">
2172
+ $pysrc_link$
2173
+ $self.callgraph_link(callgraph, token='-summary')$
2174
+ </td>
2175
+ </tr>
2176
+ </table>
2177
+ $self.render_callgraph(callgraph, token='-summary')$
2178
+ >>> #endif
2179
+ >>> if pysrc_link is None and callgraph is None:
2180
+ $description$
2181
+ >>> #endif
2182
+ </td>
2183
+ </tr>
2184
+ ''')
2185
+ # \------------------------------------------------------------/
2186
+
2187
+ #////////////////////////////////////////////////////////////
2188
+ #{ 3.6. Details Lists
2189
+ #////////////////////////////////////////////////////////////
2190
+
2191
+ def write_details_list(self, out, heading, doc, value_type):
2192
+ # Get a list of the VarDocs we should describe.
2193
+ if self._redundant_details:
2194
+ detailed = None
2195
+ else:
2196
+ detailed = True
2197
+ if isinstance(doc, ClassDoc):
2198
+ var_docs = doc.select_variables(value_type=value_type,
2199
+ imported=False, inherited=False,
2200
+ public=self._public_filter,
2201
+ detailed=detailed)
2202
+ else:
2203
+ var_docs = doc.select_variables(value_type=value_type,
2204
+ imported=False,
2205
+ public=self._public_filter,
2206
+ detailed=detailed)
2207
+ if not var_docs: return
2208
+
2209
+ # Write a header
2210
+ self.write_table_header(out, "details", heading)
2211
+ out(self.TABLE_FOOTER)
2212
+
2213
+ for var_doc in var_docs:
2214
+ self.write_details_entry(out, var_doc)
2215
+
2216
+ out('<br />\n')
2217
+
2218
+ def write_details_entry(self, out, var_doc):
2219
+ descr = self.descr(var_doc, indent=2) or ''
2220
+ if var_doc.is_public: div_class = ''
2221
+ else: div_class = ' class="private"'
2222
+
2223
+ # Functions
2224
+ if isinstance(var_doc.value, RoutineDoc):
2225
+ rtype = self.return_type(var_doc, indent=10)
2226
+ rdescr = self.return_descr(var_doc, indent=10)
2227
+ arg_descrs = []
2228
+ args = set()
2229
+ # Find the description for each arg. (Leave them in the
2230
+ # same order that they're listed in the docstring.)
2231
+ for (arg_names, arg_descr) in var_doc.value.arg_descrs:
2232
+ args.update(arg_names)
2233
+ lhs = ', '.join([self.arg_name_to_html(var_doc.value, n)
2234
+ for n in arg_names])
2235
+ rhs = self.docstring_to_html(arg_descr, var_doc.value, 10)
2236
+ arg_descrs.append( (lhs, rhs) )
2237
+ # Check for arguments for which we have @type but not @param;
2238
+ # and add them to the arg_descrs list.
2239
+ for arg in var_doc.value.arg_types:
2240
+ if arg not in args:
2241
+ argname = self.arg_name_to_html(var_doc.value, arg)
2242
+ arg_descrs.append( (argname,'') )
2243
+
2244
+ self.write_function_details_entry(out, var_doc, descr,
2245
+ var_doc.value.callgraph_uid,
2246
+ rtype, rdescr, arg_descrs,
2247
+ div_class)
2248
+
2249
+ # Properties
2250
+ elif isinstance(var_doc.value, PropertyDoc):
2251
+ prop_doc = var_doc.value
2252
+ accessors = [ (name,
2253
+ self.property_accessor_to_html(val_doc, prop_doc),
2254
+ self.summary(val_doc))
2255
+ for (name, val_doc) in
2256
+ [('Get', prop_doc.fget), ('Set', prop_doc.fset),
2257
+ ('Delete', prop_doc.fdel)]
2258
+ if val_doc not in (None, UNKNOWN)
2259
+ and val_doc.pyval is not None ]
2260
+
2261
+ self.write_property_details_entry(out, var_doc, descr,
2262
+ accessors, div_class)
2263
+
2264
+ # Variables
2265
+ else:
2266
+ self.write_variable_details_entry(out, var_doc, descr, div_class)
2267
+
2268
+ def labelled_list_item(self, lhs, rhs):
2269
+ # If the RHS starts with a paragraph, then move the
2270
+ # paragraph-start tag to the beginning of the lhs instead (so
2271
+ # there won't be a line break after the '-').
2272
+ m = re.match(r'^<p( [^>]+)?>', rhs)
2273
+ if m:
2274
+ lhs = m.group() + lhs
2275
+ rhs = rhs[m.end():]
2276
+
2277
+ if rhs:
2278
+ return '<li>%s - %s</li>' % (lhs, rhs)
2279
+ else:
2280
+ return '<li>%s</li>' % (lhs,)
2281
+
2282
+ def property_accessor_to_html(self, val_doc, context=None):
2283
+ if val_doc not in (None, UNKNOWN):
2284
+ if isinstance(val_doc, RoutineDoc):
2285
+ return self.function_signature(val_doc, is_summary=True,
2286
+ link_name=True, context=context)
2287
+ elif isinstance(val_doc, GenericValueDoc):
2288
+ return self.pprint_value(val_doc)
2289
+ else:
2290
+ return self.href(val_doc, context=context)
2291
+ else:
2292
+ return '??'
2293
+
2294
+ def arg_name_to_html(self, func_doc, arg_name):
2295
+ """
2296
+ A helper function used to format an argument name, for use in
2297
+ the argument description list under a routine's details entry.
2298
+ This just wraps strong & code tags around the arg name; and if
2299
+ the arg name is associated with a type, then adds it
2300
+ parenthetically after the name.
2301
+ """
2302
+ s = '<strong class="pname"><code>%s</code></strong>' % arg_name
2303
+ if arg_name in func_doc.arg_types:
2304
+ typ = func_doc.arg_types[arg_name]
2305
+ typ_html = self.docstring_to_html(typ, func_doc, 10)
2306
+ s += " (%s)" % typ_html
2307
+ return s
2308
+
2309
+ write_function_details_entry = compile_template(
2310
+ '''
2311
+ write_function_details_entry(self, out, var_doc, descr, callgraph, \
2312
+ rtype, rdescr, arg_descrs, div_class)
2313
+ ''',
2314
+ # /------------------------- Template -------------------------\
2315
+ '''
2316
+ >>> func_doc = var_doc.value
2317
+ <a name="$var_doc.name$"></a>
2318
+ <div$div_class$>
2319
+ >>> self.write_table_header(out, "details")
2320
+ <tr><td>
2321
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
2322
+ <tr valign="top"><td>
2323
+ <h3 class="epydoc">$self.function_signature(var_doc)$
2324
+ >>> if var_doc.name in self.SPECIAL_METHODS:
2325
+ <br /><em class="fname">($self.SPECIAL_METHODS[var_doc.name]$)</em>
2326
+ >>> #endif
2327
+ >>> if isinstance(func_doc, ClassMethodDoc):
2328
+ <br /><em class="fname">Class Method</em>
2329
+ >>> #endif
2330
+ >>> if isinstance(func_doc, StaticMethodDoc):
2331
+ <br /><em class="fname">Static Method</em>
2332
+ >>> #endif
2333
+ </h3>
2334
+ </td><td align="right" valign="top"
2335
+ >$self.pysrc_link(func_doc)$&nbsp;
2336
+ $self.callgraph_link(callgraph)$</td>
2337
+ </tr></table>
2338
+ $self.render_callgraph(callgraph)$
2339
+ $descr$
2340
+ <dl class="fields">
2341
+ >>> # === parameters ===
2342
+ >>> if arg_descrs:
2343
+ <dt>Parameters:</dt>
2344
+ <dd><ul class="nomargin-top">
2345
+ >>> for lhs, rhs in arg_descrs:
2346
+ $self.labelled_list_item(lhs, rhs)$
2347
+ >>> #endfor
2348
+ </ul></dd>
2349
+ >>> #endif
2350
+ >>> # === return type ===
2351
+ >>> if rdescr and rtype:
2352
+ <dt>Returns: $rtype$</dt>
2353
+ <dd>$rdescr$</dd>
2354
+ >>> elif rdescr:
2355
+ <dt>Returns:</dt>
2356
+ <dd>$rdescr$</dd>
2357
+ >>> elif rtype:
2358
+ <dt>Returns: $rtype$</dt>
2359
+ >>> #endif
2360
+ >>> # === decorators ===
2361
+ >>> if func_doc.decorators not in (None, UNKNOWN):
2362
+ >>> # (staticmethod & classmethod are already shown, above)
2363
+ >>> decos = filter(lambda deco:
2364
+ >>> not ((deco=="staticmethod" and
2365
+ >>> isinstance(func_doc, StaticMethodDoc)) or
2366
+ >>> (deco=="classmethod" and
2367
+ >>> isinstance(func_doc, ClassMethodDoc))),
2368
+ >>> func_doc.decorators)
2369
+ >>> else:
2370
+ >>> decos = None
2371
+ >>> #endif
2372
+ >>> if decos:
2373
+ <dt>Decorators:</dt>
2374
+ <dd><ul class="nomargin-top">
2375
+ >>> for deco in decos:
2376
+ <li><code>@$deco$</code></li>
2377
+ >>> #endfor
2378
+ </ul></dd>
2379
+ >>> #endif
2380
+ >>> # === exceptions ===
2381
+ >>> if func_doc.exception_descrs not in (None, UNKNOWN, (), []):
2382
+ <dt>Raises:</dt>
2383
+ <dd><ul class="nomargin-top">
2384
+ >>> for name, descr in func_doc.exception_descrs:
2385
+ >>> exc_name = self.docindex.find(name, func_doc)
2386
+ >>> if exc_name is not None:
2387
+ >>> name = self.href(exc_name, label=str(name))
2388
+ >>> #endif
2389
+ $self.labelled_list_item(
2390
+ "<code><strong class=\'fraise\'>" +
2391
+ str(name) + "</strong></code>",
2392
+ self.docstring_to_html(descr, func_doc, 8))$
2393
+ >>> #endfor
2394
+ </ul></dd>
2395
+ >>> #endif
2396
+ >>> # === overrides ===
2397
+ >>> if var_doc.overrides not in (None, UNKNOWN):
2398
+ <dt>Overrides:
2399
+ >>> # Avoid passing GenericValueDoc to href()
2400
+ >>> if isinstance(var_doc.overrides.value, RoutineDoc):
2401
+ $self.href(var_doc.overrides.value, context=var_doc)$
2402
+ >>> else:
2403
+ >>> # In this case, a less interesting label is generated.
2404
+ $self.href(var_doc.overrides, context=var_doc)$
2405
+ >>> #endif
2406
+ >>> if (func_doc.docstring in (None, UNKNOWN) and
2407
+ >>> var_doc.overrides.value.docstring not in (None, UNKNOWN)):
2408
+ <dd><em class="note">(inherited documentation)</em></dd>
2409
+ >>> #endif
2410
+ </dt>
2411
+ >>> #endif
2412
+ </dl>
2413
+ >>> # === metadata ===
2414
+ >>> self.write_standard_fields(out, func_doc)
2415
+ </td></tr></table>
2416
+ </div>
2417
+ ''')
2418
+ # \------------------------------------------------------------/
2419
+
2420
+ # Names for the __special__ methods.
2421
+ SPECIAL_METHODS ={
2422
+ '__init__': 'Constructor',
2423
+ '__del__': 'Destructor',
2424
+ '__add__': 'Addition operator',
2425
+ '__sub__': 'Subtraction operator',
2426
+ '__and__': 'And operator',
2427
+ '__or__': 'Or operator',
2428
+ '__xor__': 'Exclusive-Or operator',
2429
+ '__repr__': 'Representation operator',
2430
+ '__call__': 'Call operator',
2431
+ '__getattr__': 'Qualification operator',
2432
+ '__getitem__': 'Indexing operator',
2433
+ '__setitem__': 'Index assignment operator',
2434
+ '__delitem__': 'Index deletion operator',
2435
+ '__delslice__': 'Slice deletion operator',
2436
+ '__setslice__': 'Slice assignment operator',
2437
+ '__getslice__': 'Slicling operator',
2438
+ '__len__': 'Length operator',
2439
+ '__cmp__': 'Comparison operator',
2440
+ '__eq__': 'Equality operator',
2441
+ '__in__': 'Containership operator',
2442
+ '__gt__': 'Greater-than operator',
2443
+ '__lt__': 'Less-than operator',
2444
+ '__ge__': 'Greater-than-or-equals operator',
2445
+ '__le__': 'Less-than-or-equals operator',
2446
+ '__radd__': 'Right-side addition operator',
2447
+ '__hash__': 'Hashing function',
2448
+ '__contains__': 'In operator',
2449
+ '__nonzero__': 'Boolean test operator',
2450
+ '__str__': 'Informal representation operator',
2451
+ }
2452
+
2453
+ write_property_details_entry = compile_template(
2454
+ '''
2455
+ write_property_details_entry(self, out, var_doc, descr, \
2456
+ accessors, div_class)
2457
+ ''',
2458
+ # /------------------------- Template -------------------------\
2459
+ '''
2460
+ >>> prop_doc = var_doc.value
2461
+ <a name="$var_doc.name$"></a>
2462
+ <div$div_class$>
2463
+ >>> self.write_table_header(out, "details")
2464
+ <tr><td>
2465
+ <h3 class="epydoc">$var_doc.name$</h3>
2466
+ $descr$
2467
+ <dl class="fields">
2468
+ >>> for (name, val, summary) in accessors:
2469
+ <dt>$name$ Method:</dt>
2470
+ <dd class="value">$val$
2471
+ >>> if summary:
2472
+ - $summary$
2473
+ >>> #endif
2474
+ </dd>
2475
+ >>> #endfor
2476
+ >>> if prop_doc.type_descr not in (None, UNKNOWN):
2477
+ <dt>Type:</dt>
2478
+ <dd>$self.type_descr(var_doc, indent=6)$</dd>
2479
+ >>> #endif
2480
+ </dl>
2481
+ >>> self.write_standard_fields(out, prop_doc)
2482
+ </td></tr></table>
2483
+ </div>
2484
+ ''')
2485
+ # \------------------------------------------------------------/
2486
+
2487
+ write_variable_details_entry = compile_template(
2488
+ '''
2489
+ write_variable_details_entry(self, out, var_doc, descr, div_class)
2490
+ ''',
2491
+ # /------------------------- Template -------------------------\
2492
+ '''
2493
+ <a name="$var_doc.name$"></a>
2494
+ <div$div_class$>
2495
+ >>> self.write_table_header(out, "details")
2496
+ <tr><td>
2497
+ <h3 class="epydoc">$var_doc.name$</h3>
2498
+ $descr$
2499
+ <dl class="fields">
2500
+ >>> if var_doc.type_descr not in (None, UNKNOWN):
2501
+ <dt>Type:</dt>
2502
+ <dd>$self.type_descr(var_doc, indent=6)$</dd>
2503
+ >>> #endif
2504
+ </dl>
2505
+ >>> self.write_standard_fields(out, var_doc)
2506
+ >>> if var_doc.value is not UNKNOWN:
2507
+ <dl class="fields">
2508
+ <dt>Value:</dt>
2509
+ <dd>$self.pprint_value(var_doc.value)$</dd>
2510
+ </dl>
2511
+ >>> #endif
2512
+ </td></tr></table>
2513
+ </div>
2514
+ ''')
2515
+ # \------------------------------------------------------------/
2516
+
2517
+ def variable_tooltip(self, var_doc):
2518
+ if var_doc.value in (None, UNKNOWN):
2519
+ return ''
2520
+ s = var_doc.value.pyval_repr().to_plaintext(None)
2521
+ if len(s) > self._variable_tooltip_linelen:
2522
+ s = s[:self._variable_tooltip_linelen-3]+'...'
2523
+ return ' title="%s"' % plaintext_to_html(s)
2524
+
2525
+ def pprint_value(self, val_doc):
2526
+ if val_doc is UNKNOWN:
2527
+ return '??'
2528
+ elif isinstance(val_doc, GenericValueDoc):
2529
+ return ('<table><tr><td><pre class="variable">\n' +
2530
+ val_doc.pyval_repr().to_html(None) +
2531
+ '\n</pre></td></tr></table>\n')
2532
+ else:
2533
+ return self.href(val_doc)
2534
+
2535
+ #////////////////////////////////////////////////////////////
2536
+ #{ Base Tree
2537
+ #////////////////////////////////////////////////////////////
2538
+
2539
+ def base_tree(self, doc, width=None, postfix='', context=None):
2540
+ """
2541
+ @return: The HTML code for a class's base tree. The tree is
2542
+ drawn 'upside-down' and right justified, to allow for
2543
+ multiple inheritance.
2544
+ @rtype: C{string}
2545
+ """
2546
+ if context is None:
2547
+ context = doc.defining_module
2548
+ if width == None: width = self.find_tree_width(doc, context)
2549
+ if isinstance(doc, ClassDoc) and doc.bases != UNKNOWN:
2550
+ bases = doc.bases
2551
+ else:
2552
+ bases = []
2553
+
2554
+ if postfix == '':
2555
+ # [XX] use var name instead of canonical name?
2556
+ s = (' '*(width-2) + '<strong class="uidshort">'+
2557
+ self.contextual_label(doc, context)+'</strong>\n')
2558
+ else: s = ''
2559
+ for i in range(len(bases)-1, -1, -1):
2560
+ base = bases[i]
2561
+ label = self.contextual_label(base, context)
2562
+ s = (' '*(width-4-len(label)) + self.href(base, label)
2563
+ +' --+'+postfix+'\n' +
2564
+ ' '*(width-4) +
2565
+ ' |'+postfix+'\n' +
2566
+ s)
2567
+ if i != 0:
2568
+ s = (self.base_tree(base, width-4, ' |'+postfix, context)+s)
2569
+ else:
2570
+ s = (self.base_tree(base, width-4, ' '+postfix, context)+s)
2571
+ return s
2572
+
2573
+ def find_tree_width(self, doc, context):
2574
+ """
2575
+ Helper function for L{base_tree}.
2576
+ @return: The width of a base tree, when drawn
2577
+ right-justified. This is used by L{base_tree} to
2578
+ determine how far to indent lines of the base tree.
2579
+ @rtype: C{int}
2580
+ """
2581
+ if not isinstance(doc, ClassDoc): return 2
2582
+ if doc.bases == UNKNOWN: return 2
2583
+ width = 2
2584
+ for base in doc.bases:
2585
+ width = max(width, len(self.contextual_label(base, context))+4,
2586
+ self.find_tree_width(base, context)+4)
2587
+ return width
2588
+
2589
+ def contextual_label(self, doc, context):
2590
+ """
2591
+ Return the label for C{doc} to be shown in C{context}.
2592
+ """
2593
+ if doc.canonical_name is None:
2594
+ if doc.parse_repr is not None:
2595
+ return doc.parse_repr
2596
+ else:
2597
+ return '??'
2598
+ else:
2599
+ if context is UNKNOWN:
2600
+ return str(doc.canonical_name)
2601
+ else:
2602
+ context_name = context.canonical_name
2603
+ return str(doc.canonical_name.contextualize(context_name))
2604
+
2605
+ #////////////////////////////////////////////////////////////
2606
+ #{ Function Signatures
2607
+ #////////////////////////////////////////////////////////////
2608
+
2609
+ def function_signature(self, api_doc, is_summary=False,
2610
+ link_name=False, anchor=False, context=None):
2611
+ """Render a function signature in HTML.
2612
+
2613
+ @param api_doc: The object whose name is to be rendered. If a
2614
+ C{VariableDoc}, its C{value} should be a C{RoutineDoc}
2615
+ @type api_doc: L{VariableDoc} or L{RoutineDoc}
2616
+ @param is_summary: True if the fuction is to be rendered in the summary.
2617
+ type css_class: C{bool}
2618
+ @param link_name: If True, the name is a link to the object anchor.
2619
+ @type link_name: C{bool}
2620
+ @param anchor: If True, the name is the object anchor.
2621
+ @type anchor: C{bool}
2622
+ @param context: If set, represent the function name from this context.
2623
+ Only useful when C{api_doc} is a L{RoutineDoc}.
2624
+ @type context: L{DottedName}
2625
+
2626
+ @return: The HTML code for the object.
2627
+ @rtype: C{str}
2628
+ """
2629
+ if is_summary: css_class = 'summary-sig'
2630
+ else: css_class = 'sig'
2631
+
2632
+ # [XX] clean this up!
2633
+ if isinstance(api_doc, VariableDoc):
2634
+ func_doc = api_doc.value
2635
+ # This should never happen, but just in case:
2636
+ if api_doc.value in (None, UNKNOWN):
2637
+ return (('<span class="%s"><span class="%s-name">%s'+
2638
+ '</span>(...)</span>') %
2639
+ (css_class, css_class, api_doc.name))
2640
+ # Get the function's name.
2641
+ name = self.summary_name(api_doc, css_class=css_class+'-name',
2642
+ link_name=link_name, anchor=anchor)
2643
+ else:
2644
+ func_doc = api_doc
2645
+ name = self.href(api_doc, css_class=css_class+'-name',
2646
+ context=context)
2647
+
2648
+ if func_doc.posargs == UNKNOWN:
2649
+ args = ['...']
2650
+ else:
2651
+ args = [self.func_arg(n, d, css_class) for (n, d)
2652
+ in zip(func_doc.posargs, func_doc.posarg_defaults)]
2653
+ if func_doc.vararg not in (None, UNKNOWN):
2654
+ if func_doc.vararg == '...':
2655
+ args.append('<span class="%s-arg">...</span>' % css_class)
2656
+ else:
2657
+ args.append('<span class="%s-arg">*%s</span>' %
2658
+ (css_class, func_doc.vararg))
2659
+ if func_doc.kwarg not in (None, UNKNOWN):
2660
+ args.append('<span class="%s-arg">**%s</span>' %
2661
+ (css_class, func_doc.kwarg))
2662
+
2663
+ return ('<span class="%s">%s(%s)</span>' %
2664
+ (css_class, name, ',\n '.join(args)))
2665
+
2666
+ def summary_name(self, api_doc, css_class='summary-name',
2667
+ link_name=False, anchor=False):
2668
+ """Render an object name in HTML.
2669
+
2670
+ @param api_doc: The object whose name is to be rendered
2671
+ @type api_doc: L{APIDoc}
2672
+ @param css_class: The CSS class to assign to the rendered name
2673
+ type css_class: C{str}
2674
+ @param link_name: If True, the name is a link to the object anchor.
2675
+ @type link_name: C{bool}
2676
+ @param anchor: If True, the name is the object anchor.
2677
+ @type anchor: C{bool}
2678
+
2679
+ @return: The HTML code for the object.
2680
+ @rtype: C{str}
2681
+ """
2682
+ if anchor:
2683
+ rv = '<a name="%s"></a>' % api_doc.name
2684
+ else:
2685
+ rv = ''
2686
+
2687
+ if link_name:
2688
+ rv += self.href(api_doc, css_class=css_class)
2689
+ else:
2690
+ rv += '<span class="%s">%s</span>' % (css_class, api_doc.name)
2691
+
2692
+ return rv
2693
+
2694
+ # [xx] tuple args???
2695
+ def func_arg(self, name, default, css_class):
2696
+ name = self._arg_name(name)
2697
+ s = '<span class="%s-arg">%s</span>' % (css_class, name)
2698
+ if default is not None:
2699
+ s += ('=<span class="%s-default">%s</span>' %
2700
+ (css_class, default.summary_pyval_repr().to_html(None)))
2701
+ return s
2702
+
2703
+ def _arg_name(self, arg):
2704
+ if isinstance(arg, basestring):
2705
+ return arg
2706
+ elif len(arg) == 1:
2707
+ return '(%s,)' % self._arg_name(arg[0])
2708
+ else:
2709
+ return '(%s)' % (', '.join([self._arg_name(a) for a in arg]))
2710
+
2711
+
2712
+
2713
+
2714
+ #////////////////////////////////////////////////////////////
2715
+ #{ Import Lists
2716
+ #////////////////////////////////////////////////////////////
2717
+
2718
+ def write_imports(self, out, doc):
2719
+ assert isinstance(doc, NamespaceDoc)
2720
+ imports = doc.select_variables(imported=True,
2721
+ public=self._public_filter)
2722
+ if not imports: return
2723
+
2724
+ out('<p class="indent-wrapped-lines">')
2725
+ out('<b>Imports:</b>\n ')
2726
+ out(',\n '.join([self._import(v, doc) for v in imports]))
2727
+ out('\n</p><br />\n')
2728
+
2729
+ def _import(self, var_doc, context):
2730
+ if var_doc.imported_from not in (None, UNKNOWN):
2731
+ return self.href(var_doc.imported_from,
2732
+ var_doc.name, context=context,
2733
+ tooltip='%s' % var_doc.imported_from)
2734
+ elif (var_doc.value not in (None, UNKNOWN) and not
2735
+ isinstance(var_doc.value, GenericValueDoc)):
2736
+ return self.href(var_doc.value,
2737
+ var_doc.name, context=context,
2738
+ tooltip='%s' % var_doc.value.canonical_name)
2739
+ else:
2740
+ return plaintext_to_html(var_doc.name)
2741
+
2742
+ #////////////////////////////////////////////////////////////
2743
+ #{ Function Attributes
2744
+ #////////////////////////////////////////////////////////////
2745
+
2746
+ #////////////////////////////////////////////////////////////
2747
+ #{ Module Trees
2748
+ #////////////////////////////////////////////////////////////
2749
+
2750
+ def write_module_list(self, out, doc):
2751
+ if len(doc.submodules) == 0: return
2752
+ self.write_table_header(out, "summary", "Submodules")
2753
+
2754
+ for group_name in doc.group_names():
2755
+ if not doc.submodule_groups[group_name]: continue
2756
+ if group_name:
2757
+ self.write_group_header(out, group_name)
2758
+ out(' <tr><td class="summary">\n'
2759
+ ' <ul class="nomargin">\n')
2760
+ for submodule in doc.submodule_groups[group_name]:
2761
+ self.write_module_tree_item(out, submodule, package=doc)
2762
+ out(' </ul></td></tr>\n')
2763
+
2764
+ out(self.TABLE_FOOTER+'\n<br />\n')
2765
+
2766
+ def write_module_tree_item(self, out, doc, package=None):
2767
+ # If it's a private variable, then mark its <li>.
2768
+ var = package and package.variables.get(doc.canonical_name[-1])
2769
+ priv = ((var is not None and var.is_public is False) or
2770
+ (var is None and doc.canonical_name[-1].startswith('_')))
2771
+ out(' <li%s> <strong class="uidlink">%s</strong>'
2772
+ % (priv and ' class="private"' or '', self.href(doc)))
2773
+ if doc.summary not in (None, UNKNOWN):
2774
+ out(': <em class="summary">'+
2775
+ self.description(doc.summary, doc, 8)+'</em>')
2776
+ if doc.submodules != UNKNOWN and doc.submodules:
2777
+ if priv: out('\n <ul class="private">\n')
2778
+ else: out('\n <ul>\n')
2779
+ for submodule in doc.submodules:
2780
+ self.write_module_tree_item(out, submodule, package=doc)
2781
+ out(' </ul>\n')
2782
+ out(' </li>\n')
2783
+
2784
+ #////////////////////////////////////////////////////////////
2785
+ #{ Class trees
2786
+ #////////////////////////////////////////////////////////////
2787
+
2788
+ write_class_tree_item = compile_template(
2789
+ '''
2790
+ write_class_tree_item(self, out, doc, class_set)
2791
+ ''',
2792
+ # /------------------------- Template -------------------------\
2793
+ '''
2794
+ >>> if doc.summary in (None, UNKNOWN):
2795
+ <li> <strong class="uidlink">$self.href(doc)$</strong>
2796
+ >>> else:
2797
+ <li> <strong class="uidlink">$self.href(doc)$</strong>:
2798
+ <em class="summary">$self.description(doc.summary, doc, 8)$</em>
2799
+ >>> # endif
2800
+ >>> if doc.subclasses:
2801
+ <ul>
2802
+ >>> for subclass in sorted(set(doc.subclasses), key=lambda c:c.canonical_name[-1]):
2803
+ >>> if subclass in class_set:
2804
+ >>> self.write_class_tree_item(out, subclass, class_set)
2805
+ >>> #endif
2806
+ >>> #endfor
2807
+ </ul>
2808
+ >>> #endif
2809
+ </li>
2810
+ ''')
2811
+ # \------------------------------------------------------------/
2812
+
2813
+ #////////////////////////////////////////////////////////////
2814
+ #{ Standard Fields
2815
+ #////////////////////////////////////////////////////////////
2816
+
2817
+ def write_standard_fields(self, out, doc):
2818
+ """
2819
+ Write HTML code containing descriptions of any standard markup
2820
+ fields that are defined by the given L{APIDoc} object (such as
2821
+ C{@author} and C{@todo} fields).
2822
+
2823
+ @param doc: The L{APIDoc} object containing the API documentation
2824
+ for the object whose standard markup fields should be
2825
+ described.
2826
+ """
2827
+ fields = []
2828
+ field_values = {}
2829
+
2830
+ for (field, arg, descr) in doc.metadata:
2831
+ if field not in field_values:
2832
+ fields.append(field)
2833
+ if field.takes_arg:
2834
+ subfields = field_values.setdefault(field,{})
2835
+ subfields.setdefault(arg,[]).append(descr)
2836
+ else:
2837
+ field_values.setdefault(field,[]).append(descr)
2838
+
2839
+ if not fields: return
2840
+
2841
+ out('<div class="fields">')
2842
+ for field in fields:
2843
+ if field.takes_arg:
2844
+ for arg, descrs in field_values[field].items():
2845
+ self.write_standard_field(out, doc, field, descrs, arg)
2846
+
2847
+ else:
2848
+ self.write_standard_field(out, doc, field, field_values[field])
2849
+
2850
+ out('</div>')
2851
+
2852
+ write_standard_field = compile_template(
2853
+ """
2854
+ write_standard_field(self, out, doc, field, descrs, arg='')
2855
+
2856
+ """,
2857
+ # /------------------------- Template -------------------------\
2858
+ '''
2859
+ >>> if arg: arglabel = " (%s)" % arg
2860
+ >>> else: arglabel = ""
2861
+ >>> if len(descrs) == 1:
2862
+ <p><strong>$field.singular+arglabel$:</strong>
2863
+ $self.description(descrs[0], doc, 8)$
2864
+ </p>
2865
+ >>> elif field.short:
2866
+ <dl><dt>$field.plural+arglabel$:</dt>
2867
+ <dd>
2868
+ >>> for descr in descrs[:-1]:
2869
+ $self.description(descr, doc, 10)$,
2870
+ >>> # end for
2871
+ $self.description(descrs[-1], doc, 10)$
2872
+ </dd>
2873
+ </dl>
2874
+ >>> else:
2875
+ <strong>$field.plural+arglabel$:</strong>
2876
+ <ul class="nomargin-top">
2877
+ >>> for descr in descrs:
2878
+ <li>
2879
+ $self.description(descr, doc, 8)$
2880
+ </li>
2881
+ >>> # end for
2882
+ </ul>
2883
+ >>> # end else
2884
+ >>> # end for
2885
+ ''')
2886
+ # \------------------------------------------------------------/
2887
+
2888
+ #////////////////////////////////////////////////////////////
2889
+ #{ Index generation
2890
+ #////////////////////////////////////////////////////////////
2891
+
2892
+ #: A list of metadata indices that should be generated. Each
2893
+ #: entry in this list is a tuple C{(tag, label, short_label)},
2894
+ #: where C{tag} is the cannonical tag of a metadata field;
2895
+ #: C{label} is a label for the index page; and C{short_label}
2896
+ #: is a shorter label, used in the index selector.
2897
+ METADATA_INDICES = [('bug', 'Bug List', 'Bugs'),
2898
+ ('todo', 'To Do List', 'To Do'),
2899
+ ('change', 'Change Log', 'Changes'),
2900
+ ('deprecated', 'Deprecation List', 'Deprecations'),
2901
+ ('since', 'Introductions List', 'Introductions'),
2902
+ ]
2903
+
2904
+ def build_identifier_index(self):
2905
+ items = []
2906
+ for doc in self.indexed_docs:
2907
+ name = plaintext_to_html(doc.canonical_name[-1])
2908
+ if isinstance(doc, RoutineDoc): name += '()'
2909
+ url = self.url(doc)
2910
+ if not url: continue
2911
+ container = self.docindex.container(doc)
2912
+ items.append( (name, url, container) )
2913
+ return sorted(items, key=lambda v:v[0].lower())
2914
+
2915
+ def _group_by_letter(self, items):
2916
+ """Preserves sort order of the input."""
2917
+ index = {}
2918
+ for item in items:
2919
+ first_letter = item[0][0].upper()
2920
+ if not ("A" <= first_letter <= "Z"):
2921
+ first_letter = '_'
2922
+ index.setdefault(first_letter, []).append(item)
2923
+ return index
2924
+
2925
+ def build_term_index(self):
2926
+ items = []
2927
+ for doc in self.indexed_docs:
2928
+ url = self.url(doc)
2929
+ items += self._terms_from_docstring(url, doc, doc.descr)
2930
+ for (field, arg, descr) in doc.metadata:
2931
+ items += self._terms_from_docstring(url, doc, descr)
2932
+ if hasattr(doc, 'type_descr'):
2933
+ items += self._terms_from_docstring(url, doc,
2934
+ doc.type_descr)
2935
+ if hasattr(doc, 'return_descr'):
2936
+ items += self._terms_from_docstring(url, doc,
2937
+ doc.return_descr)
2938
+ if hasattr(doc, 'return_type'):
2939
+ items += self._terms_from_docstring(url, doc,
2940
+ doc.return_type)
2941
+ return sorted(items, key=lambda v:v[0].lower())
2942
+
2943
+ def _terms_from_docstring(self, base_url, container, parsed_docstring):
2944
+ if parsed_docstring in (None, UNKNOWN): return []
2945
+ terms = []
2946
+ # Strip any existing anchor off:
2947
+ base_url = re.sub('#.*', '', '%s' % (base_url,))
2948
+ for term in parsed_docstring.index_terms():
2949
+ anchor = self._term_index_to_anchor(term)
2950
+ url = '%s#%s' % (base_url, anchor)
2951
+ terms.append( (term.to_plaintext(None), url, container) )
2952
+ return terms
2953
+
2954
+ def build_metadata_index(self, field_name):
2955
+ # Build the index.
2956
+ index = {}
2957
+ for doc in self.indexed_docs:
2958
+ if (not self._show_private and
2959
+ self._doc_or_ancestor_is_private(doc)):
2960
+ continue
2961
+ descrs = {}
2962
+ if doc.metadata is not UNKNOWN:
2963
+ for (field, arg, descr) in doc.metadata:
2964
+ if field.tags[0] == field_name:
2965
+ descrs.setdefault(arg, []).append(descr)
2966
+ for (arg, descr_list) in descrs.iteritems():
2967
+ index.setdefault(arg, []).append( (doc, descr_list) )
2968
+ return index
2969
+
2970
+ def _term_index_to_anchor(self, term):
2971
+ """
2972
+ Given the name of an inline index item, construct a URI anchor.
2973
+ These anchors are used to create links from the index page to each
2974
+ index item.
2975
+ """
2976
+ # Include "-" so we don't accidentally collide with the name
2977
+ # of a python identifier.
2978
+ s = re.sub(r'\s\s+', '-', term.to_plaintext(None))
2979
+ return "index-"+re.sub("[^a-zA-Z0-9]", "_", s)
2980
+
2981
+ #////////////////////////////////////////////////////////////
2982
+ #{ Redirect page
2983
+ #////////////////////////////////////////////////////////////
2984
+
2985
+ def write_redirect_page(self, out):
2986
+ """
2987
+ Build the auto-redirect page, which translates dotted names to
2988
+ URLs using javascript. When the user visits
2989
+ <redirect.html#dotted.name>, they will automatically get
2990
+ redirected to the page for the object with the given
2991
+ fully-qualified dotted name. E.g., for epydoc,
2992
+ <redirect.html#epydoc.apidoc.UNKNOWN> redirects the user to
2993
+ <epydoc.apidoc-module.html#UNKNOWN>.
2994
+ """
2995
+ # Construct a list of all the module & class pages that we're
2996
+ # documenting. The redirect_url javascript will scan through
2997
+ # this list, looking for a page name that matches the
2998
+ # requested dotted name.
2999
+ pages = (['%s-m' % val_doc.canonical_name
3000
+ for val_doc in self.module_list] +
3001
+ ['%s-c' % val_doc.canonical_name
3002
+ for val_doc in self.class_list])
3003
+ # Sort the pages from longest to shortest. This ensures that
3004
+ # we find e.g. "x.y.z" in the list before "x.y".
3005
+ pages = sorted(pages, key=lambda p:-len(p))
3006
+
3007
+ # Write the redirect page.
3008
+ self._write_redirect_page(out, pages)
3009
+
3010
+ _write_redirect_page = compile_template(
3011
+ '''
3012
+ _write_redirect_page(self, out, pages)
3013
+ ''',
3014
+ # /------------------------- Template -------------------------\
3015
+ '''
3016
+ <html><head><title>Epydoc Redirect Page</title>
3017
+ <meta http-equiv="cache-control" content="no-cache" />
3018
+ <meta http-equiv="expires" content="0" />
3019
+ <meta http-equiv="pragma" content="no-cache" />
3020
+ <script type="text/javascript" src="epydoc.js"></script>
3021
+ </head>
3022
+ <body>
3023
+ <script type="text/javascript">
3024
+ <!--
3025
+ var pages = $"[%s]" % ", ".join(['"%s"' % v for v in pages])$;
3026
+ var dottedName = get_anchor();
3027
+ if (dottedName) {
3028
+ var target = redirect_url(dottedName);
3029
+ if (target) window.location.replace(target);
3030
+ }
3031
+ // -->
3032
+ </script>
3033
+
3034
+ <h3>Epydoc Auto-redirect page</h3>
3035
+
3036
+ <p>When javascript is enabled, this page will redirect URLs of
3037
+ the form <tt>redirect.html#<i>dotted.name</i></tt> to the
3038
+ documentation for the object with the given fully-qualified
3039
+ dotted name.</p>
3040
+ <p><a id="message"> &nbsp; </a></p>
3041
+
3042
+ <script type="text/javascript">
3043
+ <!--
3044
+ if (dottedName) {
3045
+ var msg = document.getElementById("message");
3046
+ msg.innerHTML = "No documentation found for <tt>"+
3047
+ dottedName+"</tt>";
3048
+ }
3049
+ // -->
3050
+ </script>
3051
+
3052
+ </body>
3053
+ </html>
3054
+ ''')
3055
+ # \------------------------------------------------------------/
3056
+
3057
+ #////////////////////////////////////////////////////////////
3058
+ #{ URLs list
3059
+ #////////////////////////////////////////////////////////////
3060
+
3061
+ def write_api_list(self, out):
3062
+ """
3063
+ Write a list of mapping name->url for all the documented objects.
3064
+ """
3065
+ # Construct a list of all the module & class pages that we're
3066
+ # documenting. The redirect_url javascript will scan through
3067
+ # this list, looking for a page name that matches the
3068
+ # requested dotted name.
3069
+ skip = (ModuleDoc, ClassDoc, type(UNKNOWN))
3070
+ for val_doc in self.module_list:
3071
+ self.write_url_record(out, val_doc)
3072
+ for var in val_doc.variables.itervalues():
3073
+ if not isinstance(var.value, skip):
3074
+ self.write_url_record(out, var)
3075
+
3076
+ for val_doc in self.class_list:
3077
+ self.write_url_record(out, val_doc)
3078
+ for var in val_doc.variables.itervalues():
3079
+ self.write_url_record(out, var)
3080
+
3081
+ def write_url_record(self, out, obj):
3082
+ url = self.url(obj)
3083
+ if url is not None:
3084
+ out("%s\t%s\n" % (obj.canonical_name, url))
3085
+
3086
+ #////////////////////////////////////////////////////////////
3087
+ #{ Helper functions
3088
+ #////////////////////////////////////////////////////////////
3089
+
3090
+ def _val_is_public(self, valdoc):
3091
+ """Make a best-guess as to whether the given class is public."""
3092
+ container = self.docindex.container(valdoc)
3093
+ if isinstance(container, NamespaceDoc):
3094
+ for vardoc in container.variables.values():
3095
+ if vardoc in (UNKNOWN, None): continue
3096
+ if vardoc.value is valdoc:
3097
+ return vardoc.is_public
3098
+ return True
3099
+
3100
+ # [XX] Is it worth-while to pull the anchor tricks that I do here?
3101
+ # Or should I just live with the fact that show/hide private moves
3102
+ # stuff around?
3103
+ write_table_header = compile_template(
3104
+ '''
3105
+ write_table_header(self, out, css_class, heading=None, \
3106
+ private_link=True, colspan=2)
3107
+ ''',
3108
+ # /------------------------- Template -------------------------\
3109
+ '''
3110
+ >>> if heading is not None:
3111
+ >>> anchor = "section-%s" % re.sub("\W", "", heading)
3112
+ <!-- ==================== $heading.upper()$ ==================== -->
3113
+ <a name="$anchor$"></a>
3114
+ >>> #endif
3115
+ <table class="$css_class$" border="1" cellpadding="3"
3116
+ cellspacing="0" width="100%" bgcolor="white">
3117
+ >>> if heading is not None:
3118
+ <tr bgcolor="#70b0f0" class="table-header">
3119
+ >>> if private_link and self._show_private:
3120
+ <td colspan="$colspan$" class="table-header">
3121
+ <table border="0" cellpadding="0" cellspacing="0" width="100%">
3122
+ <tr valign="top">
3123
+ <td align="left"><span class="table-header">$heading$</span></td>
3124
+ <td align="right" valign="top"
3125
+ ><span class="options">[<a href="#$anchor$"
3126
+ class="privatelink" onclick="toggle_private();"
3127
+ >hide private</a>]</span></td>
3128
+ </tr>
3129
+ </table>
3130
+ </td>
3131
+ >>> else:
3132
+ <td align="left" colspan="2" class="table-header">
3133
+ <span class="table-header">$heading$</span></td>
3134
+ >>> #endif
3135
+ </tr>
3136
+ >>> #endif
3137
+ ''')
3138
+ # \------------------------------------------------------------/
3139
+
3140
+ TABLE_FOOTER = '</table>\n'
3141
+
3142
+ PRIVATE_LINK = '''
3143
+ <span class="options">[<a href="javascript:void(0);" class="privatelink"
3144
+ onclick="toggle_private();">hide&nbsp;private</a>]</span>
3145
+ '''.strip()
3146
+
3147
+ write_group_header = compile_template(
3148
+ '''
3149
+ write_group_header(self, out, group, tr_class='')
3150
+ ''',
3151
+ # /------------------------- Template -------------------------\
3152
+ '''
3153
+ <tr bgcolor="#e8f0f8" $tr_class$>
3154
+ <th colspan="2" class="group-header"
3155
+ >&nbsp;&nbsp;&nbsp;&nbsp;$group$</th></tr>
3156
+ ''')
3157
+ # \------------------------------------------------------------/
3158
+
3159
+ _url_cache = {}
3160
+ def url(self, obj):
3161
+ """
3162
+ Return the URL for the given object, which can be a
3163
+ C{VariableDoc}, a C{ValueDoc}, or a C{DottedName}.
3164
+ """
3165
+ cached_url = self._url_cache.get(id(obj))
3166
+ if cached_url is not None:
3167
+ return cached_url
3168
+ else:
3169
+ url = self._url_cache[id(obj)] = self._url(obj)
3170
+ return url
3171
+
3172
+ def _url(self, obj):
3173
+ """
3174
+ Internal helper for L{url}.
3175
+ """
3176
+ # Module: <canonical_name>-module.html
3177
+ if isinstance(obj, ModuleDoc):
3178
+ if obj not in self.module_set: return None
3179
+ return urllib.quote('%s'%obj.canonical_name) + '-module.html'
3180
+ # Class: <canonical_name>-class.html
3181
+ elif isinstance(obj, ClassDoc):
3182
+ if obj not in self.class_set: return None
3183
+ return urllib.quote('%s'%obj.canonical_name) + '-class.html'
3184
+ # Variable
3185
+ elif isinstance(obj, VariableDoc):
3186
+ val_doc = obj.value
3187
+ if isinstance(val_doc, (ModuleDoc, ClassDoc)):
3188
+ return self.url(val_doc)
3189
+ elif obj.container in (None, UNKNOWN):
3190
+ if val_doc in (None, UNKNOWN): return None
3191
+ return self.url(val_doc)
3192
+ elif obj.is_imported == True:
3193
+ if obj.imported_from is not UNKNOWN:
3194
+ return self.url(obj.imported_from)
3195
+ else:
3196
+ return None
3197
+ else:
3198
+ container_url = self.url(obj.container)
3199
+ if container_url is None: return None
3200
+ return '%s#%s' % (container_url, urllib.quote('%s'%obj.name))
3201
+ # Value (other than module or class)
3202
+ elif isinstance(obj, ValueDoc):
3203
+ container = self.docindex.container(obj)
3204
+ if container is None:
3205
+ return None # We couldn't find it!
3206
+ else:
3207
+ container_url = self.url(container)
3208
+ if container_url is None: return None
3209
+ anchor = urllib.quote('%s'%obj.canonical_name[-1])
3210
+ return '%s#%s' % (container_url, anchor)
3211
+ # Dotted name: look up the corresponding APIDoc
3212
+ elif isinstance(obj, DottedName):
3213
+ val_doc = self.docindex.get_valdoc(obj)
3214
+ if val_doc is None: return None
3215
+ return self.url(val_doc)
3216
+ # Special pages:
3217
+ elif obj == 'indices':
3218
+ return 'identifier-index.html'
3219
+ elif obj == 'help':
3220
+ return 'help.html'
3221
+ elif obj == 'trees':
3222
+ return self._trees_url
3223
+ else:
3224
+ raise ValueError, "Don't know what to do with %r" % obj
3225
+
3226
+ def pysrc_link(self, api_doc):
3227
+ if not self._incl_sourcecode:
3228
+ return ''
3229
+ url = self.pysrc_url(api_doc)
3230
+ if url is not None:
3231
+ return ('<span class="codelink"><a href="%s">source&nbsp;'
3232
+ 'code</a></span>' % url)
3233
+ else:
3234
+ return ''
3235
+
3236
+ def pysrc_url(self, api_doc):
3237
+ if isinstance(api_doc, VariableDoc):
3238
+ if api_doc.value not in (None, UNKNOWN):
3239
+ return pysrc_url(api_doc.value)
3240
+ else:
3241
+ return None
3242
+ elif isinstance(api_doc, ModuleDoc):
3243
+ if api_doc in self.modules_with_sourcecode:
3244
+ return ('%s-pysrc.html' %
3245
+ urllib.quote('%s' % api_doc.canonical_name))
3246
+ else:
3247
+ return None
3248
+ else:
3249
+ module = api_doc.defining_module
3250
+ if module == UNKNOWN: return None
3251
+ module_pysrc_url = self.pysrc_url(module)
3252
+ if module_pysrc_url is None: return None
3253
+ module_name = module.canonical_name
3254
+ if not module_name.dominates(api_doc.canonical_name, True):
3255
+ log.debug('%r is in %r but name does not dominate' %
3256
+ (api_doc, module))
3257
+ return module_pysrc_url
3258
+ mname_len = len(module.canonical_name)
3259
+ anchor = '%s' % api_doc.canonical_name[mname_len:]
3260
+ return '%s#%s' % (module_pysrc_url, urllib.quote(anchor))
3261
+
3262
+ # We didn't find it:
3263
+ return None
3264
+
3265
+ # [xx] add code to automatically do <code> wrapping or the like?
3266
+ def href(self, target, label=None, css_class=None, context=None,
3267
+ tooltip=None):
3268
+ """
3269
+ Return the HTML code for an HREF link to the given target
3270
+ (which can be a C{VariableDoc}, a C{ValueDoc}, or a
3271
+ C{DottedName}.
3272
+ If a C{NamespaceDoc} C{context} is specified, the target label is
3273
+ contextualized to it.
3274
+ """
3275
+ assert isinstance(target, (APIDoc, DottedName))
3276
+
3277
+ # Pick a label, if none was given.
3278
+ if label is None:
3279
+ if isinstance(target, VariableDoc):
3280
+ label = target.name
3281
+ elif (isinstance(target, ValueDoc) and
3282
+ target.canonical_name is not UNKNOWN):
3283
+ label = target.canonical_name
3284
+ elif isinstance(target, DottedName):
3285
+ label = target
3286
+ elif isinstance(target, GenericValueDoc):
3287
+ raise ValueError("href() should not be called with "
3288
+ "GenericValueDoc objects (perhaps you "
3289
+ "meant to use the containing variable?)")
3290
+ else:
3291
+ raise ValueError("Unable to find a label for %r" % target)
3292
+
3293
+ if context is not None and isinstance(label, DottedName):
3294
+ label = label.contextualize(context.canonical_name.container())
3295
+
3296
+ label = plaintext_to_html(str(label))
3297
+
3298
+ # Munge names for scripts & unreachable values
3299
+ if label.startswith('script-'):
3300
+ label = label[7:] + ' (script)'
3301
+ if label.startswith('??'):
3302
+ label = '<i>unreachable</i>' + label[2:]
3303
+ label = re.sub(r'-\d+$', '', label)
3304
+
3305
+ # Get the url for the target.
3306
+ url = self.url(target)
3307
+ if url is None:
3308
+ if tooltip: return '<span title="%s">%s</span>' % (tooltip, label)
3309
+ else: return label
3310
+
3311
+ # Construct a string for the class attribute.
3312
+ if css_class is None:
3313
+ css = ''
3314
+ else:
3315
+ css = ' class="%s"' % css_class
3316
+
3317
+ onclick = ''
3318
+ if ((isinstance(target, VariableDoc) and not target.is_public) or
3319
+ (isinstance(target, ValueDoc) and
3320
+ not isinstance(target, GenericValueDoc) and
3321
+ not self._val_is_public(target))):
3322
+ onclick = ' onclick="show_private();"'
3323
+
3324
+ if tooltip:
3325
+ tooltip = ' title="%s"' % tooltip
3326
+ else:
3327
+ tooltip = ''
3328
+
3329
+ return '<a href="%s"%s%s%s>%s</a>' % (url, css, onclick, tooltip, label)
3330
+
3331
+ def _attr_to_html(self, attr, api_doc, indent):
3332
+ if api_doc in (None, UNKNOWN):
3333
+ return ''
3334
+ pds = getattr(api_doc, attr, None) # pds = ParsedDocstring.
3335
+ if pds not in (None, UNKNOWN):
3336
+ return self.docstring_to_html(pds, api_doc, indent)
3337
+ elif isinstance(api_doc, VariableDoc):
3338
+ return self._attr_to_html(attr, api_doc.value, indent)
3339
+
3340
+ def summary(self, api_doc, indent=0):
3341
+ return self._attr_to_html('summary', api_doc, indent)
3342
+
3343
+ def descr(self, api_doc, indent=0):
3344
+ return self._attr_to_html('descr', api_doc, indent)
3345
+
3346
+ def type_descr(self, api_doc, indent=0):
3347
+ return self._attr_to_html('type_descr', api_doc, indent)
3348
+
3349
+ def return_type(self, api_doc, indent=0):
3350
+ return self._attr_to_html('return_type', api_doc, indent)
3351
+
3352
+ def return_descr(self, api_doc, indent=0):
3353
+ return self._attr_to_html('return_descr', api_doc, indent)
3354
+
3355
+ def docstring_to_html(self, parsed_docstring, where=None, indent=0):
3356
+ if parsed_docstring in (None, UNKNOWN): return ''
3357
+ linker = _HTMLDocstringLinker(self, where)
3358
+ s = parsed_docstring.to_html(linker, indent=indent,
3359
+ directory=self._directory,
3360
+ docindex=self.docindex,
3361
+ context=where).strip()
3362
+ if self._mark_docstrings:
3363
+ s = '<span class="docstring">%s</span><!--end docstring-->' % s
3364
+ return s
3365
+
3366
+ def description(self, parsed_docstring, where=None, indent=0):
3367
+ assert isinstance(where, (APIDoc, type(None)))
3368
+ if parsed_docstring in (None, UNKNOWN): return ''
3369
+ linker = _HTMLDocstringLinker(self, where)
3370
+ descr = parsed_docstring.to_html(linker, indent=indent,
3371
+ directory=self._directory,
3372
+ docindex=self.docindex,
3373
+ context=where).strip()
3374
+ if descr == '': return '&nbsp;'
3375
+ return descr
3376
+
3377
+ # [xx] Should this be defined by the APIDoc classes themselves??
3378
+ def doc_kind(self, doc):
3379
+ if isinstance(doc, ModuleDoc) and doc.is_package == True:
3380
+ return 'Package'
3381
+ elif (isinstance(doc, ModuleDoc) and
3382
+ doc.canonical_name[0].startswith('script')):
3383
+ return 'Script'
3384
+ elif isinstance(doc, ModuleDoc):
3385
+ return 'Module'
3386
+ elif isinstance(doc, ClassDoc):
3387
+ return 'Class'
3388
+ elif isinstance(doc, ClassMethodDoc):
3389
+ return 'Class Method'
3390
+ elif isinstance(doc, StaticMethodDoc):
3391
+ return 'Static Method'
3392
+ elif isinstance(doc, RoutineDoc):
3393
+ if isinstance(self.docindex.container(doc), ClassDoc):
3394
+ return 'Method'
3395
+ else:
3396
+ return 'Function'
3397
+ else:
3398
+ return 'Variable'
3399
+
3400
+ def _doc_or_ancestor_is_private(self, api_doc):
3401
+ name = api_doc.canonical_name
3402
+ for i in range(len(name), 0, -1):
3403
+ # Is it (or an ancestor) a private var?
3404
+ var_doc = self.docindex.get_vardoc(name[:i])
3405
+ if var_doc is not None and var_doc.is_public == False:
3406
+ return True
3407
+ # Is it (or an ancestor) a private module?
3408
+ val_doc = self.docindex.get_valdoc(name[:i])
3409
+ if (val_doc is not None and isinstance(val_doc, ModuleDoc) and
3410
+ val_doc.canonical_name[-1].startswith('_')):
3411
+ return True
3412
+ return False
3413
+
3414
+ def _private_subclasses(self, class_doc):
3415
+ """Return a list of all subclasses of the given class that are
3416
+ private, as determined by L{_val_is_private}. Recursive
3417
+ subclasses are included in this list."""
3418
+ queue = [class_doc]
3419
+ private = set()
3420
+ for cls in queue:
3421
+ if (isinstance(cls, ClassDoc) and
3422
+ cls.subclasses not in (None, UNKNOWN)):
3423
+ queue.extend(cls.subclasses)
3424
+ private.update([c for c in cls.subclasses if
3425
+ not self._val_is_public(c)])
3426
+ return private
3427
+
3428
+ class _HTMLDocstringLinker(epydoc.markup.DocstringLinker):
3429
+ def __init__(self, htmlwriter, container):
3430
+ self.htmlwriter = htmlwriter
3431
+ self.docindex = htmlwriter.docindex
3432
+ self.container = container
3433
+
3434
+ def translate_indexterm(self, indexterm):
3435
+ key = self.htmlwriter._term_index_to_anchor(indexterm)
3436
+ return ('<a name="%s"></a><i class="indexterm">%s</i>' %
3437
+ (key, indexterm.to_html(self)))
3438
+
3439
+ def translate_identifier_xref(self, identifier, label=None):
3440
+ # Pick a label for this xref.
3441
+ if label is None: label = plaintext_to_html(identifier)
3442
+
3443
+ # Find the APIDoc for it (if it's available).
3444
+ doc = self.docindex.find(identifier, self.container)
3445
+
3446
+ # If we didn't find a target, then try checking in the contexts
3447
+ # of the ancestor classes.
3448
+ if doc is None and isinstance(self.container, RoutineDoc):
3449
+ container = self.docindex.get_vardoc(
3450
+ self.container.canonical_name)
3451
+ while (doc is None and container not in (None, UNKNOWN)
3452
+ and container.overrides not in (None, UNKNOWN)):
3453
+ container = container.overrides
3454
+ doc = self.docindex.find(identifier, container)
3455
+
3456
+ # Translate it into HTML.
3457
+ if doc is None:
3458
+ self._failed_xref(identifier)
3459
+ return '<code class="link">%s</code>' % label
3460
+ else:
3461
+ return self.htmlwriter.href(doc, label, 'link')
3462
+
3463
+ # [xx] Should this be added to the DocstringLinker interface???
3464
+ # Currently, this is *only* used by dotgraph.
3465
+ def url_for(self, identifier):
3466
+ if isinstance(identifier, (basestring, DottedName)):
3467
+ doc = self.docindex.find(identifier, self.container)
3468
+ if doc:
3469
+ return self.htmlwriter.url(doc)
3470
+ else:
3471
+ return None
3472
+
3473
+ elif isinstance(identifier, APIDoc):
3474
+ return self.htmlwriter.url(identifier)
3475
+ doc = identifier
3476
+
3477
+ else:
3478
+ raise TypeError('Expected string or APIDoc')
3479
+
3480
+ def _failed_xref(self, identifier):
3481
+ """Add an identifier to the htmlwriter's failed crossreference
3482
+ list."""
3483
+ # Don't count it as a failed xref if it's a parameter of the
3484
+ # current function.
3485
+ if (isinstance(self.container, RoutineDoc) and
3486
+ identifier in self.container.all_args()):
3487
+ return
3488
+
3489
+ failed_xrefs = self.htmlwriter._failed_xrefs
3490
+ context = self.container.canonical_name
3491
+ failed_xrefs.setdefault(identifier,{})[context] = 1