bee_python 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|