bee_python 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/{build/README → README} +1 -1
- data/egg/egg/build.yml +11 -49
- data/egg/egg.yml +19 -27
- data/egg/http/build.erb +32 -36
- data/egg/http/libhttp.py +102 -0
- data/egg/http/pylint.cfg +236 -0
- data/egg/http/server.py +7 -0
- data/egg/http/test.erb +32 -0
- data/egg/http.yml +33 -29
- data/egg/mysql/mysql.py +8 -9
- data/egg/project/build.erb +21 -53
- data/egg/project/pylint.cfg +236 -0
- data/egg/project/test.py +2 -2
- data/egg/project.yml +23 -31
- data/egg/script/build.erb +18 -23
- data/egg/script/pylint.cfg +236 -0
- data/egg/script.yml +22 -18
- data/egg/source/source.py +6 -6
- data/egg/test/test.py +1 -1
- data/lib/bee_task_python.rb +3 -34
- data/python.yml +123 -0
- metadata +61 -503
- data/test/build.yml +0 -16
- data/test/tc_bee_task_python.rb +0 -27
- data/test/test_build.rb +0 -26
- data/test/test_build_listener.rb +0 -110
- data/test/ts_bee_python.rb +0 -26
- data/tools/common/__init__.py +0 -5
- data/tools/common/common/__init__.py +0 -140
- data/tools/common/common/__pkginfo__.py +0 -43
- data/tools/common/common/adbh.py +0 -35
- data/tools/common/common/cache.py +0 -114
- data/tools/common/common/changelog.py +0 -234
- data/tools/common/common/clcommands.py +0 -181
- data/tools/common/common/cli.py +0 -212
- data/tools/common/common/compat.py +0 -328
- data/tools/common/common/configuration.py +0 -1087
- data/tools/common/common/contexts.py +0 -58
- data/tools/common/common/corbautils.py +0 -117
- data/tools/common/common/daemon.py +0 -171
- data/tools/common/common/date.py +0 -279
- data/tools/common/common/db.py +0 -49
- data/tools/common/common/dbf.py +0 -229
- data/tools/common/common/debugger.py +0 -208
- data/tools/common/common/decorators.py +0 -190
- data/tools/common/common/deprecation.py +0 -118
- data/tools/common/common/fileutils.py +0 -409
- data/tools/common/common/graph.py +0 -259
- data/tools/common/common/html.py +0 -142
- data/tools/common/common/interface.py +0 -76
- data/tools/common/common/logging_ext.py +0 -166
- data/tools/common/common/modutils.py +0 -670
- data/tools/common/common/optik_ext.py +0 -383
- data/tools/common/common/optparser.py +0 -92
- data/tools/common/common/pdf_ext.py +0 -111
- data/tools/common/common/proc.py +0 -276
- data/tools/common/common/pyro_ext.py +0 -146
- data/tools/common/common/pytest.py +0 -754
- data/tools/common/common/shellutils.py +0 -383
- data/tools/common/common/sphinx_ext.py +0 -87
- data/tools/common/common/sphinxutils.py +0 -122
- data/tools/common/common/sqlgen.py +0 -31
- data/tools/common/common/table.py +0 -930
- data/tools/common/common/tasksqueue.py +0 -97
- data/tools/common/common/test/__init__.py +0 -1
- data/tools/common/common/test/data/ChangeLog +0 -184
- data/tools/common/common/test/data/MyPyPa-0.1.0-py2.5.egg +0 -0
- data/tools/common/common/test/data/__init__.py +0 -1
- data/tools/common/common/test/data/content_differ_dir/NOTHING +0 -0
- data/tools/common/common/test/data/content_differ_dir/README +0 -1
- data/tools/common/common/test/data/content_differ_dir/subdir/coin +0 -1
- data/tools/common/common/test/data/content_differ_dir/subdir/toto.txt +0 -53
- data/tools/common/common/test/data/file_differ_dir/NOTHING +0 -0
- data/tools/common/common/test/data/file_differ_dir/README +0 -1
- data/tools/common/common/test/data/file_differ_dir/subdir/toto.txt +0 -53
- data/tools/common/common/test/data/file_differ_dir/subdirtwo/Hello +0 -0
- data/tools/common/common/test/data/find_test/__init__.py +0 -0
- data/tools/common/common/test/data/find_test/foo.txt +0 -0
- data/tools/common/common/test/data/find_test/module.py +0 -0
- data/tools/common/common/test/data/find_test/module2.py +0 -0
- data/tools/common/common/test/data/find_test/newlines.txt +0 -0
- data/tools/common/common/test/data/find_test/noendingnewline.py +0 -0
- data/tools/common/common/test/data/find_test/nonregr.py +0 -0
- data/tools/common/common/test/data/find_test/normal_file.txt +0 -0
- data/tools/common/common/test/data/find_test/spam.txt +0 -0
- data/tools/common/common/test/data/find_test/sub/doc.txt +0 -0
- data/tools/common/common/test/data/find_test/sub/momo.py +0 -0
- data/tools/common/common/test/data/find_test/test.ini +0 -0
- data/tools/common/common/test/data/find_test/test1.msg +0 -0
- data/tools/common/common/test/data/find_test/test2.msg +0 -0
- data/tools/common/common/test/data/find_test/write_protected_file.txt +0 -0
- data/tools/common/common/test/data/foo.txt +0 -9
- data/tools/common/common/test/data/module.py +0 -88
- data/tools/common/common/test/data/module2.py +0 -77
- data/tools/common/common/test/data/newlines.txt +0 -3
- data/tools/common/common/test/data/noendingnewline.py +0 -36
- data/tools/common/common/test/data/nonregr.py +0 -14
- data/tools/common/common/test/data/normal_file.txt +0 -0
- data/tools/common/common/test/data/reference_dir/NOTHING +0 -0
- data/tools/common/common/test/data/reference_dir/README +0 -1
- data/tools/common/common/test/data/reference_dir/subdir/coin +0 -1
- data/tools/common/common/test/data/reference_dir/subdir/toto.txt +0 -53
- data/tools/common/common/test/data/same_dir/NOTHING +0 -0
- data/tools/common/common/test/data/same_dir/README +0 -1
- data/tools/common/common/test/data/same_dir/subdir/coin +0 -1
- data/tools/common/common/test/data/same_dir/subdir/toto.txt +0 -53
- data/tools/common/common/test/data/spam.txt +0 -9
- data/tools/common/common/test/data/sub/doc.txt +0 -1
- data/tools/common/common/test/data/sub/momo.py +0 -1
- data/tools/common/common/test/data/subdir_differ_dir/NOTHING +0 -0
- data/tools/common/common/test/data/subdir_differ_dir/README +0 -1
- data/tools/common/common/test/data/subdir_differ_dir/subdir/coin +0 -1
- data/tools/common/common/test/data/subdir_differ_dir/subdir/toto.txt +0 -53
- data/tools/common/common/test/data/test.ini +0 -20
- data/tools/common/common/test/data/test1.msg +0 -30
- data/tools/common/common/test/data/test2.msg +0 -42
- data/tools/common/common/test/data/write_protected_file.txt +0 -0
- data/tools/common/common/test/foomod.py +0 -17
- data/tools/common/common/test/unittest_cache.py +0 -129
- data/tools/common/common/test/unittest_changelog.py +0 -37
- data/tools/common/common/test/unittest_compat.py +0 -239
- data/tools/common/common/test/unittest_configuration.py +0 -348
- data/tools/common/common/test/unittest_date.py +0 -154
- data/tools/common/common/test/unittest_decorators.py +0 -62
- data/tools/common/common/test/unittest_deprecation.py +0 -76
- data/tools/common/common/test/unittest_fileutils.py +0 -133
- data/tools/common/common/test/unittest_graph.py +0 -50
- data/tools/common/common/test/unittest_html.py +0 -76
- data/tools/common/common/test/unittest_interface.py +0 -87
- data/tools/common/common/test/unittest_modutils.py +0 -244
- data/tools/common/common/test/unittest_pytest.py +0 -50
- data/tools/common/common/test/unittest_shellutils.py +0 -248
- data/tools/common/common/test/unittest_table.py +0 -448
- data/tools/common/common/test/unittest_taskqueue.py +0 -71
- data/tools/common/common/test/unittest_testlib.py +0 -956
- data/tools/common/common/test/unittest_textutils.py +0 -247
- data/tools/common/common/test/unittest_tree.py +0 -248
- data/tools/common/common/test/unittest_umessage.py +0 -55
- data/tools/common/common/test/unittest_ureports_html.py +0 -64
- data/tools/common/common/test/unittest_ureports_text.py +0 -105
- data/tools/common/common/test/unittest_xmlutils.py +0 -75
- data/tools/common/common/test/utils.py +0 -87
- data/tools/common/common/testlib.py +0 -1927
- data/tools/common/common/textutils.py +0 -476
- data/tools/common/common/tree.py +0 -372
- data/tools/common/common/umessage.py +0 -161
- data/tools/common/common/ureports/__init__.py +0 -174
- data/tools/common/common/ureports/docbook_writer.py +0 -139
- data/tools/common/common/ureports/html_writer.py +0 -131
- data/tools/common/common/ureports/nodes.py +0 -201
- data/tools/common/common/ureports/text_writer.py +0 -140
- data/tools/common/common/vcgutils.py +0 -216
- data/tools/common/common/visitor.py +0 -107
- data/tools/common/common/xmlrpcutils.py +0 -136
- data/tools/common/common/xmlutils.py +0 -61
- data/tools/coverage/coverage.py +0 -602
- data/tools/epydoc/__init__.py +0 -227
- data/tools/epydoc/__init__.pyc +0 -0
- data/tools/epydoc/apidoc.py +0 -2203
- data/tools/epydoc/apidoc.pyc +0 -0
- data/tools/epydoc/checker.py +0 -349
- data/tools/epydoc/checker.pyc +0 -0
- data/tools/epydoc/cli.py +0 -1470
- data/tools/epydoc/cli.pyc +0 -0
- data/tools/epydoc/compat.py +0 -250
- data/tools/epydoc/compat.pyc +0 -0
- data/tools/epydoc/docbuilder.py +0 -1358
- data/tools/epydoc/docbuilder.pyc +0 -0
- data/tools/epydoc/docintrospecter.py +0 -1056
- data/tools/epydoc/docintrospecter.pyc +0 -0
- data/tools/epydoc/docparser.py +0 -2113
- data/tools/epydoc/docparser.pyc +0 -0
- data/tools/epydoc/docstringparser.py +0 -1111
- data/tools/epydoc/docstringparser.pyc +0 -0
- data/tools/epydoc/docwriter/__init__.py +0 -12
- data/tools/epydoc/docwriter/__init__.pyc +0 -0
- data/tools/epydoc/docwriter/dotgraph.py +0 -1351
- data/tools/epydoc/docwriter/dotgraph.pyc +0 -0
- data/tools/epydoc/docwriter/html.py +0 -3491
- data/tools/epydoc/docwriter/html.pyc +0 -0
- data/tools/epydoc/docwriter/html_colorize.py +0 -909
- data/tools/epydoc/docwriter/html_colorize.pyc +0 -0
- data/tools/epydoc/docwriter/html_css.py +0 -550
- data/tools/epydoc/docwriter/html_css.pyc +0 -0
- data/tools/epydoc/docwriter/html_help.py +0 -190
- data/tools/epydoc/docwriter/html_help.pyc +0 -0
- data/tools/epydoc/docwriter/latex.py +0 -1187
- data/tools/epydoc/docwriter/latex.pyc +0 -0
- data/tools/epydoc/docwriter/plaintext.py +0 -276
- data/tools/epydoc/docwriter/plaintext.pyc +0 -0
- data/tools/epydoc/docwriter/xlink.py +0 -505
- data/tools/epydoc/docwriter/xlink.pyc +0 -0
- data/tools/epydoc/gui.py +0 -1148
- data/tools/epydoc/gui.pyc +0 -0
- data/tools/epydoc/log.py +0 -204
- data/tools/epydoc/log.pyc +0 -0
- data/tools/epydoc/markup/__init__.py +0 -623
- data/tools/epydoc/markup/__init__.pyc +0 -0
- data/tools/epydoc/markup/doctest.py +0 -311
- data/tools/epydoc/markup/doctest.pyc +0 -0
- data/tools/epydoc/markup/epytext.py +0 -2116
- data/tools/epydoc/markup/epytext.pyc +0 -0
- data/tools/epydoc/markup/javadoc.py +0 -250
- data/tools/epydoc/markup/javadoc.pyc +0 -0
- data/tools/epydoc/markup/plaintext.py +0 -78
- data/tools/epydoc/markup/plaintext.pyc +0 -0
- data/tools/epydoc/markup/pyval_repr.py +0 -532
- data/tools/epydoc/markup/pyval_repr.pyc +0 -0
- data/tools/epydoc/markup/restructuredtext.py +0 -906
- data/tools/epydoc/markup/restructuredtext.pyc +0 -0
- data/tools/epydoc/test/__init__.py +0 -97
- data/tools/epydoc/test/__init__.pyc +0 -0
- data/tools/epydoc/test/util.py +0 -226
- data/tools/epydoc/test/util.pyc +0 -0
- data/tools/epydoc/util.py +0 -289
- data/tools/epydoc/util.pyc +0 -0
- data/tools/logilab/logilab/__init__.py +0 -5
- data/tools/logilab/logilab/astng/__init__.py +0 -82
- data/tools/logilab/logilab/astng/__pkginfo__.py +0 -76
- data/tools/logilab/logilab/astng/_exceptions.py +0 -64
- data/tools/logilab/logilab/astng/_nodes_ast.py +0 -667
- data/tools/logilab/logilab/astng/_nodes_compiler.py +0 -758
- data/tools/logilab/logilab/astng/bases.py +0 -608
- data/tools/logilab/logilab/astng/builder.py +0 -239
- data/tools/logilab/logilab/astng/inference.py +0 -426
- data/tools/logilab/logilab/astng/inspector.py +0 -289
- data/tools/logilab/logilab/astng/manager.py +0 -421
- data/tools/logilab/logilab/astng/mixins.py +0 -165
- data/tools/logilab/logilab/astng/node_classes.py +0 -848
- data/tools/logilab/logilab/astng/nodes.py +0 -85
- data/tools/logilab/logilab/astng/nodes_as_string.py +0 -389
- data/tools/logilab/logilab/astng/patchcomptransformer.py +0 -159
- data/tools/logilab/logilab/astng/protocols.py +0 -333
- data/tools/logilab/logilab/astng/raw_building.py +0 -212
- data/tools/logilab/logilab/astng/rebuilder.py +0 -307
- data/tools/logilab/logilab/astng/scoped_nodes.py +0 -951
- data/tools/logilab/logilab/astng/test/__init__.py +0 -19
- data/tools/logilab/logilab/astng/test/data/MyPyPa-0.1.0-py2.5.egg +0 -0
- data/tools/logilab/logilab/astng/test/data/MyPyPa-0.1.0-py2.5.zip +0 -0
- data/tools/logilab/logilab/astng/test/data/SSL1/Connection1.py +0 -33
- data/tools/logilab/logilab/astng/test/data/SSL1/__init__.py +0 -20
- data/tools/logilab/logilab/astng/test/data/__init__.py +0 -20
- data/tools/logilab/logilab/astng/test/data/all.py +0 -29
- data/tools/logilab/logilab/astng/test/data/appl/__init__.py +0 -23
- data/tools/logilab/logilab/astng/test/data/appl/myConnection.py +0 -30
- data/tools/logilab/logilab/astng/test/data/format.py +0 -34
- data/tools/logilab/logilab/astng/test/data/module.py +0 -90
- data/tools/logilab/logilab/astng/test/data/module2.py +0 -112
- data/tools/logilab/logilab/astng/test/data/noendingnewline.py +0 -57
- data/tools/logilab/logilab/astng/test/data/nonregr.py +0 -76
- data/tools/logilab/logilab/astng/test/data/notall.py +0 -28
- data/tools/logilab/logilab/astng/test/data2/__init__.py +0 -20
- data/tools/logilab/logilab/astng/test/data2/clientmodule_test.py +0 -51
- data/tools/logilab/logilab/astng/test/data2/suppliermodule_test.py +0 -32
- data/tools/logilab/logilab/astng/test/regrtest.py +0 -135
- data/tools/logilab/logilab/astng/test/regrtest_data/absimport.py +0 -22
- data/tools/logilab/logilab/astng/test/regrtest_data/descriptor_crash.py +0 -31
- data/tools/logilab/logilab/astng/test/regrtest_data/import_package_subpackage_module.py +0 -68
- data/tools/logilab/logilab/astng/test/regrtest_data/package/__init__.py +0 -24
- data/tools/logilab/logilab/astng/test/regrtest_data/package/subpackage/__init__.py +0 -20
- data/tools/logilab/logilab/astng/test/regrtest_data/package/subpackage/module.py +0 -20
- data/tools/logilab/logilab/astng/test/unittest_builder.py +0 -684
- data/tools/logilab/logilab/astng/test/unittest_inference.py +0 -1112
- data/tools/logilab/logilab/astng/test/unittest_inspector.py +0 -105
- data/tools/logilab/logilab/astng/test/unittest_lookup.py +0 -302
- data/tools/logilab/logilab/astng/test/unittest_manager.py +0 -98
- data/tools/logilab/logilab/astng/test/unittest_nodes.py +0 -302
- data/tools/logilab/logilab/astng/test/unittest_scoped_nodes.py +0 -501
- data/tools/logilab/logilab/astng/test/unittest_utils.py +0 -104
- data/tools/logilab/logilab/astng/utils.py +0 -342
- data/tools/logilab/logilab/common/__init__.py +0 -140
- data/tools/logilab/logilab/common/__pkginfo__.py +0 -43
- data/tools/logilab/logilab/common/adbh.py +0 -35
- data/tools/logilab/logilab/common/cache.py +0 -114
- data/tools/logilab/logilab/common/changelog.py +0 -234
- data/tools/logilab/logilab/common/clcommands.py +0 -181
- data/tools/logilab/logilab/common/cli.py +0 -212
- data/tools/logilab/logilab/common/compat.py +0 -328
- data/tools/logilab/logilab/common/configuration.py +0 -1087
- data/tools/logilab/logilab/common/contexts.py +0 -58
- data/tools/logilab/logilab/common/corbautils.py +0 -117
- data/tools/logilab/logilab/common/daemon.py +0 -171
- data/tools/logilab/logilab/common/date.py +0 -279
- data/tools/logilab/logilab/common/db.py +0 -49
- data/tools/logilab/logilab/common/dbf.py +0 -229
- data/tools/logilab/logilab/common/debugger.py +0 -208
- data/tools/logilab/logilab/common/decorators.py +0 -190
- data/tools/logilab/logilab/common/deprecation.py +0 -118
- data/tools/logilab/logilab/common/fileutils.py +0 -409
- data/tools/logilab/logilab/common/graph.py +0 -259
- data/tools/logilab/logilab/common/html.py +0 -142
- data/tools/logilab/logilab/common/interface.py +0 -76
- data/tools/logilab/logilab/common/logging_ext.py +0 -166
- data/tools/logilab/logilab/common/modutils.py +0 -670
- data/tools/logilab/logilab/common/optik_ext.py +0 -383
- data/tools/logilab/logilab/common/optparser.py +0 -92
- data/tools/logilab/logilab/common/pdf_ext.py +0 -111
- data/tools/logilab/logilab/common/proc.py +0 -276
- data/tools/logilab/logilab/common/pyro_ext.py +0 -146
- data/tools/logilab/logilab/common/pytest.py +0 -754
- data/tools/logilab/logilab/common/shellutils.py +0 -383
- data/tools/logilab/logilab/common/sphinx_ext.py +0 -87
- data/tools/logilab/logilab/common/sphinxutils.py +0 -122
- data/tools/logilab/logilab/common/sqlgen.py +0 -31
- data/tools/logilab/logilab/common/table.py +0 -930
- data/tools/logilab/logilab/common/tasksqueue.py +0 -97
- data/tools/logilab/logilab/common/test/__init__.py +0 -1
- data/tools/logilab/logilab/common/test/data/ChangeLog +0 -184
- data/tools/logilab/logilab/common/test/data/MyPyPa-0.1.0-py2.5.egg +0 -0
- data/tools/logilab/logilab/common/test/data/__init__.py +0 -1
- data/tools/logilab/logilab/common/test/data/content_differ_dir/NOTHING +0 -0
- data/tools/logilab/logilab/common/test/data/content_differ_dir/README +0 -1
- data/tools/logilab/logilab/common/test/data/content_differ_dir/subdir/coin +0 -1
- data/tools/logilab/logilab/common/test/data/content_differ_dir/subdir/toto.txt +0 -53
- data/tools/logilab/logilab/common/test/data/file_differ_dir/NOTHING +0 -0
- data/tools/logilab/logilab/common/test/data/file_differ_dir/README +0 -1
- data/tools/logilab/logilab/common/test/data/file_differ_dir/subdir/toto.txt +0 -53
- data/tools/logilab/logilab/common/test/data/file_differ_dir/subdirtwo/Hello +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/__init__.py +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/foo.txt +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/module.py +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/module2.py +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/newlines.txt +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/noendingnewline.py +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/nonregr.py +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/normal_file.txt +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/spam.txt +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/sub/doc.txt +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/sub/momo.py +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/test.ini +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/test1.msg +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/test2.msg +0 -0
- data/tools/logilab/logilab/common/test/data/find_test/write_protected_file.txt +0 -0
- data/tools/logilab/logilab/common/test/data/foo.txt +0 -9
- data/tools/logilab/logilab/common/test/data/module.py +0 -88
- data/tools/logilab/logilab/common/test/data/module2.py +0 -77
- data/tools/logilab/logilab/common/test/data/newlines.txt +0 -3
- data/tools/logilab/logilab/common/test/data/noendingnewline.py +0 -36
- data/tools/logilab/logilab/common/test/data/nonregr.py +0 -14
- data/tools/logilab/logilab/common/test/data/normal_file.txt +0 -0
- data/tools/logilab/logilab/common/test/data/reference_dir/NOTHING +0 -0
- data/tools/logilab/logilab/common/test/data/reference_dir/README +0 -1
- data/tools/logilab/logilab/common/test/data/reference_dir/subdir/coin +0 -1
- data/tools/logilab/logilab/common/test/data/reference_dir/subdir/toto.txt +0 -53
- data/tools/logilab/logilab/common/test/data/same_dir/NOTHING +0 -0
- data/tools/logilab/logilab/common/test/data/same_dir/README +0 -1
- data/tools/logilab/logilab/common/test/data/same_dir/subdir/coin +0 -1
- data/tools/logilab/logilab/common/test/data/same_dir/subdir/toto.txt +0 -53
- data/tools/logilab/logilab/common/test/data/spam.txt +0 -9
- data/tools/logilab/logilab/common/test/data/sub/doc.txt +0 -1
- data/tools/logilab/logilab/common/test/data/sub/momo.py +0 -1
- data/tools/logilab/logilab/common/test/data/subdir_differ_dir/NOTHING +0 -0
- data/tools/logilab/logilab/common/test/data/subdir_differ_dir/README +0 -1
- data/tools/logilab/logilab/common/test/data/subdir_differ_dir/subdir/coin +0 -1
- data/tools/logilab/logilab/common/test/data/subdir_differ_dir/subdir/toto.txt +0 -53
- data/tools/logilab/logilab/common/test/data/test.ini +0 -20
- data/tools/logilab/logilab/common/test/data/test1.msg +0 -30
- data/tools/logilab/logilab/common/test/data/test2.msg +0 -42
- data/tools/logilab/logilab/common/test/data/write_protected_file.txt +0 -0
- data/tools/logilab/logilab/common/test/foomod.py +0 -17
- data/tools/logilab/logilab/common/test/unittest_cache.py +0 -129
- data/tools/logilab/logilab/common/test/unittest_changelog.py +0 -37
- data/tools/logilab/logilab/common/test/unittest_compat.py +0 -239
- data/tools/logilab/logilab/common/test/unittest_configuration.py +0 -348
- data/tools/logilab/logilab/common/test/unittest_date.py +0 -154
- data/tools/logilab/logilab/common/test/unittest_decorators.py +0 -62
- data/tools/logilab/logilab/common/test/unittest_deprecation.py +0 -76
- data/tools/logilab/logilab/common/test/unittest_fileutils.py +0 -133
- data/tools/logilab/logilab/common/test/unittest_graph.py +0 -50
- data/tools/logilab/logilab/common/test/unittest_html.py +0 -76
- data/tools/logilab/logilab/common/test/unittest_interface.py +0 -87
- data/tools/logilab/logilab/common/test/unittest_modutils.py +0 -244
- data/tools/logilab/logilab/common/test/unittest_pytest.py +0 -50
- data/tools/logilab/logilab/common/test/unittest_shellutils.py +0 -248
- data/tools/logilab/logilab/common/test/unittest_table.py +0 -448
- data/tools/logilab/logilab/common/test/unittest_taskqueue.py +0 -71
- data/tools/logilab/logilab/common/test/unittest_testlib.py +0 -956
- data/tools/logilab/logilab/common/test/unittest_textutils.py +0 -247
- data/tools/logilab/logilab/common/test/unittest_tree.py +0 -248
- data/tools/logilab/logilab/common/test/unittest_umessage.py +0 -55
- data/tools/logilab/logilab/common/test/unittest_ureports_html.py +0 -64
- data/tools/logilab/logilab/common/test/unittest_ureports_text.py +0 -105
- data/tools/logilab/logilab/common/test/unittest_xmlutils.py +0 -75
- data/tools/logilab/logilab/common/test/utils.py +0 -87
- data/tools/logilab/logilab/common/testlib.py +0 -1927
- data/tools/logilab/logilab/common/textutils.py +0 -476
- data/tools/logilab/logilab/common/tree.py +0 -372
- data/tools/logilab/logilab/common/umessage.py +0 -161
- data/tools/logilab/logilab/common/ureports/__init__.py +0 -174
- data/tools/logilab/logilab/common/ureports/docbook_writer.py +0 -139
- data/tools/logilab/logilab/common/ureports/html_writer.py +0 -131
- data/tools/logilab/logilab/common/ureports/nodes.py +0 -201
- data/tools/logilab/logilab/common/ureports/text_writer.py +0 -140
- data/tools/logilab/logilab/common/vcgutils.py +0 -216
- data/tools/logilab/logilab/common/visitor.py +0 -107
- data/tools/logilab/logilab/common/xmlrpcutils.py +0 -136
- data/tools/logilab/logilab/common/xmlutils.py +0 -61
- data/tools/pychecker/COPYRIGHT +0 -31
- data/tools/pychecker/ChangeLog +0 -349
- data/tools/pychecker/CodeChecks.py +0 -1969
- data/tools/pychecker/CodeChecks.pyc +0 -0
- data/tools/pychecker/CodeChecks.pyo +0 -0
- data/tools/pychecker/Config.py +0 -475
- data/tools/pychecker/Config.pyc +0 -0
- data/tools/pychecker/Config.pyo +0 -0
- data/tools/pychecker/KNOWN_BUGS +0 -100
- data/tools/pychecker/MAINTAINERS +0 -81
- data/tools/pychecker/NEWS +0 -406
- data/tools/pychecker/OP.py +0 -131
- data/tools/pychecker/OP.pyc +0 -0
- data/tools/pychecker/OP.pyo +0 -0
- data/tools/pychecker/OptionTypes.py +0 -117
- data/tools/pychecker/OptionTypes.pyc +0 -0
- data/tools/pychecker/OptionTypes.pyo +0 -0
- data/tools/pychecker/README +0 -152
- data/tools/pychecker/Stack.py +0 -115
- data/tools/pychecker/Stack.pyc +0 -0
- data/tools/pychecker/Stack.pyo +0 -0
- data/tools/pychecker/TODO +0 -101
- data/tools/pychecker/VERSION +0 -1
- data/tools/pychecker/Warning.py +0 -50
- data/tools/pychecker/Warning.pyc +0 -0
- data/tools/pychecker/Warning.pyo +0 -0
- data/tools/pychecker/__init__.py +0 -17
- data/tools/pychecker/__init__.pyc +0 -0
- data/tools/pychecker/__init__.pyo +0 -0
- data/tools/pychecker/checker.py +0 -961
- data/tools/pychecker/checker.pyc +0 -0
- data/tools/pychecker/checker.pyo +0 -0
- data/tools/pychecker/function.py +0 -159
- data/tools/pychecker/function.pyc +0 -0
- data/tools/pychecker/function.pyo +0 -0
- data/tools/pychecker/msgs.py +0 -175
- data/tools/pychecker/msgs.pyc +0 -0
- data/tools/pychecker/msgs.pyo +0 -0
- data/tools/pychecker/options.py +0 -275
- data/tools/pychecker/options.pyc +0 -0
- data/tools/pychecker/options.pyo +0 -0
- data/tools/pychecker/pcmodules.py +0 -19
- data/tools/pychecker/pcmodules.pyc +0 -0
- data/tools/pychecker/pcmodules.pyo +0 -0
- data/tools/pychecker/printer.py +0 -47
- data/tools/pychecker/printer.pyc +0 -0
- data/tools/pychecker/printer.pyo +0 -0
- data/tools/pychecker/python.py +0 -427
- data/tools/pychecker/python.pyc +0 -0
- data/tools/pychecker/python.pyo +0 -0
- data/tools/pychecker/utils.py +0 -102
- data/tools/pychecker/utils.pyc +0 -0
- data/tools/pychecker/utils.pyo +0 -0
- data/tools/pychecker/warn.py +0 -778
- data/tools/pychecker/warn.pyc +0 -0
- data/tools/pychecker/warn.pyo +0 -0
- data/tools/pylint2/pylint/__init__.py +0 -16
- data/tools/pylint2/pylint/__pkginfo__.py +0 -67
- data/tools/pylint2/pylint/checkers/__init__.py +0 -155
- data/tools/pylint2/pylint/checkers/base.py +0 -749
- data/tools/pylint2/pylint/checkers/classes.py +0 -527
- data/tools/pylint2/pylint/checkers/design_analysis.py +0 -344
- data/tools/pylint2/pylint/checkers/exceptions.py +0 -183
- data/tools/pylint2/pylint/checkers/format.py +0 -367
- data/tools/pylint2/pylint/checkers/imports.py +0 -379
- data/tools/pylint2/pylint/checkers/logging.py +0 -98
- data/tools/pylint2/pylint/checkers/misc.py +0 -128
- data/tools/pylint2/pylint/checkers/newstyle.py +0 -107
- data/tools/pylint2/pylint/checkers/raw_metrics.py +0 -125
- data/tools/pylint2/pylint/checkers/similar.py +0 -333
- data/tools/pylint2/pylint/checkers/string_format.py +0 -239
- data/tools/pylint2/pylint/checkers/typecheck.py +0 -364
- data/tools/pylint2/pylint/checkers/utils.py +0 -208
- data/tools/pylint2/pylint/checkers/variables.py +0 -498
- data/tools/pylint2/pylint/config.py +0 -149
- data/tools/pylint2/pylint/epylint.py +0 -149
- data/tools/pylint2/pylint/gui.py +0 -433
- data/tools/pylint2/pylint/interfaces.py +0 -98
- data/tools/pylint2/pylint/lint.py +0 -914
- data/tools/pylint2/pylint/pyreverse/__init__.py +0 -5
- data/tools/pylint2/pylint/pyreverse/diadefslib.py +0 -229
- data/tools/pylint2/pylint/pyreverse/diagrams.py +0 -247
- data/tools/pylint2/pylint/pyreverse/main.py +0 -123
- data/tools/pylint2/pylint/pyreverse/utils.py +0 -131
- data/tools/pylint2/pylint/pyreverse/writer.py +0 -196
- data/tools/pylint2/pylint/reporters/__init__.py +0 -67
- data/tools/pylint2/pylint/reporters/guireporter.py +0 -36
- data/tools/pylint2/pylint/reporters/html.py +0 -69
- data/tools/pylint2/pylint/reporters/text.py +0 -156
- data/tools/pylint2/pylint/utils.py +0 -518
- data/tools/pylint2/pylint.py +0 -16
@@ -1,1351 +0,0 @@
|
|
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
|
-
.
|
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
|
-
|