import sys
from coverage import cmdline
class CustomScript(cmdline.CoverageScript):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.module_coverage = {}
self.coverage_args = {}
self.current_coverage = None
self.import_coverage = None
self.options = None
self.code_ran = None
self.ran = False
def make_import_coverage(self, roots):
kwargs = self.coverage_args.copy()
kwargs['source'] = sorted(set(kwargs['source']).union(roots))
self.import_coverage = self.covpkg.Coverage(**kwargs)
def new_coverage(self, test_path, module_path):
# This should only be called during "run"
kwargs = self.coverage_args.copy()
kwargs['source'] = kwargs['source'].copy() + [test_path, module_path]
self.module_coverage[test_path] = self.covpkg.Coverage(**kwargs)
def _end_coverage(self):
self.current_coverage.stop()
if self.code_ran:
data_file = self.current_coverage.get_option("run:data_file")
if self.path_exists(data_file):
self.current_coverage.combine(data_paths=[data_file])
self.current_coverage.save()
def switch_coverage(self, target_coverage):
if self.current_coverage is not target_coverage:
self._end_coverage()
self.current_coverage = target_coverage
self.current_coverage.start()
def activate_coverage(self, test_path):
target_coverage = self.module_coverage.get(test_path)
if target_coverage:
self.switch_coverage(target_coverage)
else:
print('Unknown test path: {}'.format(test_path))
def deactivate_coverage(self):
self.switch_coverage(self.coverage)
def complete(self):
if self.ran:
self._end_coverage()
def command_line(self, argv):
"""The bulk of the command line interface to coverage.py.
`argv` is the argument list to process.
Returns 0 if all is well, 1 if something went wrong.
"""
# Collect the command-line options.
if not argv:
self.help_fn(topic='minimum_help')
return cmdline.OK
# The command syntax we parse depends on the first argument. Global
# switch syntax always starts with an option.
self.global_option = argv[0].startswith('-')
if self.global_option:
parser = cmdline.GlobalOptionParser()
else:
parser = cmdline.CMDS.get(argv[0])
if not parser:
self.help_fn("Unknown command: '%s'" % argv[0])
return cmdline.ERR
argv = argv[1:]
parser.help_fn = self.help_fn
ok, options, args = parser.parse_args_ok(argv)
if not ok:
return cmdline.ERR
# Handle help and version.
if self.do_help(options, args, parser):
return cmdline.OK
# We need to be able to import from the current directory, because
# plugins may try to, for example, to read Django settings.
sys.path[0] = ''
# Listify the list options.
source = (cmdline.unshell_list(options.source) or []) + ['tests']
omit = cmdline.unshell_list(options.omit)
include = cmdline.unshell_list(options.include)
debug = cmdline.unshell_list(options.debug)
self.options = options
self.coverage_args = dict(
data_suffix=options.parallel_mode,
cover_pylib=options.pylib,
timid=options.timid,
branch=options.branch,
config_file=options.rcfile,
source=source,
omit=omit,
include=include,
debug=debug,
concurrency=options.concurrency,
)
# Do something.
self.current_coverage = self.coverage = self.covpkg.Coverage(
**self.coverage_args)
if options.action == "debug":
return self.do_debug(args)
elif options.action == "erase":
self.coverage.erase()
return cmdline.OK
elif options.action == "run":
return self.do_run(options, args)
elif options.action == "combine":
if options.append:
self.coverage.load()
data_dirs = args or None
self.coverage.combine(data_dirs, strict=True)
self.coverage.save()
return cmdline.OK
# Remaining actions are reporting, with some common options.
report_args = dict(
morfs=cmdline.unglob_args(args),
ignore_errors=options.ignore_errors,
omit=omit,
include=include,
)
self.coverage.load()
total = None
if options.action == "report":
total = self.coverage.report(
show_missing=options.show_missing,
skip_covered=options.skip_covered, **report_args)
elif options.action == "annotate":
self.coverage.annotate(
directory=options.directory, **report_args)
elif options.action == "html":
total = self.coverage.html_report(
directory=options.directory, title=options.title,
skip_covered=options.skip_covered, **report_args)
elif options.action == "xml":
outfile = options.outfile
total = self.coverage.xml_report(outfile=outfile, **report_args)
if total is not None:
# Apply the command line fail-under options, and then use the
# config value, so we can get fail_under from the config file.
if options.fail_under is not None:
self.coverage.set_option("report:fail_under",
options.fail_under)
fail_under = self.coverage.get_option("report:fail_under")
if cmdline.should_fail_under(total, fail_under):
return cmdline.FAIL_UNDER
return cmdline.OK
def do_run(self, options, args):
"""Implementation of 'coverage run'."""
self.ran = True
if not args:
self.help_fn("Nothing to do.")
return cmdline.ERR
if options.append and self.coverage.get_option("run:parallel"):
self.help_fn("Can't append to data files in parallel mode.")
return cmdline.ERR
if options.concurrency == "multiprocessing":
# Can't set other run-affecting command line options with
# multiprocessing.
for opt_name in ['branch', 'include', 'omit', 'pylib', 'source',
'timid']:
# As it happens, all of these options have no default, meaning
# they will be None if they have not been specified.
if getattr(options, opt_name) is not None:
self.help_fn(
"Options affecting multiprocessing must be specified "
"in a configuration file."
)
return cmdline.ERR
if not self.coverage.get_option("run:parallel"):
if not options.append:
self.coverage.erase()
self.coverage.start()
self.code_ran = True
try:
if options.module:
self.run_python_module(args[0], args)
else:
filename = args[0]
self.run_python_file(filename, args)
except cmdline.NoSource:
self.code_ran = False
raise
return cmdline.OK
coverage_script = CustomScript()
Comments