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,1351 @@
1
+ # epydoc -- Graph generation
2
+ #
3
+ # Copyright (C) 2005 Edward Loper
4
+ # Author: Edward Loper <edloper@loper.org>
5
+ # URL: <http://epydoc.sf.net>
6
+ #
7
+ # $Id: dotgraph.py,v 1.1 2009/11/26 13:20:43 casa Exp $
8
+
9
+ """
10
+ Render Graphviz directed graphs as images. Below are some examples.
11
+
12
+ .. importgraph::
13
+
14
+ .. classtree:: epydoc.apidoc.APIDoc
15
+
16
+ .. packagetree:: epydoc
17
+
18
+ :see: `The Graphviz Homepage
19
+ <http://www.research.att.com/sw/tools/graphviz/>`__
20
+ """
21
+ __docformat__ = 'restructuredtext'
22
+
23
+ import re
24
+ import sys
25
+ from epydoc import log
26
+ from epydoc.apidoc import *
27
+ from epydoc.util import *
28
+ from epydoc.compat import * # Backwards compatibility
29
+
30
+ # colors for graphs of APIDocs
31
+ MODULE_BG = '#d8e8ff'
32
+ CLASS_BG = '#d8ffe8'
33
+ SELECTED_BG = '#ffd0d0'
34
+ BASECLASS_BG = '#e0b0a0'
35
+ SUBCLASS_BG = '#e0b0a0'
36
+ ROUTINE_BG = '#e8d0b0' # maybe?
37
+ INH_LINK_COLOR = '#800000'
38
+
39
+ ######################################################################
40
+ #{ Dot Graphs
41
+ ######################################################################
42
+
43
+ DOT_COMMAND = 'dot'
44
+ """The command that should be used to spawn dot"""
45
+
46
+ class DotGraph:
47
+ """
48
+ A ``dot`` directed graph. The contents of the graph are
49
+ constructed from the following instance variables:
50
+
51
+ - `nodes`: A list of `DotGraphNode`\\s, encoding the nodes
52
+ that are present in the graph. Each node is characterized
53
+ a set of attributes, including an optional label.
54
+ - `edges`: A list of `DotGraphEdge`\\s, encoding the edges
55
+ that are present in the graph. Each edge is characterized
56
+ by a set of attributes, including an optional label.
57
+ - `node_defaults`: Default attributes for nodes.
58
+ - `edge_defaults`: Default attributes for edges.
59
+ - `body`: A string that is appended as-is in the body of
60
+ the graph. This can be used to build more complex dot
61
+ graphs.
62
+
63
+ The `link()` method can be used to resolve crossreference links
64
+ within the graph. In particular, if the 'href' attribute of any
65
+ node or edge is assigned a value of the form ``<name>``, then it
66
+ will be replaced by the URL of the object with that name. This
67
+ applies to the `body` as well as the `nodes` and `edges`.
68
+
69
+ To render the graph, use the methods `write()` and `render()`.
70
+ Usually, you should call `link()` before you render the graph.
71
+ """
72
+ _uids = set()
73
+ """A set of all uids that that have been generated, used to ensure
74
+ that each new graph has a unique uid."""
75
+
76
+ DEFAULT_NODE_DEFAULTS={'fontsize':10, 'fontname': 'Helvetica'}
77
+ DEFAULT_EDGE_DEFAULTS={'fontsize':10, 'fontname': 'Helvetica'}
78
+
79
+ def __init__(self, title, body='', node_defaults=None,
80
+ edge_defaults=None, caption=None):
81
+ """
82
+ Create a new `DotGraph`.
83
+ """
84
+ self.title = title
85
+ """The title of the graph."""
86
+
87
+ self.caption = caption
88
+ """A caption for the graph."""
89
+
90
+ self.nodes = []
91
+ """A list of the nodes that are present in the graph.
92
+
93
+ :type: ``list`` of `DotGraphNode`"""
94
+
95
+ self.edges = []
96
+ """A list of the edges that are present in the graph.
97
+
98
+ :type: ``list`` of `DotGraphEdge`"""
99
+
100
+ self.body = body
101
+ """A string that should be included as-is in the body of the
102
+ graph.
103
+
104
+ :type: ``str``"""
105
+
106
+ self.node_defaults = node_defaults or self.DEFAULT_NODE_DEFAULTS
107
+ """Default attribute values for nodes."""
108
+
109
+ self.edge_defaults = edge_defaults or self.DEFAULT_EDGE_DEFAULTS
110
+ """Default attribute values for edges."""
111
+
112
+ self.uid = re.sub(r'\W', '_', title).lower()
113
+ """A unique identifier for this graph. This can be used as a
114
+ filename when rendering the graph. No two `DotGraph`\s will
115
+ have the same uid."""
116
+
117
+ # Encode the title, if necessary.
118
+ if isinstance(self.title, unicode):
119
+ self.title = self.title.encode('ascii', 'xmlcharrefreplace')
120
+
121
+ # Make sure the UID isn't too long.
122
+ self.uid = self.uid[:30]
123
+
124
+ # Make sure the UID is unique
125
+ if self.uid in self._uids:
126
+ n = 2
127
+ while ('%s_%s' % (self.uid, n)) in self._uids: n += 1
128
+ self.uid = '%s_%s' % (self.uid, n)
129
+ self._uids.add(self.uid)
130
+
131
+ def to_html(self, image_file, image_url, center=True):
132
+ """
133
+ Return the HTML code that should be uesd to display this graph
134
+ (including a client-side image map).
135
+
136
+ :param image_url: The URL of the image file for this graph;
137
+ this should be generated separately with the `write()` method.
138
+ """
139
+ # If dotversion >1.8.10, then we can generate the image and
140
+ # the cmapx with a single call to dot. Otherwise, we need to
141
+ # run dot twice.
142
+ if get_dot_version() > [1,8,10]:
143
+ cmapx = self._run_dot('-Tgif', '-o%s' % image_file, '-Tcmapx')
144
+ if cmapx is None: return '' # failed to render
145
+ else:
146
+ if not self.write(image_file):
147
+ return '' # failed to render
148
+ cmapx = self.render('cmapx') or ''
149
+
150
+ # Decode the cmapx (dot uses utf-8)
151
+ try:
152
+ cmapx = cmapx.decode('utf-8')
153
+ except UnicodeDecodeError:
154
+ log.debug('%s: unable to decode cmapx from dot; graph will '
155
+ 'not have clickable regions' % image_file)
156
+ cmapx = ''
157
+
158
+ title = plaintext_to_html(self.title or '')
159
+ caption = plaintext_to_html(self.caption or '')
160
+ if title or caption:
161
+ css_class = 'graph-with-title'
162
+ else:
163
+ css_class = 'graph-without-title'
164
+ if len(title)+len(caption) > 80:
165
+ title_align = 'left'
166
+ table_width = ' width="600"'
167
+ else:
168
+ title_align = 'center'
169
+ table_width = ''
170
+
171
+ if center: s = '<center>'
172
+ if title or caption:
173
+ s += ('<table border="0" cellpadding="0" cellspacing="0" '
174
+ 'class="graph"%s>\n <tr><td align="center">\n' %
175
+ table_width)
176
+ s += (' %s\n <img src="%s" alt=%r usemap="#%s" '
177
+ 'ismap="ismap" class="%s" />\n' %
178
+ (cmapx.strip(), image_url, title, self.uid, css_class))
179
+ if title or caption:
180
+ s += ' </td></tr>\n <tr><td align=%r>\n' % title_align
181
+ if title:
182
+ s += '<span class="graph-title">%s</span>' % title
183
+ if title and caption:
184
+ s += ' -- '
185
+ if caption:
186
+ s += '<span class="graph-caption">%s</span>' % caption
187
+ s += '\n </td></tr>\n</table><br />'
188
+ if center: s += '</center>'
189
+ return s
190
+
191
+ def link(self, docstring_linker):
192
+ """
193
+ Replace any href attributes whose value is ``<name>`` with
194
+ the url of the object whose name is ``<name>``.
195
+ """
196
+ # Link xrefs in nodes
197
+ self._link_href(self.node_defaults, docstring_linker)
198
+ for node in self.nodes:
199
+ self._link_href(node.attribs, docstring_linker)
200
+
201
+ # Link xrefs in edges
202
+ self._link_href(self.edge_defaults, docstring_linker)
203
+ for edge in self.nodes:
204
+ self._link_href(edge.attribs, docstring_linker)
205
+
206
+ # Link xrefs in body
207
+ def subfunc(m):
208
+ url = docstring_linker.url_for(m.group(1))
209
+ if url: return 'href="%s"%s' % (url, m.group(2))
210
+ else: return ''
211
+ self.body = re.sub("href\s*=\s*['\"]?<([\w\.]+)>['\"]?\s*(,?)",
212
+ subfunc, self.body)
213
+
214
+ def _link_href(self, attribs, docstring_linker):
215
+ """Helper for `link()`"""
216
+ if 'href' in attribs:
217
+ m = re.match(r'^<([\w\.]+)>$', attribs['href'])
218
+ if m:
219
+ url = docstring_linker.url_for(m.group(1))
220
+ if url: attribs['href'] = url
221
+ else: del attribs['href']
222
+
223
+ def write(self, filename, language='gif'):
224
+ """
225
+ Render the graph using the output format `language`, and write
226
+ the result to `filename`.
227
+
228
+ :return: True if rendering was successful.
229
+ """
230
+ result = self._run_dot('-T%s' % language,
231
+ '-o%s' % filename)
232
+ # Decode into unicode, if necessary.
233
+ if language == 'cmapx' and result is not None:
234
+ result = result.decode('utf-8')
235
+ return (result is not None)
236
+
237
+ def render(self, language='gif'):
238
+ """
239
+ Use the ``dot`` command to render this graph, using the output
240
+ format `language`. Return the result as a string, or ``None``
241
+ if the rendering failed.
242
+ """
243
+ return self._run_dot('-T%s' % language)
244
+
245
+ def _run_dot(self, *options):
246
+ try:
247
+ result, err = run_subprocess((DOT_COMMAND,)+options,
248
+ self.to_dotfile())
249
+ if err: log.warning("Graphviz dot warning(s):\n%s" % err)
250
+ except OSError, e:
251
+ log.warning("Unable to render Graphviz dot graph:\n%s" % e)
252
+ #log.debug(self.to_dotfile())
253
+ return None
254
+
255
+ return result
256
+
257
+ def to_dotfile(self):
258
+ """
259
+ Return the string contents of the dot file that should be used
260
+ to render this graph.
261
+ """
262
+ lines = ['digraph %s {' % self.uid,
263
+ 'node [%s]' % ','.join(['%s="%s"' % (k,v) for (k,v)
264
+ in self.node_defaults.items()]),
265
+ 'edge [%s]' % ','.join(['%s="%s"' % (k,v) for (k,v)
266
+ in self.edge_defaults.items()])]
267
+ if self.body:
268
+ lines.append(self.body)
269
+ lines.append('/* Nodes */')
270
+ for node in self.nodes:
271
+ lines.append(node.to_dotfile())
272
+ lines.append('/* Edges */')
273
+ for edge in self.edges:
274
+ lines.append(edge.to_dotfile())
275
+ lines.append('}')
276
+
277
+ # Default dot input encoding is UTF-8
278
+ return u'\n'.join(lines).encode('utf-8')
279
+
280
+ class DotGraphNode:
281
+ _next_id = 0
282
+ def __init__(self, label=None, html_label=None, **attribs):
283
+ if label is not None and html_label is not None:
284
+ raise ValueError('Use label or html_label, not both.')
285
+ if label is not None: attribs['label'] = label
286
+ self._html_label = html_label
287
+ self._attribs = attribs
288
+ self.id = self.__class__._next_id
289
+ self.__class__._next_id += 1
290
+ self.port = None
291
+
292
+ def __getitem__(self, attr):
293
+ return self._attribs[attr]
294
+
295
+ def __setitem__(self, attr, val):
296
+ if attr == 'html_label':
297
+ self._attribs.pop('label')
298
+ self._html_label = val
299
+ else:
300
+ if attr == 'label': self._html_label = None
301
+ self._attribs[attr] = val
302
+
303
+ def to_dotfile(self):
304
+ """
305
+ Return the dot commands that should be used to render this node.
306
+ """
307
+ attribs = ['%s="%s"' % (k,v) for (k,v) in self._attribs.items()
308
+ if v is not None]
309
+ if self._html_label:
310
+ attribs.insert(0, 'label=<%s>' % (self._html_label,))
311
+ if attribs: attribs = ' [%s]' % (','.join(attribs))
312
+ return 'node%d%s' % (self.id, attribs)
313
+
314
+ class DotGraphEdge:
315
+ def __init__(self, start, end, label=None, **attribs):
316
+ """
317
+ :type start: `DotGraphNode`
318
+ :type end: `DotGraphNode`
319
+ """
320
+ assert isinstance(start, DotGraphNode)
321
+ assert isinstance(end, DotGraphNode)
322
+ if label is not None: attribs['label'] = label
323
+ self.start = start #: :type: `DotGraphNode`
324
+ self.end = end #: :type: `DotGraphNode`
325
+ self._attribs = attribs
326
+
327
+ def __getitem__(self, attr):
328
+ return self._attribs[attr]
329
+
330
+ def __setitem__(self, attr, val):
331
+ self._attribs[attr] = val
332
+
333
+ def to_dotfile(self):
334
+ """
335
+ Return the dot commands that should be used to render this edge.
336
+ """
337
+ # Set head & tail ports, if the nodes have preferred ports.
338
+ attribs = self._attribs.copy()
339
+ if (self.start.port is not None and 'headport' not in attribs):
340
+ attribs['headport'] = self.start.port
341
+ if (self.end.port is not None and 'tailport' not in attribs):
342
+ attribs['tailport'] = self.end.port
343
+ # Convert attribs to a string
344
+ attribs = ','.join(['%s="%s"' % (k,v) for (k,v) in attribs.items()
345
+ if v is not None])
346
+ if attribs: attribs = ' [%s]' % attribs
347
+ # Return the dotfile edge.
348
+ return 'node%d -> node%d%s' % (self.start.id, self.end.id, attribs)
349
+
350
+ ######################################################################
351
+ #{ Specialized Nodes for UML Graphs
352
+ ######################################################################
353
+
354
+ class DotGraphUmlClassNode(DotGraphNode):
355
+ """
356
+ A specialized dot graph node used to display `ClassDoc`\s using
357
+ UML notation. The node is rendered as a table with three cells:
358
+ the top cell contains the class name; the middle cell contains a
359
+ list of attributes; and the bottom cell contains a list of
360
+ operations::
361
+
362
+ +-------------+
363
+ | ClassName |
364
+ +-------------+
365
+ | x: int |
366
+ | ... |
367
+ +-------------+
368
+ | f(self, x) |
369
+ | ... |
370
+ +-------------+
371
+
372
+ `DotGraphUmlClassNode`\s may be *collapsed*, in which case they are
373
+ drawn as a simple box containing the class name::
374
+
375
+ +-------------+
376
+ | ClassName |
377
+ +-------------+
378
+
379
+ Attributes with types corresponding to documented classes can
380
+ optionally be converted into edges, using `link_attributes()`.
381
+
382
+ :todo: Add more options?
383
+ - show/hide operation signature
384
+ - show/hide operation signature types
385
+ - show/hide operation signature return type
386
+ - show/hide attribute types
387
+ - use qualifiers
388
+ """
389
+
390
+ def __init__(self, class_doc, linker, context, collapsed=False,
391
+ bgcolor=CLASS_BG, **options):
392
+ """
393
+ Create a new `DotGraphUmlClassNode` based on the class
394
+ `class_doc`.
395
+
396
+ :Parameters:
397
+ `linker` : `markup.DocstringLinker`
398
+ Used to look up URLs for classes.
399
+ `context` : `APIDoc`
400
+ The context in which this node will be drawn; dotted
401
+ names will be contextualized to this context.
402
+ `collapsed` : ``bool``
403
+ If true, then display this node as a simple box.
404
+ `bgcolor` : ```str```
405
+ The background color for this node.
406
+ `options` : ``dict``
407
+ A set of options used to control how the node should
408
+ be displayed.
409
+
410
+ :Keywords:
411
+ - `show_private_vars`: If false, then private variables
412
+ are filtered out of the attributes & operations lists.
413
+ (Default: *False*)
414
+ - `show_magic_vars`: If false, then magic variables
415
+ (such as ``__init__`` and ``__add__``) are filtered out of
416
+ the attributes & operations lists. (Default: *True*)
417
+ - `show_inherited_vars`: If false, then inherited variables
418
+ are filtered out of the attributes & operations lists.
419
+ (Default: *False*)
420
+ - `max_attributes`: The maximum number of attributes that
421
+ should be listed in the attribute box. If the class has
422
+ more than this number of attributes, some will be
423
+ ellided. Ellipsis is marked with ``'...'``.
424
+ - `max_operations`: The maximum number of operations that
425
+ should be listed in the operation box.
426
+ - `add_nodes_for_linked_attributes`: If true, then
427
+ `link_attributes()` will create new a collapsed node for
428
+ the types of a linked attributes if no node yet exists for
429
+ that type.
430
+ """
431
+ if not isinstance(class_doc, ClassDoc):
432
+ raise TypeError('Expected a ClassDoc as 1st argument')
433
+
434
+ self.class_doc = class_doc
435
+ """The class represented by this node."""
436
+
437
+ self.linker = linker
438
+ """Used to look up URLs for classes."""
439
+
440
+ self.context = context
441
+ """The context in which the node will be drawn."""
442
+
443
+ self.bgcolor = bgcolor
444
+ """The background color of the node."""
445
+
446
+ self.options = options
447
+ """Options used to control how the node is displayed."""
448
+
449
+ self.collapsed = collapsed
450
+ """If true, then draw this node as a simple box."""
451
+
452
+ self.attributes = []
453
+ """The list of VariableDocs for attributes"""
454
+
455
+ self.operations = []
456
+ """The list of VariableDocs for operations"""
457
+
458
+ self.qualifiers = []
459
+ """List of (key_label, port) tuples."""
460
+
461
+ self.edges = []
462
+ """List of edges used to represent this node's attributes.
463
+ These should not be added to the `DotGraph`; this node will
464
+ generate their dotfile code directly."""
465
+
466
+ # Initialize operations & attributes lists.
467
+ show_private = options.get('show_private_vars', False)
468
+ show_magic = options.get('show_magic_vars', True)
469
+ show_inherited = options.get('show_inherited_vars', False)
470
+ for var in class_doc.sorted_variables:
471
+ name = var.canonical_name[-1]
472
+ if ((not show_private and var.is_public == False) or
473
+ (not show_magic and re.match('__\w+__$', name)) or
474
+ (not show_inherited and var.container != class_doc)):
475
+ pass
476
+ elif isinstance(var.value, RoutineDoc):
477
+ self.operations.append(var)
478
+ else:
479
+ self.attributes.append(var)
480
+
481
+ # Initialize our dot node settings.
482
+ tooltip = self._summary(class_doc)
483
+ if tooltip:
484
+ # dot chokes on a \n in the attribute...
485
+ tooltip = " ".join(tooltip.split())
486
+ else:
487
+ tooltip = class_doc.canonical_name
488
+ DotGraphNode.__init__(self, tooltip=tooltip,
489
+ width=0, height=0, shape='plaintext',
490
+ href=linker.url_for(class_doc) or NOOP_URL)
491
+
492
+ #/////////////////////////////////////////////////////////////////
493
+ #{ Attribute Linking
494
+ #/////////////////////////////////////////////////////////////////
495
+
496
+ SIMPLE_TYPE_RE = re.compile(
497
+ r'^([\w\.]+)$')
498
+ """A regular expression that matches descriptions of simple types."""
499
+
500
+ COLLECTION_TYPE_RE = re.compile(
501
+ r'^(list|set|sequence|tuple|collection) of ([\w\.]+)$')
502
+ """A regular expression that matches descriptions of collection types."""
503
+
504
+ MAPPING_TYPE_RE = re.compile(
505
+ r'^(dict|dictionary|map|mapping) from ([\w\.]+) to ([\w\.]+)$')
506
+ """A regular expression that matches descriptions of mapping types."""
507
+
508
+ MAPPING_TO_COLLECTION_TYPE_RE = re.compile(
509
+ r'^(dict|dictionary|map|mapping) from ([\w\.]+) to '
510
+ r'(list|set|sequence|tuple|collection) of ([\w\.]+)$')
511
+ """A regular expression that matches descriptions of mapping types
512
+ whose value type is a collection."""
513
+
514
+ OPTIONAL_TYPE_RE = re.compile(
515
+ r'^(None or|optional) ([\w\.]+)$|^([\w\.]+) or None$')
516
+ """A regular expression that matches descriptions of optional types."""
517
+
518
+ def link_attributes(self, nodes):
519
+ """
520
+ Convert any attributes with type descriptions corresponding to
521
+ documented classes to edges. The following type descriptions
522
+ are currently handled:
523
+
524
+ - Dotted names: Create an attribute edge to the named type,
525
+ labelled with the variable name.
526
+ - Collections: Create an attribute edge to the named type,
527
+ labelled with the variable name, and marked with '*' at the
528
+ type end of the edge.
529
+ - Mappings: Create an attribute edge to the named type,
530
+ labelled with the variable name, connected to the class by
531
+ a qualifier box that contains the key type description.
532
+ - Optional: Create an attribute edge to the named type,
533
+ labelled with the variable name, and marked with '0..1' at
534
+ the type end of the edge.
535
+
536
+ The edges created by `link_attributes()` are handled internally
537
+ by `DotGraphUmlClassNode`; they should *not* be added directly
538
+ to the `DotGraph`.
539
+
540
+ :param nodes: A dictionary mapping from `ClassDoc`\s to
541
+ `DotGraphUmlClassNode`\s, used to look up the nodes for
542
+ attribute types. If the ``add_nodes_for_linked_attributes``
543
+ option is used, then new nodes will be added to this
544
+ dictionary for any types that are not already listed.
545
+ These added nodes must be added to the `DotGraph`.
546
+ """
547
+ # Try to convert each attribute var into a graph edge. If
548
+ # _link_attribute returns true, then it succeeded, so remove
549
+ # that var from our attribute list; otherwise, leave that var
550
+ # in our attribute list.
551
+ self.attributes = [var for var in self.attributes
552
+ if not self._link_attribute(var, nodes)]
553
+
554
+ def _link_attribute(self, var, nodes):
555
+ """
556
+ Helper for `link_attributes()`: try to convert the attribute
557
+ variable `var` into an edge, and add that edge to
558
+ `self.edges`. Return ``True`` iff the variable was
559
+ successfully converted to an edge (in which case, it should be
560
+ removed from the attributes list).
561
+ """
562
+ type_descr = self._type_descr(var) or self._type_descr(var.value)
563
+
564
+ # Simple type.
565
+ m = self.SIMPLE_TYPE_RE.match(type_descr)
566
+ if m and self._add_attribute_edge(var, nodes, m.group(1)):
567
+ return True
568
+
569
+ # Collection type.
570
+ m = self.COLLECTION_TYPE_RE.match(type_descr)
571
+ if m and self._add_attribute_edge(var, nodes, m.group(2),
572
+ headlabel='*'):
573
+ return True
574
+
575
+ # Optional type.
576
+ m = self.OPTIONAL_TYPE_RE.match(type_descr)
577
+ if m and self._add_attribute_edge(var, nodes, m.group(2) or m.group(3),
578
+ headlabel='0..1'):
579
+ return True
580
+
581
+ # Mapping type.
582
+ m = self.MAPPING_TYPE_RE.match(type_descr)
583
+ if m:
584
+ port = 'qualifier_%s' % var.name
585
+ if self._add_attribute_edge(var, nodes, m.group(3),
586
+ tailport='%s:e' % port):
587
+ self.qualifiers.append( (m.group(2), port) )
588
+ return True
589
+
590
+ # Mapping to collection type.
591
+ m = self.MAPPING_TO_COLLECTION_TYPE_RE.match(type_descr)
592
+ if m:
593
+ port = 'qualifier_%s' % var.name
594
+ if self._add_attribute_edge(var, nodes, m.group(4), headlabel='*',
595
+ tailport='%s:e' % port):
596
+ self.qualifiers.append( (m.group(2), port) )
597
+ return True
598
+
599
+ # We were unable to link this attribute.
600
+ return False
601
+
602
+ def _add_attribute_edge(self, var, nodes, type_str, **attribs):
603
+ """
604
+ Helper for `link_attributes()`: try to add an edge for the
605
+ given attribute variable `var`. Return ``True`` if
606
+ successful.
607
+ """
608
+ # Use the type string to look up a corresponding ValueDoc.
609
+ type_doc = self.linker.docindex.find(type_str, var)
610
+ if not type_doc: return False
611
+
612
+ # Make sure the type is a class.
613
+ if not isinstance(type_doc, ClassDoc): return False
614
+
615
+ # Get the type ValueDoc's node. If it doesn't have one (and
616
+ # add_nodes_for_linked_attributes=True), then create it.
617
+ type_node = nodes.get(type_doc)
618
+ if not type_node:
619
+ if self.options.get('add_nodes_for_linked_attributes', True):
620
+ type_node = DotGraphUmlClassNode(type_doc, self.linker,
621
+ self.context, collapsed=True)
622
+ nodes[type_doc] = type_node
623
+ else:
624
+ return False
625
+
626
+ # Add an edge from self to the target type node.
627
+ # [xx] should I set constraint=false here?
628
+ attribs.setdefault('headport', 'body')
629
+ attribs.setdefault('tailport', 'body')
630
+ url = self.linker.url_for(var) or NOOP_URL
631
+ self.edges.append(DotGraphEdge(self, type_node, label=var.name,
632
+ arrowhead='open', href=url,
633
+ tooltip=var.canonical_name, labeldistance=1.5,
634
+ **attribs))
635
+ return True
636
+
637
+ #/////////////////////////////////////////////////////////////////
638
+ #{ Helper Methods
639
+ #/////////////////////////////////////////////////////////////////
640
+ def _summary(self, api_doc):
641
+ """Return a plaintext summary for `api_doc`"""
642
+ if not isinstance(api_doc, APIDoc): return ''
643
+ if api_doc.summary in (None, UNKNOWN): return ''
644
+ summary = api_doc.summary.to_plaintext(None).strip()
645
+ return plaintext_to_html(summary)
646
+
647
+ _summary = classmethod(_summary)
648
+
649
+ def _type_descr(self, api_doc):
650
+ """Return a plaintext type description for `api_doc`"""
651
+ if not hasattr(api_doc, 'type_descr'): return ''
652
+ if api_doc.type_descr in (None, UNKNOWN): return ''
653
+ type_descr = api_doc.type_descr.to_plaintext(self.linker).strip()
654
+ return plaintext_to_html(type_descr)
655
+
656
+ def _tooltip(self, var_doc):
657
+ """Return a tooltip for `var_doc`."""
658
+ return (self._summary(var_doc) or
659
+ self._summary(var_doc.value) or
660
+ var_doc.canonical_name)
661
+
662
+ #/////////////////////////////////////////////////////////////////
663
+ #{ Rendering
664
+ #/////////////////////////////////////////////////////////////////
665
+
666
+ def _attribute_cell(self, var_doc):
667
+ # Construct the label
668
+ label = var_doc.name
669
+ type_descr = (self._type_descr(var_doc) or
670
+ self._type_descr(var_doc.value))
671
+ if type_descr: label += ': %s' % type_descr
672
+ # Get the URL
673
+ url = self.linker.url_for(var_doc) or NOOP_URL
674
+ # Construct & return the pseudo-html code
675
+ return self._ATTRIBUTE_CELL % (url, self._tooltip(var_doc), label)
676
+
677
+ def _operation_cell(self, var_doc):
678
+ """
679
+ :todo: do 'word wrapping' on the signature, by starting a new
680
+ row in the table, if necessary. How to indent the new
681
+ line? Maybe use align=right? I don't think dot has a
682
+ &nbsp;.
683
+ :todo: Optionally add return type info?
684
+ """
685
+ # Construct the label (aka function signature)
686
+ func_doc = var_doc.value
687
+ args = [self._operation_arg(n, d, func_doc) for (n, d)
688
+ in zip(func_doc.posargs, func_doc.posarg_defaults)]
689
+ args = [plaintext_to_html(arg) for arg in args]
690
+ if func_doc.vararg: args.append('*'+func_doc.vararg)
691
+ if func_doc.kwarg: args.append('**'+func_doc.kwarg)
692
+ label = '%s(%s)' % (var_doc.name, ', '.join(args))
693
+ # Get the URL
694
+ url = self.linker.url_for(var_doc) or NOOP_URL
695
+ # Construct & return the pseudo-html code
696
+ return self._OPERATION_CELL % (url, self._tooltip(var_doc), label)
697
+
698
+ def _operation_arg(self, name, default, func_doc):
699
+ """
700
+ :todo: Handle tuple args better
701
+ :todo: Optionally add type info?
702
+ """
703
+ if default is None:
704
+ return '%s' % name
705
+ else:
706
+ pyval_repr = default.summary_pyval_repr().to_plaintext(None)
707
+ return '%s=%s' % (name, pyval_repr)
708
+
709
+ def _qualifier_cell(self, key_label, port):
710
+ return self._QUALIFIER_CELL % (port, self.bgcolor, key_label)
711
+
712
+ #: args: (url, tooltip, label)
713
+ _ATTRIBUTE_CELL = '''
714
+ <TR><TD ALIGN="LEFT" HREF="%s" TOOLTIP="%s">%s</TD></TR>
715
+ '''
716
+
717
+ #: args: (url, tooltip, label)
718
+ _OPERATION_CELL = '''
719
+ <TR><TD ALIGN="LEFT" HREF="%s" TOOLTIP="%s">%s</TD></TR>
720
+ '''
721
+
722
+ #: args: (port, bgcolor, label)
723
+ _QUALIFIER_CELL = '''
724
+ <TR><TD VALIGN="BOTTOM" PORT="%s" BGCOLOR="%s" BORDER="1">%s</TD></TR>
725
+ '''
726
+
727
+ _QUALIFIER_DIV = '''
728
+ <TR><TD VALIGN="BOTTOM" HEIGHT="10" WIDTH="10" FIXEDSIZE="TRUE"></TD></TR>
729
+ '''
730
+
731
+ #: Args: (rowspan, bgcolor, classname, attributes, operations, qualifiers)
732
+ _LABEL = '''
733
+ <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0">
734
+ <TR><TD ROWSPAN="%s">
735
+ <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"
736
+ CELLPADDING="0" PORT="body" BGCOLOR="%s">
737
+ <TR><TD>%s</TD></TR>
738
+ <TR><TD><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">
739
+ %s</TABLE></TD></TR>
740
+ <TR><TD><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">
741
+ %s</TABLE></TD></TR>
742
+ </TABLE>
743
+ </TD></TR>
744
+ %s
745
+ </TABLE>'''
746
+
747
+ _COLLAPSED_LABEL = '''
748
+ <TABLE CELLBORDER="0" BGCOLOR="%s" PORT="body">
749
+ <TR><TD>%s</TD></TR>
750
+ </TABLE>'''
751
+
752
+ def _get_html_label(self):
753
+ # Get the class name & contextualize it.
754
+ classname = self.class_doc.canonical_name
755
+ classname = classname.contextualize(self.context.canonical_name)
756
+
757
+ # If we're collapsed, display the node as a single box.
758
+ if self.collapsed:
759
+ return self._COLLAPSED_LABEL % (self.bgcolor, classname)
760
+
761
+ # Construct the attribute list. (If it's too long, truncate)
762
+ attrib_cells = [self._attribute_cell(a) for a in self.attributes]
763
+ max_attributes = self.options.get('max_attributes', 15)
764
+ if len(attrib_cells) == 0:
765
+ attrib_cells = ['<TR><TD></TD></TR>']
766
+ elif len(attrib_cells) > max_attributes:
767
+ attrib_cells[max_attributes-2:-1] = ['<TR><TD>...</TD></TR>']
768
+ attributes = ''.join(attrib_cells)
769
+
770
+ # Construct the operation list. (If it's too long, truncate)
771
+ oper_cells = [self._operation_cell(a) for a in self.operations]
772
+ max_operations = self.options.get('max_operations', 15)
773
+ if len(oper_cells) == 0:
774
+ oper_cells = ['<TR><TD></TD></TR>']
775
+ elif len(oper_cells) > max_operations:
776
+ oper_cells[max_operations-2:-1] = ['<TR><TD>...</TD></TR>']
777
+ operations = ''.join(oper_cells)
778
+
779
+ # Construct the qualifier list & determine the rowspan.
780
+ if self.qualifiers:
781
+ rowspan = len(self.qualifiers)*2+2
782
+ div = self._QUALIFIER_DIV
783
+ qualifiers = div+div.join([self._qualifier_cell(l,p) for
784
+ (l,p) in self.qualifiers])+div
785
+ else:
786
+ rowspan = 1
787
+ qualifiers = ''
788
+
789
+ # Put it all together.
790
+ return self._LABEL % (rowspan, self.bgcolor, classname,
791
+ attributes, operations, qualifiers)
792
+
793
+ def to_dotfile(self):
794
+ attribs = ['%s="%s"' % (k,v) for (k,v) in self._attribs.items()]
795
+ attribs.append('label=<%s>' % self._get_html_label())
796
+ s = 'node%d%s' % (self.id, ' [%s]' % (','.join(attribs)))
797
+ if not self.collapsed:
798
+ for edge in self.edges:
799
+ s += '\n' + edge.to_dotfile()
800
+ return s
801
+
802
+ class DotGraphUmlModuleNode(DotGraphNode):
803
+ """
804
+ A specialized dot grah node used to display `ModuleDoc`\s using
805
+ UML notation. Simple module nodes look like::
806
+
807
+ .----.
808
+ +------------+
809
+ | modulename |
810
+ +------------+
811
+
812
+ Packages nodes are drawn with their modules & subpackages nested
813
+ inside::
814
+
815
+ .----.
816
+ +----------------------------------------+
817
+ | packagename |
818
+ | |
819
+ | .----. .----. .----. |
820
+ | +---------+ +---------+ +---------+ |
821
+ | | module1 | | module2 | | module3 | |
822
+ | +---------+ +---------+ +---------+ |
823
+ | |
824
+ +----------------------------------------+
825
+
826
+ """
827
+ def __init__(self, module_doc, linker, context, collapsed=False,
828
+ excluded_submodules=(), **options):
829
+ self.module_doc = module_doc
830
+ self.linker = linker
831
+ self.context = context
832
+ self.collapsed = collapsed
833
+ self.options = options
834
+ self.excluded_submodules = excluded_submodules
835
+ DotGraphNode.__init__(self, shape='plaintext',
836
+ href=linker.url_for(module_doc) or NOOP_URL,
837
+ tooltip=module_doc.canonical_name)
838
+
839
+ #: Expects: (color, color, url, tooltip, body)
840
+ _MODULE_LABEL = '''
841
+ <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" ALIGN="LEFT">
842
+ <TR><TD ALIGN="LEFT" VALIGN="BOTTOM" HEIGHT="8" WIDTH="16"
843
+ FIXEDSIZE="true" BGCOLOR="%s" BORDER="1" PORT="tab"></TD></TR>
844
+ <TR><TD ALIGN="LEFT" VALIGN="TOP" BGCOLOR="%s" BORDER="1" WIDTH="20"
845
+ PORT="body" HREF="%s" TOOLTIP="%s">%s</TD></TR>
846
+ </TABLE>'''
847
+
848
+ #: Expects: (name, body_rows)
849
+ _NESTED_BODY = '''
850
+ <TABLE BORDER="0" CELLBORDER="0" CELLPADDING="0" CELLSPACING="0">
851
+ <TR><TD ALIGN="LEFT">%s</TD></TR>
852
+ %s
853
+ </TABLE>'''
854
+
855
+ #: Expects: (cells,)
856
+ _NESTED_BODY_ROW = '''
857
+ <TR><TD>
858
+ <TABLE BORDER="0" CELLBORDER="0"><TR>%s</TR></TABLE>
859
+ </TD></TR>'''
860
+
861
+ def _get_html_label(self, package):
862
+ """
863
+ :Return: (label, depth, width) where:
864
+
865
+ - ``label`` is the HTML label
866
+ - ``depth`` is the depth of the package tree (for coloring)
867
+ - ``width`` is the max width of the HTML label, roughly in
868
+ units of characters.
869
+ """
870
+ MAX_ROW_WIDTH = 80 # unit is roughly characters.
871
+ pkg_name = package.canonical_name
872
+ pkg_url = self.linker.url_for(package) or NOOP_URL
873
+
874
+ if (not package.is_package or len(package.submodules) == 0 or
875
+ self.collapsed):
876
+ pkg_color = self._color(package, 1)
877
+ label = self._MODULE_LABEL % (pkg_color, pkg_color,
878
+ pkg_url, pkg_name, pkg_name[-1])
879
+ return (label, 1, len(pkg_name[-1])+3)
880
+
881
+ # Get the label for each submodule, and divide them into rows.
882
+ row_list = ['']
883
+ row_width = 0
884
+ max_depth = 0
885
+ max_row_width = len(pkg_name[-1])+3
886
+ for submodule in package.submodules:
887
+ if submodule in self.excluded_submodules: continue
888
+ # Get the submodule's label.
889
+ label, depth, width = self._get_html_label(submodule)
890
+ # Check if we should start a new row.
891
+ if row_width > 0 and width+row_width > MAX_ROW_WIDTH:
892
+ row_list.append('')
893
+ row_width = 0
894
+ # Add the submodule's label to the row.
895
+ row_width += width
896
+ row_list[-1] += '<TD ALIGN="LEFT">%s</TD>' % label
897
+ # Update our max's.
898
+ max_depth = max(depth, max_depth)
899
+ max_row_width = max(row_width, max_row_width)
900
+
901
+ # Figure out which color to use.
902
+ pkg_color = self._color(package, depth+1)
903
+
904
+ # Assemble & return the label.
905
+ rows = ''.join([self._NESTED_BODY_ROW % r for r in row_list])
906
+ body = self._NESTED_BODY % (pkg_name, rows)
907
+ label = self._MODULE_LABEL % (pkg_color, pkg_color,
908
+ pkg_url, pkg_name, body)
909
+ return label, max_depth+1, max_row_width
910
+
911
+ _COLOR_DIFF = 24
912
+ def _color(self, package, depth):
913
+ if package == self.context: return SELECTED_BG
914
+ else:
915
+ # Parse the base color.
916
+ if re.match(MODULE_BG, 'r#[0-9a-fA-F]{6}$'):
917
+ base = int(MODULE_BG[1:], 16)
918
+ else:
919
+ base = int('d8e8ff', 16)
920
+ red = (base & 0xff0000) >> 16
921
+ green = (base & 0x00ff00) >> 8
922
+ blue = (base & 0x0000ff)
923
+ # Make it darker with each level of depth. (but not *too*
924
+ # dark -- package name needs to be readable)
925
+ red = max(64, red-(depth-1)*self._COLOR_DIFF)
926
+ green = max(64, green-(depth-1)*self._COLOR_DIFF)
927
+ blue = max(64, blue-(depth-1)*self._COLOR_DIFF)
928
+ # Convert it back to a color string
929
+ return '#%06x' % ((red<<16)+(green<<8)+blue)
930
+
931
+ def to_dotfile(self):
932
+ attribs = ['%s="%s"' % (k,v) for (k,v) in self._attribs.items()]
933
+ label, depth, width = self._get_html_label(self.module_doc)
934
+ attribs.append('label=<%s>' % label)
935
+ return 'node%d%s' % (self.id, ' [%s]' % (','.join(attribs)))
936
+
937
+
938
+
939
+ ######################################################################
940
+ #{ Graph Generation Functions
941
+ ######################################################################
942
+
943
+ def package_tree_graph(packages, linker, context=None, **options):
944
+ """
945
+ Return a `DotGraph` that graphically displays the package
946
+ hierarchies for the given packages.
947
+ """
948
+ if options.get('style', 'uml') == 'uml': # default to uml style?
949
+ if get_dot_version() >= [2]:
950
+ return uml_package_tree_graph(packages, linker, context,
951
+ **options)
952
+ elif 'style' in options:
953
+ log.warning('UML style package trees require dot version 2.0+')
954
+
955
+ graph = DotGraph('Package Tree for %s' % name_list(packages, context),
956
+ body='ranksep=.3\n;nodesep=.1\n',
957
+ edge_defaults={'dir':'none'})
958
+
959
+ # Options
960
+ if options.get('dir', 'TB') != 'TB': # default: top-to-bottom
961
+ graph.body += 'rankdir=%s\n' % options.get('dir', 'TB')
962
+
963
+ # Get a list of all modules in the package.
964
+ queue = list(packages)
965
+ modules = set(packages)
966
+ for module in queue:
967
+ queue.extend(module.submodules)
968
+ modules.update(module.submodules)
969
+
970
+ # Add a node for each module.
971
+ nodes = add_valdoc_nodes(graph, modules, linker, context)
972
+
973
+ # Add an edge for each package/submodule relationship.
974
+ for module in modules:
975
+ for submodule in module.submodules:
976
+ graph.edges.append(DotGraphEdge(nodes[module], nodes[submodule],
977
+ headport='tab'))
978
+
979
+ return graph
980
+
981
+ def uml_package_tree_graph(packages, linker, context=None, **options):
982
+ """
983
+ Return a `DotGraph` that graphically displays the package
984
+ hierarchies for the given packages as a nested set of UML
985
+ symbols.
986
+ """
987
+ graph = DotGraph('Package Tree for %s' % name_list(packages, context))
988
+ # Remove any packages whose containers are also in the list.
989
+ root_packages = []
990
+ for package1 in packages:
991
+ for package2 in packages:
992
+ if (package1 is not package2 and
993
+ package2.canonical_name.dominates(package1.canonical_name)):
994
+ break
995
+ else:
996
+ root_packages.append(package1)
997
+ # If the context is a variable, then get its value.
998
+ if isinstance(context, VariableDoc) and context.value is not UNKNOWN:
999
+ context = context.value
1000
+ # Return a graph with one node for each root package.
1001
+ for package in root_packages:
1002
+ graph.nodes.append(DotGraphUmlModuleNode(package, linker, context))
1003
+ return graph
1004
+
1005
+ ######################################################################
1006
+ def class_tree_graph(bases, linker, context=None, **options):
1007
+ """
1008
+ Return a `DotGraph` that graphically displays the class
1009
+ hierarchy for the given classes. Options:
1010
+
1011
+ - exclude
1012
+ - dir: LR|RL|BT requests a left-to-right, right-to-left, or
1013
+ bottom-to- top, drawing. (corresponds to the dot option
1014
+ 'rankdir'
1015
+ """
1016
+ if isinstance(bases, ClassDoc): bases = [bases]
1017
+ graph = DotGraph('Class Hierarchy for %s' % name_list(bases, context),
1018
+ body='ranksep=0.3\n',
1019
+ edge_defaults={'sametail':True, 'dir':'none'})
1020
+
1021
+ # Options
1022
+ if options.get('dir', 'TB') != 'TB': # default: top-down
1023
+ graph.body += 'rankdir=%s\n' % options.get('dir', 'TB')
1024
+ exclude = options.get('exclude', ())
1025
+
1026
+ # Find all superclasses & subclasses of the given classes.
1027
+ classes = set(bases)
1028
+ queue = list(bases)
1029
+ for cls in queue:
1030
+ if isinstance(cls, ClassDoc):
1031
+ if cls.subclasses not in (None, UNKNOWN):
1032
+ subclasses = cls.subclasses
1033
+ if exclude:
1034
+ subclasses = [d for d in subclasses if d not in exclude]
1035
+ queue.extend(subclasses)
1036
+ classes.update(subclasses)
1037
+ queue = list(bases)
1038
+ for cls in queue:
1039
+ if isinstance(cls, ClassDoc):
1040
+ if cls.bases not in (None, UNKNOWN):
1041
+ bases = cls.bases
1042
+ if exclude:
1043
+ bases = [d for d in bases if d not in exclude]
1044
+ queue.extend(bases)
1045
+ classes.update(bases)
1046
+
1047
+ # Add a node for each cls.
1048
+ classes = [d for d in classes if isinstance(d, ClassDoc)
1049
+ if d.pyval is not object]
1050
+ nodes = add_valdoc_nodes(graph, classes, linker, context)
1051
+
1052
+ # Add an edge for each package/subclass relationship.
1053
+ edges = set()
1054
+ for cls in classes:
1055
+ for subcls in cls.subclasses:
1056
+ if cls in nodes and subcls in nodes:
1057
+ edges.add((nodes[cls], nodes[subcls]))
1058
+ graph.edges = [DotGraphEdge(src,dst) for (src,dst) in edges]
1059
+
1060
+ return graph
1061
+
1062
+ ######################################################################
1063
+ def uml_class_tree_graph(class_doc, linker, context=None, **options):
1064
+ """
1065
+ Return a `DotGraph` that graphically displays the class hierarchy
1066
+ for the given class, using UML notation. Options:
1067
+
1068
+ - max_attributes
1069
+ - max_operations
1070
+ - show_private_vars
1071
+ - show_magic_vars
1072
+ - link_attributes
1073
+ """
1074
+ nodes = {} # ClassDoc -> DotGraphUmlClassNode
1075
+ exclude = options.get('exclude', ())
1076
+
1077
+ # Create nodes for class_doc and all its bases.
1078
+ for cls in class_doc.mro():
1079
+ if cls.pyval is object: continue # don't include `object`.
1080
+ if cls in exclude: break # stop if we get to an excluded class.
1081
+ if cls == class_doc: color = SELECTED_BG
1082
+ else: color = BASECLASS_BG
1083
+ nodes[cls] = DotGraphUmlClassNode(cls, linker, context,
1084
+ show_inherited_vars=False,
1085
+ collapsed=False, bgcolor=color)
1086
+
1087
+ # Create nodes for all class_doc's subclasses.
1088
+ queue = [class_doc]
1089
+ for cls in queue:
1090
+ if (isinstance(cls, ClassDoc) and
1091
+ cls.subclasses not in (None, UNKNOWN)):
1092
+ for subcls in cls.subclasses:
1093
+ subcls_name = subcls.canonical_name[-1]
1094
+ if subcls not in nodes and subcls not in exclude:
1095
+ queue.append(subcls)
1096
+ nodes[subcls] = DotGraphUmlClassNode(
1097
+ subcls, linker, context, collapsed=True,
1098
+ bgcolor=SUBCLASS_BG)
1099
+
1100
+ # Only show variables in the class where they're defined for
1101
+ # *class_doc*.
1102
+ mro = class_doc.mro()
1103
+ for name, var in class_doc.variables.items():
1104
+ i = mro.index(var.container)
1105
+ for base in mro[i+1:]:
1106
+ if base.pyval is object: continue # don't include `object`.
1107
+ overridden_var = base.variables.get(name)
1108
+ if overridden_var and overridden_var.container == base:
1109
+ try:
1110
+ if isinstance(overridden_var.value, RoutineDoc):
1111
+ nodes[base].operations.remove(overridden_var)
1112
+ else:
1113
+ nodes[base].attributes.remove(overridden_var)
1114
+ except ValueError:
1115
+ pass # var is filtered (eg private or magic)
1116
+
1117
+ # Keep track of which nodes are part of the inheritance graph
1118
+ # (since link_attributes might add new nodes)
1119
+ inheritance_nodes = set(nodes.values())
1120
+
1121
+ # Turn attributes into links.
1122
+ if options.get('link_attributes', True):
1123
+ for node in nodes.values():
1124
+ node.link_attributes(nodes)
1125
+ # Make sure that none of the new attribute edges break the
1126
+ # rank ordering assigned by inheritance.
1127
+ for edge in node.edges:
1128
+ if edge.end in inheritance_nodes:
1129
+ edge['constraint'] = 'False'
1130
+
1131
+ # Construct the graph.
1132
+ graph = DotGraph('UML class diagram for %s' % class_doc.canonical_name,
1133
+ body='ranksep=.2\n;nodesep=.3\n')
1134
+ graph.nodes = nodes.values()
1135
+
1136
+ # Add inheritance edges.
1137
+ for node in inheritance_nodes:
1138
+ for base in node.class_doc.bases:
1139
+ if base in nodes:
1140
+ graph.edges.append(DotGraphEdge(nodes[base], node,
1141
+ dir='back', arrowtail='empty',
1142
+ headport='body', tailport='body',
1143
+ color=INH_LINK_COLOR, weight=100,
1144
+ style='bold'))
1145
+
1146
+ # And we're done!
1147
+ return graph
1148
+
1149
+ ######################################################################
1150
+ def import_graph(modules, docindex, linker, context=None, **options):
1151
+ graph = DotGraph('Import Graph', body='ranksep=.3\n;nodesep=.3\n')
1152
+
1153
+ # Options
1154
+ if options.get('dir', 'RL') != 'TB': # default: right-to-left.
1155
+ graph.body += 'rankdir=%s\n' % options.get('dir', 'RL')
1156
+
1157
+ # Add a node for each module.
1158
+ nodes = add_valdoc_nodes(graph, modules, linker, context)
1159
+
1160
+ # Edges.
1161
+ edges = set()
1162
+ for dst in modules:
1163
+ if dst.imports in (None, UNKNOWN): continue
1164
+ for var_name in dst.imports:
1165
+ for i in range(len(var_name), 0, -1):
1166
+ val_doc = docindex.find(var_name[:i], context)
1167
+ if isinstance(val_doc, ModuleDoc):
1168
+ if val_doc in nodes and dst in nodes:
1169
+ edges.add((nodes[val_doc], nodes[dst]))
1170
+ break
1171
+ graph.edges = [DotGraphEdge(src,dst) for (src,dst) in edges]
1172
+
1173
+ return graph
1174
+
1175
+ ######################################################################
1176
+ def call_graph(api_docs, docindex, linker, context=None, **options):
1177
+ """
1178
+ :param options:
1179
+ - ``dir``: rankdir for the graph. (default=LR)
1180
+ - ``add_callers``: also include callers for any of the
1181
+ routines in ``api_docs``. (default=False)
1182
+ - ``add_callees``: also include callees for any of the
1183
+ routines in ``api_docs``. (default=False)
1184
+ :todo: Add an ``exclude`` option?
1185
+ """
1186
+ if docindex.callers is None:
1187
+ log.warning("No profiling information for call graph!")
1188
+ return DotGraph('Call Graph') # return None instead?
1189
+
1190
+ if isinstance(context, VariableDoc):
1191
+ context = context.value
1192
+
1193
+ # Get the set of requested functions.
1194
+ functions = []
1195
+ for api_doc in api_docs:
1196
+ # If it's a variable, get its value.
1197
+ if isinstance(api_doc, VariableDoc):
1198
+ api_doc = api_doc.value
1199
+ # Add the value to the functions list.
1200
+ if isinstance(api_doc, RoutineDoc):
1201
+ functions.append(api_doc)
1202
+ elif isinstance(api_doc, NamespaceDoc):
1203
+ for vardoc in api_doc.variables.values():
1204
+ if isinstance(vardoc.value, RoutineDoc):
1205
+ functions.append(vardoc.value)
1206
+
1207
+ # Filter out functions with no callers/callees?
1208
+ # [xx] this isnt' quite right, esp if add_callers or add_callees
1209
+ # options are fales.
1210
+ functions = [f for f in functions if
1211
+ (f in docindex.callers) or (f in docindex.callees)]
1212
+
1213
+ # Add any callers/callees of the selected functions
1214
+ func_set = set(functions)
1215
+ if options.get('add_callers', False) or options.get('add_callees', False):
1216
+ for func_doc in functions:
1217
+ if options.get('add_callers', False):
1218
+ func_set.update(docindex.callers.get(func_doc, ()))
1219
+ if options.get('add_callees', False):
1220
+ func_set.update(docindex.callees.get(func_doc, ()))
1221
+
1222
+ graph = DotGraph('Call Graph for %s' % name_list(api_docs, context),
1223
+ node_defaults={'shape':'box', 'width': 0, 'height': 0})
1224
+
1225
+ # Options
1226
+ if options.get('dir', 'LR') != 'TB': # default: left-to-right
1227
+ graph.body += 'rankdir=%s\n' % options.get('dir', 'LR')
1228
+
1229
+ nodes = add_valdoc_nodes(graph, func_set, linker, context)
1230
+
1231
+ # Find the edges.
1232
+ edges = set()
1233
+ for func_doc in functions:
1234
+ for caller in docindex.callers.get(func_doc, ()):
1235
+ if caller in nodes:
1236
+ edges.add( (nodes[caller], nodes[func_doc]) )
1237
+ for callee in docindex.callees.get(func_doc, ()):
1238
+ if callee in nodes:
1239
+ edges.add( (nodes[func_doc], nodes[callee]) )
1240
+ graph.edges = [DotGraphEdge(src,dst) for (src,dst) in edges]
1241
+
1242
+ return graph
1243
+
1244
+ ######################################################################
1245
+ #{ Dot Version
1246
+ ######################################################################
1247
+
1248
+ _dot_version = None
1249
+ _DOT_VERSION_RE = re.compile(r'dot version ([\d\.]+)')
1250
+ def get_dot_version():
1251
+ global _dot_version
1252
+ if _dot_version is None:
1253
+ try:
1254
+ out, err = run_subprocess([DOT_COMMAND, '-V'])
1255
+ version_info = err or out
1256
+ m = _DOT_VERSION_RE.match(version_info)
1257
+ if m:
1258
+ _dot_version = [int(x) for x in m.group(1).split('.')]
1259
+ else:
1260
+ _dot_version = (0,)
1261
+ except OSError, e:
1262
+ _dot_version = (0,)
1263
+ log.info('Detected dot version %s' % _dot_version)
1264
+ return _dot_version
1265
+
1266
+ ######################################################################
1267
+ #{ Helper Functions
1268
+ ######################################################################
1269
+
1270
+ def add_valdoc_nodes(graph, val_docs, linker, context):
1271
+ """
1272
+ :todo: Use different node styles for different subclasses of APIDoc
1273
+ """
1274
+ nodes = {}
1275
+ val_docs = sorted(val_docs, key=lambda d:d.canonical_name)
1276
+ for i, val_doc in enumerate(val_docs):
1277
+ label = val_doc.canonical_name.contextualize(context.canonical_name)
1278
+ node = nodes[val_doc] = DotGraphNode(label)
1279
+ graph.nodes.append(node)
1280
+ specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc))
1281
+ return nodes
1282
+
1283
+ NOOP_URL = 'javascript:void(0);'
1284
+ MODULE_NODE_HTML = '''
1285
+ <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"
1286
+ CELLPADDING="0" PORT="table" ALIGN="LEFT">
1287
+ <TR><TD ALIGN="LEFT" VALIGN="BOTTOM" HEIGHT="8" WIDTH="16" FIXEDSIZE="true"
1288
+ BGCOLOR="%s" BORDER="1" PORT="tab"></TD></TR>
1289
+ <TR><TD ALIGN="LEFT" VALIGN="TOP" BGCOLOR="%s" BORDER="1"
1290
+ PORT="body" HREF="%s" TOOLTIP="%s">%s</TD></TR>
1291
+ </TABLE>'''.strip()
1292
+
1293
+ def specialize_valdoc_node(node, val_doc, context, url):
1294
+ """
1295
+ Update the style attributes of `node` to reflext its type
1296
+ and context.
1297
+ """
1298
+ # We can only use html-style nodes if dot_version>2.
1299
+ dot_version = get_dot_version()
1300
+
1301
+ # If val_doc or context is a variable, get its value.
1302
+ if isinstance(val_doc, VariableDoc) and val_doc.value is not UNKNOWN:
1303
+ val_doc = val_doc.value
1304
+ if isinstance(context, VariableDoc) and context.value is not UNKNOWN:
1305
+ context = context.value
1306
+
1307
+ # Set the URL. (Do this even if it points to the page we're
1308
+ # currently on; otherwise, the tooltip is ignored.)
1309
+ node['href'] = url or NOOP_URL
1310
+
1311
+ if isinstance(val_doc, ModuleDoc) and dot_version >= [2]:
1312
+ node['shape'] = 'plaintext'
1313
+ if val_doc == context: color = SELECTED_BG
1314
+ else: color = MODULE_BG
1315
+ node['tooltip'] = node['label']
1316
+ node['html_label'] = MODULE_NODE_HTML % (color, color, url,
1317
+ val_doc.canonical_name,
1318
+ node['label'])
1319
+ node['width'] = node['height'] = 0
1320
+ node.port = 'body'
1321
+
1322
+ elif isinstance(val_doc, RoutineDoc):
1323
+ node['shape'] = 'box'
1324
+ node['style'] = 'rounded'
1325
+ node['width'] = 0
1326
+ node['height'] = 0
1327
+ node['label'] = '%s()' % node['label']
1328
+ node['tooltip'] = node['label']
1329
+ if val_doc == context:
1330
+ node['fillcolor'] = SELECTED_BG
1331
+ node['style'] = 'filled,rounded,bold'
1332
+
1333
+ else:
1334
+ node['shape'] = 'box'
1335
+ node['width'] = 0
1336
+ node['height'] = 0
1337
+ node['tooltip'] = node['label']
1338
+ if val_doc == context:
1339
+ node['fillcolor'] = SELECTED_BG
1340
+ node['style'] = 'filled,bold'
1341
+
1342
+ def name_list(api_docs, context=None):
1343
+ if context is not None:
1344
+ context = context.canonical_name
1345
+ names = [str(d.canonical_name.contextualize(context)) for d in api_docs]
1346
+ if len(names) == 0: return ''
1347
+ if len(names) == 1: return '%s' % names[0]
1348
+ elif len(names) == 2: return '%s and %s' % (names[0], names[1])
1349
+ else:
1350
+ return '%s, and %s' % (', '.join(names[:-1]), names[-1])
1351
+