diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2019-11-17 07:52:24 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2019-12-01 08:35:04 -0500 |
commit | 013e9de08c0747fddf92cc1fdac1d1f39185aa3b (patch) | |
tree | ada87dee565d5b167e424df6d9143ec49de349fa /coverage | |
parent | 10e41ab0dc165b3fd010345ab0fb1ed319d2f230 (diff) | |
download | python-coveragepy-git-013e9de08c0747fddf92cc1fdac1d1f39185aa3b.tar.gz |
Experimental: relative_files to support relative file names.
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/collector.py | 30 | ||||
-rw-r--r-- | coverage/config.py | 4 | ||||
-rw-r--r-- | coverage/control.py | 14 | ||||
-rw-r--r-- | coverage/results.py | 4 |
4 files changed, 31 insertions, 21 deletions
diff --git a/coverage/collector.py b/coverage/collector.py index 703f65b8..01acb78d 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -10,7 +10,6 @@ from coverage import env from coverage.backward import litems, range # pylint: disable=redefined-builtin from coverage.debug import short_stack from coverage.disposition import FileDisposition -from coverage.files import abs_file from coverage.misc import CoverageException, isolate_module from coverage.pytracer import PyTracer @@ -59,7 +58,7 @@ class Collector(object): SUPPORTED_CONCURRENCIES = set(["greenlet", "eventlet", "gevent", "thread"]) def __init__( - self, should_trace, check_include, should_start_context, + self, should_trace, check_include, should_start_context, file_mapper, timid, branch, warn, concurrency, ): """Create a collector. @@ -75,6 +74,10 @@ class Collector(object): is the new context. If the frame should not be the start of a new context, return None. + `file_mapper` is a function taking a filename, and returning a Unicode + filename. The result is the name that will be recorded in the data + file. + If `timid` is true, then a slower simpler trace function will be used. This is important for some environments where manipulation of tracing functions make the faster more sophisticated trace function not @@ -97,6 +100,7 @@ class Collector(object): self.should_trace = should_trace self.check_include = check_include self.should_start_context = should_start_context + self.file_mapper = file_mapper self.warn = warn self.branch = branch self.threading = None @@ -107,7 +111,7 @@ class Collector(object): self.origin = short_stack() self.concur_id_func = None - self.abs_file_cache = {} + self.mapped_file_cache = {} # We can handle a few concurrency options here, but only one at a time. these_concurrencies = self.SUPPORTED_CONCURRENCIES.intersection(concurrency) @@ -381,16 +385,16 @@ class Collector(object): context = new_context self.covdata.set_context(context) - def cached_abs_file(self, filename): - """A locally cached version of `abs_file`.""" + def cached_mapped_file(self, filename): + """A locally cached version of file names mapped through file_mapper.""" key = (type(filename), filename) try: - return self.abs_file_cache[key] + return self.mapped_file_cache[key] except KeyError: - return self.abs_file_cache.setdefault(key, abs_file(filename)) + return self.mapped_file_cache.setdefault(key, self.file_mapper(filename)) - def abs_file_dict(self, d): - """Return a dict like d, but with keys modified by `abs_file`.""" + def mapped_file_dict(self, d): + """Return a dict like d, but with keys modified by file_mapper.""" # The call to litems() ensures that the GIL protects the dictionary # iterator against concurrent modifications by tracers running # in other threads. We try three times in case of concurrent @@ -406,7 +410,7 @@ class Collector(object): else: raise runtime_err - return dict((self.cached_abs_file(k), v) for k, v in items if v) + return dict((self.cached_mapped_file(k), v) for k, v in items if v) def flush_data(self): """Save the collected data to our associated `CoverageData`. @@ -420,10 +424,10 @@ class Collector(object): return False if self.branch: - self.covdata.add_arcs(self.abs_file_dict(self.data)) + self.covdata.add_arcs(self.mapped_file_dict(self.data)) else: - self.covdata.add_lines(self.abs_file_dict(self.data)) - self.covdata.add_file_tracers(self.abs_file_dict(self.file_tracers)) + self.covdata.add_lines(self.mapped_file_dict(self.data)) + self.covdata.add_file_tracers(self.mapped_file_dict(self.file_tracers)) self._clear_data() return True diff --git a/coverage/config.py b/coverage/config.py index b8789fbf..997fc036 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -190,9 +190,10 @@ class CoverageConfig(object): self.note = None self.parallel = False self.plugins = [] - self.source = None + self.relative_files = False self.run_include = None self.run_omit = None + self.source = None self.timid = False # Defaults for [report] @@ -353,6 +354,7 @@ class CoverageConfig(object): ('note', 'run:note'), ('parallel', 'run:parallel', 'boolean'), ('plugins', 'run:plugins', 'list'), + ('relative_files', 'run:relative_files', 'boolean'), ('run_include', 'run:include', 'list'), ('run_omit', 'run:omit', 'list'), ('source', 'run:source', 'list'), diff --git a/coverage/control.py b/coverage/control.py index 0f306aa2..3a19695a 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -20,7 +20,7 @@ from coverage.context import should_start_context_test_function, combine_context from coverage.data import CoverageData, combine_parallel_data from coverage.debug import DebugControl, write_formatted_info from coverage.disposition import disposition_debug_msg -from coverage.files import PathAliases, set_relative_directory, abs_file +from coverage.files import PathAliases, abs_file, relative_filename, set_relative_directory from coverage.html import HtmlReporter from coverage.inorout import InOrOut from coverage.jsonreport import JsonReporter @@ -198,6 +198,7 @@ class Coverage(object): self._data_suffix = self._run_suffix = None self._exclude_re = None self._debug = None + self._file_mapper = None # State machine variables: # Have we initialized everything? @@ -238,6 +239,7 @@ class Coverage(object): self._exclude_re = {} set_relative_directory() + self._file_mapper = relative_filename if self.config.relative_files else abs_file # Load plugins self._plugins = Plugins.load_plugins(self.config.plugins, self.config, self._debug) @@ -405,6 +407,7 @@ class Coverage(object): should_trace=self._should_trace, check_include=self._check_include_omit_etc, should_start_context=should_start_context, + file_mapper=self._file_mapper, timid=self.config.timid, branch=self.config.branch, warn=self._warn, @@ -671,6 +674,7 @@ class Coverage(object): # mark completely unexecuted files as 0% covered. if self._data: for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files(): + file_path = self._file_mapper(file_path) self._data.touch_file(file_path, plugin_name) if self.config.note: @@ -723,7 +727,7 @@ class Coverage(object): if not isinstance(it, FileReporter): it = self._get_file_reporter(it) - return Analysis(data, it) + return Analysis(data, it, self._file_mapper) def _get_file_reporter(self, morf): """Get a FileReporter for a module or file name.""" @@ -731,13 +735,13 @@ class Coverage(object): file_reporter = "python" if isinstance(morf, string_class): - abs_morf = abs_file(morf) - plugin_name = self._data.file_tracer(abs_morf) + mapped_morf = self._file_mapper(morf) + plugin_name = self._data.file_tracer(mapped_morf) if plugin_name: plugin = self._plugins.get(plugin_name) if plugin: - file_reporter = plugin.file_reporter(abs_morf) + file_reporter = plugin.file_reporter(mapped_morf) if file_reporter is None: raise CoverageException( "Plugin %r did not provide a file reporter for %r." % ( diff --git a/coverage/results.py b/coverage/results.py index c88da919..ae8366bf 100644 --- a/coverage/results.py +++ b/coverage/results.py @@ -13,10 +13,10 @@ from coverage.misc import contract, CoverageException, nice_pair class Analysis(object): """The results of analyzing a FileReporter.""" - def __init__(self, data, file_reporter): + def __init__(self, data, file_reporter, file_mapper): self.data = data self.file_reporter = file_reporter - self.filename = self.file_reporter.filename + self.filename = file_mapper(self.file_reporter.filename) self.statements = self.file_reporter.lines() self.excluded = self.file_reporter.excluded_lines() |