diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2014-09-06 14:24:15 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2014-09-06 14:24:15 -0400 |
commit | 0891e02eb490494ee40a7f840e4ab9fd6b3d2d7b (patch) | |
tree | 394c5e68d986579d94a153b15dee8ac977c643bd /coverage/control.py | |
parent | 7862e1fee15b6ef8fcadfd112cf93121ed8f388e (diff) | |
download | python-coveragepy-git-0891e02eb490494ee40a7f840e4ab9fd6b3d2d7b.tar.gz |
Move dispositions closer to useful plugins
Diffstat (limited to 'coverage/control.py')
-rw-r--r-- | coverage/control.py | 133 |
1 files changed, 92 insertions, 41 deletions
diff --git a/coverage/control.py b/coverage/control.py index fab2ea19..42550508 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -135,8 +135,14 @@ class Coverage(object): self.debug = DebugControl(self.config.debug, debug_file or sys.stderr) # Load plugins - plugins = load_plugins(self.config.plugins, self.config) - self.tracer_plugins = []#[cls() for cls in tracer_classes] + self.plugins = load_plugins(self.config.plugins, self.config) + + self.trace_judges = [] + for plugin in self.plugins: + trace_judge = plugin.trace_judge() + if trace_judge: + self.trace_judges.append((plugin, trace_judge)) + self.trace_judges.append((None, None)) # The Python case. self.auto_data = auto_data @@ -160,8 +166,11 @@ class Coverage(object): self.include = prep_patterns(self.config.include) self.collector = Collector( - self._should_trace, timid=self.config.timid, - branch=self.config.branch, warn=self._warn, + should_trace=self._should_trace, + check_include=self._tracing_check_include_omit_etc, + timid=self.config.timid, + branch=self.config.branch, + warn=self._warn, coroutine=self.config.coroutine, ) @@ -246,21 +255,11 @@ class Coverage(object): Returns a FileDisposition object. """ - disp = FileDisposition(filename) - - if not filename: - # Empty string is pretty useless - return disp.nope("empty string isn't a filename") - - if filename.startswith('memory:'): - return disp.nope("memory isn't traceable") - - 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 disp.nope("not a real filename") + original_filename = filename + def nope(reason): + disp = FileDisposition(original_filename) + disp.nope(reason) + return disp self._check_for_packages() @@ -274,6 +273,20 @@ class Coverage(object): if dunder_file: filename = self._source_for_file(dunder_file) + if not filename: + # Empty string is pretty useless + return nope("empty string isn't a filename") + + if filename.startswith('memory:'): + return nope("memory isn't traceable") + + 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 nope("not a real filename") + # Jython reports the .class file to the tracer, use the source file. if filename.endswith("$py.class"): filename = filename[:-9] + ".py" @@ -281,39 +294,60 @@ class Coverage(object): canonical = self.file_locator.canonical_filename(filename) # Try the plugins, see if they have an opinion about the file. - for tracer in self.tracer_plugins: - plugin_disp = tracer.should_trace(canonical) - if plugin_disp: - plugin_disp.plugin = tracer - return plugin_disp + for plugin, judge in self.trace_judges: + if plugin: + disp = judge(canonical) + else: + disp = FileDisposition(original_filename) + if disp is not None: + if plugin: + disp.source_filename = plugin.source_file_name(canonical) + else: + disp.source_filename = canonical + disp.plugin = plugin + + reason = self._check_include_omit_etc(disp.source_filename) + if reason: + disp.nope(reason) + + return disp + + return nope("no plugin found") # TODO: a test that causes this. + def _check_include_omit_etc(self, filename): + """Check a filename against the include, omit, etc, rules. + + Returns a string or None. String means, don't trace, and is the reason + why. None means no reason found to not trace. + + """ # If the user specified source or include, then that's authoritative # about the outer bound of what to measure and we don't have to apply # any canned exclusions. If they didn't, then we have to exclude the # stdlib and coverage.py directories. if self.source_match: - if not self.source_match.match(canonical): - return disp.nope("falls outside the --source trees") + if not self.source_match.match(filename): + return "falls outside the --source trees" elif self.include_match: - if not self.include_match.match(canonical): - return disp.nope("falls outside the --include trees") + if not self.include_match.match(filename): + return "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 disp.nope("is in the stdlib") + if self.pylib_match and self.pylib_match.match(filename): + return "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 disp.nope("is part of coverage.py") + if self.cover_match and self.cover_match.match(filename): + return "is part of coverage.py" # Check the file against the omit pattern. - if self.omit_match and self.omit_match.match(canonical): - return disp.nope("is inside an --omit pattern") + if self.omit_match and self.omit_match.match(filename): + return "is inside an --omit pattern" - disp.filename = canonical - return disp + # No reason found to skip this file. + return None def _should_trace(self, filename, frame): """Decide whether to trace execution in `filename`. @@ -326,6 +360,22 @@ class Coverage(object): self.debug.write(disp.debug_message()) return disp + def _tracing_check_include_omit_etc(self, filename): + """Check a filename against the include, omit, etc, rules, and say so. + + Returns a boolean: True if the file should be traced, False if not. + + """ + reason = self._check_include_omit_etc(filename) + if self.debug.should('trace'): + if not reason: + msg = "Tracing %r" % (filename,) + else: + msg = "Not tracing %r: %s" % (filename, reason) + self.debug.write(msg) + + return not reason + def _warn(self, msg): """Use `msg` as a warning.""" self._warnings.append(msg) @@ -783,21 +833,22 @@ class FileDisposition(object): """A simple object for noting a number of details of files to trace.""" def __init__(self, original_filename): self.original_filename = original_filename - self.filename = None + self.source_filename = None + self.trace = True self.reason = "" self.plugin = None def nope(self, reason): """A helper for returning a NO answer from should_trace.""" + self.trace = False self.reason = reason - return self def debug_message(self): """Produce a debugging message explaining the outcome.""" - if not self.filename: - msg = "Not tracing %r: %s" % (self.original_filename, self.reason) - else: + if self.trace: msg = "Tracing %r" % (self.original_filename,) + else: + msg = "Not tracing %r: %s" % (self.original_filename, self.reason) return msg |