DebugClients/Python/coverage/data.py

changeset 0
de9c2efb9d02
child 31
744cd0b4b8cd
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 """Coverage data for Coverage."""
2
3 import os
4 import cPickle as pickle
5
6 from backward import sorted # pylint: disable-msg=W0622
7
8
9 class CoverageData:
10 """Manages collected coverage data, including file storage.
11
12 The data file format is a pickled dict, with these keys:
13
14 * collector: a string identifying the collecting software
15
16 * lines: a dict mapping filenames to sorted lists of line numbers
17 executed:
18 { 'file1': [17,23,45], 'file2': [1,2,3], ... }
19
20 """
21
22 # Name of the data file (unless environment variable is set).
23 filename_default = ".coverage"
24
25 # Environment variable naming the data file.
26 filename_env = "COVERAGE_FILE"
27
28 def __init__(self, basename=None, suffix=None, collector=None):
29 """Create a CoverageData.
30
31 `basename` is the name of the file to use for storing data.
32
33 `suffix` is a suffix to append to the base file name. This can be used
34 for multiple or parallel execution, so that many coverage data files
35 can exist simultaneously.
36
37 `collector` is a string describing the coverage measurement software.
38
39 """
40 self.basename = basename
41 self.collector = collector
42 self.suffix = suffix
43
44 self.use_file = True
45 self.filename = None
46
47 # A map from canonical Python source file name to a dictionary in
48 # which there's an entry for each line number that has been
49 # executed:
50 #
51 # {
52 # 'filename1.py': { 12: True, 47: True, ... },
53 # ...
54 # }
55 #
56 self.lines = {}
57
58 def usefile(self, use_file=True):
59 """Set whether or not to use a disk file for data."""
60 self.use_file = use_file
61
62 def _make_filename(self):
63 """Construct the filename that will be used for data file storage."""
64 assert self.use_file
65 if not self.filename:
66 self.filename = (self.basename or
67 os.environ.get(self.filename_env, self.filename_default))
68
69 if self.suffix:
70 self.filename += self.suffix
71
72 def read(self):
73 """Read coverage data from the coverage data file (if it exists)."""
74 data = {}
75 if self.use_file:
76 self._make_filename()
77 data = self._read_file(self.filename)
78 self.lines = data
79
80 def write(self):
81 """Write the collected coverage data to a file."""
82 if self.use_file:
83 self._make_filename()
84 self.write_file(self.filename)
85
86 def erase(self):
87 """Erase the data, both in this object, and from its file storage."""
88 if self.use_file:
89 self._make_filename()
90 if self.filename and os.path.exists(self.filename):
91 os.remove(self.filename)
92 self.lines = {}
93
94 def line_data(self):
95 """Return the map from filenames to lists of line numbers executed."""
96 return dict(
97 [(f, sorted(linemap.keys())) for f, linemap in self.lines.items()]
98 )
99
100 def write_file(self, filename):
101 """Write the coverage data to `filename`."""
102
103 # Create the file data.
104 data = {}
105
106 data['lines'] = self.line_data()
107
108 if self.collector:
109 data['collector'] = self.collector
110
111 # Write the pickle to the file.
112 fdata = open(filename, 'wb')
113 try:
114 pickle.dump(data, fdata, 2)
115 finally:
116 fdata.close()
117
118 def read_file(self, filename):
119 """Read the coverage data from `filename`."""
120 self.lines = self._read_file(filename)
121
122 def _read_file(self, filename):
123 """Return the stored coverage data from the given file."""
124 try:
125 fdata = open(filename, 'rb')
126 try:
127 data = pickle.load(fdata)
128 finally:
129 fdata.close()
130 if isinstance(data, dict):
131 # Unpack the 'lines' item.
132 lines = dict([
133 (f, dict([(l, True) for l in linenos]))
134 for f,linenos in data['lines'].items()
135 ])
136 return lines
137 else:
138 return {}
139 except Exception:
140 return {}
141
142 def combine_parallel_data(self):
143 """ Treat self.filename as a file prefix, and combine the data from all
144 of the files starting with that prefix.
145 """
146 self._make_filename()
147 data_dir, local = os.path.split(self.filename)
148 for f in os.listdir(data_dir or '.'):
149 if f.startswith(local):
150 full_path = os.path.join(data_dir, f)
151 new_data = self._read_file(full_path)
152 for filename, file_data in new_data.items():
153 self.lines.setdefault(filename, {}).update(file_data)
154
155 def add_line_data(self, data_points):
156 """Add executed line data.
157
158 `data_points` is (filename, lineno) pairs.
159
160 """
161 for filename, lineno in data_points:
162 self.lines.setdefault(filename, {})[lineno] = True
163
164 def executed_files(self):
165 """A list of all files that had been measured as executed."""
166 return self.lines.keys()
167
168 def executed_lines(self, filename):
169 """A map containing all the line numbers executed in `filename`.
170
171 If `filename` hasn't been collected at all (because it wasn't executed)
172 then return an empty map.
173
174 """
175 return self.lines.get(filename) or {}
176
177 def summary(self):
178 """Return a dict summarizing the coverage data.
179
180 Keys are the basename of the filenames, and values are the number of
181 executed lines. This is useful in the unit tests.
182
183 """
184 summ = {}
185 for filename, lines in self.lines.items():
186 summ[os.path.basename(filename)] = len(lines)
187 return summ

eric ide

mercurial