diff options
-rw-r--r-- | CHANGES.txt | 4 | ||||
-rw-r--r-- | coverage/cmdline.py | 8 | ||||
-rw-r--r-- | coverage/collector.py | 4 | ||||
-rw-r--r-- | coverage/config.py | 4 | ||||
-rw-r--r-- | coverage/control.py | 52 | ||||
-rw-r--r-- | tests/test_cmdline.py | 80 |
6 files changed, 103 insertions, 49 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 79794dc5..f3b2e45c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,10 @@ Change history for Coverage.py 3.6.1 ----- +- Added the ``--debug`` switch to ``coverage run``. It accepts one option now, + ``notrace``, to log decisions not to trace files. I'm hoping this will help + people diagnose why their code isn't being traced. + - Improved the branch coverage facility, fixing `issue 90`_ and `issue 175`_. - Running code with ``coverage run -m`` now behaves more like Python does, diff --git a/coverage/cmdline.py b/coverage/cmdline.py index ac803109..51d8043f 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -19,6 +19,10 @@ class Opts(object): '', '--branch', action='store_true', help="Measure branch coverage in addition to statement coverage." ) + debug = optparse.make_option( + '', '--debug', action='store', metavar="OPTS", + help="Debug options, separated by commas" + ) directory = optparse.make_option( '-d', '--directory', action='store', metavar="DIR", help="Write the output files to DIR." @@ -117,6 +121,7 @@ class CoverageOptionParser(optparse.OptionParser, object): self.set_defaults( actions=[], branch=None, + debug=None, directory=None, fail_under=None, help=None, @@ -310,6 +315,7 @@ CMDS = { [ Opts.append, Opts.branch, + Opts.debug, Opts.pylib, Opts.parallel_mode, Opts.module, @@ -404,6 +410,7 @@ class CoverageScript(object): source = unshell_list(options.source) omit = unshell_list(options.omit) include = unshell_list(options.include) + debug = unshell_list(options.debug) # Do something. self.coverage = self.covpkg.coverage( @@ -415,6 +422,7 @@ class CoverageScript(object): source = source, omit = omit, include = include, + debug = debug, ) if 'debug' in options.actions: diff --git a/coverage/collector.py b/coverage/collector.py index 781a0fa6..2abec169 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -166,7 +166,7 @@ class Collector(object): """Create a collector. `should_trace` is a function, taking a filename, and returning a - canonicalized filename, or False depending on whether the file should + canonicalized filename, or None depending on whether the file should be traced or not. If `timid` is true, then a slower simpler trace function will be @@ -210,7 +210,7 @@ class Collector(object): # A cache of the results from should_trace, the decision about whether # to trace execution in a file. A dict of filename to (filename or - # False). + # None). self.should_trace_cache = {} # Our active Tracers. diff --git a/coverage/config.py b/coverage/config.py index c2ebecb2..87318ff1 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -111,6 +111,7 @@ class CoverageConfig(object): self.parallel = False self.timid = False self.source = None + self.debug = [] # Defaults for [report] self.exclude_list = DEFAULT_EXCLUDE[:] @@ -142,7 +143,7 @@ class CoverageConfig(object): if env: self.timid = ('--timid' in env) - MUST_BE_LIST = ["omit", "include"] + MUST_BE_LIST = ["omit", "include", "debug"] def from_args(self, **kwargs): """Read config values from `kwargs`.""" @@ -178,6 +179,7 @@ class CoverageConfig(object): ('branch', 'run:branch', 'boolean'), ('cover_pylib', 'run:cover_pylib', 'boolean'), ('data_file', 'run:data_file'), + ('debug', 'run:debug', 'list'), ('include', 'run:include', 'list'), ('omit', 'run:omit', 'list'), ('parallel', 'run:parallel', 'boolean'), diff --git a/coverage/control.py b/coverage/control.py index 98e4e78b..fa85be68 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -41,7 +41,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, - source=None, omit=None, include=None): + source=None, omit=None, include=None, debug=()): """ `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 @@ -76,6 +76,9 @@ class coverage(object): `include` will be measured, files that match `omit` will not. Each will also accept a single string argument. + `debug` is a sequence of strings indicating what debugging information + is desired. + """ from coverage import __version__ @@ -108,7 +111,7 @@ class coverage(object): self.config.from_args( data_file=data_file, cover_pylib=cover_pylib, timid=timid, branch=branch, parallel=bool_or_none(data_suffix), - source=source, omit=omit, include=include + source=source, omit=omit, include=include, debug=debug, ) self.auto_data = auto_data @@ -209,26 +212,28 @@ class coverage(object): filename = filename[:-9] + ".py" return filename - def _should_trace(self, filename, frame): - """Decide whether to trace execution in `filename` + def _should_trace_with_reason(self, filename, frame): + """Decide whether to trace execution in `filename`, with a reason. This function is called from the trace function. As each new file name is encountered, this function determines whether it is traced or not. - Returns a canonicalized filename if it should be traced, False if it - should not. + Returns a pair of values: the first indicates whether the file should + be traced: it's a canonicalized filename if it should be traced, None + if it should not. The second value is a string, the resason for the + decision. """ if not filename: # Empty string is pretty useless - return False + return None, "empty string isn't a filename" if filename.startswith('<'): # Lots of non-file execution is represented with artificial # filenames like "<string>", "<doctest readme.txt[0]>", or # "<exec_function>". Don't ever trace these executions, since we # can't do anything with the data later anyway. - return False + return None, "not a real filename" self._check_for_packages() @@ -254,35 +259,38 @@ class coverage(object): # stdlib and coverage.py directories. if self.source_match: if not self.source_match.match(canonical): - return False + return None, "falls outside the --source trees" elif self.include_match: if not self.include_match.match(canonical): - return False + return None, "falls outside the --include trees" else: # If we aren't supposed to trace installed code, then check if this # is near the Python standard library and skip it if so. if self.pylib_match and self.pylib_match.match(canonical): - return False + return None, "is in the stdlib" # We exclude the coverage code itself, since a little of it will be # measured otherwise. if self.cover_match and self.cover_match.match(canonical): - return False + return None, "is part of coverage.py" # Check the file against the omit pattern. if self.omit_match and self.omit_match.match(canonical): - return False + return None, "is inside an --omit pattern" - return canonical + return canonical, "because we love you" + + def _should_trace(self, filename, frame): + """Decide whether to trace execution in `filename`. + + Calls `_should_trace_with_reason`, and returns just the decision. - # To log what should_trace returns, change this to "if 1:" - if 0: - _real_should_trace = _should_trace - def _should_trace(self, filename, frame): # pylint: disable=E0102 - """A logging decorator around the real _should_trace function.""" - ret = self._real_should_trace(filename, frame) - print("should_trace: %r -> %r" % (filename, ret)) - return ret + """ + canonical, reason = self._should_trace_with_reason(filename, frame) + if not canonical: + if 'notrace' in self.config.debug: + sys.stderr.write("Not tracing %r: %s\n" % (filename, reason)) + return canonical def _warn(self, msg): """Use `msg` as a warning.""" diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 6b11b3e9..493ce185 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -15,7 +15,7 @@ class CmdLineTest(CoverageTest): run_in_temp_dir = False INIT_LOAD = """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .load()\n""" def model_object(self): @@ -104,7 +104,7 @@ class ClassicCmdLineTest(CmdLineTest): def test_erase(self): # coverage -e self.cmd_executes("-e", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .erase() """) self.cmd_executes_same("-e", "--erase") @@ -114,7 +114,7 @@ class ClassicCmdLineTest(CmdLineTest): # -x calls coverage.load first. self.cmd_executes("-x foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .load() .start() .run_python_file('foo.py', ['foo.py']) @@ -123,7 +123,7 @@ class ClassicCmdLineTest(CmdLineTest): """) # -e -x calls coverage.erase first. self.cmd_executes("-e -x foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -132,7 +132,7 @@ class ClassicCmdLineTest(CmdLineTest): """) # --timid sets a flag, and program arguments get passed through. self.cmd_executes("-x --timid foo.py abc 123", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=True, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=True, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .load() .start() .run_python_file('foo.py', ['foo.py', 'abc', '123']) @@ -141,7 +141,7 @@ class ClassicCmdLineTest(CmdLineTest): """) # -L sets a flag, and flags for the program don't confuse us. self.cmd_executes("-x -p -L foo.py -a -b", """\ - .coverage(cover_pylib=True, data_suffix=True, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=True, data_suffix=True, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .load() .start() .run_python_file('foo.py', ['foo.py', '-a', '-b']) @@ -158,7 +158,7 @@ class ClassicCmdLineTest(CmdLineTest): def test_combine(self): # coverage -c self.cmd_executes("-c", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .load() .combine() .save() @@ -180,13 +180,13 @@ class ClassicCmdLineTest(CmdLineTest): show_missing=True) """) self.cmd_executes("-r -o fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"], debug=None) .load() .report(ignore_errors=None, omit=["fooey"], include=None, morfs=[], show_missing=None) """) self.cmd_executes("-r -o fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"], debug=None) .load() .report(ignore_errors=None, omit=["fooey", "booey"], include=None, morfs=[], show_missing=None) @@ -225,13 +225,13 @@ class ClassicCmdLineTest(CmdLineTest): omit=None, include=None, morfs=[]) """) self.cmd_executes("-a -o fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"], debug=None) .load() .annotate(directory=None, ignore_errors=None, omit=["fooey"], include=None, morfs=[]) """) self.cmd_executes("-a -o fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"], debug=None) .load() .annotate(directory=None, ignore_errors=None, omit=["fooey", "booey"], include=None, morfs=[]) @@ -270,13 +270,13 @@ class ClassicCmdLineTest(CmdLineTest): omit=None, include=None, morfs=[]) """) self.cmd_executes("-b -o fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"], debug=None) .load() .html_report(directory=None, ignore_errors=None, title=None, omit=["fooey"], include=None, morfs=[]) """) self.cmd_executes("-b -o fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"], debug=None) .load() .html_report(directory=None, ignore_errors=None, title=None, omit=["fooey", "booey"], include=None, morfs=[]) @@ -481,7 +481,7 @@ class NewCmdLineTest(CmdLineTest): self.cmd_executes_same("run --timid f.py", "-e -x --timid f.py") self.cmd_executes_same("run", "-x") self.cmd_executes("run --branch foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, source=None, include=None, omit=None, debug=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -489,7 +489,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run --rcfile=myrc.rc foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file="myrc.rc", source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file="myrc.rc", source=None, include=None, omit=None, debug=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -497,7 +497,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run --include=pre1,pre2 foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=["pre1", "pre2"], omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=["pre1", "pre2"], omit=None, debug=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -505,7 +505,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run --omit=opre1,opre2 foo.py", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["opre1", "opre2"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["opre1", "opre2"], debug=None) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -517,7 +517,9 @@ class NewCmdLineTest(CmdLineTest): .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=["pre1", "pre2"], - omit=["opre1", "opre2"]) + omit=["opre1", "opre2"], + debug=None, + ) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -529,7 +531,37 @@ class NewCmdLineTest(CmdLineTest): .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=["quux", "hi.there", "/home/bar"], include=None, - omit=None) + omit=None, + debug=None, + ) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + + def test_run_debug(self): + self.cmd_executes("run --debug=opt1 foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, + branch=None, config_file=True, + source=None, include=None, + omit=None, + debug=["opt1"], + ) + .erase() + .start() + .run_python_file('foo.py', ['foo.py']) + .stop() + .save() + """) + self.cmd_executes("run --debug=opt1,opt2 foo.py", """\ + .coverage(cover_pylib=None, data_suffix=None, timid=None, + branch=None, config_file=True, + source=None, include=None, + omit=None, + debug=["opt1","opt2"], + ) .erase() .start() .run_python_file('foo.py', ['foo.py']) @@ -539,7 +571,7 @@ class NewCmdLineTest(CmdLineTest): def test_run_module(self): self.cmd_executes("run -m mymodule", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .erase() .start() .run_python_module('mymodule', ['mymodule']) @@ -547,7 +579,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run -m mymodule -qq arg1 arg2", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=None, debug=None) .erase() .start() .run_python_module('mymodule', ['mymodule', '-qq', 'arg1', 'arg2']) @@ -555,7 +587,7 @@ class NewCmdLineTest(CmdLineTest): .save() """) self.cmd_executes("run --branch -m mymodule", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, source=None, include=None, omit=None) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=True, config_file=True, source=None, include=None, omit=None, debug=None) .erase() .start() .run_python_module('mymodule', ['mymodule']) @@ -583,13 +615,13 @@ class NewCmdLineTest(CmdLineTest): outfile="-") """) self.cmd_executes("xml --omit fooey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey"], debug=None) .load() .xml_report(ignore_errors=None, omit=["fooey"], include=None, morfs=[], outfile=None) """) self.cmd_executes("xml --omit fooey,booey", """\ - .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"]) + .coverage(cover_pylib=None, data_suffix=None, timid=None, branch=None, config_file=True, source=None, include=None, omit=["fooey", "booey"], debug=None) .load() .xml_report(ignore_errors=None, omit=["fooey", "booey"], include=None, morfs=[], outfile=None) |