summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2014-12-28 10:09:09 -0500
committerNed Batchelder <ned@nedbatchelder.com>2014-12-28 10:09:09 -0500
commit93561c76f281656169e5c062d93bc30da95d8c94 (patch)
treeac5a824337730c134d9e822bc63aa2a4ce98414c
parent98a7b99ed97af0f5dfc6fc7f5219ad4b026a6dfc (diff)
downloadpython-coveragepy-git-93561c76f281656169e5c062d93bc30da95d8c94.tar.gz
Further consolidation of code reading Python source.
-rw-r--r--coverage/execfile.py11
-rw-r--r--coverage/files.py38
-rw-r--r--coverage/parser.py14
-rw-r--r--coverage/phystokens.py16
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()