summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.txt2
-rw-r--r--coverage/cmdline.py8
-rw-r--r--coverage/execfile.py16
-rw-r--r--coverage/misc.py8
-rw-r--r--test/test_process.py29
5 files changed, 57 insertions, 6 deletions
diff --git a/TODO.txt b/TODO.txt
index cdbc6736..360a34f9 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -210,5 +210,5 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
- Tests that tracing stops after calling stop()
- More intensive thread testing.
x Tests about the "import __main__" in cmdline.py
-- What happens if the -x script raises an exception?
++ What happens if the -x script raises an exception?
- Test that the kit has all the proper contents.
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index 60f9cdd3..f7cb28c3 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -1,10 +1,10 @@
"""Command-line support for Coverage."""
-import optparse, re, sys
+import optparse, re, sys, traceback
from coverage.backward import sorted # pylint: disable-msg=W0622
from coverage.execfile import run_python_file
-from coverage.misc import CoverageException
+from coverage.misc import CoverageException, ExceptionDuringRun
class Opts(object):
@@ -596,6 +596,10 @@ def main():
"""
try:
status = CoverageScript().command_line(sys.argv[1:])
+ except ExceptionDuringRun:
+ _, err, _ = sys.exc_info()
+ traceback.print_exception(*err.args)
+ status = ERR
except CoverageException:
_, err, _ = sys.exc_info()
print(err)
diff --git a/coverage/execfile.py b/coverage/execfile.py
index 15f0a5f8..f04840b5 100644
--- a/coverage/execfile.py
+++ b/coverage/execfile.py
@@ -3,7 +3,7 @@
import imp, os, sys
from coverage.backward import exec_function
-from coverage.misc import NoSource
+from coverage.misc import NoSource, ExceptionDuringRun
try:
@@ -36,11 +36,23 @@ def run_python_file(filename, args):
sys.path[0] = os.path.dirname(filename)
try:
+ # Open the source file.
try:
source = open(filename, 'rU').read()
except IOError:
raise NoSource("No file to run: %r" % filename)
- exec_function(source, filename, main_mod.__dict__)
+
+ # Execute the source file.
+ try:
+ exec_function(source, filename, main_mod.__dict__)
+ except:
+ # Something went wrong while executing the user code.
+ # Get the exc_info, and pack them into an exception that we can
+ # throw up to the outer loop. We peel two layers off the traceback
+ # so that the coverage.py code doesn't appear in the final printed
+ # traceback.
+ typ, err, tb = sys.exc_info()
+ raise ExceptionDuringRun(typ, err, tb.tb_next.tb_next)
finally:
# Restore the old __main__
sys.modules['__main__'] = old_main_mod
diff --git a/coverage/misc.py b/coverage/misc.py
index 4959efe0..4218536d 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -75,3 +75,11 @@ class CoverageException(Exception):
class NoSource(CoverageException):
"""Used to indicate we couldn't find the source for a module."""
pass
+
+class ExceptionDuringRun(CoverageException):
+ """An exception happened while running customer code.
+
+ Construct it with three arguments, the values from `sys.exc_info`.
+
+ """
+ pass
diff --git a/test/test_process.py b/test/test_process.py
index 814a80d9..b8ccb942 100644
--- a/test/test_process.py
+++ b/test/test_process.py
@@ -1,6 +1,6 @@
"""Tests for process behavior of coverage.py."""
-import os, sys
+import os, sys, textwrap
import coverage
sys.path.insert(0, os.path.split(__file__)[0]) # Force relative import for Py3k
@@ -148,3 +148,30 @@ class ProcessTest(CoverageTest):
out = self.run_command("coverage run xyzzy.py")
self.assertRegexpMatches(out, "No file to run: .*xyzzy.py")
self.assertFalse("Traceback" in out)
+
+ def test_code_throws(self):
+ self.make_file("throw.py", """\
+ def f1():
+ raise Exception("hey!")
+
+ def f2():
+ f1()
+
+ f2()
+ """)
+
+ out = self.run_command("coverage run throw.py")
+ # Different versions of Python report the module-level code differently
+ # in tracebacks, so canononicalize it.
+ out = out.replace(", in ?", ", in <module>")
+ expected = textwrap.dedent("""\
+ Traceback (most recent call last):
+ File "throw.py", line 7, in <module>
+ f2()
+ File "throw.py", line 5, in f2
+ f1()
+ File "throw.py", line 2, in f1
+ raise Exception("hey!")
+ Exception: hey!
+ """)
+ self.assertMultiLineEqual(out, expected)