OLD | NEW |
1 """Results of coverage measurement.""" | 1 """Results of coverage measurement.""" |
2 | 2 |
3 import os | 3 import os |
4 | 4 |
5 from coverage.backward import iitems, set, sorted # pylint: disable=W0622 | 5 from coverage.backward import iitems, set, sorted # pylint: disable=W0622 |
6 from coverage.misc import format_lines, join_regex, NoSource | 6 from coverage.misc import format_lines, join_regex, NoSource |
7 from coverage.parser import CodeParser | 7 from coverage.parser import CodeParser |
8 | 8 |
9 | 9 |
10 class Analysis(object): | 10 class Analysis(object): |
11 """The results of analyzing a code unit.""" | 11 """The results of analyzing a code unit.""" |
12 | 12 |
13 def __init__(self, cov, code_unit): | 13 def __init__(self, cov, code_unit): |
14 self.coverage = cov | 14 self.coverage = cov |
15 self.code_unit = code_unit | 15 self.code_unit = code_unit |
16 | 16 |
17 self.filename = self.code_unit.filename | 17 self.filename = self.code_unit.filename |
18 ext = os.path.splitext(self.filename)[1] | 18 actual_filename, source = self.find_source(self.filename) |
19 source = None | |
20 if ext == '.py': | |
21 if not os.path.exists(self.filename): | |
22 source = self.coverage.file_locator.get_zip_data(self.filename) | |
23 if not source: | |
24 raise NoSource("No source for code: '%s'" % self.filename) | |
25 | 19 |
26 self.parser = CodeParser( | 20 self.parser = CodeParser( |
27 text=source, filename=self.filename, | 21 text=source, filename=actual_filename, |
28 exclude=self.coverage._exclude_regex('exclude') | 22 exclude=self.coverage._exclude_regex('exclude') |
29 ) | 23 ) |
30 self.statements, self.excluded = self.parser.parse_source() | 24 self.statements, self.excluded = self.parser.parse_source() |
31 | 25 |
32 # Identify missing statements. | 26 # Identify missing statements. |
33 executed = self.coverage.data.executed_lines(self.filename) | 27 executed = self.coverage.data.executed_lines(self.filename) |
34 exec1 = self.parser.first_lines(executed) | 28 exec1 = self.parser.first_lines(executed) |
35 self.missing = sorted(set(self.statements) - set(exec1)) | 29 self.missing = self.statements - exec1 |
36 | 30 |
37 if self.coverage.data.has_arcs(): | 31 if self.coverage.data.has_arcs(): |
38 self.no_branch = self.parser.lines_matching( | 32 self.no_branch = self.parser.lines_matching( |
39 join_regex(self.coverage.config.partial_list), | 33 join_regex(self.coverage.config.partial_list), |
40 join_regex(self.coverage.config.partial_always_list) | 34 join_regex(self.coverage.config.partial_always_list) |
41 ) | 35 ) |
42 n_branches = self.total_branches() | 36 n_branches = self.total_branches() |
43 mba = self.missing_branch_arcs() | 37 mba = self.missing_branch_arcs() |
44 n_partial_branches = sum( | 38 n_partial_branches = sum( |
45 [len(v) for k,v in iitems(mba) if k not in self.missing] | 39 [len(v) for k,v in iitems(mba) if k not in self.missing] |
46 ) | 40 ) |
47 n_missing_branches = sum([len(v) for k,v in iitems(mba)]) | 41 n_missing_branches = sum([len(v) for k,v in iitems(mba)]) |
48 else: | 42 else: |
49 n_branches = n_partial_branches = n_missing_branches = 0 | 43 n_branches = n_partial_branches = n_missing_branches = 0 |
50 self.no_branch = set() | 44 self.no_branch = set() |
51 | 45 |
52 self.numbers = Numbers( | 46 self.numbers = Numbers( |
53 n_files=1, | 47 n_files=1, |
54 n_statements=len(self.statements), | 48 n_statements=len(self.statements), |
55 n_excluded=len(self.excluded), | 49 n_excluded=len(self.excluded), |
56 n_missing=len(self.missing), | 50 n_missing=len(self.missing), |
57 n_branches=n_branches, | 51 n_branches=n_branches, |
58 n_partial_branches=n_partial_branches, | 52 n_partial_branches=n_partial_branches, |
59 n_missing_branches=n_missing_branches, | 53 n_missing_branches=n_missing_branches, |
60 ) | 54 ) |
61 | 55 |
| 56 def find_source(self, filename): |
| 57 """Find the source for `filename`. |
| 58 |
| 59 Returns two values: the actual filename, and the source. |
| 60 |
| 61 The source returned depends on which of these cases holds: |
| 62 |
| 63 * The filename seems to be a non-source file: returns None |
| 64 |
| 65 * The filename is a source file, and actually exists: returns None. |
| 66 |
| 67 * The filename is a source file, and is in a zip file or egg: |
| 68 returns the source. |
| 69 |
| 70 * The filename is a source file, but couldn't be found: raises |
| 71 `NoSource`. |
| 72 |
| 73 """ |
| 74 source = None |
| 75 |
| 76 base, ext = os.path.splitext(filename) |
| 77 TRY_EXTS = { |
| 78 '.py': ['.py', '.pyw'], |
| 79 '.pyw': ['.pyw'], |
| 80 } |
| 81 try_exts = TRY_EXTS.get(ext) |
| 82 if not try_exts: |
| 83 return filename, None |
| 84 |
| 85 for try_ext in try_exts: |
| 86 try_filename = base + try_ext |
| 87 if os.path.exists(try_filename): |
| 88 return try_filename, None |
| 89 source = self.coverage.file_locator.get_zip_data(try_filename) |
| 90 if source: |
| 91 return try_filename, source |
| 92 raise NoSource("No source for code: '%s'" % filename) |
| 93 |
62 def missing_formatted(self): | 94 def missing_formatted(self): |
63 """The missing line numbers, formatted nicely. | 95 """The missing line numbers, formatted nicely. |
64 | 96 |
65 Returns a string like "1-2, 5-11, 13-14". | 97 Returns a string like "1-2, 5-11, 13-14". |
66 | 98 |
67 """ | 99 """ |
68 return format_lines(self.statements, self.missing) | 100 return format_lines(self.statements, self.missing) |
69 | 101 |
70 def has_arcs(self): | 102 def has_arcs(self): |
71 """Were arcs measured in this result?""" | 103 """Were arcs measured in this result?""" |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 nums.n_missing_branches = ( | 277 nums.n_missing_branches = ( |
246 self.n_missing_branches + other.n_missing_branches | 278 self.n_missing_branches + other.n_missing_branches |
247 ) | 279 ) |
248 return nums | 280 return nums |
249 | 281 |
250 def __radd__(self, other): | 282 def __radd__(self, other): |
251 # Implementing 0+Numbers allows us to sum() a list of Numbers. | 283 # Implementing 0+Numbers allows us to sum() a list of Numbers. |
252 if other == 0: | 284 if other == 0: |
253 return self | 285 return self |
254 return NotImplemented | 286 return NotImplemented |
OLD | NEW |