1 """Reporter foundation for Coverage.""" |
1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
|
2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt |
2 |
3 |
3 import fnmatch, os |
4 """Reporter foundation for coverage.py.""" |
4 from .codeunit import code_unit_factory |
5 |
5 from .files import prep_patterns |
6 import os |
6 from .misc import CoverageException, NoSource, NotPython |
7 |
|
8 from coverage.files import prep_patterns, FnmatchMatcher |
|
9 from coverage.misc import CoverageException, NoSource, NotPython |
|
10 |
7 |
11 |
8 class Reporter(object): |
12 class Reporter(object): |
9 """A base class for all reporters.""" |
13 """A base class for all reporters.""" |
10 |
14 |
11 def __init__(self, coverage, config): |
15 def __init__(self, coverage, config): |
16 |
20 |
17 """ |
21 """ |
18 self.coverage = coverage |
22 self.coverage = coverage |
19 self.config = config |
23 self.config = config |
20 |
24 |
21 # The code units to report on. Set by find_code_units. |
25 # The FileReporters to report on. Set by find_file_reporters. |
22 self.code_units = [] |
26 self.file_reporters = [] |
23 |
27 |
24 # The directory into which to place the report, used by some derived |
28 # The directory into which to place the report, used by some derived |
25 # classes. |
29 # classes. |
26 self.directory = None |
30 self.directory = None |
27 |
31 |
28 def find_code_units(self, morfs): |
32 def find_file_reporters(self, morfs): |
29 """Find the code units we'll report on. |
33 """Find the FileReporters we'll report on. |
30 |
34 |
31 `morfs` is a list of modules or filenames. |
35 `morfs` is a list of modules or file names. |
32 |
36 |
33 """ |
37 """ |
34 morfs = morfs or self.coverage.data.measured_files() |
38 self.file_reporters = self.coverage._get_file_reporters(morfs) |
35 file_locator = self.coverage.file_locator |
|
36 self.code_units = code_unit_factory(morfs, file_locator) |
|
37 |
39 |
38 if self.config.include: |
40 if self.config.include: |
39 patterns = prep_patterns(self.config.include) |
41 patterns = prep_patterns(self.config.include) |
|
42 matcher = FnmatchMatcher(patterns) |
40 filtered = [] |
43 filtered = [] |
41 for cu in self.code_units: |
44 for fr in self.file_reporters: |
42 for pattern in patterns: |
45 if matcher.match(fr.filename): |
43 if fnmatch.fnmatch(cu.filename, pattern): |
46 filtered.append(fr) |
44 filtered.append(cu) |
47 self.file_reporters = filtered |
45 break |
|
46 self.code_units = filtered |
|
47 |
48 |
48 if self.config.omit: |
49 if self.config.omit: |
49 patterns = prep_patterns(self.config.omit) |
50 patterns = prep_patterns(self.config.omit) |
|
51 matcher = FnmatchMatcher(patterns) |
50 filtered = [] |
52 filtered = [] |
51 for cu in self.code_units: |
53 for fr in self.file_reporters: |
52 for pattern in patterns: |
54 if not matcher.match(fr.filename): |
53 if fnmatch.fnmatch(cu.filename, pattern): |
55 filtered.append(fr) |
54 break |
56 self.file_reporters = filtered |
55 else: |
|
56 filtered.append(cu) |
|
57 self.code_units = filtered |
|
58 |
57 |
59 self.code_units.sort() |
58 self.file_reporters.sort() |
60 |
59 |
61 def report_files(self, report_fn, morfs, directory=None): |
60 def report_files(self, report_fn, morfs, directory=None): |
62 """Run a reporting function on a number of morfs. |
61 """Run a reporting function on a number of morfs. |
63 |
62 |
64 `report_fn` is called for each relative morf in `morfs`. It is called |
63 `report_fn` is called for each relative morf in `morfs`. It is called |
65 as:: |
64 as:: |
66 |
65 |
67 report_fn(code_unit, analysis) |
66 report_fn(file_reporter, analysis) |
68 |
67 |
69 where `code_unit` is the `CodeUnit` for the morf, and `analysis` is |
68 where `file_reporter` is the `FileReporter` for the morf, and |
70 the `Analysis` for the morf. |
69 `analysis` is the `Analysis` for the morf. |
71 |
70 |
72 """ |
71 """ |
73 self.find_code_units(morfs) |
72 self.find_file_reporters(morfs) |
74 |
73 |
75 if not self.code_units: |
74 if not self.file_reporters: |
76 raise CoverageException("No data to report.") |
75 raise CoverageException("No data to report.") |
77 |
76 |
78 self.directory = directory |
77 self.directory = directory |
79 if self.directory and not os.path.exists(self.directory): |
78 if self.directory and not os.path.exists(self.directory): |
80 os.makedirs(self.directory) |
79 os.makedirs(self.directory) |
81 |
80 |
82 for cu in self.code_units: |
81 for fr in self.file_reporters: |
83 try: |
82 try: |
84 report_fn(cu, self.coverage._analyze(cu)) |
83 report_fn(fr, self.coverage._analyze(fr)) |
85 except NoSource: |
84 except NoSource: |
86 if not self.config.ignore_errors: |
85 if not self.config.ignore_errors: |
87 raise |
86 raise |
88 except NotPython: |
87 except NotPython: |
89 # Only report errors for .py files, and only if we didn't |
88 # Only report errors for .py files, and only if we didn't |
90 # explicitly suppress those errors. |
89 # explicitly suppress those errors. |
91 if cu.should_be_python() and not self.config.ignore_errors: |
90 # NotPython is only raised by PythonFileReporter, which has a |
|
91 # should_be_python() method. |
|
92 if fr.should_be_python() and not self.config.ignore_errors: |
92 raise |
93 raise |
93 |
|
94 # |
|
95 # eflag: FileType = Python2 |
|