5 |
5 |
6 import os.path |
6 import os.path |
7 import types |
7 import types |
8 import zipimport |
8 import zipimport |
9 |
9 |
10 from coverage import env, files |
10 from coverage import env |
|
11 from coverage.exceptions import CoverageException, NoSource |
|
12 from coverage.files import canonical_filename, relative_filename |
11 from coverage.misc import contract, expensive, isolate_module, join_regex |
13 from coverage.misc import contract, expensive, isolate_module, join_regex |
12 from coverage.misc import CoverageException, NoSource |
|
13 from coverage.parser import PythonParser |
14 from coverage.parser import PythonParser |
14 from coverage.phystokens import source_token_lines, source_encoding |
15 from coverage.phystokens import source_token_lines, source_encoding |
15 from coverage.plugin import FileReporter |
16 from coverage.plugin import FileReporter |
16 |
17 |
17 os = isolate_module(os) |
18 os = isolate_module(os) |
54 source = get_zip_bytes(try_filename) |
55 source = get_zip_bytes(try_filename) |
55 if source is not None: |
56 if source is not None: |
56 break |
57 break |
57 else: |
58 else: |
58 # Couldn't find source. |
59 # Couldn't find source. |
59 exc_msg = "No source for code: '%s'.\n" % (filename,) |
60 raise NoSource(f"No source for code: '{filename}'.") |
60 exc_msg += "Aborting report output, consider using -i." |
|
61 raise NoSource(exc_msg) |
|
62 |
61 |
63 # Replace \f because of http://bugs.python.org/issue19035 |
62 # Replace \f because of http://bugs.python.org/issue19035 |
64 source = source.replace(b'\f', b' ') |
63 source = source.replace(b'\f', b' ') |
65 source = source.decode(source_encoding(source), "replace") |
64 source = source.decode(source_encoding(source), "replace") |
66 |
65 |
134 if hasattr(morf, '__file__') and morf.__file__: |
133 if hasattr(morf, '__file__') and morf.__file__: |
135 filename = morf.__file__ |
134 filename = morf.__file__ |
136 elif isinstance(morf, types.ModuleType): |
135 elif isinstance(morf, types.ModuleType): |
137 # A module should have had .__file__, otherwise we can't use it. |
136 # A module should have had .__file__, otherwise we can't use it. |
138 # This could be a PEP-420 namespace package. |
137 # This could be a PEP-420 namespace package. |
139 raise CoverageException("Module {} has no file".format(morf)) |
138 raise CoverageException(f"Module {morf} has no file") |
140 else: |
139 else: |
141 filename = morf |
140 filename = morf |
142 |
141 |
143 filename = source_for_file(files.unicode_filename(filename)) |
142 filename = source_for_file(filename) |
144 return filename |
143 return filename |
145 |
144 |
146 |
145 |
147 class PythonFileReporter(FileReporter): |
146 class PythonFileReporter(FileReporter): |
148 """Report support for a Python file.""" |
147 """Report support for a Python file.""" |
150 def __init__(self, morf, coverage=None): |
149 def __init__(self, morf, coverage=None): |
151 self.coverage = coverage |
150 self.coverage = coverage |
152 |
151 |
153 filename = source_for_morf(morf) |
152 filename = source_for_morf(morf) |
154 |
153 |
155 super(PythonFileReporter, self).__init__(files.canonical_filename(filename)) |
154 super().__init__(canonical_filename(filename)) |
156 |
155 |
157 if hasattr(morf, '__name__'): |
156 if hasattr(morf, '__name__'): |
158 name = morf.__name__.replace(".", os.sep) |
157 name = morf.__name__.replace(".", os.sep) |
159 if os.path.basename(filename).startswith('__init__.'): |
158 if os.path.basename(filename).startswith('__init__.'): |
160 name += os.sep + "__init__" |
159 name += os.sep + "__init__" |
161 name += ".py" |
160 name += ".py" |
162 name = files.unicode_filename(name) |
|
163 else: |
161 else: |
164 name = files.relative_filename(filename) |
162 name = relative_filename(filename) |
165 self.relname = name |
163 self.relname = name |
166 |
164 |
167 self._source = None |
165 self._source = None |
168 self._parser = None |
166 self._parser = None |
169 self._excluded = None |
167 self._excluded = None |
170 |
168 |
171 def __repr__(self): |
169 def __repr__(self): |
172 return "<PythonFileReporter {!r}>".format(self.filename) |
170 return f"<PythonFileReporter {self.filename!r}>" |
173 |
171 |
174 @contract(returns='unicode') |
172 @contract(returns='unicode') |
175 def relative_filename(self): |
173 def relative_filename(self): |
176 return self.relname |
174 return self.relname |
177 |
175 |