diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2009-03-05 21:21:43 -0500 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2009-03-05 21:21:43 -0500 |
commit | 0cc8fd18714cb214327a0a58b704401a9efdf8dc (patch) | |
tree | 0a6ac59eaf1b7acaf26053a5f3c69304b9ddbda3 /coverage/data.py | |
download | python-coveragepy-0cc8fd18714cb214327a0a58b704401a9efdf8dc.tar.gz |
Initial coverage.py 3.0 beta 1
Diffstat (limited to 'coverage/data.py')
-rw-r--r-- | coverage/data.py | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/coverage/data.py b/coverage/data.py new file mode 100644 index 0000000..5d14a33 --- /dev/null +++ b/coverage/data.py @@ -0,0 +1,122 @@ +"""Coverage data for coverage.py""" + +import os, marshal, socket, types + +class CoverageData: + """Manages collected coverage data.""" + # Name of the data file (unless environment variable is set). + filename_default = ".coverage" + + # Environment variable naming the data file. + filename_env = "COVERAGE_FILE" + + def __init__(self): + self.filename = None + self.use_file = True + + # A map from canonical Python source file name to a dictionary in + # which there's an entry for each line number that has been + # executed: + # + # { + # 'filename1.py': { 12: True, 47: True, ... }, + # ... + # } + # + self.executed = {} + + def usefile(self, use_file=True, filename_default=None): + self.use_file = use_file + if filename_default and not self.filename: + self.filename_default = filename_default + + def read(self, parallel=False): + """Read coverage data from the coverage data file (if it exists).""" + data = {} + if self.use_file and not self.filename: + self.filename = os.environ.get( + self.filename_env, self.filename_default) + if parallel: + self.filename += "." + socket.gethostname() + self.filename += "." + str(os.getpid()) + if os.path.exists(self.filename): + data = self._read_file(self.filename) + self.executed = data + + def write(self): + """Write the collected coverage data to a file.""" + if self.use_file and self.filename: + self.write_file(self.filename) + + def erase(self): + if self.filename and os.path.exists(self.filename): + os.remove(self.filename) + + def write_file(self, filename): + """Write the coverage data to `filename`.""" + f = open(filename, 'wb') + try: + marshal.dump(self.executed, f) + finally: + f.close() + + def read_file(self, filename): + self.executed = self._read_file(filename) + + def _read_file(self, filename): + """ Return the stored coverage data from the given file. + """ + try: + fdata = open(filename, 'rb') + executed = marshal.load(fdata) + fdata.close() + if isinstance(executed, types.DictType): + return executed + else: + return {} + except: + return {} + + def combine_parallel_data(self): + """ Treat self.filename as a file prefix, and combine the data from all + of the files starting with that prefix. + """ + data_dir, local = os.path.split(self.filename) + for f in os.listdir(data_dir or '.'): + if f.startswith(local): + full_path = os.path.join(data_dir, f) + file_data = self._read_file(full_path) + self._combine_data(file_data) + + def _combine_data(self, new_data): + """Combine the `new_data` into `executed`.""" + for filename, file_data in new_data.items(): + self.executed.setdefault(filename, {}).update(file_data) + + def add_raw_data(self, data_points): + """Add raw data. + + `data_points` is (filename, lineno) pairs. + + """ + for filename, lineno in data_points: + self.executed.setdefault(filename, {})[lineno] = True + + def executed_lines(self, filename): + """Return a mapping object such that "lineno in obj" is true if that + line number had been executed in `filename`. + """ + # TODO: Write a better description. + return self.executed[filename] + + def summary(self): + """Return a dict summarizing the coverage data. + + Keys are the basename of the filenames, and values are the number of + executed lines. This is useful in the unit tests. + + """ + summ = {} + for filename, lines in self.executed.items(): + summ[os.path.basename(filename)] = len(lines) + return summ |