diff options
-rw-r--r-- | coverage/execfile.py | 11 | ||||
-rw-r--r-- | coverage/files.py | 38 | ||||
-rw-r--r-- | coverage/parser.py | 14 | ||||
-rw-r--r-- | coverage/phystokens.py | 16 |
4 files changed, 41 insertions, 38 deletions
diff --git a/coverage/execfile.py b/coverage/execfile.py index ecb03372..beaf1913 100644 --- a/coverage/execfile.py +++ b/coverage/execfile.py @@ -7,8 +7,8 @@ import types from coverage.backward import BUILTINS from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec +from coverage.files import get_python_source from coverage.misc import ExceptionDuringRun, NoCode, NoSource -from coverage.phystokens import read_python_source class DummyLoader(object): @@ -178,16 +178,11 @@ def make_code_from_py(filename): """Get source from `filename` and make a code object of it.""" # Open the source file. try: - source = read_python_source(filename) - except IOError: + source = get_python_source(filename) + except (IOError, NoSource): raise NoSource("No file to run: %r" % filename) - # We have the source. `compile` still needs the last line to be clean, - # so make sure it is, then compile a code object from it. - if not source or source[-1] != '\n': - source += '\n' code = compile(source, filename, "exec") - return code diff --git a/coverage/files.py b/coverage/files.py index 7f2431d3..8fa8067f 100644 --- a/coverage/files.py +++ b/coverage/files.py @@ -1,15 +1,16 @@ """File wrangling.""" import fnmatch +import ntpath import os import os.path +import posixpath import re import sys -import ntpath -import posixpath +import tokenize from coverage.misc import CoverageException, NoSource, join_regex -from coverage.phystokens import read_python_source, source_encoding +from coverage.phystokens import source_encoding class FileLocator(object): @@ -55,6 +56,22 @@ class FileLocator(object): return self.canonical_filename_cache[filename] +def read_python_source(filename): + """Read the Python source text from `filename`. + + Returns a str: unicode on Python 3, bytes on Python 2. + + """ + # Python 3.2 provides `tokenize.open`, the best way to open source files. + if sys.version_info >= (3, 2): + f = tokenize.open(filename) + else: + f = open(filename, "rU") + + with f: + return f.read() + + def get_python_source(filename): """Return the source code, as a str.""" base, ext = os.path.splitext(filename) @@ -67,17 +84,24 @@ def get_python_source(filename): try_filename = base + ext if os.path.exists(try_filename): # A regular text file: open it. - return read_python_source(try_filename) + source = read_python_source(try_filename) + break # Maybe it's in a zip file? source = get_zip_bytes(try_filename) if source is not None: if sys.version_info >= (3, 0): source = source.decode(source_encoding(source)) - return source + break + else: + # Couldn't find source. + raise NoSource("No source for code: %r." % filename) + + # Python code should always end with a line with a newline. + if source and source[-1] != '\n': + source += '\n' - # Couldn't find source. - raise NoSource("No source for code: %r." % filename) + return source def get_zip_bytes(filename): diff --git a/coverage/parser.py b/coverage/parser.py index b50bc578..05c0d0c2 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -56,11 +56,10 @@ class PythonParser(CodeParser): if self.text: assert isinstance(self.text, str) # Scrap the BOM if it exists. - if ord(self.text[0]) == 0xfeff: - self.text = self.text[1:] - # Python source should always have a final newline. - if self.text[-1] != "\n": - self.text += "\n" + # (Used to do this, but no longer. Not sure what bad will happen + # if we don't do it.) + # if ord(self.text[0]) == 0xfeff: + # self.text = self.text[1:] self.exclude = exclude @@ -352,9 +351,10 @@ class ByteParser(object): self.code = compile(text, filename, "exec") except SyntaxError as synerr: raise NotPython( - "Couldn't parse '%s' as Python source: '%s' at line %d" % - (filename, synerr.msg, synerr.lineno) + "Couldn't parse %r as Python source: '%s' at line %d" % ( + filename, synerr.msg, synerr.lineno ) + ) # Alternative Python implementations don't always provide all the # attributes on code objects that we need to do the analysis. diff --git a/coverage/phystokens.py b/coverage/phystokens.py index f3f633dd..c52e28ae 100644 --- a/coverage/phystokens.py +++ b/coverage/phystokens.py @@ -262,19 +262,3 @@ if sys.version_info >= (3, 0): source_encoding = _source_encoding_py3 else: source_encoding = _source_encoding_py2 - - -def read_python_source(filename): - """Read the Python source text from `filename`. - - Returns unicode on Python 3, bytes on Python 2. - - """ - # Python 3.2 provides `tokenize.open`, the best way to open source files. - if sys.version_info >= (3, 2): - f = tokenize.open(filename) - else: - f = open(filename, "rU") - - with f: - return f.read() |