summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2014-12-26 10:48:29 -0500
committerNed Batchelder <ned@nedbatchelder.com>2014-12-26 10:48:29 -0500
commitff0f1ae98dad66594cd34b5aa7c24e909554bad3 (patch)
treefdcac4f02baa46101c4ab0cfe6904823b3b600cc
parent0dd9c8fb84dab988f1e34421bb91d53d45da807b (diff)
downloadpython-coveragepy-ff0f1ae98dad66594cd34b5aa7c24e909554bad3.tar.gz
Unify and clarify reading Python source. Probably broke .pyw files
-rw-r--r--coverage/codeunit.py2
-rw-r--r--coverage/execfile.py7
-rw-r--r--coverage/files.py13
-rw-r--r--coverage/parser.py15
-rw-r--r--coverage/phystokens.py25
-rw-r--r--coverage/summary.py2
-rw-r--r--lab/parser.py4
7 files changed, 34 insertions, 34 deletions
diff --git a/coverage/codeunit.py b/coverage/codeunit.py
index 09b86fe..1182398 100644
--- a/coverage/codeunit.py
+++ b/coverage/codeunit.py
@@ -169,6 +169,8 @@ class PythonCodeUnit(CodeUnit):
return self._source
def get_parser(self, exclude=None):
+ return PythonParser(filename=self.filename, exclude=exclude)
+ # TODO: REMOVE THIS USELESS CODE!
actual_filename, source = self._find_source(self.filename)
return PythonParser(
text=source, filename=actual_filename, exclude=exclude,
diff --git a/coverage/execfile.py b/coverage/execfile.py
index 246d79a..ecb0337 100644
--- a/coverage/execfile.py
+++ b/coverage/execfile.py
@@ -8,7 +8,7 @@ import types
from coverage.backward import BUILTINS
from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
from coverage.misc import ExceptionDuringRun, NoCode, NoSource
-from coverage.phystokens import open_python_source
+from coverage.phystokens import read_python_source
class DummyLoader(object):
@@ -178,13 +178,10 @@ def make_code_from_py(filename):
"""Get source from `filename` and make a code object of it."""
# Open the source file.
try:
- source_file = open_python_source(filename)
+ source = read_python_source(filename)
except IOError:
raise NoSource("No file to run: %r" % filename)
- with source_file:
- source = source_file.read()
-
# 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':
diff --git a/coverage/files.py b/coverage/files.py
index 6ba7983..2945054 100644
--- a/coverage/files.py
+++ b/coverage/files.py
@@ -8,8 +8,8 @@ import sys
import ntpath
import posixpath
-from coverage.misc import CoverageException, join_regex
-from coverage.phystokens import open_python_source
+from coverage.misc import CoverageException, NoSource, join_regex
+from coverage.phystokens import read_python_source, source_encoding
class FileLocator(object):
@@ -56,19 +56,20 @@ class FileLocator(object):
def get_python_source(filename):
- """Return the source code, as a string."""
+ """Return the source code, as a str."""
if os.path.exists(filename):
# A regular text file: open it.
- with open_python_source(filename) as f:
- return f.read()
+ return read_python_source(filename)
# Maybe it's in a zip file?
source = get_zip_bytes(filename)
if source is not None:
+ if sys.version_info >= (3, 0):
+ source = source.decode(source_encoding(source))
return source
# Couldn't find source.
- raise CoverageException(
+ raise NoSource(
"No source for code: '%s'." % filename
)
diff --git a/coverage/parser.py b/coverage/parser.py
index 317f7ec..b50bc57 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -54,6 +54,7 @@ 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:]
@@ -90,8 +91,7 @@ class PythonParser(CodeParser):
def byte_parser(self):
"""Create a ByteParser on demand."""
if not self._byte_parser:
- self._byte_parser = \
- ByteParser(text=self.text, filename=self.filename)
+ self._byte_parser = ByteParser(self.text, filename=self.filename)
return self._byte_parser
def lines_matching(self, *regexes):
@@ -343,16 +343,11 @@ OP_RETURN_VALUE = _opcode('RETURN_VALUE')
class ByteParser(object):
"""Parse byte codes to understand the structure of code."""
- def __init__(self, code=None, text=None, filename=None):
+ def __init__(self, text, code=None, filename=None):
+ self.text = text
if code:
self.code = code
- self.text = text
else:
- if not text:
- assert filename, "If no code or text, need a filename"
- text = get_python_source(filename)
- self.text = text
-
try:
self.code = compile(text, filename, "exec")
except SyntaxError as synerr:
@@ -378,7 +373,7 @@ class ByteParser(object):
"""
children = CodeObjects(self.code)
- return (ByteParser(code=c, text=self.text) for c in children)
+ return (ByteParser(self.text, code=c) for c in children)
def _bytes_lines(self):
"""Map byte offsets to line numbers in `code`.
diff --git a/coverage/phystokens.py b/coverage/phystokens.py
index 4faa3c3..70deb80 100644
--- a/coverage/phystokens.py
+++ b/coverage/phystokens.py
@@ -153,17 +153,17 @@ def source_encoding(source):
Returns a string, the name of the encoding.
"""
- # Note: this function should never be called on Python 3, since py3 has
- # built-in tools to do this.
- assert sys.version_info < (3, 0)
+ if sys.version_info >= (3, 0):
+ readline = iter(source.splitlines(True)).__next__
+ return tokenize.detect_encoding(readline)[0]
+
+ # Do this so the detect_encode code we copied will work.
+ readline = iter(source.splitlines(True)).next
# This is mostly code adapted from Py3.2's tokenize module.
cookie_re = re.compile(r"^\s*#.*coding[:=]\s*([-\w.]+)")
- # Do this so the detect_encode code we copied will work.
- readline = iter(source.splitlines(True)).next
-
def _get_normal_name(orig_enc):
"""Imitates get_normal_name in tokenizer.c."""
# Only care about the first 12 characters.
@@ -246,9 +246,12 @@ def source_encoding(source):
# Reading Python source and interpreting the coding comment is a big deal.
if sys.version_info >= (3, 0):
# Python 3.2 provides `tokenize.open`, the best way to open source files.
- import tokenize
- open_python_source = tokenize.open
+ def read_python_source(filename):
+ # Returns unicode on Py3, bytes on Py2
+ with tokenize.open(filename) as f:
+ return f.read()
else:
- def open_python_source(fname):
- """Open a source file the best way."""
- return open(fname, "rU")
+ def read_python_source(filename):
+ # Returns unicode on Py3, bytes on Py2
+ with open(filename, "rU") as f:
+ return f.read()
diff --git a/coverage/summary.py b/coverage/summary.py
index 10ac7e2..cfcdfcd 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -4,7 +4,7 @@ import sys
from coverage.report import Reporter
from coverage.results import Numbers
-from coverage.misc import NotPython
+from coverage.misc import CoverageException, NotPython
class SummaryReporter(Reporter):
diff --git a/lab/parser.py b/lab/parser.py
index 932480d..1783468 100644
--- a/lab/parser.py
+++ b/lab/parser.py
@@ -9,6 +9,7 @@ from optparse import OptionParser
import disgen
from coverage.misc import CoverageException
+from coverage.files import get_python_source
from coverage.parser import ByteParser, PythonParser
opcode_counts = collections.Counter()
@@ -70,7 +71,8 @@ class ParserMain(object):
"""Process just one file."""
try:
- bp = ByteParser(filename=filename)
+ text = get_python_source(filename)
+ bp = ByteParser(text, filename=filename)
except Exception as err:
print("%s" % (err,))
return