diff options
-rw-r--r-- | coverage/control.py | 17 | ||||
-rw-r--r-- | coverage/data.py | 34 |
2 files changed, 47 insertions, 4 deletions
diff --git a/coverage/control.py b/coverage/control.py index 16cf74ee..13261de2 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -4,6 +4,7 @@ """Core control stuff for coverage.py.""" import atexit +import datetime import inspect import os import platform @@ -195,8 +196,6 @@ class Coverage(object): is called. """ - from coverage import __version__ - if self._inited: return @@ -282,7 +281,7 @@ class Coverage(object): # environments (virtualenv, for example), these modules may be # spread across a few locations. Look at all the candidate modules # we've imported, and take all the different ones. - for m in (atexit, inspect, os, platform, _structseq, traceback): + for m in (atexit, datetime, inspect, os, platform, _structseq, traceback): if m is not None and hasattr(m, "__file__"): self.pylib_dirs.add(self._canonical_dir(m)) if _structseq and not hasattr(_structseq, '__file__'): @@ -780,6 +779,18 @@ class Coverage(object): self.data.touch_file(py_file) + # Add run information. + from coverage import __version__ + + self.data.add_run_info( + collector="coverage.py v%s" % __version__, + when=datetime.datetime.now().isoformat(), + command_line=sys.argv, + python=sys.version.replace('\n', ''), + platform=platform.platform(), + implementation=platform.python_implementation(), + ) + self._measured = False return self.data diff --git a/coverage/data.py b/coverage/data.py index 386c1567..e8aaf1c0 100644 --- a/coverage/data.py +++ b/coverage/data.py @@ -19,7 +19,9 @@ from coverage.misc import CoverageException, file_be_gone class CoverageData(object): """Manages collected coverage data, including file storage. - This class is the public supported API to coverage.py's data. + This class is the public supported API to the data coverage.py collects + during program execution. It includes information about what code was + executed. .. note:: @@ -81,6 +83,13 @@ class CoverageData(object): # # { 'file1': "django.coverage", ... } # + # * run: a dict of information about the coverage.py run:: + # + # { 'collector': 'coverage.py', + # 'collected': '20150724T162717', + # ... + # } + # # Only one of `lines` or `arcs` will be present: with branch coverage, data # is stored as arcs. Without branch coverage, it is stored as lines. The # line data is easily recovered from the arcs: it is all the first elements @@ -115,6 +124,9 @@ class CoverageData(object): # self._file_tracers = {} + # A dict of information about the coverage.py run. + self._run_info = {} + ## ## Reading data ## @@ -175,6 +187,10 @@ class CoverageData(object): return self._file_tracers.get(filename, "") return None + def run_info(self): + """Return the dict of run information.""" + return self._run_info + def measured_files(self): """A list of all files that had been measured.""" return list(self._arcs or self._lines) @@ -218,6 +234,7 @@ class CoverageData(object): for fname, arcs in iitems(data.get('arcs', {})) ) self._file_tracers = data.get('file_tracers', {}) + self._run_info = data.get('run', {}) self._validate() @@ -324,6 +341,17 @@ class CoverageData(object): self._validate() + def add_run_info(self, **kwargs): + """Add information about the run. + + Keywords are arbitrary, and are stored in the run dictionary. Values + must be JSON serializable. You may use this function more than once, + but repeated keywords overwrite each other. + + """ + self._run_info.update(kwargs) + self._validate() + def touch_file(self, filename): """Ensure that `filename` appears in the data, empty if needed.""" (self._arcs or self._lines).setdefault(filename, []) @@ -343,6 +371,9 @@ class CoverageData(object): if self._file_tracers: file_data['file_tracers'] = self._file_tracers + if self._run_info: + file_data['run'] = self._run_info + # Write the data to the file. file_obj.write(self.GO_AWAY) json.dump(file_data, file_obj) @@ -359,6 +390,7 @@ class CoverageData(object): self._lines = {} self._arcs = {} self._file_tracers = {} + self._run_info = {} self._validate() def update(self, other_data, aliases=None): |