diff options
Diffstat (limited to 'igor.py')
-rw-r--r-- | igor.py | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/igor.py b/igor.py new file mode 100644 index 00000000..0c37efca --- /dev/null +++ b/igor.py @@ -0,0 +1,257 @@ +"""Helper for building, testing, and linting coverage.py. + +To get portability, all these operations are written in Python here instead +of in shell scripts, batch files, or Makefiles. + +""" + +import fnmatch +import glob +import inspect +import os +import platform +import socket +import sys +import zipfile + + +def do_remove_extension(): + """Remove the compiled C extension, no matter what its name.""" + + so_patterns = """ + tracer.so + tracer.*.so + tracer.pyd + """.split() + + for pattern in so_patterns: + pattern = os.path.join("coverage", pattern) + for filename in glob.glob(pattern): + try: + os.remove(filename) + except OSError: + pass + +def run_tests(tracer, *nose_args): + """The actual running of tests.""" + import nose.core + if tracer == "py": + label = "with Python tracer" + else: + label = "with C tracer" + if os.environ.get("COVERAGE_NO_EXTENSION"): + print("Skipping tests, no C extension in this environment") + return + print_banner(label) + os.environ["COVERAGE_TEST_TRACER"] = tracer + nose_args = ["nosetests"] + list(nose_args) + nose.core.main(argv=nose_args) + +def run_tests_with_coverage(tracer, *nose_args): + """Run tests, but with coverage.""" + import coverage + + os.environ['COVERAGE_PROCESS_START'] = os.path.abspath('metacov.ini') + os.environ['COVERAGE_HOME'] = os.getcwd() + + # Create the .pth file that will let us measure coverage in sub-processes. + import nose + pth_dir = os.path.dirname(os.path.dirname(nose.__file__)) + pth_path = os.path.join(pth_dir, "covcov.pth") + pth_file = open(pth_path, "w") + try: + pth_file.write("import coverage; coverage.process_startup()\n") + finally: + pth_file.close() + + version = "%s%s" % sys.version_info[:2] + suffix = "%s_%s_%s" % (version, tracer, socket.gethostname()) + + cov = coverage.coverage(config_file="metacov.ini", data_suffix=suffix) + # Cheap trick: the coverage code itself is excluded from measurement, but + # if we clobber the cover_prefix in the coverage object, we can defeat the + # self-detection. + cov.cover_prefix = "Please measure coverage.py!" + cov.erase() + cov.start() + + try: + # Re-import coverage to get it coverage tested! I don't understand all + # the mechanics here, but if I don't carry over the imported modules + # (in covmods), then things go haywire (os == None, eventually). + covmods = {} + covdir = os.path.split(coverage.__file__)[0] + # We have to make a list since we'll be deleting in the loop. + modules = list(sys.modules.items()) + for name, mod in modules: + if name.startswith('coverage'): + if getattr(mod, '__file__', "??").startswith(covdir): + covmods[name] = mod + del sys.modules[name] + import coverage # don't warn about re-import: pylint: disable=W0404 + sys.modules.update(covmods) + + # Run nosetests, with the arguments from our command line. + try: + run_tests(tracer, *nose_args) + except SystemExit: + # nose3 seems to raise SystemExit, not sure why? + pass + finally: + cov.stop() + os.remove(pth_path) + + cov.save() + +def do_combine_html(): + """Combine data from a meta-coverage run, and make the HTML report.""" + import coverage + os.environ['COVERAGE_HOME'] = os.getcwd() + cov = coverage.coverage(config_file="metacov.ini") + cov.load() + cov.combine() + cov.save() + cov.html_report() + +def do_test_with_tracer(tracer, *noseargs): + """Run nosetests with a particular tracer.""" + if os.environ.get("COVERAGE_COVERAGE", ""): + return run_tests_with_coverage(tracer, *noseargs) + else: + return run_tests(tracer, *noseargs) + +def do_zip_mods(): + """Build the zipmods.zip file.""" + zf = zipfile.ZipFile("tests/zipmods.zip", "w") + zf.write("tests/covmodzip1.py", "covmodzip1.py") + zf.close() + +def do_install_egg(): + """Install the egg1 egg for tests.""" + # I am pretty certain there are easier ways to install eggs... + # pylint: disable=F0401,E0611,E1101 + import distutils.core + cur_dir = os.getcwd() + os.chdir("tests/eggsrc") + distutils.core.run_setup("setup.py", ["--quiet", "bdist_egg"]) + egg = glob.glob("dist/*.egg")[0] + distutils.core.run_setup( + "setup.py", ["--quiet", "easy_install", "--no-deps", "--zip-ok", egg] + ) + os.chdir(cur_dir) + +def do_check_eol(): + """Check files for incorrect newlines and trailing whitespace.""" + + ignore_dirs = [ + '.svn', '.hg', '.tox', '.tox_kits', 'coverage.egg-info', + '_build', 'covtestegg1.egg-info', + ] + checked = set([]) + + def check_file(fname, crlf=True, trail_white=True): + """Check a single file for whitespace abuse.""" + fname = os.path.relpath(fname) + if fname in checked: + return + checked.add(fname) + + line = None + for n, line in enumerate(open(fname, "rb")): + if crlf: + if "\r" in line: + print("%s@%d: CR found" % (fname, n+1)) + return + if trail_white: + line = line[:-1] + if not crlf: + line = line.rstrip('\r') + if line.rstrip() != line: + print("%s@%d: trailing whitespace found" % (fname, n+1)) + return + + if line is not None and not line.strip(): + print("%s: final blank line" % (fname,)) + + def check_files(root, patterns, **kwargs): + """Check a number of files for whitespace abuse.""" + for root, dirs, files in os.walk(root): + for f in files: + fname = os.path.join(root, f) + for p in patterns: + if fnmatch.fnmatch(fname, p): + check_file(fname, **kwargs) + break + for dir_name in ignore_dirs: + if dir_name in dirs: + dirs.remove(dir_name) + + check_files("coverage", ["*.py", "*.c"]) + check_files("coverage/htmlfiles", ["*.html", "*.css", "*.js"]) + check_file("tests/farm/html/src/bom.py", crlf=False) + check_files("tests", ["*.py"]) + check_files("tests", ["*,cover"], trail_white=False) + check_files("tests/js", ["*.js", "*.html"]) + check_file("setup.py") + check_file("igor.py") + check_file("Makefile") + check_file(".hgignore") + check_files("doc", ["*.rst"]) + check_files(".", ["*.txt"]) + + +def print_banner(label): + """Print the version of Python.""" + try: + impl = platform.python_implementation() + except AttributeError: + impl = "Python" + + version = platform.python_version() + + if '__pypy__' in sys.builtin_module_names: + pypy_version = sys.pypy_version_info # pylint: disable=E1101 + version += " (pypy %s)" % ".".join([str(v) for v in pypy_version]) + + print('=== %s %s %s (%s) ===' % (impl, version, label, sys.executable)) + + +def do_help(): + """List the available commands""" + items = list(globals().items()) + items.sort() + for name, value in items: + if name.startswith('do_'): + print("%-20s%s" % (name[3:], value.__doc__)) + + +def main(args): + """Main command-line execution for igor. + + Verbs are taken from the command line, and extra words taken as directed + by the arguments needed by the handler. + + """ + while args: + verb = args.pop(0) + handler = globals().get('do_'+verb) + if handler is None: + print("*** No handler for %r" % verb) + return 1 + argspec = inspect.getargspec(handler) + if argspec[1]: + # Handler has *args, give it all the rest of the command line. + handler_args = args + args = [] + else: + # Handler has specific arguments, give it only what it needs. + num_args = len(argspec[0]) + handler_args = args[:num_args] + args = args[num_args:] + ret = handler(*handler_args) + # If a handler returns a failure-like value, stop. + if ret: + return ret + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) |