summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2009-09-27 10:01:05 -0400
committerNed Batchelder <ned@nedbatchelder.com>2009-09-27 10:01:05 -0400
commite432a86f71929dc3ec00ab1664783aaa1afbcc8d (patch)
tree4181c48059649d1a2b8b599f902cc023e6275826
parent7f8bf5fe9cd67a6654f6b5993adec919ff16e23e (diff)
downloadpython-coveragepy-git-e432a86f71929dc3ec00ab1664783aaa1afbcc8d.tar.gz
Added a 'coverage debug' command to get internal information for diagnosing problems. Also, all commands should at least take -h.
-rw-r--r--CHANGES.txt5
-rw-r--r--coverage/cmdline.py44
-rw-r--r--coverage/collector.py4
-rw-r--r--coverage/control.py24
-rw-r--r--coverage/data.py13
-rw-r--r--test/test_cmdline.py25
6 files changed, 102 insertions, 13 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 7165ec9f..d9e50f1f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -25,7 +25,10 @@ Version 3.1, unreleased
- Programs that change directory will still write .coverage files in the
directory where execution started. Fixed `issue 24`_.
-
+
+- Added a "coverage info" command for getting diagnostic information about the
+ coverage.py installation.
+
.. _issue 11: http://bitbucket.org/ned/coveragepy/issue/11
.. _issue 12: http://bitbucket.org/ned/coveragepy/issue/12
.. _issue 13: http://bitbucket.org/ned/coveragepy/issue/13
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index accade5b..d421aabe 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -199,7 +199,7 @@ CMDS = {
"missed with !."
),
- 'help': CmdOptionParser("help",
+ 'help': CmdOptionParser("help", [Opts.help],
usage = "[command]",
description = "Describe how to use coverage.py"
),
@@ -217,14 +217,22 @@ CMDS = {
"executed, excluded, and missed lines."
),
- 'combine': CmdOptionParser("combine",
+ 'combine': CmdOptionParser("combine", [Opts.help],
usage = " ",
description = "Combine data from multiple coverage files collected "
"with 'run -p'. The combined results are stored into a single "
"file representing the union of the coverage."
),
- 'erase': CmdOptionParser("erase",
+ 'debug': CmdOptionParser("debug", [Opts.help],
+ usage = "<topic>",
+ description = "Display information the internals of coverage.py, "
+ "for diagnosing problems. "
+ "Topics are 'data' to show a summary of the data collected in "
+ ".coverage, or 'sys' to show installation information."
+ ),
+
+ 'erase': CmdOptionParser("erase", [Opts.help],
usage = " ",
description = "Erase previously collected coverage data."
),
@@ -371,6 +379,7 @@ class CoverageScript:
'execute' in options.actions or
'annotate' in options.actions or
'html' in options.actions or
+ 'debug' in options.actions or
'report' in options.actions or
'xml' in options.actions
)
@@ -389,6 +398,35 @@ class CoverageScript:
timid = options.timid,
)
+ if 'debug' in options.actions:
+ if not args:
+ self.help_fn("What information would you like: data, sys?")
+ return ERR
+ for info in args:
+ if info == 'sys':
+ print("-- sys ----------------------------------------")
+ for label, info in self.coverage.sysinfo():
+ if isinstance(info, list):
+ print("%15s:" % label)
+ for e in info:
+ print("%15s %s" % ("", e))
+ else:
+ print("%15s: %s" % (label, info))
+ elif info == 'data':
+ print("-- data ---------------------------------------")
+ self.coverage.load()
+ summary = self.coverage.data.summary(fullpath=True)
+ if summary:
+ filenames = sorted(summary.keys())
+ for f in filenames:
+ print("%s: %d lines" % (f, summary[f]))
+ else:
+ print("No data collected")
+ else:
+ self.help_fn("Don't know what you mean by %r" % info)
+ return ERR
+ return OK
+
if 'erase' in options.actions or options.erase_first:
self.coverage.erase()
else:
diff --git a/coverage/collector.py b/coverage/collector.py
index a121193d..3b56d241 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -118,6 +118,10 @@ class Collector:
# trace function.
self._trace_class = Tracer or PyTracer
+ def tracer_name(self):
+ """Return the class name of the tracer we're using."""
+ return self._trace_class.__name__
+
def reset(self):
"""Clear collected data, and prepare to collect more."""
# A dictionary with an entry for (Python source file name, line number
diff --git a/coverage/control.py b/coverage/control.py
index 4fdec930..d51dd294 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -332,3 +332,27 @@ class coverage:
outfile = open(outfile, "w")
reporter = XmlReporter(self, ignore_errors)
reporter.report(morfs, omit_prefixes=omit_prefixes, outfile=outfile)
+
+ def sysinfo(self):
+ """Return a list of key,value pairs showing internal information."""
+
+ import coverage as covmod
+ import platform, re, sys
+
+ info = [
+ ('version', covmod.__version__),
+ ('coverage', covmod.__file__),
+ ('cover_prefix', self.cover_prefix),
+ ('pylib_prefix', self.pylib_prefix),
+ ('tracer', self.collector.tracer_name()),
+ ('data_file', self.data.filename),
+ ('python', sys.version),
+ ('platform', platform.platform()),
+ ('cwd', os.getcwd()),
+ ('path', sys.path),
+ ('environment', [
+ ("%s = %s" % (k, v)) for k, v in os.environ.items()
+ if re.search("^COV|^PY", k)
+ ]),
+ ]
+ return info
diff --git a/coverage/data.py b/coverage/data.py
index 4d368f55..54979658 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -164,14 +164,19 @@ class CoverageData:
"""
return self.lines.get(filename) or {}
- def summary(self):
+ def summary(self, fullpath=False):
"""Return a dict summarizing the coverage data.
- Keys are the basename of the filenames, and values are the number of
- executed lines. This is useful in the unit tests.
+ Keys are based on the filenames, and values are the number of executed
+ lines. If `fullpath` is true, then the keys are the full pathnames of
+ the files, otherwise they are the basenames of the files.
"""
summ = {}
+ if fullpath:
+ filename_fn = lambda f: f
+ else:
+ filename_fn = os.path.basename
for filename, lines in self.lines.items():
- summ[os.path.basename(filename)] = len(lines)
+ summ[filename_fn(filename)] = len(lines)
return summ
diff --git a/test/test_cmdline.py b/test/test_cmdline.py
index d2413712..7e7cba2a 100644
--- a/test/test_cmdline.py
+++ b/test/test_cmdline.py
@@ -66,18 +66,23 @@ class CmdLineTest(CoverageTest):
self.assertEqual(m1.method_calls, m2.method_calls)
def cmd_help(self, args, help_msg=None, topic=None, ret=ERR):
- """Run a command line, and check that it prints the right help."""
+ """Run a command line, and check that it prints the right help.
+
+ Only the last function call in the mock is checked, which should be the
+ help message that we want to see.
+
+ """
m, r = self.mock_command_line(args)
self.assertEqual(r, ret,
"Wrong status: got %s, wanted %s" % (r, ret)
)
if help_msg:
- self.assertEqual(m.method_calls,
- [('help_fn', (help_msg,), {})]
+ self.assertEqual(m.method_calls[-1],
+ ('help_fn', (help_msg,), {})
)
else:
- self.assertEqual(m.method_calls,
- [('help_fn', (), {'topic':topic})]
+ self.assertEqual(m.method_calls[-1],
+ ('help_fn', (), {'topic':topic})
)
@@ -340,6 +345,16 @@ class NewCmdLineTest(CmdLineTest):
def testCombine(self):
self.cmd_executes_same("combine", "-c")
+ def testDebug(self):
+ self.cmd_help("debug", "What information would you like: data, sys?")
+ self.cmd_help("debug foo", "Don't know what you mean by 'foo'")
+
+ def testDebugSys(self):
+ self.command_line("debug sys")
+ out = self.stdout()
+ assert "version:" in out
+ assert "data_file:" in out
+
def testErase(self):
self.cmd_executes_same("erase", "-e")