summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
Diffstat (limited to 'coverage')
-rw-r--r--coverage/control.py11
-rw-r--r--coverage/debug.py5
-rw-r--r--coverage/env.py5
-rw-r--r--coverage/html.py18
-rw-r--r--coverage/misc.py16
-rw-r--r--coverage/parser.py8
-rw-r--r--coverage/pytracer.py5
-rw-r--r--coverage/summary.py7
8 files changed, 57 insertions, 18 deletions
diff --git a/coverage/control.py b/coverage/control.py
index 243fe587..ebb449c3 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -24,7 +24,6 @@ from coverage.files import ModuleMatcher, abs_file
from coverage.html import HtmlReporter
from coverage.misc import CoverageException, bool_or_none, join_regex
from coverage.misc import file_be_gone, isolate_module
-from coverage.multiproc import patch_multiprocessing
from coverage.plugin import FileReporter
from coverage.plugin_support import Plugins
from coverage.python import PythonFileReporter, source_for_file
@@ -32,6 +31,12 @@ from coverage.results import Analysis, Numbers
from coverage.summary import SummaryReporter
from coverage.xmlreport import XmlReporter
+try:
+ from coverage.multiproc import patch_multiprocessing
+except ImportError: # pragma: only jython
+ # Jython has no multiprocessing module.
+ patch_multiprocessing = None
+
os = isolate_module(os)
# Pypy has some unusual stuff in the "stdlib". Consider those locations
@@ -231,6 +236,10 @@ class Coverage(object):
concurrency = self.config.concurrency or []
if "multiprocessing" in concurrency:
+ if not patch_multiprocessing:
+ raise CoverageException( # pragma: only jython
+ "multiprocessing is not supported on this Python"
+ )
patch_multiprocessing(rcfile=self.config_file)
# Multi-processing uses parallel for the subprocesses, so also use
# it for the main process.
diff --git a/coverage/debug.py b/coverage/debug.py
index 91ccba77..5ffed39d 100644
--- a/coverage/debug.py
+++ b/coverage/debug.py
@@ -202,8 +202,9 @@ def enable_aspectlib_maybe(): # pragma: debugging
Define COVERAGE_ASPECTLIB to enable and configure aspectlib to trace
execution::
- export COVERAGE_LOG=covaspect.txt
- COVERAGE_ASPECTLIB=coverage.Coverage:coverage.data.CoverageData program...
+ $ export COVERAGE_LOG=covaspect.txt
+ $ export COVERAGE_ASPECTLIB=coverage.Coverage:coverage.data.CoverageData
+ $ coverage run blah.py ...
This will trace all the public methods on Coverage and CoverageData,
writing the information to covaspect.txt.
diff --git a/coverage/env.py b/coverage/env.py
index 6db3b857..528c774a 100644
--- a/coverage/env.py
+++ b/coverage/env.py
@@ -4,6 +4,7 @@
"""Determine facts about the environment."""
import os
+import platform
import sys
# Operating systems.
@@ -11,10 +12,12 @@ WINDOWS = sys.platform == "win32"
LINUX = sys.platform == "linux2"
# Python implementations.
-PYPY = '__pypy__' in sys.builtin_module_names
+PYPY = (platform.python_implementation() == 'PyPy')
if PYPY:
PYPYVERSION = sys.pypy_version_info
+JYTHON = (platform.python_implementation() == 'Jython')
+
# Python versions.
PYVERSION = sys.version_info
PY2 = PYVERSION < (3, 0)
diff --git a/coverage/html.py b/coverage/html.py
index 22783ef7..b0c61649 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -12,7 +12,7 @@ import coverage
from coverage import env
from coverage.backward import iitems
from coverage.files import flat_rootname
-from coverage.misc import CoverageException, Hasher, isolate_module
+from coverage.misc import CoverageException, file_be_gone, Hasher, isolate_module
from coverage.report import Reporter
from coverage.results import Numbers
from coverage.templite import Templite
@@ -105,6 +105,7 @@ class HtmlReporter(Reporter):
self.coverage = cov
self.files = []
+ self.all_files_nums = []
self.has_arcs = self.coverage.data.has_arcs()
self.status = HtmlStatus()
self.extra_css = None
@@ -137,7 +138,7 @@ class HtmlReporter(Reporter):
# Process all the files.
self.report_files(self.html_file, morfs, self.config.html_dir)
- if not self.files:
+ if not self.all_files_nums:
raise CoverageException("No data to report.")
# Write the index file.
@@ -171,19 +172,26 @@ class HtmlReporter(Reporter):
def html_file(self, fr, analysis):
"""Generate an HTML file for one source file."""
+ rootname = flat_rootname(fr.relative_filename())
+ html_filename = rootname + ".html"
+ html_path = os.path.join(self.directory, html_filename)
+
# Get the numbers for this file.
nums = analysis.numbers
+ self.all_files_nums.append(nums)
+
if self.config.skip_covered:
# Don't report on 100% files.
no_missing_lines = (nums.n_missing == 0)
no_missing_branches = (nums.n_partial_branches == 0)
if no_missing_lines and no_missing_branches:
+ # If there's an existing file, remove it.
+ file_be_gone(html_path)
return
source = fr.source()
# Find out if the file on disk is already correct.
- rootname = flat_rootname(fr.relative_filename())
this_hash = self.file_hash(source.encode('utf-8'), fr)
that_hash = self.status.file_hash(rootname)
if this_hash == that_hash:
@@ -275,8 +283,6 @@ class HtmlReporter(Reporter):
'time_stamp': self.time_stamp,
})
- html_filename = rootname + ".html"
- html_path = os.path.join(self.directory, html_filename)
write_html(html_path, html)
# Save this file's information for the index file.
@@ -292,7 +298,7 @@ class HtmlReporter(Reporter):
"""Write the index.html file for this report."""
index_tmpl = Templite(read_data("index.html"), self.template_globals)
- self.totals = sum(f['nums'] for f in self.files)
+ self.totals = sum(self.all_files_nums)
html = index_tmpl.render({
'has_arcs': self.has_arcs,
diff --git a/coverage/misc.py b/coverage/misc.py
index 5d330c6d..240a2587 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -13,6 +13,7 @@ import types
from coverage import env
from coverage.backward import to_bytes, unicode_class
+from coverage.backunittest import unittest
ISOLATED_MODULES = {}
@@ -264,3 +265,18 @@ class ExceptionDuringRun(CoverageException):
"""
pass
+
+
+class StopEverything(unittest.SkipTest):
+ """An exception that means everything should stop.
+
+ This derives from SkipTest so that tests that spring this trap will be
+ skipped automatically, without a lot of boilerplate all over the place.
+
+ """
+ pass
+
+
+class IncapablePython(CoverageException, StopEverything):
+ """An operation is attempted that this version of Python cannot do."""
+ pass
diff --git a/coverage/parser.py b/coverage/parser.py
index 540ad098..54603bf3 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -16,7 +16,7 @@ from coverage.backward import bytes_to_ints, string_class
from coverage.bytecode import CodeObjects
from coverage.debug import short_stack
from coverage.misc import contract, new_contract, nice_pair, join_regex
-from coverage.misc import CoverageException, NoSource, NotPython
+from coverage.misc import NoSource, IncapablePython, NotPython
from coverage.phystokens import compile_unicode, generate_tokens, neuter_encoding_declaration
@@ -371,11 +371,11 @@ class ByteParser(object):
# Alternative Python implementations don't always provide all the
# attributes on code objects that we need to do the analysis.
- for attr in ['co_lnotab', 'co_firstlineno', 'co_consts']:
+ for attr in ['co_lnotab', 'co_firstlineno']:
if not hasattr(self.code, attr):
- raise CoverageException(
+ raise IncapablePython( # pragma: only jython
"This implementation of Python doesn't support code analysis.\n"
- "Run coverage.py under CPython for this command."
+ "Run coverage.py under another Python for this command."
)
def child_parsers(self):
diff --git a/coverage/pytracer.py b/coverage/pytracer.py
index 5a1b5d7b..bb300563 100644
--- a/coverage/pytracer.py
+++ b/coverage/pytracer.py
@@ -116,8 +116,9 @@ class PyTracer(object):
if self.trace_arcs and self.cur_file_dict:
# Record an arc leaving the function, but beware that a
# "return" event might just mean yielding from a generator.
- bytecode = frame.f_code.co_code[frame.f_lasti]
- if bytecode != YIELD_VALUE:
+ # Jython seems to have an empty co_code, so just assume return.
+ code = frame.f_code.co_code
+ if (not code) or code[frame.f_lasti] != YIELD_VALUE:
first = frame.f_code.co_firstlineno
self.cur_file_dict[(self.last_line, -first)] = None
# Leaving this function, pop the filename stack.
diff --git a/coverage/summary.py b/coverage/summary.py
index d94ce8b2..271b648a 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -8,7 +8,7 @@ import sys
from coverage import env
from coverage.report import Reporter
from coverage.results import Numbers
-from coverage.misc import NotPython, CoverageException, output_encoding
+from coverage.misc import NotPython, CoverageException, output_encoding, StopEverything
class SummaryReporter(Reporter):
@@ -55,13 +55,16 @@ class SummaryReporter(Reporter):
skipped_count += 1
continue
fr_analysis.append((fr, analysis))
+ except StopEverything:
+ # Don't report this on single files, it's a systemic problem.
+ raise
except Exception:
report_it = not self.config.ignore_errors
if report_it:
typ, msg = sys.exc_info()[:2]
# NotPython is only raised by PythonFileReporter, which has a
# should_be_python() method.
- if typ is NotPython and not fr.should_be_python():
+ if issubclass(typ, NotPython) and not fr.should_be_python():
report_it = False
if report_it:
writeout(fmt_err % (fr.relative_filename(), typ.__name__, msg))