--- a/DebugClients/Python3/coverage/python.py Sat Jul 23 13:33:54 2016 +0200 +++ b/DebugClients/Python3/coverage/python.py Sun Jul 24 12:01:01 2016 +0200 @@ -4,14 +4,19 @@ """Python source expertise for coverage.py""" import os.path +import types import zipimport from coverage import env, files -from coverage.misc import contract, expensive, NoSource, join_regex +from coverage.misc import ( + contract, CoverageException, expensive, NoSource, join_regex, isolate_module, +) from coverage.parser import PythonParser from coverage.phystokens import source_token_lines, source_encoding from coverage.plugin import FileReporter +os = isolate_module(os) + @contract(returns='bytes') def read_python_source(filename): @@ -48,6 +53,8 @@ # Couldn't find source. raise NoSource("No source for code: '%s'." % filename) + # Replace \f because of http://bugs.python.org/issue19035 + source = source.replace(b'\f', b' ') source = source.decode(source_encoding(source), "replace") # Python code should always end with a line with a newline. @@ -90,9 +97,15 @@ if hasattr(morf, '__file__'): filename = morf.__file__ + elif isinstance(morf, types.ModuleType): + # A module should have had .__file__, otherwise we can't use it. + # This could be a PEP-420 namespace package. + raise CoverageException("Module {0} has no file".format(morf)) else: filename = morf + filename = files.unicode_filename(filename) + # .pyc files should always refer to a .py instead. if filename.endswith(('.pyc', '.pyo')): filename = filename[:-1] @@ -104,6 +117,7 @@ if hasattr(morf, '__name__'): name = morf.__name__ name = name.replace(".", os.sep) + ".py" + name = files.unicode_filename(name) else: name = files.relative_filename(filename) self.relname = name @@ -113,6 +127,7 @@ self._statements = None self._excluded = None + @contract(returns='unicode') def relative_filename(self): return self.relname @@ -124,21 +139,16 @@ filename=self.filename, exclude=self.coverage._exclude_regex('exclude'), ) + self._parser.parse_source() return self._parser - @expensive def lines(self): """Return the line numbers of statements in the file.""" - if self._statements is None: - self._statements, self._excluded = self.parser.parse_source() - return self._statements + return self.parser.statements - @expensive def excluded_lines(self): """Return the line numbers of statements in the file.""" - if self._excluded is None: - self._statements, self._excluded = self.parser.parse_source() - return self._excluded + return self.parser.excluded def translate_lines(self, lines): return self.parser.translate_lines(lines) @@ -162,6 +172,9 @@ def exit_counts(self): return self.parser.exit_counts() + def missing_arc_description(self, start, end, executed_arcs=None): + return self.parser.missing_arc_description(start, end, executed_arcs) + @contract(returns='unicode') def source(self): if self._source is None: