$ coveragereport --help
Usage: coveragereport [options] [inputpath [outputdir]]
Converts coverage reports to HTML. If the input path is omitted, it defaults
to coverage or .coverage, whichever exists. If the output directory is
omitted, it defaults to inputpath + /report or ./coverage-reports, depending
on whether the input path points to a directory or a file.
Options:
-h, --help show this help message and exit
-q, --quiet be quiet
-v, --verbose be verbose (default)
--strip-prefix=PREFIX
strip base directory from filenames loaded from
.coverage
--path-alias=PATH=LOCALPATH
define path mappings for filenames loaded from
.coverage

Usage: coveragediff [options] olddir newdir
Options:
-h, --help show this help message and exit
--include=REGEX only consider files matching REGEX
--exclude=REGEX ignore files matching REGEX
--email=ADDR send the report to a given email address (only if
regressions were found)
--from=ADDR set the email sender address
--subject=SUBJECT set the email subject
--web-url=BASEURL include hyperlinks to HTML-ized coverage reports at a
given URL

coverage.py can produce
a .coverage file, which is actually a Python pickle containing
(incomplete) coverage information. To get it, use

coverage run bin/testrunner

coveragereport can take the .coverage file as an input, but it
also needs access to the matching source files. And you have to manually
specify the absolute pathname prefix of your source tree so that the
report know how to translate filenames into dotted package names. Also,
it’s not enough to have absolute pathnames, you need to supply the
canonical absolute pathname (with no symlink segments), such as returned
by os.path.realpath. This is very inconvenient. Sorry.

coveragediff is unable to compare two .coverage files and report
regressions. One reason for that is the incompleteness of the data format
(the pickle contains line numbers of executed statements, but doesn’t
say which lines contain code and which ones are
blank/comments/continuation lines/excluded source lines). The other
reason is simpler: nobody wrote the code. ;)

Unfortunately coverage annotate does not produce files compatible
with coveragereport/coveragediff. This could also be remedied
if somebody wrote a patch.

Note

If you want to use a .coverage file produced on another machine
or simply in a different working directory, you will need to
tell coveragereport how to adjust the absolute filenames so that
the sources can be found. Use the --path-alias option for that.
Alternatively you could use coverage combine to manipulate the
.coverage file itself, as described in the documentation.

Note

.coverage files are Python pickles, which has important
security ramifications. You can craft a Pickle file that executes
arbitrary code during load time. Do not ever attempt to use
pickles received from untrusted sources.

coverage.py is much faster, but using it (and hooking it up to z3c.coverage)
is perhaps less convenient. E.g. if you use zc.buildout 1.5.x with
zc.recipe.testrunner, you will be unable to use coverage run bin/test
because of mystic semi-broken site isolation magic of the former.