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,1927 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
|
3
|
-
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
4
|
-
#
|
5
|
-
# This file is part of logilab-common.
|
6
|
-
#
|
7
|
-
# logilab-common is free software: you can redistribute it and/or modify it under
|
8
|
-
# the terms of the GNU Lesser General Public License as published by the Free
|
9
|
-
# Software Foundation, either version 2.1 of the License, or (at your option) any
|
10
|
-
# later version.
|
11
|
-
#
|
12
|
-
# logilab-common is distributed in the hope that it will be useful, but WITHOUT
|
13
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
14
|
-
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
15
|
-
# details.
|
16
|
-
#
|
17
|
-
# You should have received a copy of the GNU Lesser General Public License along
|
18
|
-
# with logilab-common. If not, see <http://www.gnu.org/licenses/>.
|
19
|
-
"""Run tests.
|
20
|
-
|
21
|
-
This will find all modules whose name match a given prefix in the test
|
22
|
-
directory, and run them. Various command line options provide
|
23
|
-
additional facilities.
|
24
|
-
|
25
|
-
Command line options:
|
26
|
-
|
27
|
-
-v verbose -- run tests in verbose mode with output to stdout
|
28
|
-
-q quiet -- don't print anything except if a test fails
|
29
|
-
-t testdir -- directory where the tests will be found
|
30
|
-
-x exclude -- add a test to exclude
|
31
|
-
-p profile -- profiled execution
|
32
|
-
-c capture -- capture standard out/err during tests
|
33
|
-
-d dbc -- enable design-by-contract
|
34
|
-
-m match -- only run test matching the tag pattern which follow
|
35
|
-
|
36
|
-
If no non-option arguments are present, prefixes used are 'test',
|
37
|
-
'regrtest', 'smoketest' and 'unittest'.
|
38
|
-
|
39
|
-
"""
|
40
|
-
__docformat__ = "restructuredtext en"
|
41
|
-
# modified copy of some functions from test/regrtest.py from PyXml
|
42
|
-
# disable camel case warning
|
43
|
-
# pylint: disable-msg=C0103
|
44
|
-
|
45
|
-
import sys
|
46
|
-
import os, os.path as osp
|
47
|
-
import re
|
48
|
-
import time
|
49
|
-
import getopt
|
50
|
-
import traceback
|
51
|
-
import inspect
|
52
|
-
import unittest
|
53
|
-
import difflib
|
54
|
-
import types
|
55
|
-
import tempfile
|
56
|
-
import math
|
57
|
-
from shutil import rmtree
|
58
|
-
from operator import itemgetter
|
59
|
-
import warnings
|
60
|
-
from compiler.consts import CO_GENERATOR
|
61
|
-
from ConfigParser import ConfigParser
|
62
|
-
from itertools import dropwhile
|
63
|
-
from functools import wraps
|
64
|
-
|
65
|
-
try:
|
66
|
-
from test import test_support
|
67
|
-
except ImportError:
|
68
|
-
# not always available
|
69
|
-
class TestSupport:
|
70
|
-
def unload(self, test):
|
71
|
-
pass
|
72
|
-
test_support = TestSupport()
|
73
|
-
|
74
|
-
# pylint: disable-msg=W0622
|
75
|
-
from logilab.common.compat import set, enumerate, any, sorted
|
76
|
-
# pylint: enable-msg=W0622
|
77
|
-
from logilab.common.modutils import load_module_from_name
|
78
|
-
from logilab.common.debugger import Debugger, colorize_source
|
79
|
-
from logilab.common.decorators import cached, classproperty
|
80
|
-
from logilab.common import textutils
|
81
|
-
|
82
|
-
|
83
|
-
__all__ = ['main', 'unittest_main', 'find_tests', 'run_test', 'spawn']
|
84
|
-
|
85
|
-
DEFAULT_PREFIXES = ('test', 'regrtest', 'smoketest', 'unittest',
|
86
|
-
'func', 'validation')
|
87
|
-
|
88
|
-
ENABLE_DBC = False
|
89
|
-
|
90
|
-
FILE_RESTART = ".pytest.restart"
|
91
|
-
|
92
|
-
# used by unittest to count the number of relevant levels in the traceback
|
93
|
-
__unittest = 1
|
94
|
-
|
95
|
-
|
96
|
-
def with_tempdir(callable):
|
97
|
-
"""A decorator ensuring no temporary file left when the function return
|
98
|
-
Work only for temporary file create with the tempfile module"""
|
99
|
-
@wraps(callable)
|
100
|
-
def proxy(*args, **kargs):
|
101
|
-
|
102
|
-
old_tmpdir = tempfile.gettempdir()
|
103
|
-
new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-")
|
104
|
-
tempfile.tempdir = new_tmpdir
|
105
|
-
try:
|
106
|
-
return callable(*args, **kargs)
|
107
|
-
finally:
|
108
|
-
try:
|
109
|
-
rmtree(new_tmpdir, ignore_errors=True)
|
110
|
-
finally:
|
111
|
-
tempfile.tempdir = old_tmpdir
|
112
|
-
return proxy
|
113
|
-
|
114
|
-
def in_tempdir(callable):
|
115
|
-
"""A decorator moving the enclosed function inside the tempfile.tempfdir
|
116
|
-
"""
|
117
|
-
@wraps(callable)
|
118
|
-
def proxy(*args, **kargs):
|
119
|
-
|
120
|
-
old_cwd = os.getcwd()
|
121
|
-
os.chdir(tempfile.tempdir)
|
122
|
-
try:
|
123
|
-
return callable(*args, **kargs)
|
124
|
-
finally:
|
125
|
-
os.chdir(old_cwd)
|
126
|
-
return proxy
|
127
|
-
|
128
|
-
def within_tempdir(callable):
|
129
|
-
"""A decorator run the enclosed function inside a tmpdir removed after execution
|
130
|
-
"""
|
131
|
-
proxy = with_tempdir(in_tempdir(callable))
|
132
|
-
proxy.__name__ = callable.__name__
|
133
|
-
return proxy
|
134
|
-
|
135
|
-
def run_tests(tests, quiet, verbose, runner=None, capture=0):
|
136
|
-
"""Execute a list of tests.
|
137
|
-
|
138
|
-
:rtype: tuple
|
139
|
-
:return: tuple (list of passed tests, list of failed tests, list of skipped tests)
|
140
|
-
"""
|
141
|
-
good = []
|
142
|
-
bad = []
|
143
|
-
skipped = []
|
144
|
-
all_result = None
|
145
|
-
for test in tests:
|
146
|
-
if not quiet:
|
147
|
-
print
|
148
|
-
print '-'*80
|
149
|
-
print "Executing", test
|
150
|
-
result = run_test(test, verbose, runner, capture)
|
151
|
-
if type(result) is type(''):
|
152
|
-
# an unexpected error occurred
|
153
|
-
skipped.append( (test, result))
|
154
|
-
else:
|
155
|
-
if all_result is None:
|
156
|
-
all_result = result
|
157
|
-
else:
|
158
|
-
all_result.testsRun += result.testsRun
|
159
|
-
all_result.failures += result.failures
|
160
|
-
all_result.errors += result.errors
|
161
|
-
all_result.skipped += result.skipped
|
162
|
-
if result.errors or result.failures:
|
163
|
-
bad.append(test)
|
164
|
-
if verbose:
|
165
|
-
print "test", test, \
|
166
|
-
"failed -- %s errors, %s failures" % (
|
167
|
-
len(result.errors), len(result.failures))
|
168
|
-
else:
|
169
|
-
good.append(test)
|
170
|
-
|
171
|
-
return good, bad, skipped, all_result
|
172
|
-
|
173
|
-
def find_tests(testdir,
|
174
|
-
prefixes=DEFAULT_PREFIXES, suffix=".py",
|
175
|
-
excludes=(),
|
176
|
-
remove_suffix=True):
|
177
|
-
"""
|
178
|
-
Return a list of all applicable test modules.
|
179
|
-
"""
|
180
|
-
tests = []
|
181
|
-
for name in os.listdir(testdir):
|
182
|
-
if not suffix or name.endswith(suffix):
|
183
|
-
for prefix in prefixes:
|
184
|
-
if name.startswith(prefix):
|
185
|
-
if remove_suffix and name.endswith(suffix):
|
186
|
-
name = name[:-len(suffix)]
|
187
|
-
if name not in excludes:
|
188
|
-
tests.append(name)
|
189
|
-
tests.sort()
|
190
|
-
return tests
|
191
|
-
|
192
|
-
|
193
|
-
def run_test(test, verbose, runner=None, capture=0):
|
194
|
-
"""
|
195
|
-
Run a single test.
|
196
|
-
|
197
|
-
test -- the name of the test
|
198
|
-
verbose -- if true, print more messages
|
199
|
-
"""
|
200
|
-
test_support.unload(test)
|
201
|
-
try:
|
202
|
-
m = load_module_from_name(test, path=sys.path)
|
203
|
-
# m = __import__(test, globals(), locals(), sys.path)
|
204
|
-
try:
|
205
|
-
suite = m.suite
|
206
|
-
if callable(suite):
|
207
|
-
suite = suite()
|
208
|
-
except AttributeError:
|
209
|
-
loader = unittest.TestLoader()
|
210
|
-
suite = loader.loadTestsFromModule(m)
|
211
|
-
if runner is None:
|
212
|
-
runner = SkipAwareTextTestRunner(capture=capture) # verbosity=0)
|
213
|
-
return runner.run(suite)
|
214
|
-
except KeyboardInterrupt, v:
|
215
|
-
raise KeyboardInterrupt, v, sys.exc_info()[2]
|
216
|
-
except:
|
217
|
-
# raise
|
218
|
-
type, value = sys.exc_info()[:2]
|
219
|
-
msg = "test %s crashed -- %s : %s" % (test, type, value)
|
220
|
-
if verbose:
|
221
|
-
traceback.print_exc()
|
222
|
-
return msg
|
223
|
-
|
224
|
-
def _count(n, word):
|
225
|
-
"""format word according to n"""
|
226
|
-
if n == 1:
|
227
|
-
return "%d %s" % (n, word)
|
228
|
-
else:
|
229
|
-
return "%d %ss" % (n, word)
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
## PostMortem Debug facilities #####
|
235
|
-
def start_interactive_mode(result):
|
236
|
-
"""starts an interactive shell so that the user can inspect errors
|
237
|
-
"""
|
238
|
-
debuggers = result.debuggers
|
239
|
-
descrs = result.error_descrs + result.fail_descrs
|
240
|
-
if len(debuggers) == 1:
|
241
|
-
# don't ask for test name if there's only one failure
|
242
|
-
debuggers[0].start()
|
243
|
-
else:
|
244
|
-
while True:
|
245
|
-
testindex = 0
|
246
|
-
print "Choose a test to debug:"
|
247
|
-
# order debuggers in the same way than errors were printed
|
248
|
-
print "\n".join(['\t%s : %s' % (i, descr) for i, (_, descr)
|
249
|
-
in enumerate(descrs)])
|
250
|
-
print "Type 'exit' (or ^D) to quit"
|
251
|
-
print
|
252
|
-
try:
|
253
|
-
todebug = raw_input('Enter a test name: ')
|
254
|
-
if todebug.strip().lower() == 'exit':
|
255
|
-
print
|
256
|
-
break
|
257
|
-
else:
|
258
|
-
try:
|
259
|
-
testindex = int(todebug)
|
260
|
-
debugger = debuggers[descrs[testindex][0]]
|
261
|
-
except (ValueError, IndexError):
|
262
|
-
print "ERROR: invalid test number %r" % (todebug, )
|
263
|
-
else:
|
264
|
-
debugger.start()
|
265
|
-
except (EOFError, KeyboardInterrupt):
|
266
|
-
print
|
267
|
-
break
|
268
|
-
|
269
|
-
|
270
|
-
# test utils ##################################################################
|
271
|
-
from cStringIO import StringIO
|
272
|
-
|
273
|
-
class SkipAwareTestResult(unittest._TextTestResult):
|
274
|
-
|
275
|
-
def __init__(self, stream, descriptions, verbosity,
|
276
|
-
exitfirst=False, capture=0, printonly=None,
|
277
|
-
pdbmode=False, cvg=None, colorize=False):
|
278
|
-
super(SkipAwareTestResult, self).__init__(stream,
|
279
|
-
descriptions, verbosity)
|
280
|
-
self.skipped = []
|
281
|
-
self.debuggers = []
|
282
|
-
self.fail_descrs = []
|
283
|
-
self.error_descrs = []
|
284
|
-
self.exitfirst = exitfirst
|
285
|
-
self.capture = capture
|
286
|
-
self.printonly = printonly
|
287
|
-
self.pdbmode = pdbmode
|
288
|
-
self.cvg = cvg
|
289
|
-
self.colorize = colorize
|
290
|
-
self.pdbclass = Debugger
|
291
|
-
self.verbose = verbosity > 1
|
292
|
-
|
293
|
-
def descrs_for(self, flavour):
|
294
|
-
return getattr(self, '%s_descrs' % flavour.lower())
|
295
|
-
|
296
|
-
def _create_pdb(self, test_descr, flavour):
|
297
|
-
self.descrs_for(flavour).append( (len(self.debuggers), test_descr) )
|
298
|
-
if self.pdbmode:
|
299
|
-
self.debuggers.append(self.pdbclass(sys.exc_info()[2]))
|
300
|
-
|
301
|
-
|
302
|
-
def _iter_valid_frames(self, frames):
|
303
|
-
"""only consider non-testlib frames when formatting traceback"""
|
304
|
-
lgc_testlib = osp.abspath(__file__)
|
305
|
-
std_testlib = osp.abspath(unittest.__file__)
|
306
|
-
invalid = lambda fi: osp.abspath(fi[1]) in (lgc_testlib, std_testlib)
|
307
|
-
for frameinfo in dropwhile(invalid, frames):
|
308
|
-
yield frameinfo
|
309
|
-
|
310
|
-
def _exc_info_to_string(self, err, test):
|
311
|
-
"""Converts a sys.exc_info()-style tuple of values into a string.
|
312
|
-
|
313
|
-
This method is overridden here because we want to colorize
|
314
|
-
lines if --color is passed, and display local variables if
|
315
|
-
--verbose is passed
|
316
|
-
"""
|
317
|
-
exctype, exc, tb = err
|
318
|
-
output = ['Traceback (most recent call last)']
|
319
|
-
frames = inspect.getinnerframes(tb)
|
320
|
-
colorize = self.colorize
|
321
|
-
frames = enumerate(self._iter_valid_frames(frames))
|
322
|
-
for index, (frame, filename, lineno, funcname, ctx, ctxindex) in frames:
|
323
|
-
filename = osp.abspath(filename)
|
324
|
-
if ctx is None: # pyc files or C extensions for instance
|
325
|
-
source = '<no source available>'
|
326
|
-
else:
|
327
|
-
source = ''.join(ctx)
|
328
|
-
if colorize:
|
329
|
-
filename = textutils.colorize_ansi(filename, 'magenta')
|
330
|
-
source = colorize_source(source)
|
331
|
-
output.append(' File "%s", line %s, in %s' % (filename, lineno, funcname))
|
332
|
-
output.append(' %s' % source.strip())
|
333
|
-
if self.verbose:
|
334
|
-
output.append('%r == %r' % (dir(frame), test.__module__))
|
335
|
-
output.append('')
|
336
|
-
output.append(' ' + ' local variables '.center(66, '-'))
|
337
|
-
for varname, value in sorted(frame.f_locals.items()):
|
338
|
-
output.append(' %s: %r' % (varname, value))
|
339
|
-
if varname == 'self': # special handy processing for self
|
340
|
-
for varname, value in sorted(vars(value).items()):
|
341
|
-
output.append(' self.%s: %r' % (varname, value))
|
342
|
-
output.append(' ' + '-' * 66)
|
343
|
-
output.append('')
|
344
|
-
output.append(''.join(traceback.format_exception_only(exctype, exc)))
|
345
|
-
return '\n'.join(output)
|
346
|
-
|
347
|
-
def addError(self, test, err):
|
348
|
-
"""err == (exc_type, exc, tcbk)"""
|
349
|
-
exc_type, exc, _ = err #
|
350
|
-
if exc_type == TestSkipped:
|
351
|
-
self.addSkipped(test, exc)
|
352
|
-
else:
|
353
|
-
if self.exitfirst:
|
354
|
-
self.shouldStop = True
|
355
|
-
descr = self.getDescription(test)
|
356
|
-
super(SkipAwareTestResult, self).addError(test, err)
|
357
|
-
self._create_pdb(descr, 'error')
|
358
|
-
|
359
|
-
def addFailure(self, test, err):
|
360
|
-
if self.exitfirst:
|
361
|
-
self.shouldStop = True
|
362
|
-
descr = self.getDescription(test)
|
363
|
-
super(SkipAwareTestResult, self).addFailure(test, err)
|
364
|
-
self._create_pdb(descr, 'fail')
|
365
|
-
|
366
|
-
def addSkipped(self, test, reason):
|
367
|
-
self.skipped.append((test, self.getDescription(test), reason))
|
368
|
-
if self.showAll:
|
369
|
-
self.stream.writeln("SKIPPED")
|
370
|
-
elif self.dots:
|
371
|
-
self.stream.write('S')
|
372
|
-
|
373
|
-
def printErrors(self):
|
374
|
-
super(SkipAwareTestResult, self).printErrors()
|
375
|
-
self.printSkippedList()
|
376
|
-
|
377
|
-
def printSkippedList(self):
|
378
|
-
for _, descr, err in self.skipped: # test, descr, err
|
379
|
-
self.stream.writeln(self.separator1)
|
380
|
-
self.stream.writeln("%s: %s" % ('SKIPPED', descr))
|
381
|
-
self.stream.writeln("\t%s" % err)
|
382
|
-
|
383
|
-
def printErrorList(self, flavour, errors):
|
384
|
-
for (_, descr), (test, err) in zip(self.descrs_for(flavour), errors):
|
385
|
-
self.stream.writeln(self.separator1)
|
386
|
-
if self.colorize:
|
387
|
-
self.stream.writeln("%s: %s" % (
|
388
|
-
textutils.colorize_ansi(flavour, color='red'), descr))
|
389
|
-
else:
|
390
|
-
self.stream.writeln("%s: %s" % (flavour, descr))
|
391
|
-
|
392
|
-
self.stream.writeln(self.separator2)
|
393
|
-
self.stream.writeln(err)
|
394
|
-
try:
|
395
|
-
output, errput = test.captured_output()
|
396
|
-
except AttributeError:
|
397
|
-
pass # original unittest
|
398
|
-
else:
|
399
|
-
if output:
|
400
|
-
self.stream.writeln(self.separator2)
|
401
|
-
self.stream.writeln("captured stdout".center(
|
402
|
-
len(self.separator2)))
|
403
|
-
self.stream.writeln(self.separator2)
|
404
|
-
self.stream.writeln(output)
|
405
|
-
else:
|
406
|
-
self.stream.writeln('no stdout'.center(
|
407
|
-
len(self.separator2)))
|
408
|
-
if errput:
|
409
|
-
self.stream.writeln(self.separator2)
|
410
|
-
self.stream.writeln("captured stderr".center(
|
411
|
-
len(self.separator2)))
|
412
|
-
self.stream.writeln(self.separator2)
|
413
|
-
self.stream.writeln(errput)
|
414
|
-
else:
|
415
|
-
self.stream.writeln('no stderr'.center(
|
416
|
-
len(self.separator2)))
|
417
|
-
|
418
|
-
|
419
|
-
def run(self, result, runcondition=None, options=None):
|
420
|
-
for test in self._tests:
|
421
|
-
if result.shouldStop:
|
422
|
-
break
|
423
|
-
try:
|
424
|
-
test(result, runcondition, options)
|
425
|
-
except TypeError:
|
426
|
-
# this might happen if a raw unittest.TestCase is defined
|
427
|
-
# and used with python (and not pytest)
|
428
|
-
warnings.warn("%s should extend lgc.testlib.TestCase instead of unittest.TestCase"
|
429
|
-
% test)
|
430
|
-
test(result)
|
431
|
-
return result
|
432
|
-
unittest.TestSuite.run = run
|
433
|
-
|
434
|
-
# backward compatibility: TestSuite might be imported from lgc.testlib
|
435
|
-
TestSuite = unittest.TestSuite
|
436
|
-
|
437
|
-
# python2.3 compat
|
438
|
-
def __call__(self, *args, **kwds):
|
439
|
-
return self.run(*args, **kwds)
|
440
|
-
unittest.TestSuite.__call__ = __call__
|
441
|
-
|
442
|
-
|
443
|
-
class SkipAwareTextTestRunner(unittest.TextTestRunner):
|
444
|
-
|
445
|
-
def __init__(self, stream=sys.stderr, verbosity=1,
|
446
|
-
exitfirst=False, capture=False, printonly=None,
|
447
|
-
pdbmode=False, cvg=None, test_pattern=None,
|
448
|
-
skipped_patterns=(), colorize=False, batchmode=False,
|
449
|
-
options=None):
|
450
|
-
super(SkipAwareTextTestRunner, self).__init__(stream=stream,
|
451
|
-
verbosity=verbosity)
|
452
|
-
self.exitfirst = exitfirst
|
453
|
-
self.capture = capture
|
454
|
-
self.printonly = printonly
|
455
|
-
self.pdbmode = pdbmode
|
456
|
-
self.cvg = cvg
|
457
|
-
self.test_pattern = test_pattern
|
458
|
-
self.skipped_patterns = skipped_patterns
|
459
|
-
self.colorize = colorize
|
460
|
-
self.batchmode = batchmode
|
461
|
-
self.options = options
|
462
|
-
|
463
|
-
def _this_is_skipped(self, testedname):
|
464
|
-
return any([(pat in testedname) for pat in self.skipped_patterns])
|
465
|
-
|
466
|
-
def _runcondition(self, test, skipgenerator=True):
|
467
|
-
if isinstance(test, InnerTest):
|
468
|
-
testname = test.name
|
469
|
-
else:
|
470
|
-
if isinstance(test, TestCase):
|
471
|
-
meth = test._get_test_method()
|
472
|
-
func = meth.im_func
|
473
|
-
testname = '%s.%s' % (meth.im_class.__name__, func.__name__)
|
474
|
-
elif isinstance(test, types.FunctionType):
|
475
|
-
func = test
|
476
|
-
testname = func.__name__
|
477
|
-
elif isinstance(test, types.MethodType):
|
478
|
-
func = test.im_func
|
479
|
-
testname = '%s.%s' % (test.im_class.__name__, func.__name__)
|
480
|
-
else:
|
481
|
-
return True # Not sure when this happens
|
482
|
-
|
483
|
-
if is_generator(func) and skipgenerator:
|
484
|
-
return self.does_match_tags(func) # Let inner tests decide at run time
|
485
|
-
|
486
|
-
# print 'testname', testname, self.test_pattern
|
487
|
-
if self._this_is_skipped(testname):
|
488
|
-
return False # this was explicitly skipped
|
489
|
-
if self.test_pattern is not None:
|
490
|
-
try:
|
491
|
-
classpattern, testpattern = self.test_pattern.split('.')
|
492
|
-
klass, name = testname.split('.')
|
493
|
-
if classpattern not in klass or testpattern not in name:
|
494
|
-
return False
|
495
|
-
except ValueError:
|
496
|
-
if self.test_pattern not in testname:
|
497
|
-
return False
|
498
|
-
|
499
|
-
return self.does_match_tags(test)
|
500
|
-
|
501
|
-
def does_match_tags(self, test):
|
502
|
-
if self.options is not None:
|
503
|
-
tags_pattern = getattr(self.options, 'tags_pattern', None)
|
504
|
-
if tags_pattern is not None:
|
505
|
-
tags = getattr(test, 'tags', None)
|
506
|
-
if tags is not None:
|
507
|
-
return tags.match(tags_pattern)
|
508
|
-
if isinstance(test, types.MethodType):
|
509
|
-
tags = getattr(test.im_class, 'tags', Tags())
|
510
|
-
return tags.match(tags_pattern)
|
511
|
-
return False
|
512
|
-
return True # no pattern
|
513
|
-
|
514
|
-
def _makeResult(self):
|
515
|
-
return SkipAwareTestResult(self.stream, self.descriptions,
|
516
|
-
self.verbosity, self.exitfirst, self.capture,
|
517
|
-
self.printonly, self.pdbmode, self.cvg,
|
518
|
-
self.colorize)
|
519
|
-
|
520
|
-
def run(self, test):
|
521
|
-
"Run the given test case or test suite."
|
522
|
-
result = self._makeResult()
|
523
|
-
startTime = time.time()
|
524
|
-
test(result, self._runcondition, self.options)
|
525
|
-
stopTime = time.time()
|
526
|
-
timeTaken = stopTime - startTime
|
527
|
-
result.printErrors()
|
528
|
-
if not self.batchmode:
|
529
|
-
self.stream.writeln(result.separator2)
|
530
|
-
run = result.testsRun
|
531
|
-
self.stream.writeln("Ran %d test%s in %.3fs" %
|
532
|
-
(run, run != 1 and "s" or "", timeTaken))
|
533
|
-
self.stream.writeln()
|
534
|
-
if not result.wasSuccessful():
|
535
|
-
if self.colorize:
|
536
|
-
self.stream.write(textutils.colorize_ansi("FAILED", color='red'))
|
537
|
-
else:
|
538
|
-
self.stream.write("FAILED")
|
539
|
-
else:
|
540
|
-
if self.colorize:
|
541
|
-
self.stream.write(textutils.colorize_ansi("OK", color='green'))
|
542
|
-
else:
|
543
|
-
self.stream.write("OK")
|
544
|
-
failed, errored, skipped = map(len, (result.failures, result.errors,
|
545
|
-
result.skipped))
|
546
|
-
|
547
|
-
det_results = []
|
548
|
-
for name, value in (("failures", result.failures),
|
549
|
-
("errors",result.errors),
|
550
|
-
("skipped", result.skipped)):
|
551
|
-
if value:
|
552
|
-
det_results.append("%s=%i" % (name, len(value)))
|
553
|
-
if det_results:
|
554
|
-
self.stream.write(" (")
|
555
|
-
self.stream.write(', '.join(det_results))
|
556
|
-
self.stream.write(")")
|
557
|
-
self.stream.writeln("")
|
558
|
-
return result
|
559
|
-
|
560
|
-
|
561
|
-
class keywords(dict):
|
562
|
-
"""Keyword args (**kwargs) support for generative tests."""
|
563
|
-
|
564
|
-
class starargs(tuple):
|
565
|
-
"""Variable arguments (*args) for generative tests."""
|
566
|
-
def __new__(cls, *args):
|
567
|
-
return tuple.__new__(cls, args)
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
class NonStrictTestLoader(unittest.TestLoader):
|
572
|
-
"""
|
573
|
-
Overrides default testloader to be able to omit classname when
|
574
|
-
specifying tests to run on command line.
|
575
|
-
|
576
|
-
For example, if the file test_foo.py contains ::
|
577
|
-
|
578
|
-
class FooTC(TestCase):
|
579
|
-
def test_foo1(self): # ...
|
580
|
-
def test_foo2(self): # ...
|
581
|
-
def test_bar1(self): # ...
|
582
|
-
|
583
|
-
class BarTC(TestCase):
|
584
|
-
def test_bar2(self): # ...
|
585
|
-
|
586
|
-
'python test_foo.py' will run the 3 tests in FooTC
|
587
|
-
'python test_foo.py FooTC' will run the 3 tests in FooTC
|
588
|
-
'python test_foo.py test_foo' will run test_foo1 and test_foo2
|
589
|
-
'python test_foo.py test_foo1' will run test_foo1
|
590
|
-
'python test_foo.py test_bar' will run FooTC.test_bar1 and BarTC.test_bar2
|
591
|
-
"""
|
592
|
-
|
593
|
-
def __init__(self):
|
594
|
-
self.skipped_patterns = []
|
595
|
-
|
596
|
-
def loadTestsFromNames(self, names, module=None):
|
597
|
-
suites = []
|
598
|
-
for name in names:
|
599
|
-
suites.extend(self.loadTestsFromName(name, module))
|
600
|
-
return self.suiteClass(suites)
|
601
|
-
|
602
|
-
def _collect_tests(self, module):
|
603
|
-
tests = {}
|
604
|
-
for obj in vars(module).values():
|
605
|
-
if (issubclass(type(obj), (types.ClassType, type)) and
|
606
|
-
issubclass(obj, unittest.TestCase)):
|
607
|
-
classname = obj.__name__
|
608
|
-
if classname[0] == '_' or self._this_is_skipped(classname):
|
609
|
-
continue
|
610
|
-
methodnames = []
|
611
|
-
# obj is a TestCase class
|
612
|
-
for attrname in dir(obj):
|
613
|
-
if attrname.startswith(self.testMethodPrefix):
|
614
|
-
attr = getattr(obj, attrname)
|
615
|
-
if callable(attr):
|
616
|
-
methodnames.append(attrname)
|
617
|
-
# keep track of class (obj) for convenience
|
618
|
-
tests[classname] = (obj, methodnames)
|
619
|
-
return tests
|
620
|
-
|
621
|
-
def loadTestsFromSuite(self, module, suitename):
|
622
|
-
try:
|
623
|
-
suite = getattr(module, suitename)()
|
624
|
-
except AttributeError:
|
625
|
-
return []
|
626
|
-
assert hasattr(suite, '_tests'), \
|
627
|
-
"%s.%s is not a valid TestSuite" % (module.__name__, suitename)
|
628
|
-
# python2.3 does not implement __iter__ on suites, we need to return
|
629
|
-
# _tests explicitly
|
630
|
-
return suite._tests
|
631
|
-
|
632
|
-
def loadTestsFromName(self, name, module=None):
|
633
|
-
parts = name.split('.')
|
634
|
-
if module is None or len(parts) > 2:
|
635
|
-
# let the base class do its job here
|
636
|
-
return [super(NonStrictTestLoader, self).loadTestsFromName(name)]
|
637
|
-
tests = self._collect_tests(module)
|
638
|
-
# import pprint
|
639
|
-
# pprint.pprint(tests)
|
640
|
-
collected = []
|
641
|
-
if len(parts) == 1:
|
642
|
-
pattern = parts[0]
|
643
|
-
if callable(getattr(module, pattern, None)
|
644
|
-
) and pattern not in tests:
|
645
|
-
# consider it as a suite
|
646
|
-
return self.loadTestsFromSuite(module, pattern)
|
647
|
-
if pattern in tests:
|
648
|
-
# case python unittest_foo.py MyTestTC
|
649
|
-
klass, methodnames = tests[pattern]
|
650
|
-
for methodname in methodnames:
|
651
|
-
collected = [klass(methodname)
|
652
|
-
for methodname in methodnames]
|
653
|
-
else:
|
654
|
-
# case python unittest_foo.py something
|
655
|
-
for klass, methodnames in tests.values():
|
656
|
-
collected += [klass(methodname)
|
657
|
-
for methodname in methodnames]
|
658
|
-
elif len(parts) == 2:
|
659
|
-
# case "MyClass.test_1"
|
660
|
-
classname, pattern = parts
|
661
|
-
klass, methodnames = tests.get(classname, (None, []))
|
662
|
-
for methodname in methodnames:
|
663
|
-
collected = [klass(methodname) for methodname in methodnames]
|
664
|
-
return collected
|
665
|
-
|
666
|
-
def _this_is_skipped(self, testedname):
|
667
|
-
return any([(pat in testedname) for pat in self.skipped_patterns])
|
668
|
-
|
669
|
-
def getTestCaseNames(self, testCaseClass):
|
670
|
-
"""Return a sorted sequence of method names found within testCaseClass
|
671
|
-
"""
|
672
|
-
is_skipped = self._this_is_skipped
|
673
|
-
classname = testCaseClass.__name__
|
674
|
-
if classname[0] == '_' or is_skipped(classname):
|
675
|
-
return []
|
676
|
-
testnames = super(NonStrictTestLoader, self).getTestCaseNames(
|
677
|
-
testCaseClass)
|
678
|
-
return [testname for testname in testnames if not is_skipped(testname)]
|
679
|
-
|
680
|
-
|
681
|
-
class SkipAwareTestProgram(unittest.TestProgram):
|
682
|
-
# XXX: don't try to stay close to unittest.py, use optparse
|
683
|
-
USAGE = """\
|
684
|
-
Usage: %(progName)s [options] [test] [...]
|
685
|
-
|
686
|
-
Options:
|
687
|
-
-h, --help Show this message
|
688
|
-
-v, --verbose Verbose output
|
689
|
-
-i, --pdb Enable test failure inspection
|
690
|
-
-x, --exitfirst Exit on first failure
|
691
|
-
-c, --capture Captures and prints standard out/err only on errors
|
692
|
-
-p, --printonly Only prints lines matching specified pattern
|
693
|
-
(implies capture)
|
694
|
-
-s, --skip skip test matching this pattern (no regexp for now)
|
695
|
-
-q, --quiet Minimal output
|
696
|
-
--color colorize tracebacks
|
697
|
-
|
698
|
-
-m, --match Run only test whose tag match this pattern
|
699
|
-
|
700
|
-
-P, --profile FILE: Run the tests using cProfile and saving results
|
701
|
-
in FILE
|
702
|
-
|
703
|
-
Examples:
|
704
|
-
%(progName)s - run default set of tests
|
705
|
-
%(progName)s MyTestSuite - run suite 'MyTestSuite'
|
706
|
-
%(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
|
707
|
-
%(progName)s MyTestCase - run all 'test*' test methods
|
708
|
-
in MyTestCase
|
709
|
-
"""
|
710
|
-
def __init__(self, module='__main__', defaultTest=None, batchmode=False,
|
711
|
-
cvg=None, options=None, outstream=sys.stderr):
|
712
|
-
self.batchmode = batchmode
|
713
|
-
self.cvg = cvg
|
714
|
-
self.options = options
|
715
|
-
self.outstream = outstream
|
716
|
-
super(SkipAwareTestProgram, self).__init__(
|
717
|
-
module=module, defaultTest=defaultTest,
|
718
|
-
testLoader=NonStrictTestLoader())
|
719
|
-
|
720
|
-
def parseArgs(self, argv):
|
721
|
-
self.pdbmode = False
|
722
|
-
self.exitfirst = False
|
723
|
-
self.capture = 0
|
724
|
-
self.printonly = None
|
725
|
-
self.skipped_patterns = []
|
726
|
-
self.test_pattern = None
|
727
|
-
self.tags_pattern = None
|
728
|
-
self.colorize = False
|
729
|
-
self.profile_name = None
|
730
|
-
import getopt
|
731
|
-
try:
|
732
|
-
options, args = getopt.getopt(argv[1:], 'hHvixrqcp:s:m:P:',
|
733
|
-
['help', 'verbose', 'quiet', 'pdb',
|
734
|
-
'exitfirst', 'restart', 'capture', 'printonly=',
|
735
|
-
'skip=', 'color', 'match=', 'profile='])
|
736
|
-
for opt, value in options:
|
737
|
-
if opt in ('-h', '-H', '--help'):
|
738
|
-
self.usageExit()
|
739
|
-
if opt in ('-i', '--pdb'):
|
740
|
-
self.pdbmode = True
|
741
|
-
if opt in ('-x', '--exitfirst'):
|
742
|
-
self.exitfirst = True
|
743
|
-
if opt in ('-r', '--restart'):
|
744
|
-
self.restart = True
|
745
|
-
self.exitfirst = True
|
746
|
-
if opt in ('-q', '--quiet'):
|
747
|
-
self.verbosity = 0
|
748
|
-
if opt in ('-v', '--verbose'):
|
749
|
-
self.verbosity = 2
|
750
|
-
if opt in ('-c', '--capture'):
|
751
|
-
self.capture += 1
|
752
|
-
if opt in ('-p', '--printonly'):
|
753
|
-
self.printonly = re.compile(value)
|
754
|
-
if opt in ('-s', '--skip'):
|
755
|
-
self.skipped_patterns = [pat.strip() for pat in
|
756
|
-
value.split(', ')]
|
757
|
-
if opt == '--color':
|
758
|
-
self.colorize = True
|
759
|
-
if opt in ('-m', '--match'):
|
760
|
-
#self.tags_pattern = value
|
761
|
-
self.options["tag_pattern"] = value
|
762
|
-
if opt in ('-P', '--profile'):
|
763
|
-
self.profile_name = value
|
764
|
-
self.testLoader.skipped_patterns = self.skipped_patterns
|
765
|
-
if self.printonly is not None:
|
766
|
-
self.capture += 1
|
767
|
-
if len(args) == 0 and self.defaultTest is None:
|
768
|
-
suitefunc = getattr(self.module, 'suite', None)
|
769
|
-
if isinstance(suitefunc, (types.FunctionType,
|
770
|
-
types.MethodType)):
|
771
|
-
self.test = self.module.suite()
|
772
|
-
else:
|
773
|
-
self.test = self.testLoader.loadTestsFromModule(self.module)
|
774
|
-
return
|
775
|
-
if len(args) > 0:
|
776
|
-
self.test_pattern = args[0]
|
777
|
-
self.testNames = args
|
778
|
-
else:
|
779
|
-
self.testNames = (self.defaultTest, )
|
780
|
-
self.createTests()
|
781
|
-
except getopt.error, msg:
|
782
|
-
self.usageExit(msg)
|
783
|
-
|
784
|
-
|
785
|
-
def runTests(self):
|
786
|
-
if self.profile_name:
|
787
|
-
import cProfile
|
788
|
-
cProfile.runctx('self._runTests()', globals(), locals(), self.profile_name )
|
789
|
-
else:
|
790
|
-
return self._runTests()
|
791
|
-
|
792
|
-
def _runTests(self):
|
793
|
-
if hasattr(self.module, 'setup_module'):
|
794
|
-
try:
|
795
|
-
self.module.setup_module(self.options)
|
796
|
-
except Exception, exc:
|
797
|
-
print 'setup_module error:', exc
|
798
|
-
sys.exit(1)
|
799
|
-
self.testRunner = SkipAwareTextTestRunner(verbosity=self.verbosity,
|
800
|
-
stream=self.outstream,
|
801
|
-
exitfirst=self.exitfirst,
|
802
|
-
capture=self.capture,
|
803
|
-
printonly=self.printonly,
|
804
|
-
pdbmode=self.pdbmode,
|
805
|
-
cvg=self.cvg,
|
806
|
-
test_pattern=self.test_pattern,
|
807
|
-
skipped_patterns=self.skipped_patterns,
|
808
|
-
colorize=self.colorize,
|
809
|
-
batchmode=self.batchmode,
|
810
|
-
options=self.options)
|
811
|
-
|
812
|
-
def removeSucceededTests(obj, succTests):
|
813
|
-
""" Recursive function that removes succTests from
|
814
|
-
a TestSuite or TestCase
|
815
|
-
"""
|
816
|
-
if isinstance(obj, TestSuite):
|
817
|
-
removeSucceededTests(obj._tests, succTests)
|
818
|
-
if isinstance(obj, list):
|
819
|
-
for el in obj[:]:
|
820
|
-
if isinstance(el, TestSuite):
|
821
|
-
removeSucceededTests(el, succTests)
|
822
|
-
elif isinstance(el, TestCase):
|
823
|
-
descr = '.'.join((el.__class__.__module__,
|
824
|
-
el.__class__.__name__,
|
825
|
-
el._testMethodName))
|
826
|
-
if descr in succTests:
|
827
|
-
obj.remove(el)
|
828
|
-
# take care, self.options may be None
|
829
|
-
if getattr(self.options, 'restart', False):
|
830
|
-
# retrieve succeeded tests from FILE_RESTART
|
831
|
-
try:
|
832
|
-
restartfile = open(FILE_RESTART, 'r')
|
833
|
-
try:
|
834
|
-
try:
|
835
|
-
succeededtests = list(elem.rstrip('\n\r') for elem in
|
836
|
-
restartfile.readlines())
|
837
|
-
removeSucceededTests(self.test, succeededtests)
|
838
|
-
except Exception, e:
|
839
|
-
raise e
|
840
|
-
finally:
|
841
|
-
restartfile.close()
|
842
|
-
except Exception ,e:
|
843
|
-
raise "Error while reading \
|
844
|
-
succeeded tests into", osp.join(os.getcwd(),FILE_RESTART)
|
845
|
-
|
846
|
-
result = self.testRunner.run(self.test)
|
847
|
-
# help garbage collection: we want TestSuite, which hold refs to every
|
848
|
-
# executed TestCase, to be gc'ed
|
849
|
-
del self.test
|
850
|
-
if hasattr(self.module, 'teardown_module'):
|
851
|
-
try:
|
852
|
-
self.module.teardown_module(self.options, result)
|
853
|
-
except Exception, exc:
|
854
|
-
print 'teardown_module error:', exc
|
855
|
-
sys.exit(1)
|
856
|
-
if result.debuggers and self.pdbmode:
|
857
|
-
start_interactive_mode(result)
|
858
|
-
if not self.batchmode:
|
859
|
-
sys.exit(not result.wasSuccessful())
|
860
|
-
self.result = result
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
class FDCapture:
|
866
|
-
"""adapted from py lib (http://codespeak.net/py)
|
867
|
-
Capture IO to/from a given os-level filedescriptor.
|
868
|
-
"""
|
869
|
-
def __init__(self, fd, attr='stdout', printonly=None):
|
870
|
-
self.targetfd = fd
|
871
|
-
self.tmpfile = os.tmpfile() # self.maketempfile()
|
872
|
-
self.printonly = printonly
|
873
|
-
# save original file descriptor
|
874
|
-
self._savefd = os.dup(fd)
|
875
|
-
# override original file descriptor
|
876
|
-
os.dup2(self.tmpfile.fileno(), fd)
|
877
|
-
# also modify sys module directly
|
878
|
-
self.oldval = getattr(sys, attr)
|
879
|
-
setattr(sys, attr, self) # self.tmpfile)
|
880
|
-
self.attr = attr
|
881
|
-
|
882
|
-
def write(self, msg):
|
883
|
-
# msg might be composed of several lines
|
884
|
-
for line in msg.splitlines():
|
885
|
-
line += '\n' # keepdend=True is not enough
|
886
|
-
if self.printonly is None or self.printonly.search(line) is None:
|
887
|
-
self.tmpfile.write(line)
|
888
|
-
else:
|
889
|
-
os.write(self._savefd, line)
|
890
|
-
|
891
|
-
## def maketempfile(self):
|
892
|
-
## tmpf = os.tmpfile()
|
893
|
-
## fd = os.dup(tmpf.fileno())
|
894
|
-
## newf = os.fdopen(fd, tmpf.mode, 0) # No buffering
|
895
|
-
## tmpf.close()
|
896
|
-
## return newf
|
897
|
-
|
898
|
-
def restore(self):
|
899
|
-
"""restore original fd and returns captured output"""
|
900
|
-
#XXX: hack hack hack
|
901
|
-
self.tmpfile.flush()
|
902
|
-
try:
|
903
|
-
ref_file = getattr(sys, '__%s__' % self.attr)
|
904
|
-
ref_file.flush()
|
905
|
-
except AttributeError:
|
906
|
-
pass
|
907
|
-
if hasattr(self.oldval, 'flush'):
|
908
|
-
self.oldval.flush()
|
909
|
-
# restore original file descriptor
|
910
|
-
os.dup2(self._savefd, self.targetfd)
|
911
|
-
# restore sys module
|
912
|
-
setattr(sys, self.attr, self.oldval)
|
913
|
-
# close backup descriptor
|
914
|
-
os.close(self._savefd)
|
915
|
-
# go to beginning of file and read it
|
916
|
-
self.tmpfile.seek(0)
|
917
|
-
return self.tmpfile.read()
|
918
|
-
|
919
|
-
|
920
|
-
def _capture(which='stdout', printonly=None):
|
921
|
-
"""private method, should not be called directly
|
922
|
-
(cf. capture_stdout() and capture_stderr())
|
923
|
-
"""
|
924
|
-
assert which in ('stdout', 'stderr'
|
925
|
-
), "Can only capture stdout or stderr, not %s" % which
|
926
|
-
if which == 'stdout':
|
927
|
-
fd = 1
|
928
|
-
else:
|
929
|
-
fd = 2
|
930
|
-
return FDCapture(fd, which, printonly)
|
931
|
-
|
932
|
-
def capture_stdout(printonly=None):
|
933
|
-
"""captures the standard output
|
934
|
-
|
935
|
-
returns a handle object which has a `restore()` method.
|
936
|
-
The restore() method returns the captured stdout and restores it
|
937
|
-
"""
|
938
|
-
return _capture('stdout', printonly)
|
939
|
-
|
940
|
-
def capture_stderr(printonly=None):
|
941
|
-
"""captures the standard error output
|
942
|
-
|
943
|
-
returns a handle object which has a `restore()` method.
|
944
|
-
The restore() method returns the captured stderr and restores it
|
945
|
-
"""
|
946
|
-
return _capture('stderr', printonly)
|
947
|
-
|
948
|
-
|
949
|
-
def unittest_main(module='__main__', defaultTest=None,
|
950
|
-
batchmode=False, cvg=None, options=None,
|
951
|
-
outstream=sys.stderr):
|
952
|
-
"""use this function if you want to have the same functionality
|
953
|
-
as unittest.main"""
|
954
|
-
return SkipAwareTestProgram(module, defaultTest, batchmode,
|
955
|
-
cvg, options, outstream)
|
956
|
-
|
957
|
-
class TestSkipped(Exception):
|
958
|
-
"""raised when a test is skipped"""
|
959
|
-
|
960
|
-
class InnerTestSkipped(TestSkipped):
|
961
|
-
"""raised when a test is skipped"""
|
962
|
-
|
963
|
-
def is_generator(function):
|
964
|
-
flags = function.func_code.co_flags
|
965
|
-
return flags & CO_GENERATOR
|
966
|
-
|
967
|
-
|
968
|
-
def parse_generative_args(params):
|
969
|
-
args = []
|
970
|
-
varargs = ()
|
971
|
-
kwargs = {}
|
972
|
-
flags = 0 # 2 <=> starargs, 4 <=> kwargs
|
973
|
-
for param in params:
|
974
|
-
if isinstance(param, starargs):
|
975
|
-
varargs = param
|
976
|
-
if flags:
|
977
|
-
raise TypeError('found starargs after keywords !')
|
978
|
-
flags |= 2
|
979
|
-
args += list(varargs)
|
980
|
-
elif isinstance(param, keywords):
|
981
|
-
kwargs = param
|
982
|
-
if flags & 4:
|
983
|
-
raise TypeError('got multiple keywords parameters')
|
984
|
-
flags |= 4
|
985
|
-
elif flags & 2 or flags & 4:
|
986
|
-
raise TypeError('found parameters after kwargs or args')
|
987
|
-
else:
|
988
|
-
args.append(param)
|
989
|
-
|
990
|
-
return args, kwargs
|
991
|
-
|
992
|
-
class InnerTest(tuple):
|
993
|
-
def __new__(cls, name, *data):
|
994
|
-
instance = tuple.__new__(cls, data)
|
995
|
-
instance.name = name
|
996
|
-
return instance
|
997
|
-
|
998
|
-
|
999
|
-
class TestCase(unittest.TestCase):
|
1000
|
-
"""unittest.TestCase with some additional methods"""
|
1001
|
-
|
1002
|
-
capture = False
|
1003
|
-
pdbclass = Debugger
|
1004
|
-
|
1005
|
-
def __init__(self, methodName='runTest'):
|
1006
|
-
super(TestCase, self).__init__(methodName)
|
1007
|
-
# internal API changed in python2.5
|
1008
|
-
if sys.version_info >= (2, 5):
|
1009
|
-
self.__exc_info = self._exc_info
|
1010
|
-
self.__testMethodName = self._testMethodName
|
1011
|
-
else:
|
1012
|
-
# let's give easier access to _testMethodName to every subclasses
|
1013
|
-
self._testMethodName = self.__testMethodName
|
1014
|
-
self._captured_stdout = ""
|
1015
|
-
self._captured_stderr = ""
|
1016
|
-
self._out = []
|
1017
|
-
self._err = []
|
1018
|
-
self._current_test_descr = None
|
1019
|
-
self._options_ = None
|
1020
|
-
|
1021
|
-
def datadir(cls): # pylint: disable-msg=E0213
|
1022
|
-
"""helper attribute holding the standard test's data directory
|
1023
|
-
|
1024
|
-
NOTE: this is a logilab's standard
|
1025
|
-
"""
|
1026
|
-
mod = __import__(cls.__module__)
|
1027
|
-
return osp.join(osp.dirname(osp.abspath(mod.__file__)), 'data')
|
1028
|
-
# cache it (use a class method to cache on class since TestCase is
|
1029
|
-
# instantiated for each test run)
|
1030
|
-
datadir = classproperty(cached(datadir))
|
1031
|
-
|
1032
|
-
def datapath(cls, *fname):
|
1033
|
-
"""joins the object's datadir and `fname`"""
|
1034
|
-
return osp.join(cls.datadir, *fname)
|
1035
|
-
datapath = classmethod(datapath)
|
1036
|
-
|
1037
|
-
def set_description(self, descr):
|
1038
|
-
"""sets the current test's description.
|
1039
|
-
This can be useful for generative tests because it allows to specify
|
1040
|
-
a description per yield
|
1041
|
-
"""
|
1042
|
-
self._current_test_descr = descr
|
1043
|
-
|
1044
|
-
# override default's unittest.py feature
|
1045
|
-
def shortDescription(self):
|
1046
|
-
"""override default unitest shortDescription to handle correctly
|
1047
|
-
generative tests
|
1048
|
-
"""
|
1049
|
-
if self._current_test_descr is not None:
|
1050
|
-
return self._current_test_descr
|
1051
|
-
return super(TestCase, self).shortDescription()
|
1052
|
-
|
1053
|
-
|
1054
|
-
def captured_output(self):
|
1055
|
-
"""return a two tuple with standard output and error stripped"""
|
1056
|
-
return self._captured_stdout.strip(), self._captured_stderr.strip()
|
1057
|
-
|
1058
|
-
def _start_capture(self):
|
1059
|
-
"""start_capture if enable"""
|
1060
|
-
if self.capture:
|
1061
|
-
warnings.simplefilter('ignore', DeprecationWarning)
|
1062
|
-
self.start_capture()
|
1063
|
-
|
1064
|
-
def _stop_capture(self):
|
1065
|
-
"""stop_capture and restore previous output"""
|
1066
|
-
self._force_output_restore()
|
1067
|
-
|
1068
|
-
def start_capture(self, printonly=None):
|
1069
|
-
"""start_capture"""
|
1070
|
-
self._out.append(capture_stdout(printonly or self._printonly))
|
1071
|
-
self._err.append(capture_stderr(printonly or self._printonly))
|
1072
|
-
|
1073
|
-
def printonly(self, pattern, flags=0):
|
1074
|
-
"""set the pattern of line to print"""
|
1075
|
-
rgx = re.compile(pattern, flags)
|
1076
|
-
if self._out:
|
1077
|
-
self._out[-1].printonly = rgx
|
1078
|
-
self._err[-1].printonly = rgx
|
1079
|
-
else:
|
1080
|
-
self.start_capture(printonly=rgx)
|
1081
|
-
|
1082
|
-
def stop_capture(self):
|
1083
|
-
"""stop output and error capture"""
|
1084
|
-
if self._out:
|
1085
|
-
_out = self._out.pop()
|
1086
|
-
_err = self._err.pop()
|
1087
|
-
return _out.restore(), _err.restore()
|
1088
|
-
return '', ''
|
1089
|
-
|
1090
|
-
def _force_output_restore(self):
|
1091
|
-
"""remove all capture set"""
|
1092
|
-
while self._out:
|
1093
|
-
self._captured_stdout += self._out.pop().restore()
|
1094
|
-
self._captured_stderr += self._err.pop().restore()
|
1095
|
-
|
1096
|
-
def quiet_run(self, result, func, *args, **kwargs):
|
1097
|
-
self._start_capture()
|
1098
|
-
try:
|
1099
|
-
func(*args, **kwargs)
|
1100
|
-
except (KeyboardInterrupt, SystemExit):
|
1101
|
-
self._stop_capture()
|
1102
|
-
raise
|
1103
|
-
except:
|
1104
|
-
self._stop_capture()
|
1105
|
-
result.addError(self, self.__exc_info())
|
1106
|
-
return False
|
1107
|
-
self._stop_capture()
|
1108
|
-
return True
|
1109
|
-
|
1110
|
-
def _get_test_method(self):
|
1111
|
-
"""return the test method"""
|
1112
|
-
return getattr(self, self.__testMethodName)
|
1113
|
-
|
1114
|
-
|
1115
|
-
def optval(self, option, default=None):
|
1116
|
-
"""return the option value or default if the option is not define"""
|
1117
|
-
return getattr(self._options_, option, default)
|
1118
|
-
|
1119
|
-
def __call__(self, result=None, runcondition=None, options=None):
|
1120
|
-
"""rewrite TestCase.__call__ to support generative tests
|
1121
|
-
This is mostly a copy/paste from unittest.py (i.e same
|
1122
|
-
variable names, same logic, except for the generative tests part)
|
1123
|
-
"""
|
1124
|
-
if result is None:
|
1125
|
-
result = self.defaultTestResult()
|
1126
|
-
result.pdbclass = self.pdbclass
|
1127
|
-
# if self.capture is True here, it means it was explicitly specified
|
1128
|
-
# in the user's TestCase class. If not, do what was asked on cmd line
|
1129
|
-
self.capture = self.capture or getattr(result, 'capture', False)
|
1130
|
-
self._options_ = options
|
1131
|
-
self._printonly = getattr(result, 'printonly', None)
|
1132
|
-
# if result.cvg:
|
1133
|
-
# result.cvg.start()
|
1134
|
-
testMethod = self._get_test_method()
|
1135
|
-
if runcondition and not runcondition(testMethod):
|
1136
|
-
return # test is skipped
|
1137
|
-
result.startTest(self)
|
1138
|
-
try:
|
1139
|
-
if not self.quiet_run(result, self.setUp):
|
1140
|
-
return
|
1141
|
-
generative = is_generator(testMethod.im_func)
|
1142
|
-
# generative tests
|
1143
|
-
if generative:
|
1144
|
-
self._proceed_generative(result, testMethod,
|
1145
|
-
runcondition)
|
1146
|
-
else:
|
1147
|
-
status = self._proceed(result, testMethod)
|
1148
|
-
success = (status == 0)
|
1149
|
-
if not self.quiet_run(result, self.tearDown):
|
1150
|
-
return
|
1151
|
-
if not generative and success:
|
1152
|
-
if hasattr(options, "exitfirst") and options.exitfirst:
|
1153
|
-
# add this test to restart file
|
1154
|
-
try:
|
1155
|
-
restartfile = open(FILE_RESTART, 'a')
|
1156
|
-
try:
|
1157
|
-
try:
|
1158
|
-
descr = '.'.join((self.__class__.__module__,
|
1159
|
-
self.__class__.__name__,
|
1160
|
-
self._testMethodName))
|
1161
|
-
restartfile.write(descr+os.linesep)
|
1162
|
-
except Exception, e:
|
1163
|
-
raise e
|
1164
|
-
finally:
|
1165
|
-
restartfile.close()
|
1166
|
-
except Exception, e:
|
1167
|
-
print >> sys.__stderr__, "Error while saving \
|
1168
|
-
succeeded test into", osp.join(os.getcwd(),FILE_RESTART)
|
1169
|
-
raise e
|
1170
|
-
result.addSuccess(self)
|
1171
|
-
finally:
|
1172
|
-
# if result.cvg:
|
1173
|
-
# result.cvg.stop()
|
1174
|
-
result.stopTest(self)
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
def _proceed_generative(self, result, testfunc, runcondition=None):
|
1179
|
-
# cancel startTest()'s increment
|
1180
|
-
result.testsRun -= 1
|
1181
|
-
self._start_capture()
|
1182
|
-
success = True
|
1183
|
-
try:
|
1184
|
-
for params in testfunc():
|
1185
|
-
if runcondition and not runcondition(testfunc,
|
1186
|
-
skipgenerator=False):
|
1187
|
-
if not (isinstance(params, InnerTest)
|
1188
|
-
and runcondition(params)):
|
1189
|
-
continue
|
1190
|
-
if not isinstance(params, (tuple, list)):
|
1191
|
-
params = (params, )
|
1192
|
-
func = params[0]
|
1193
|
-
args, kwargs = parse_generative_args(params[1:])
|
1194
|
-
# increment test counter manually
|
1195
|
-
result.testsRun += 1
|
1196
|
-
status = self._proceed(result, func, args, kwargs)
|
1197
|
-
if status == 0:
|
1198
|
-
result.addSuccess(self)
|
1199
|
-
success = True
|
1200
|
-
else:
|
1201
|
-
success = False
|
1202
|
-
if status == 2:
|
1203
|
-
result.shouldStop = True
|
1204
|
-
if result.shouldStop: # either on error or on exitfirst + error
|
1205
|
-
break
|
1206
|
-
except:
|
1207
|
-
# if an error occurs between two yield
|
1208
|
-
result.addError(self, self.__exc_info())
|
1209
|
-
success = False
|
1210
|
-
self._stop_capture()
|
1211
|
-
return success
|
1212
|
-
|
1213
|
-
def _proceed(self, result, testfunc, args=(), kwargs=None):
|
1214
|
-
"""proceed the actual test
|
1215
|
-
returns 0 on success, 1 on failure, 2 on error
|
1216
|
-
|
1217
|
-
Note: addSuccess can't be called here because we have to wait
|
1218
|
-
for tearDown to be successfully executed to declare the test as
|
1219
|
-
successful
|
1220
|
-
"""
|
1221
|
-
self._start_capture()
|
1222
|
-
kwargs = kwargs or {}
|
1223
|
-
try:
|
1224
|
-
testfunc(*args, **kwargs)
|
1225
|
-
self._stop_capture()
|
1226
|
-
except self.failureException:
|
1227
|
-
self._stop_capture()
|
1228
|
-
result.addFailure(self, self.__exc_info())
|
1229
|
-
return 1
|
1230
|
-
except KeyboardInterrupt:
|
1231
|
-
self._stop_capture()
|
1232
|
-
raise
|
1233
|
-
except InnerTestSkipped, e:
|
1234
|
-
result.addSkipped(self, e)
|
1235
|
-
return 1
|
1236
|
-
except:
|
1237
|
-
self._stop_capture()
|
1238
|
-
result.addError(self, self.__exc_info())
|
1239
|
-
return 2
|
1240
|
-
return 0
|
1241
|
-
|
1242
|
-
def defaultTestResult(self):
|
1243
|
-
"""return a new instance of the defaultTestResult"""
|
1244
|
-
return SkipAwareTestResult()
|
1245
|
-
|
1246
|
-
def skip(self, msg=None):
|
1247
|
-
"""mark a test as skipped for the <msg> reason"""
|
1248
|
-
msg = msg or 'test was skipped'
|
1249
|
-
raise TestSkipped(msg)
|
1250
|
-
|
1251
|
-
def innerSkip(self, msg=None):
|
1252
|
-
"""mark a generative test as skipped for the <msg> reason"""
|
1253
|
-
msg = msg or 'test was skipped'
|
1254
|
-
raise InnerTestSkipped(msg)
|
1255
|
-
|
1256
|
-
def assertIn(self, object, set):
|
1257
|
-
"""assert <object> are in <set>"""
|
1258
|
-
self.assert_(object in set, "%s not in %s" % (object, set))
|
1259
|
-
|
1260
|
-
def assertNotIn(self, object, set):
|
1261
|
-
"""assert <object> are not in <set>"""
|
1262
|
-
self.assert_(object not in set, "%s in %s" % (object, set))
|
1263
|
-
|
1264
|
-
def assertDictEquals(self, dict1, dict2):
|
1265
|
-
"""compares two dicts
|
1266
|
-
|
1267
|
-
If the two dict differ, the first difference is shown in the error
|
1268
|
-
message
|
1269
|
-
"""
|
1270
|
-
dict1 = dict(dict1)
|
1271
|
-
msgs = []
|
1272
|
-
for key, value in dict2.items():
|
1273
|
-
try:
|
1274
|
-
if dict1[key] != value:
|
1275
|
-
msgs.append('%r != %r for key %r' % (dict1[key], value,
|
1276
|
-
key))
|
1277
|
-
del dict1[key]
|
1278
|
-
except KeyError:
|
1279
|
-
msgs.append('missing %r key' % key)
|
1280
|
-
if dict1:
|
1281
|
-
msgs.append('dict2 is lacking %r' % dict1)
|
1282
|
-
if msgs:
|
1283
|
-
self.fail('\n'.join(msgs))
|
1284
|
-
assertDictEqual = assertDictEquals
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
def assertUnorderedIterableEquals(self, got, expected, msg=None):
|
1289
|
-
"""compares two iterable and shows difference between both"""
|
1290
|
-
got, expected = list(got), list(expected)
|
1291
|
-
self.assertSetEqual(set(got), set(expected), msg)
|
1292
|
-
if len(got) != len(expected):
|
1293
|
-
if msg is None:
|
1294
|
-
msg = ['Iterable have the same elements but not the same number',
|
1295
|
-
'\t<element>\t<expected>i\t<got>']
|
1296
|
-
got_count = {}
|
1297
|
-
expected_count = {}
|
1298
|
-
for element in got:
|
1299
|
-
got_count[element] = got_count.get(element,0) + 1
|
1300
|
-
for element in expected:
|
1301
|
-
expected_count[element] = expected_count.get(element,0) + 1
|
1302
|
-
# we know that got_count.key() == expected_count.key()
|
1303
|
-
# because of assertSetEquals
|
1304
|
-
for element, count in got_count.iteritems():
|
1305
|
-
other_count = expected_count[element]
|
1306
|
-
if other_count != count:
|
1307
|
-
msg.append('\t%s\t%s\t%s' % (element, other_count, count))
|
1308
|
-
|
1309
|
-
self.fail(msg)
|
1310
|
-
|
1311
|
-
assertUnorderedIterableEqual = assertUnorderedIterableEquals
|
1312
|
-
assertUnordIterEquals = assertUnordIterEqual = assertUnorderedIterableEqual
|
1313
|
-
|
1314
|
-
def assertSetEquals(self,got,expected, msg=None):
|
1315
|
-
if not(isinstance(got, set) and isinstance(expected, set)):
|
1316
|
-
warnings.warn("the assertSetEquals function if now intended for set only."\
|
1317
|
-
"use assertUnorderedIterableEquals instead.",
|
1318
|
-
DeprecationWarning, 2)
|
1319
|
-
return self.assertUnorderedIterableEquals(got,expected, msg)
|
1320
|
-
|
1321
|
-
items={}
|
1322
|
-
items['missing'] = expected - got
|
1323
|
-
items['unexpected'] = got - expected
|
1324
|
-
if any(items.itervalues()):
|
1325
|
-
if msg is None:
|
1326
|
-
msg = '\n'.join('%s:\n\t%s' % (key,"\n\t".join(str(value) for value in values))
|
1327
|
-
for key, values in items.iteritems() if values)
|
1328
|
-
self.fail(msg)
|
1329
|
-
|
1330
|
-
|
1331
|
-
assertSetEqual = assertSetEquals
|
1332
|
-
|
1333
|
-
def assertListEquals(self, list_1, list_2, msg=None):
|
1334
|
-
"""compares two lists
|
1335
|
-
|
1336
|
-
If the two list differ, the first difference is shown in the error
|
1337
|
-
message
|
1338
|
-
"""
|
1339
|
-
_l1 = list_1[:]
|
1340
|
-
for i, value in enumerate(list_2):
|
1341
|
-
try:
|
1342
|
-
if _l1[0] != value:
|
1343
|
-
from pprint import pprint
|
1344
|
-
pprint(list_1)
|
1345
|
-
pprint(list_2)
|
1346
|
-
self.fail('%r != %r for index %d' % (_l1[0], value, i))
|
1347
|
-
del _l1[0]
|
1348
|
-
except IndexError:
|
1349
|
-
if msg is None:
|
1350
|
-
msg = 'list_1 has only %d elements, not %s '\
|
1351
|
-
'(at least %r missing)'% (i, len(list_2), value)
|
1352
|
-
self.fail(msg)
|
1353
|
-
if _l1:
|
1354
|
-
if msg is None:
|
1355
|
-
msg = 'list_2 is lacking %r' % _l1
|
1356
|
-
self.fail(msg)
|
1357
|
-
assertListEqual = assertListEquals
|
1358
|
-
|
1359
|
-
def assertLinesEquals(self, list_1, list_2, msg=None, striplines=False):
|
1360
|
-
"""assert list of lines are equal"""
|
1361
|
-
lines1 = list_1.splitlines()
|
1362
|
-
if striplines:
|
1363
|
-
lines1 = [l.strip() for l in lines1]
|
1364
|
-
lines2 = list_2.splitlines()
|
1365
|
-
if striplines:
|
1366
|
-
lines2 = [l.strip() for l in lines2]
|
1367
|
-
self.assertListEquals(lines1, lines2, msg)
|
1368
|
-
assertLineEqual = assertLinesEquals
|
1369
|
-
|
1370
|
-
def assertXMLWellFormed(self, stream, msg=None, context=2):
|
1371
|
-
"""asserts the XML stream is well-formed (no DTD conformance check)
|
1372
|
-
:context: number of context lines in standard msg. all data if negativ
|
1373
|
-
only available with element tree
|
1374
|
-
"""
|
1375
|
-
try:
|
1376
|
-
from xml.etree.ElementTree import parse
|
1377
|
-
self._assertETXMLWellFormed(stream, parse, msg)
|
1378
|
-
except ImportError:
|
1379
|
-
from xml.sax import make_parser, SAXParseException
|
1380
|
-
parser = make_parser()
|
1381
|
-
try:
|
1382
|
-
parser.parse(stream)
|
1383
|
-
except SAXParseException, ex:
|
1384
|
-
if msg is None:
|
1385
|
-
stream.seek(0)
|
1386
|
-
for _ in xrange(ex.getLineNumber()):
|
1387
|
-
line = stream.readline()
|
1388
|
-
pointer = ('' * (ex.getLineNumber() - 1)) + '^'
|
1389
|
-
msg = 'XML stream not well formed: %s\n%s%s' % (ex, line, pointer)
|
1390
|
-
self.fail(msg)
|
1391
|
-
|
1392
|
-
def assertXMLStringWellFormed(self, xml_string, msg=None, context=2):
|
1393
|
-
"""asserts the XML string is well-formed (no DTD conformance check)
|
1394
|
-
:context: number of context lines in standard msg. all data if negativ
|
1395
|
-
only available with element tree
|
1396
|
-
"""
|
1397
|
-
try:
|
1398
|
-
from xml.etree.ElementTree import fromstring
|
1399
|
-
self._assertETXMLWellFormed(xml_string, fromstring, msg)
|
1400
|
-
except ImportError:
|
1401
|
-
raise
|
1402
|
-
stream = StringIO(xml_string)
|
1403
|
-
self.assertXMLWellFormed(stream, msg)
|
1404
|
-
|
1405
|
-
def _assertETXMLWellFormed(self, data, parse, msg=None, context=2):
|
1406
|
-
"""internal function used by /assertXML(String)?WellFormed/ functions
|
1407
|
-
:data: xml_data
|
1408
|
-
:parse: appropriate parser function for this data
|
1409
|
-
:msg: error message
|
1410
|
-
:context: number of context lines in standard msg. all data if negativ
|
1411
|
-
only available with element tree
|
1412
|
-
"""
|
1413
|
-
from xml.parsers.expat import ExpatError
|
1414
|
-
try:
|
1415
|
-
parse(data)
|
1416
|
-
except ExpatError, ex:
|
1417
|
-
if msg is None:
|
1418
|
-
if hasattr(data, 'readlines'): #file like object
|
1419
|
-
stream.seek(0)
|
1420
|
-
lines = stream.readlines()
|
1421
|
-
else:
|
1422
|
-
lines =data.splitlines(True)
|
1423
|
-
nb_lines = len(lines)
|
1424
|
-
context_lines = []
|
1425
|
-
|
1426
|
-
if context < 0:
|
1427
|
-
start = 1
|
1428
|
-
end = nb_lines
|
1429
|
-
else:
|
1430
|
-
start = max(ex.lineno-context, 1)
|
1431
|
-
end = min(ex.lineno+context, nb_lines)
|
1432
|
-
line_number_length = len('%i' % end)
|
1433
|
-
line_pattern = " %%%ii: %%s" % line_number_length
|
1434
|
-
|
1435
|
-
for line_no in xrange(start, ex.lineno):
|
1436
|
-
context_lines.append(line_pattern % (line_no, lines[line_no-1]))
|
1437
|
-
context_lines.append(line_pattern % (ex.lineno, lines[ex.lineno-1]))
|
1438
|
-
context_lines.append('%s^\n' % (' ' * (1 + line_number_length + 2 +ex.offset)))
|
1439
|
-
for line_no in xrange(ex.lineno+1, end+1):
|
1440
|
-
context_lines.append(line_pattern % (line_no, lines[line_no-1]))
|
1441
|
-
|
1442
|
-
rich_context = ''.join(context_lines)
|
1443
|
-
msg = 'XML stream not well formed: %s\n%s' % (ex, rich_context)
|
1444
|
-
self.fail(msg)
|
1445
|
-
|
1446
|
-
|
1447
|
-
def assertXMLEqualsTuple(self, element, tup):
|
1448
|
-
"""compare an ElementTree Element to a tuple formatted as follow:
|
1449
|
-
(tagname, [attrib[, children[, text[, tail]]]])"""
|
1450
|
-
# check tag
|
1451
|
-
self.assertTextEquals(element.tag, tup[0])
|
1452
|
-
# check attrib
|
1453
|
-
if len(element.attrib) or len(tup)>1:
|
1454
|
-
if len(tup)<=1:
|
1455
|
-
self.fail( "tuple %s has no attributes (%s expected)"%(tup,
|
1456
|
-
dict(element.attrib)))
|
1457
|
-
self.assertDictEquals(element.attrib, tup[1])
|
1458
|
-
# check children
|
1459
|
-
if len(element) or len(tup)>2:
|
1460
|
-
if len(tup)<=2:
|
1461
|
-
self.fail( "tuple %s has no children (%i expected)"%(tup,
|
1462
|
-
len(element)))
|
1463
|
-
if len(element) != len(tup[2]):
|
1464
|
-
self.fail( "tuple %s has %i children%s (%i expected)"%(tup,
|
1465
|
-
len(tup[2]),
|
1466
|
-
('', 's')[len(tup[2])>1], len(element)))
|
1467
|
-
for index in xrange(len(tup[2])):
|
1468
|
-
self.assertXMLEqualsTuple(element[index], tup[2][index])
|
1469
|
-
#check text
|
1470
|
-
if element.text or len(tup)>3:
|
1471
|
-
if len(tup)<=3:
|
1472
|
-
self.fail( "tuple %s has no text value (%r expected)"%(tup,
|
1473
|
-
element.text))
|
1474
|
-
self.assertTextEquals(element.text, tup[3])
|
1475
|
-
#check tail
|
1476
|
-
if element.tail or len(tup)>4:
|
1477
|
-
if len(tup)<=4:
|
1478
|
-
self.fail( "tuple %s has no tail value (%r expected)"%(tup,
|
1479
|
-
element.tail))
|
1480
|
-
self.assertTextEquals(element.tail, tup[4])
|
1481
|
-
|
1482
|
-
def _difftext(self, lines1, lines2, junk=None, msg_prefix='Texts differ'):
|
1483
|
-
junk = junk or (' ', '\t')
|
1484
|
-
# result is a generator
|
1485
|
-
result = difflib.ndiff(lines1, lines2, charjunk=lambda x: x in junk)
|
1486
|
-
read = []
|
1487
|
-
for line in result:
|
1488
|
-
read.append(line)
|
1489
|
-
# lines that don't start with a ' ' are diff ones
|
1490
|
-
if not line.startswith(' '):
|
1491
|
-
self.fail('\n'.join(['%s\n'%msg_prefix]+read + list(result)))
|
1492
|
-
|
1493
|
-
def assertTextEquals(self, text1, text2, junk=None,
|
1494
|
-
msg_prefix='Text differ', striplines=False):
|
1495
|
-
"""compare two multiline strings (using difflib and splitlines())"""
|
1496
|
-
msg = []
|
1497
|
-
if not isinstance(text1, basestring):
|
1498
|
-
msg.append('text1 is not a string (%s)'%(type(text1)))
|
1499
|
-
if not isinstance(text2, basestring):
|
1500
|
-
msg.append('text2 is not a string (%s)'%(type(text2)))
|
1501
|
-
if msg:
|
1502
|
-
self.fail('\n'.join(msg))
|
1503
|
-
lines1 = text1.strip().splitlines(True)
|
1504
|
-
lines2 = text2.strip().splitlines(True)
|
1505
|
-
if striplines:
|
1506
|
-
lines1 = [line.strip() for line in lines1]
|
1507
|
-
lines2 = [line.strip() for line in lines2]
|
1508
|
-
self._difftext(lines1, lines2, junk, msg_prefix)
|
1509
|
-
assertTextEqual = assertTextEquals
|
1510
|
-
|
1511
|
-
def assertStreamEquals(self, stream1, stream2, junk=None,
|
1512
|
-
msg_prefix='Stream differ'):
|
1513
|
-
"""compare two streams (using difflib and readlines())"""
|
1514
|
-
# if stream2 is stream2, readlines() on stream1 will also read lines
|
1515
|
-
# in stream2, so they'll appear different, although they're not
|
1516
|
-
if stream1 is stream2:
|
1517
|
-
return
|
1518
|
-
# make sure we compare from the beginning of the stream
|
1519
|
-
stream1.seek(0)
|
1520
|
-
stream2.seek(0)
|
1521
|
-
# compare
|
1522
|
-
self._difftext(stream1.readlines(), stream2.readlines(), junk,
|
1523
|
-
msg_prefix)
|
1524
|
-
|
1525
|
-
assertStreamEqual = assertStreamEquals
|
1526
|
-
def assertFileEquals(self, fname1, fname2, junk=(' ', '\t')):
|
1527
|
-
"""compares two files using difflib"""
|
1528
|
-
self.assertStreamEqual(file(fname1), file(fname2), junk,
|
1529
|
-
msg_prefix='Files differs\n-:%s\n+:%s\n'%(fname1, fname2))
|
1530
|
-
assertFileEqual = assertFileEquals
|
1531
|
-
|
1532
|
-
|
1533
|
-
def assertDirEquals(self, path_a, path_b):
|
1534
|
-
"""compares two files using difflib"""
|
1535
|
-
assert osp.exists(path_a), "%s doesn't exists" % path_a
|
1536
|
-
assert osp.exists(path_b), "%s doesn't exists" % path_b
|
1537
|
-
|
1538
|
-
all_a = [ (ipath[len(path_a):].lstrip('/'), idirs, ifiles)
|
1539
|
-
for ipath, idirs, ifiles in os.walk(path_a)]
|
1540
|
-
all_a.sort(key=itemgetter(0))
|
1541
|
-
|
1542
|
-
all_b = [ (ipath[len(path_b):].lstrip('/'), idirs, ifiles)
|
1543
|
-
for ipath, idirs, ifiles in os.walk(path_b)]
|
1544
|
-
all_b.sort(key=itemgetter(0))
|
1545
|
-
|
1546
|
-
iter_a, iter_b = iter(all_a), iter(all_b)
|
1547
|
-
partial_iter = True
|
1548
|
-
ipath_a, idirs_a, ifiles_a = data_a = None, None, None
|
1549
|
-
while True:
|
1550
|
-
try:
|
1551
|
-
ipath_a, idirs_a, ifiles_a = datas_a = iter_a.next()
|
1552
|
-
partial_iter = False
|
1553
|
-
ipath_b, idirs_b, ifiles_b = datas_b = iter_b.next()
|
1554
|
-
partial_iter = True
|
1555
|
-
|
1556
|
-
|
1557
|
-
self.assert_(ipath_a == ipath_b,
|
1558
|
-
"unexpected %s in %s while looking %s from %s" %
|
1559
|
-
(ipath_a, path_a, ipath_b, path_b))
|
1560
|
-
|
1561
|
-
|
1562
|
-
errors = {}
|
1563
|
-
sdirs_a = set(idirs_a)
|
1564
|
-
sdirs_b = set(idirs_b)
|
1565
|
-
errors["unexpected directories"] = sdirs_a - sdirs_b
|
1566
|
-
errors["missing directories"] = sdirs_b - sdirs_a
|
1567
|
-
|
1568
|
-
sfiles_a = set(ifiles_a)
|
1569
|
-
sfiles_b = set(ifiles_b)
|
1570
|
-
errors["unexpected files"] = sfiles_a - sfiles_b
|
1571
|
-
errors["missing files"] = sfiles_b - sfiles_a
|
1572
|
-
|
1573
|
-
|
1574
|
-
msgs = [ "%s: %s"% (name, items)
|
1575
|
-
for name, items in errors.iteritems() if items]
|
1576
|
-
|
1577
|
-
if msgs:
|
1578
|
-
msgs.insert(0,"%s and %s differ :" % (
|
1579
|
-
osp.join(path_a, ipath_a),
|
1580
|
-
osp.join(path_b, ipath_b),
|
1581
|
-
))
|
1582
|
-
self.fail("\n".join(msgs))
|
1583
|
-
|
1584
|
-
for files in (ifiles_a, ifiles_b):
|
1585
|
-
files.sort()
|
1586
|
-
|
1587
|
-
for index, path in enumerate(ifiles_a):
|
1588
|
-
self.assertFileEquals(osp.join(path_a, ipath_a, path),
|
1589
|
-
osp.join(path_b, ipath_b, ifiles_b[index]))
|
1590
|
-
|
1591
|
-
except StopIteration:
|
1592
|
-
break
|
1593
|
-
|
1594
|
-
|
1595
|
-
assertDirEqual = assertDirEquals
|
1596
|
-
|
1597
|
-
|
1598
|
-
def assertIsInstance(self, obj, klass, msg=None, strict=False):
|
1599
|
-
"""compares two files using difflib"""
|
1600
|
-
if msg is None:
|
1601
|
-
if strict:
|
1602
|
-
msg = '%r is not of class %s but of %s'
|
1603
|
-
else:
|
1604
|
-
msg = '%r is not an instance of %s but of %s'
|
1605
|
-
msg = msg % (obj, klass, type(obj))
|
1606
|
-
if strict:
|
1607
|
-
self.assert_(obj.__class__ is klass, msg)
|
1608
|
-
else:
|
1609
|
-
self.assert_(isinstance(obj, klass), msg)
|
1610
|
-
|
1611
|
-
def assertIs(self, obj, other, msg=None):
|
1612
|
-
"""compares identity of two reference"""
|
1613
|
-
if msg is None:
|
1614
|
-
msg = "%r is not %r"%(obj, other)
|
1615
|
-
self.assert_(obj is other, msg)
|
1616
|
-
|
1617
|
-
|
1618
|
-
def assertIsNot(self, obj, other, msg=None):
|
1619
|
-
"""compares identity of two reference"""
|
1620
|
-
if msg is None:
|
1621
|
-
msg = "%r is %r"%(obj, other)
|
1622
|
-
self.assert_(obj is not other, msg )
|
1623
|
-
|
1624
|
-
def assertNone(self, obj, msg=None):
|
1625
|
-
"""assert obj is None"""
|
1626
|
-
if msg is None:
|
1627
|
-
msg = "reference to %r when None expected"%(obj,)
|
1628
|
-
self.assert_( obj is None, msg )
|
1629
|
-
|
1630
|
-
def assertNotNone(self, obj, msg=None):
|
1631
|
-
"""assert obj is not None"""
|
1632
|
-
if msg is None:
|
1633
|
-
msg = "unexpected reference to None"
|
1634
|
-
self.assert_( obj is not None, msg )
|
1635
|
-
|
1636
|
-
def assertFloatAlmostEquals(self, obj, other, prec=1e-5, msg=None):
|
1637
|
-
"""compares two floats"""
|
1638
|
-
if msg is None:
|
1639
|
-
msg = "%r != %r" % (obj, other)
|
1640
|
-
self.assert_(math.fabs(obj - other) < prec, msg)
|
1641
|
-
|
1642
|
-
def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
|
1643
|
-
"""override default failUnlessRaise method to return the raised
|
1644
|
-
exception instance.
|
1645
|
-
|
1646
|
-
Fail unless an exception of class excClass is thrown
|
1647
|
-
by callableObj when invoked with arguments args and keyword
|
1648
|
-
arguments kwargs. If a different type of exception is
|
1649
|
-
thrown, it will not be caught, and the test case will be
|
1650
|
-
deemed to have suffered an error, exactly as for an
|
1651
|
-
unexpected exception.
|
1652
|
-
"""
|
1653
|
-
try:
|
1654
|
-
callableObj(*args, **kwargs)
|
1655
|
-
except excClass, exc:
|
1656
|
-
return exc
|
1657
|
-
else:
|
1658
|
-
if hasattr(excClass, '__name__'):
|
1659
|
-
excName = excClass.__name__
|
1660
|
-
else:
|
1661
|
-
excName = str(excClass)
|
1662
|
-
raise self.failureException, "%s not raised" % excName
|
1663
|
-
|
1664
|
-
assertRaises = failUnlessRaises
|
1665
|
-
|
1666
|
-
import doctest
|
1667
|
-
|
1668
|
-
class SkippedSuite(unittest.TestSuite):
|
1669
|
-
def test(self):
|
1670
|
-
"""just there to trigger test execution"""
|
1671
|
-
self.skipped_test('doctest module has no DocTestSuite class')
|
1672
|
-
|
1673
|
-
|
1674
|
-
# DocTestFinder was introduced in python2.4
|
1675
|
-
if sys.version_info >= (2, 4):
|
1676
|
-
class DocTestFinder(doctest.DocTestFinder):
|
1677
|
-
|
1678
|
-
def __init__(self, *args, **kwargs):
|
1679
|
-
self.skipped = kwargs.pop('skipped', ())
|
1680
|
-
doctest.DocTestFinder.__init__(self, *args, **kwargs)
|
1681
|
-
|
1682
|
-
def _get_test(self, obj, name, module, globs, source_lines):
|
1683
|
-
"""override default _get_test method to be able to skip tests
|
1684
|
-
according to skipped attribute's value
|
1685
|
-
|
1686
|
-
Note: Python (<=2.4) use a _name_filter which could be used for that
|
1687
|
-
purpose but it's no longer available in 2.5
|
1688
|
-
Python 2.5 seems to have a [SKIP] flag
|
1689
|
-
"""
|
1690
|
-
if getattr(obj, '__name__', '') in self.skipped:
|
1691
|
-
return None
|
1692
|
-
return doctest.DocTestFinder._get_test(self, obj, name, module,
|
1693
|
-
globs, source_lines)
|
1694
|
-
else:
|
1695
|
-
# this is a hack to make skipped work with python <= 2.3
|
1696
|
-
class DocTestFinder(object):
|
1697
|
-
def __init__(self, skipped):
|
1698
|
-
self.skipped = skipped
|
1699
|
-
self.original_find_tests = doctest._find_tests
|
1700
|
-
doctest._find_tests = self._find_tests
|
1701
|
-
|
1702
|
-
def _find_tests(self, module, prefix=None):
|
1703
|
-
tests = []
|
1704
|
-
for testinfo in self.original_find_tests(module, prefix):
|
1705
|
-
testname, _, _, _ = testinfo
|
1706
|
-
# testname looks like A.B.C.function_name
|
1707
|
-
testname = testname.split('.')[-1]
|
1708
|
-
if testname not in self.skipped:
|
1709
|
-
tests.append(testinfo)
|
1710
|
-
return tests
|
1711
|
-
|
1712
|
-
|
1713
|
-
class DocTest(TestCase):
|
1714
|
-
"""trigger module doctest
|
1715
|
-
I don't know how to make unittest.main consider the DocTestSuite instance
|
1716
|
-
without this hack
|
1717
|
-
"""
|
1718
|
-
skipped = ()
|
1719
|
-
def __call__(self, result=None, runcondition=None, options=None):\
|
1720
|
-
# pylint: disable-msg=W0613
|
1721
|
-
try:
|
1722
|
-
finder = DocTestFinder(skipped=self.skipped)
|
1723
|
-
if sys.version_info >= (2, 4):
|
1724
|
-
suite = doctest.DocTestSuite(self.module, test_finder=finder)
|
1725
|
-
else:
|
1726
|
-
suite = doctest.DocTestSuite(self.module)
|
1727
|
-
except AttributeError:
|
1728
|
-
suite = SkippedSuite()
|
1729
|
-
return suite.run(result)
|
1730
|
-
run = __call__
|
1731
|
-
|
1732
|
-
def test(self):
|
1733
|
-
"""just there to trigger test execution"""
|
1734
|
-
|
1735
|
-
MAILBOX = None
|
1736
|
-
|
1737
|
-
class MockSMTP:
|
1738
|
-
"""fake smtplib.SMTP"""
|
1739
|
-
|
1740
|
-
def __init__(self, host, port):
|
1741
|
-
self.host = host
|
1742
|
-
self.port = port
|
1743
|
-
global MAILBOX
|
1744
|
-
self.reveived = MAILBOX = []
|
1745
|
-
|
1746
|
-
def set_debuglevel(self, debuglevel):
|
1747
|
-
"""ignore debug level"""
|
1748
|
-
|
1749
|
-
def sendmail(self, fromaddr, toaddres, body):
|
1750
|
-
"""push sent mail in the mailbox"""
|
1751
|
-
self.reveived.append((fromaddr, toaddres, body))
|
1752
|
-
|
1753
|
-
def quit(self):
|
1754
|
-
"""ignore quit"""
|
1755
|
-
|
1756
|
-
|
1757
|
-
class MockConfigParser(ConfigParser):
|
1758
|
-
"""fake ConfigParser.ConfigParser"""
|
1759
|
-
|
1760
|
-
def __init__(self, options):
|
1761
|
-
ConfigParser.__init__(self)
|
1762
|
-
for section, pairs in options.iteritems():
|
1763
|
-
self.add_section(section)
|
1764
|
-
for key, value in pairs.iteritems():
|
1765
|
-
self.set(section,key,value)
|
1766
|
-
def write(self, _):
|
1767
|
-
raise NotImplementedError()
|
1768
|
-
|
1769
|
-
|
1770
|
-
class MockConnection:
|
1771
|
-
"""fake DB-API 2.0 connexion AND cursor (i.e. cursor() return self)"""
|
1772
|
-
|
1773
|
-
def __init__(self, results):
|
1774
|
-
self.received = []
|
1775
|
-
self.states = []
|
1776
|
-
self.results = results
|
1777
|
-
|
1778
|
-
def cursor(self):
|
1779
|
-
"""Mock cursor method"""
|
1780
|
-
return self
|
1781
|
-
def execute(self, query, args=None):
|
1782
|
-
"""Mock execute method"""
|
1783
|
-
self.received.append( (query, args) )
|
1784
|
-
def fetchone(self):
|
1785
|
-
"""Mock fetchone method"""
|
1786
|
-
return self.results[0]
|
1787
|
-
def fetchall(self):
|
1788
|
-
"""Mock fetchall method"""
|
1789
|
-
return self.results
|
1790
|
-
def commit(self):
|
1791
|
-
"""Mock commiy method"""
|
1792
|
-
self.states.append( ('commit', len(self.received)) )
|
1793
|
-
def rollback(self):
|
1794
|
-
"""Mock rollback method"""
|
1795
|
-
self.states.append( ('rollback', len(self.received)) )
|
1796
|
-
def close(self):
|
1797
|
-
"""Mock close method"""
|
1798
|
-
pass
|
1799
|
-
|
1800
|
-
|
1801
|
-
def mock_object(**params):
|
1802
|
-
"""creates an object using params to set attributes
|
1803
|
-
>>> option = mock_object(verbose=False, index=range(5))
|
1804
|
-
>>> option.verbose
|
1805
|
-
False
|
1806
|
-
>>> option.index
|
1807
|
-
[0, 1, 2, 3, 4]
|
1808
|
-
"""
|
1809
|
-
return type('Mock', (), params)()
|
1810
|
-
|
1811
|
-
|
1812
|
-
def create_files(paths, chroot):
|
1813
|
-
"""Creates directories and files found in <path>.
|
1814
|
-
|
1815
|
-
:param paths: list of relative paths to files or directories
|
1816
|
-
:param chroot: the root directory in which paths will be created
|
1817
|
-
|
1818
|
-
>>> from os.path import isdir, isfile
|
1819
|
-
>>> isdir('/tmp/a')
|
1820
|
-
False
|
1821
|
-
>>> create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], '/tmp')
|
1822
|
-
>>> isdir('/tmp/a')
|
1823
|
-
True
|
1824
|
-
>>> isdir('/tmp/a/b/c')
|
1825
|
-
True
|
1826
|
-
>>> isfile('/tmp/a/b/c/d/e.py')
|
1827
|
-
True
|
1828
|
-
>>> isfile('/tmp/a/b/foo.py')
|
1829
|
-
True
|
1830
|
-
"""
|
1831
|
-
dirs, files = set(), set()
|
1832
|
-
for path in paths:
|
1833
|
-
path = osp.join(chroot, path)
|
1834
|
-
filename = osp.basename(path)
|
1835
|
-
# path is a directory path
|
1836
|
-
if filename == '':
|
1837
|
-
dirs.add(path)
|
1838
|
-
# path is a filename path
|
1839
|
-
else:
|
1840
|
-
dirs.add(osp.dirname(path))
|
1841
|
-
files.add(path)
|
1842
|
-
for dirpath in dirs:
|
1843
|
-
if not osp.isdir(dirpath):
|
1844
|
-
os.makedirs(dirpath)
|
1845
|
-
for filepath in files:
|
1846
|
-
file(filepath, 'w').close()
|
1847
|
-
|
1848
|
-
def enable_dbc(*args):
|
1849
|
-
"""
|
1850
|
-
Without arguments, return True if contracts can be enabled and should be
|
1851
|
-
enabled (see option -d), return False otherwise.
|
1852
|
-
|
1853
|
-
With arguments, return False if contracts can't or shouldn't be enabled,
|
1854
|
-
otherwise weave ContractAspect with items passed as arguments.
|
1855
|
-
"""
|
1856
|
-
if not ENABLE_DBC:
|
1857
|
-
return False
|
1858
|
-
try:
|
1859
|
-
from logilab.aspects.weaver import weaver
|
1860
|
-
from logilab.aspects.lib.contracts import ContractAspect
|
1861
|
-
except ImportError:
|
1862
|
-
sys.stderr.write(
|
1863
|
-
'Warning: logilab.aspects is not available. Contracts disabled.')
|
1864
|
-
return False
|
1865
|
-
for arg in args:
|
1866
|
-
weaver.weave_module(arg, ContractAspect)
|
1867
|
-
return True
|
1868
|
-
|
1869
|
-
|
1870
|
-
class AttrObject: # XXX cf mock_object
|
1871
|
-
def __init__(self, **kwargs):
|
1872
|
-
self.__dict__.update(kwargs)
|
1873
|
-
|
1874
|
-
def tag(*args):
|
1875
|
-
"""descriptor adding tag to a function"""
|
1876
|
-
def desc(func):
|
1877
|
-
assert not hasattr(func, 'tags')
|
1878
|
-
func.tags = Tags(args)
|
1879
|
-
return func
|
1880
|
-
return desc
|
1881
|
-
|
1882
|
-
class Tags(set):
|
1883
|
-
"""A set of tag able validate an expression"""
|
1884
|
-
def __getitem__(self, key):
|
1885
|
-
return key in self
|
1886
|
-
|
1887
|
-
def match(self, exp):
|
1888
|
-
return eval(exp, {}, self)
|
1889
|
-
|
1890
|
-
def require_version(version):
|
1891
|
-
""" Compare version of python interpreter to the given one. Skip the test
|
1892
|
-
if older.
|
1893
|
-
"""
|
1894
|
-
def check_require_version(f):
|
1895
|
-
version_elements = version.split('.')
|
1896
|
-
try:
|
1897
|
-
compare = tuple([int(v) for v in version_elements])
|
1898
|
-
except ValueError:
|
1899
|
-
raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version)
|
1900
|
-
current = sys.version_info[:3]
|
1901
|
-
#print 'comp', current, compare
|
1902
|
-
if current < compare:
|
1903
|
-
#print 'version too old'
|
1904
|
-
def new_f(self, *args, **kwargs):
|
1905
|
-
self.skip('Need at least %s version of python. Current version is %s.' % (version, '.'.join([str(element) for element in current])))
|
1906
|
-
new_f.__name__ = f.__name__
|
1907
|
-
return new_f
|
1908
|
-
else:
|
1909
|
-
#print 'version young enough'
|
1910
|
-
return f
|
1911
|
-
return check_require_version
|
1912
|
-
|
1913
|
-
def require_module(module):
|
1914
|
-
""" Check if the given module is loaded. Skip the test if not.
|
1915
|
-
"""
|
1916
|
-
def check_require_module(f):
|
1917
|
-
try:
|
1918
|
-
__import__(module)
|
1919
|
-
#print module, 'imported'
|
1920
|
-
return f
|
1921
|
-
except ImportError:
|
1922
|
-
#print module, 'can not be imported'
|
1923
|
-
def new_f(self, *args, **kwargs):
|
1924
|
-
self.skip('%s can not be imported.' % module)
|
1925
|
-
new_f.__name__ = f.__name__
|
1926
|
-
return new_f
|
1927
|
-
return check_require_module
|