diff options
Diffstat (limited to 'coverage/collector.py')
-rw-r--r-- | coverage/collector.py | 100 |
1 files changed, 52 insertions, 48 deletions
diff --git a/coverage/collector.py b/coverage/collector.py index 948cbbb0..ae5f6c8b 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -1,14 +1,19 @@ -"""Raw data collector for Coverage.""" +# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt + +"""Raw data collector for coverage.py.""" import os, sys from coverage import env +from coverage.backward import iitems +from coverage.files import abs_file from coverage.misc import CoverageException from coverage.pytracer import PyTracer try: # Use the C extension code when we can, for speed. - from coverage.tracer import CTracer # pylint: disable=no-name-in-module + from coverage.tracer import CTracer, CFileDisposition # pylint: disable=no-name-in-module except ImportError: # Couldn't import the C extension, maybe it isn't built. if os.getenv('COVERAGE_TEST_TRACER') == 'c': @@ -20,11 +25,16 @@ except ImportError: # exception here causes all sorts of other noise in unittest. sys.stderr.write( "*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n" - ) + ) sys.exit(1) CTracer = None +class FileDisposition(object): + """A simple value type for recording what to do with a file.""" + pass + + class Collector(object): """Collects trace data. @@ -46,14 +56,14 @@ class Collector(object): # the top, and resumed when they become the top again. _collectors = [] - def __init__(self, + def __init__( + self, should_trace, check_include, timid, branch, warn, concurrency, ): """Create a collector. `should_trace` is a function, taking a filename, and returning a - canonicalized filename, or None depending on whether the file should - be traced or not. + `coverage.FileDisposition object`. TODO: `check_include` @@ -85,13 +95,13 @@ class Collector(object): try: if concurrency == "greenlet": - import greenlet # pylint: disable=import-error + import greenlet # pylint: disable=import-error,useless-suppression self.concur_id_func = greenlet.getcurrent elif concurrency == "eventlet": - import eventlet.greenthread # pylint: disable=import-error + import eventlet.greenthread # pylint: disable=import-error,useless-suppression self.concur_id_func = eventlet.greenthread.getcurrent elif concurrency == "gevent": - import gevent # pylint: disable=import-error + import gevent # pylint: disable=import-error,useless-suppression self.concur_id_func = gevent.getcurrent elif concurrency == "thread" or not concurrency: # It's important to import threading only if we need it. If @@ -119,7 +129,12 @@ class Collector(object): # trace function. self._trace_class = CTracer or PyTracer - self.supports_plugins = self._trace_class is CTracer + if self._trace_class is CTracer: + self.file_disposition_class = CFileDisposition + self.supports_plugins = True + else: + self.file_disposition_class = FileDisposition + self.supports_plugins = False def __repr__(self): return "<Collector at 0x%x>" % id(self) @@ -130,15 +145,23 @@ class Collector(object): def reset(self): """Clear collected data, and prepare to collect more.""" - # A dictionary mapping filenames to dicts with line number keys, - # or mapping filenames to dicts with line number pairs as keys. + # A dictionary mapping filenames to dicts with line number keys (if not + # branch coverage), or mapping filenames to dicts with line number + # pairs as keys (if branch coverage). self.data = {} - self.plugin_data = {} - - # 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 - # None). + # A dictionary mapping filenames to file tracer plugin names that will + # handle them. + self.file_tracers = {} + + # The .should_trace_cache attribute is a cache from filenames to + # coverage.FileDisposition objects, or None. When a file is first + # considered for tracing, a FileDisposition is obtained from + # Coverage.should_trace. Its .trace attribute indicates whether the + # file should be traced or not. If it should be, a plugin with dynamic + # filenames can decide not to trace it based on the dynamic filename + # being excluded by the inclusion rules, in which case the + # FileDisposition will be replaced by None in the cache. if env.PYPY: import __pypy__ # pylint: disable=import-error # Alex Gaynor said: @@ -181,8 +204,8 @@ class Collector(object): ) ) - if hasattr(tracer, 'plugin_data'): - tracer.plugin_data = self.plugin_data + if hasattr(tracer, 'file_tracers'): + tracer.file_tracers = self.file_tracers if hasattr(tracer, 'threading'): tracer.threading = self.threading if hasattr(tracer, 'check_include'): @@ -283,39 +306,20 @@ class Collector(object): else: self._start_tracer() - def get_line_data(self): - """Return the line data collected. + def save_data(self, covdata): + """Save the collected data to a `CoverageData`. - Data is { filename: { lineno: None, ...}, ...} + Also resets the collector. """ - if self.branch: - # If we were measuring branches, then we have to re-build the dict - # to show line data. We'll use the first lines of all the arcs, - # if they are actual lines. We don't need the second lines, because - # the second lines will also be first lines, sometimes to exits. - line_data = {} - for f, arcs in self.data.items(): - line_data[f] = dict( - (l1, None) for l1, _ in arcs.keys() if l1 > 0 - ) - return line_data - else: - return self.data - - def get_arc_data(self): - """Return the arc data collected. - - Data is { filename: { (l1, l2): None, ...}, ...} + def abs_file_dict(d): + """Return a dict like d, but with keys modified by `abs_file`.""" + return dict((abs_file(k), v) for k, v in iitems(d)) - Note that no data is collected or returned if the Collector wasn't - created with `branch` true. - - """ if self.branch: - return self.data + covdata.set_arcs(abs_file_dict(self.data)) else: - return {} + covdata.set_lines(abs_file_dict(self.data)) + covdata.set_file_tracers(abs_file_dict(self.file_tracers)) - def get_plugin_data(self): - return self.plugin_data + self.reset() |