summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZooko Ofsimplegeo <zooko@simplegeo.com>2010-04-19 16:08:37 -0600
committerZooko Ofsimplegeo <zooko@simplegeo.com>2010-04-19 16:08:37 -0600
commitce4dbf3b92dcccf146cdf8c0bfb2f6da0a7c5dd5 (patch)
treec1841e353e5968604551161e2afbd4ebad38fe75
parent6638c632a10af6477a712f1a86af6d9e9ab3a3e3 (diff)
downloadpython-coveragepy-git-ce4dbf3b92dcccf146cdf8c0bfb2f6da0a7c5dd5.tar.gz
add a --require option to specify directories which are required to be at the beginning of the path for any file that is going to be included in code coverage
make the --omit and --require options apply to code coverage generation as well as to reporting; This speeds up tests from 6 seconds to 1 second on my system, as well as making the resulting .coverage db include *only* the code that I care about, which helps with my code coverage progression/regression tool. --HG-- extra : transplant_source : %1F.4%81%E8%DA%0B%D0%D5%9D%89%DE%E1vY%E6%CD%1A%EB%C9
-rw-r--r--coverage/__init__.py2
-rw-r--r--coverage/annotate.py12
-rw-r--r--coverage/cmdline.py17
-rw-r--r--coverage/codeunit.py18
-rw-r--r--coverage/config.py31
-rw-r--r--coverage/control.py76
-rw-r--r--coverage/html.py9
-rw-r--r--coverage/report.py19
-rw-r--r--coverage/summary.py12
9 files changed, 168 insertions, 28 deletions
diff --git a/coverage/__init__.py b/coverage/__init__.py
index 9fd4a8d2..3741805b 100644
--- a/coverage/__init__.py
+++ b/coverage/__init__.py
@@ -5,7 +5,7 @@ http://nedbatchelder.com/code/coverage
"""
-__version__ = "3.3.2a1" # see detailed history in CHANGES.txt
+__version__ = "3.3.2a1z8" # see detailed history in CHANGES.txt
__url__ = "http://nedbatchelder.com/code/coverage"
diff --git a/coverage/annotate.py b/coverage/annotate.py
index 2117b657..9c2c7a0a 100644
--- a/coverage/annotate.py
+++ b/coverage/annotate.py
@@ -33,9 +33,15 @@ class AnnotateReporter(Reporter):
blank_re = re.compile(r"\s*(#|$)")
else_re = re.compile(r"\s*else\s*:\s*(#|$)")
- def report(self, morfs, directory=None, omit_prefixes=None):
- """Run the report."""
- self.report_files(self.annotate_file, morfs, directory, omit_prefixes)
+ def report(self, morfs, directory=None, omit_prefixes=None, require_prefixes=None):
+ """Run the report.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
+ """
+ self.report_files(self.annotate_file, morfs, directory, omit_prefixes, require_prefixes)
def annotate_file(self, cu, analysis):
"""Annotate a single file.
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index 9e15074b..acf8903d 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -54,6 +54,12 @@ class Opts(object):
help="Omit files when their filename path starts with one of these "
"prefixes."
)
+ require = optparse.Option(
+ '', '--require', action='store',
+ metavar="PRE1,PRE2,...",
+ help="Include files only when their filename path starts with one of these "
+ "prefixes."
+ )
output_xml = optparse.Option(
'-o', '', action='store', dest="outfile",
metavar="OUTFILE",
@@ -99,6 +105,7 @@ class CoverageOptionParser(optparse.OptionParser, object):
help=None,
ignore_errors=None,
omit=None,
+ require=None,
parallel_mode=None,
pylib=None,
rcfile=True,
@@ -219,6 +226,7 @@ CMDS = {
Opts.directory,
Opts.ignore_errors,
Opts.omit,
+ Opts.require,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
description = "Make annotated copies of the given files, marking "
@@ -256,6 +264,7 @@ CMDS = {
Opts.directory,
Opts.ignore_errors,
Opts.omit,
+ Opts.require,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
description = "Create an HTML report of the coverage of the files. "
@@ -267,6 +276,7 @@ CMDS = {
[
Opts.ignore_errors,
Opts.omit,
+ Opts.require,
Opts.show_missing,
] + GLOBAL_ARGS,
usage = "[options] [modules]",
@@ -280,6 +290,8 @@ CMDS = {
Opts.pylib,
Opts.parallel_mode,
Opts.timid,
+ Opts.omit,
+ Opts.require,
] + GLOBAL_ARGS,
defaults = {'erase_first': True},
cmd = "run",
@@ -291,6 +303,7 @@ CMDS = {
[
Opts.ignore_errors,
Opts.omit,
+ Opts.require,
Opts.output_xml,
] + GLOBAL_ARGS,
cmd = "xml",
@@ -495,6 +508,10 @@ class CoverageScript(object):
if options.omit:
omit = options.omit.split(',')
report_args['omit_prefixes'] = omit
+ require = None
+ if options.require:
+ require = options.require.split(',')
+ report_args['require_prefixes'] = require
if 'report' in options.actions:
self.coverage.report(
diff --git a/coverage/codeunit.py b/coverage/codeunit.py
index 9bf6dc97..51640b80 100644
--- a/coverage/codeunit.py
+++ b/coverage/codeunit.py
@@ -6,13 +6,16 @@ from coverage.backward import string_class, StringIO
from coverage.misc import CoverageException
-def code_unit_factory(morfs, file_locator, omit_prefixes=None):
+def code_unit_factory(morfs, file_locator, omit_prefixes=None, require_prefixes=None):
"""Construct a list of CodeUnits from polymorphic inputs.
`morfs` is a module or a filename, or a list of same.
`file_locator` is a FileLocator that can help resolve filenames.
`omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
Returns a list of CodeUnit objects.
@@ -33,7 +36,18 @@ def code_unit_factory(morfs, file_locator, omit_prefixes=None):
code_units = [CodeUnit(morf, file_locator) for morf in morfs]
- if omit_prefixes:
+ if require_prefixes:
+ assert not isinstance(require_prefixes, string_class) # common mistake
+ prefixes = [file_locator.abs_file(p) for p in require_prefixes]
+ filtered = []
+ for cu in code_units:
+ for prefix in prefixes:
+ if cu.filename.startswith(prefix):
+ filtered.append(cu)
+ break
+
+ code_units = filtered
+ elif omit_prefixes:
assert not isinstance(omit_prefixes, string_class) # common mistake
prefixes = [file_locator.abs_file(p) for p in omit_prefixes]
filtered = []
diff --git a/coverage/config.py b/coverage/config.py
index 8f508f28..92d83016 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -25,6 +25,7 @@ class CoverageConfig(object):
self.exclude_list = ['(?i)# *pragma[: ]*no *cover']
self.ignore_errors = False
self.omit_prefixes = None
+ self.require_prefixes = None
# Defaults for [html]
self.html_dir = "htmlcov"
@@ -67,6 +68,26 @@ class CoverageConfig(object):
self.parallel = cp.getboolean('run', 'parallel')
if cp.has_option('run', 'timid'):
self.timid = cp.getboolean('run', 'timid')
+ if cp.has_option('run', 'omit'):
+ # omit is a list of prefixes, on separate lines, or separated by
+ # commas.
+ omit_list = cp.get('run', 'omit')
+ self.omit_prefixes = []
+ for omit_line in omit_list.split('\n'):
+ for omit in omit_line.split(','):
+ omit = omit.strip()
+ if omit:
+ self.omit_prefixes.append(omit)
+ if cp.has_option('run', 'require'):
+ # require is a list of prefixes, on separate lines, or separated by
+ # commas.
+ require_list = cp.get('run', 'require')
+ self.require_prefixes = []
+ for require_line in require_list.split('\n'):
+ for require in require_line.split(','):
+ require = require.strip()
+ if require:
+ self.require_prefixes.append(require)
# [report]
if cp.has_option('report', 'exclude_lines'):
@@ -85,6 +106,16 @@ class CoverageConfig(object):
omit = omit.strip()
if omit:
self.omit_prefixes.append(omit)
+ if cp.has_option('report', 'require'):
+ # require is a list of prefixes, on separate lines, or separated by
+ # commas.
+ require_list = cp.get('report', 'require')
+ self.require_prefixes = []
+ for require_line in require_list.split('\n'):
+ for require in require_line.split(','):
+ require = require.strip()
+ if require:
+ self.require_prefixes.append(require)
# [html]
if cp.has_option('html', 'directory'):
diff --git a/coverage/control.py b/coverage/control.py
index e4f5fae7..a337d6bd 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -31,7 +31,7 @@ class coverage(object):
"""
def __init__(self, data_file=None, data_suffix=None, cover_pylib=None,
- auto_data=False, timid=None, branch=None, config_file=True):
+ auto_data=False, timid=None, branch=None, config_file=True, omit_prefixes=None, require_prefixes=None):
"""
`data_file` is the base name of the data file to use, defaulting to
".coverage". `data_suffix` is appended (with a dot) to `data_file` to
@@ -58,6 +58,13 @@ class coverage(object):
standard file is read (".coveragerc"). If it is False, then no file is
read.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
from coverage import __version__
@@ -80,7 +87,9 @@ class coverage(object):
# 4: from constructor arguments:
self.config.from_args(
data_file=data_file, cover_pylib=cover_pylib, timid=timid,
- branch=branch, parallel=bool_or_none(data_suffix)
+ branch=branch, parallel=bool_or_none(data_suffix),
+ omit_prefixes=omit_prefixes,
+ require_prefixes=require_prefixes
)
self.auto_data = auto_data
@@ -91,6 +100,11 @@ class coverage(object):
self.file_locator = FileLocator()
+ if self.config.omit_prefixes:
+ self.omit_prefixes = [self.file_locator.abs_file(p) for p in self.config.omit_prefixes]
+ if self.config.require_prefixes:
+ self.require_prefixes = [self.file_locator.abs_file(p) for p in self.config.require_prefixes]
+
self.collector = Collector(
self._should_trace, timid=self.config.timid,
branch=self.config.branch
@@ -172,6 +186,19 @@ class coverage(object):
if canonical.startswith(self.cover_prefix):
return False
+ if self.require_prefixes:
+ for prefix in self.require_prefixes:
+ if canonical.startswith(prefix):
+ return canonical
+ else:
+ return False
+ elif omit_prefixes:
+ for prefix in prefixes:
+ if canonical.startswith(prefix):
+ return False
+
+ code_units = filtered
+
return canonical
# To log what should_trace returns, change this to "if 1:"
@@ -322,26 +349,32 @@ class coverage(object):
return Analysis(self, it)
def report(self, morfs=None, show_missing=True, ignore_errors=None,
- file=None, omit_prefixes=None): # pylint: disable-msg=W0622
+ file=None, omit_prefixes=None, require_prefixes=None): # pylint: disable-msg=W0622
"""Write a summary report to `file`.
Each module in `morfs` is listed, with counts of statements, executed
statements, missing statements, and a list of lines missed.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
self.config.from_args(
ignore_errors=ignore_errors,
- omit_prefixes=omit_prefixes
+ omit_prefixes=omit_prefixes,
+ require_prefixes=require_prefixes
)
reporter = SummaryReporter(
self, show_missing, self.config.ignore_errors
)
reporter.report(
- morfs, outfile=file, omit_prefixes=self.config.omit_prefixes
+ morfs, outfile=file, omit_prefixes=self.config.omit_prefixes, require_prefixes=self.config.require_prefixes
)
def annotate(self, morfs=None, directory=None, ignore_errors=None,
- omit_prefixes=None):
+ omit_prefixes=None, require_prefixes=None):
"""Annotate a list of modules.
Each module in `morfs` is annotated. The source is written to a new
@@ -349,34 +382,47 @@ class coverage(object):
marker to indicate the coverage of the line. Covered lines have ">",
excluded lines have "-", and missing lines have "!".
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
self.config.from_args(
ignore_errors=ignore_errors,
- omit_prefixes=omit_prefixes
+ omit_prefixes=omit_prefixes,
+ require_prefixes=require_prefixes
)
reporter = AnnotateReporter(self, self.config.ignore_errors)
reporter.report(
- morfs, directory=directory, omit_prefixes=self.config.omit_prefixes
+ morfs, directory=directory, omit_prefixes=self.config.omit_prefixes, require_prefixes=self.config.require_prefixes
)
def html_report(self, morfs=None, directory=None, ignore_errors=None,
- omit_prefixes=None):
+ omit_prefixes=None, require_prefixes=None):
"""Generate an HTML report.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
self.config.from_args(
ignore_errors=ignore_errors,
omit_prefixes=omit_prefixes,
+ require_prefixes=require_prefixes,
html_dir=directory,
)
reporter = HtmlReporter(self, self.config.ignore_errors)
reporter.report(
morfs, directory=self.config.html_dir,
- omit_prefixes=self.config.omit_prefixes
+ omit_prefixes=self.config.omit_prefixes,
+ require_prefixes=self.config.require_prefixes
)
def xml_report(self, morfs=None, outfile=None, ignore_errors=None,
- omit_prefixes=None):
+ omit_prefixes=None, require_prefixes=None):
"""Generate an XML report of coverage results.
The report is compatible with Cobertura reports.
@@ -384,10 +430,16 @@ class coverage(object):
Each module in `morfs` is included in the report. `outfile` is the
path to write the file to, "-" will write to stdout.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
self.config.from_args(
ignore_errors=ignore_errors,
omit_prefixes=omit_prefixes,
+ require_prefixes=require_prefixes,
xml_output=outfile,
)
file_to_close = None
@@ -400,7 +452,7 @@ class coverage(object):
try:
reporter = XmlReporter(self, self.config.ignore_errors)
reporter.report(
- morfs, omit_prefixes=self.config.omit_prefixes, outfile=outfile
+ morfs, omit_prefixes=self.config.omit_prefixes, require_prefixes=self.config.require_prefixes, outfile=outfile
)
finally:
if file_to_close:
diff --git a/coverage/html.py b/coverage/html.py
index 7d0d064b..61712f36 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -31,18 +31,23 @@ class HtmlReporter(Reporter):
self.files = []
self.arcs = coverage.data.has_arcs()
- def report(self, morfs, directory, omit_prefixes=None):
+ def report(self, morfs, directory, omit_prefixes=None, require_prefixes=None):
"""Generate an HTML report for `morfs`.
`morfs` is a list of modules or filenames. `directory` is where to put
the HTML files. `omit_prefixes` is a list of strings, prefixes of
modules to omit from the report.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
assert directory, "must provide a directory for html reporting"
# Process all the files.
- self.report_files(self.html_file, morfs, directory, omit_prefixes)
+ self.report_files(self.html_file, morfs, directory, omit_prefixes, require_prefixes)
# Write the index file.
self.index_file()
diff --git a/coverage/report.py b/coverage/report.py
index 5b66f999..e2a09fbf 100644
--- a/coverage/report.py
+++ b/coverage/report.py
@@ -24,26 +24,35 @@ class Reporter(object):
# classes.
self.directory = None
- def find_code_units(self, morfs, omit_prefixes):
+ def find_code_units(self, morfs, omit_prefixes, require_prefixes):
"""Find the code units we'll report on.
-
`morfs` is a list of modules or filenames. `omit_prefixes` is a list
of prefixes to leave out of the list.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
morfs = morfs or self.coverage.data.executed_files()
self.code_units = code_unit_factory(
- morfs, self.coverage.file_locator, omit_prefixes)
+ morfs, self.coverage.file_locator, omit_prefixes, require_prefixes)
self.code_units.sort()
def report_files(self, report_fn, morfs, directory=None,
- omit_prefixes=None):
+ omit_prefixes=None, require_prefixes=None):
"""Run a reporting function on a number of morfs.
`report_fn` is called for each relative morf in `morfs`.
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
"""
- self.find_code_units(morfs, omit_prefixes)
+ self.find_code_units(morfs, omit_prefixes, require_prefixes)
if not self.code_units:
raise CoverageException("No data to report.")
diff --git a/coverage/summary.py b/coverage/summary.py
index 7348acab..696f6b36 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -14,10 +14,16 @@ class SummaryReporter(Reporter):
self.show_missing = show_missing
self.branches = coverage.data.has_arcs()
- def report(self, morfs, omit_prefixes=None, outfile=None):
- """Writes a report summarizing coverage statistics per module."""
+ def report(self, morfs, omit_prefixes=None, outfile=None, require_prefixes=None):
+ """Writes a report summarizing coverage statistics per module.
- self.find_code_units(morfs, omit_prefixes)
+ `omit_prefixes` is a list of prefixes. CodeUnits that match those prefixes
+ will be omitted from the list.
+ `require_prefixes` is a list of prefixes. Only CodeUnits that match those prefixes
+ will be included in the list.
+ You are required to pass at most one of `omit_prefixes` and `require_prefixes`.
+ """
+ self.find_code_units(morfs, omit_prefixes, require_prefixes)
# Prepare the formatting strings
max_name = max([len(cu.name) for cu in self.code_units] + [5])