summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/codeunit.py56
-rw-r--r--coverage/control.py3
-rw-r--r--coverage/execfile.py2
-rw-r--r--coverage/files.py78
-rw-r--r--coverage/parser.py2
-rw-r--r--coverage/plugin.py2
-rw-r--r--coverage/python.py136
-rw-r--r--tests/test_codeunit.py3
-rw-r--r--tests/test_files.py22
-rw-r--r--tests/test_python.py27
10 files changed, 172 insertions, 159 deletions
diff --git a/coverage/codeunit.py b/coverage/codeunit.py
index e9efa396..998aa098 100644
--- a/coverage/codeunit.py
+++ b/coverage/codeunit.py
@@ -1,12 +1,9 @@
"""Code unit (module) handling for Coverage."""
import os
-import sys
from coverage.backward import unicode_class
-from coverage.files import get_python_source, FileLocator
-from coverage.parser import PythonParser
-from coverage.phystokens import source_token_lines, source_encoding
+from coverage.files import FileLocator
class CodeUnit(object):
@@ -114,54 +111,3 @@ class CodeUnit(object):
def get_parser(self, exclude=None):
raise NotImplementedError
-
-
-class PythonCodeUnit(CodeUnit):
- """Represents a Python file."""
-
- def __init__(self, morf, file_locator=None):
- super(PythonCodeUnit, self).__init__(morf, file_locator)
- self._source = None
-
- def _adjust_filename(self, fname):
- # .pyc files should always refer to a .py instead.
- if fname.endswith(('.pyc', '.pyo')):
- fname = fname[:-1]
- elif fname.endswith('$py.class'): # Jython
- fname = fname[:-9] + ".py"
- return fname
-
- def source(self):
- if self._source is None:
- self._source = get_python_source(self.filename)
- if sys.version_info < (3, 0):
- encoding = source_encoding(self._source)
- self._source = self._source.decode(encoding, "replace")
- assert isinstance(self._source, unicode_class)
- return self._source
-
- def get_parser(self, exclude=None):
- return PythonParser(filename=self.filename, exclude=exclude)
-
- def should_be_python(self):
- """Does it seem like this file should contain Python?
-
- This is used to decide if a file reported as part of the execution of
- a program was really likely to have contained Python in the first
- place.
-
- """
- # Get the file extension.
- _, ext = os.path.splitext(self.filename)
-
- # Anything named *.py* should be Python.
- if ext.startswith('.py'):
- return True
- # A file with no extension should be Python.
- if not ext:
- return True
- # Everything else is probably not Python.
- return False
-
- def source_token_lines(self):
- return source_token_lines(self.source())
diff --git a/coverage/control.py b/coverage/control.py
index 0ca1e95c..4aaf1af3 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -10,7 +10,7 @@ import sys
from coverage.annotate import AnnotateReporter
from coverage.backward import string_class, iitems
-from coverage.codeunit import CodeUnit, PythonCodeUnit
+from coverage.codeunit import CodeUnit
from coverage.collector import Collector
from coverage.config import CoverageConfig
from coverage.data import CoverageData
@@ -22,6 +22,7 @@ from coverage.files import ModuleMatcher
from coverage.html import HtmlReporter
from coverage.misc import CoverageException, bool_or_none, join_regex
from coverage.misc import file_be_gone, overrides
+from coverage.python import PythonCodeUnit
from coverage.results import Analysis, Numbers
from coverage.summary import SummaryReporter
from coverage.xmlreport import XmlReporter
diff --git a/coverage/execfile.py b/coverage/execfile.py
index fc7ad174..2d856897 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.python import get_python_source
class DummyLoader(object):
diff --git a/coverage/files.py b/coverage/files.py
index 9f0827fa..15ccabce 100644
--- a/coverage/files.py
+++ b/coverage/files.py
@@ -7,10 +7,8 @@ import os.path
import posixpath
import re
import sys
-import tokenize
-from coverage.misc import CoverageException, NoSource, join_regex
-from coverage.phystokens import source_encoding
+from coverage.misc import CoverageException, join_regex
class FileLocator(object):
@@ -56,80 +54,6 @@ 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)
- if ext == ".py" and sys.platform == "win32":
- exts = [".py", ".pyw"]
- else:
- exts = [ext]
-
- for ext in exts:
- try_filename = base + ext
- if os.path.exists(try_filename):
- # A regular text file: open it.
- 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))
- break
- else:
- # Couldn't find source.
- raise NoSource("No source for code: '%s'." % filename)
-
- # Python code should always end with a line with a newline.
- if source and source[-1] != '\n':
- source += '\n'
-
- return source
-
-
-def get_zip_bytes(filename):
- """Get data from `filename` if it is a zip file path.
-
- Returns the bytestring data read from the zip file, or None if no zip file
- could be found or `filename` isn't in it. The data returned will be
- an empty string if the file is empty.
-
- """
- import zipimport
- markers = ['.zip'+os.sep, '.egg'+os.sep]
- for marker in markers:
- if marker in filename:
- parts = filename.split(marker)
- try:
- zi = zipimport.zipimporter(parts[0]+marker[:-1])
- except zipimport.ZipImportError:
- continue
- try:
- data = zi.get_data(parts[1])
- except IOError:
- continue
- assert isinstance(data, bytes)
- return data
- return None
-
-
if sys.platform == 'win32':
def actual_path(path):
diff --git a/coverage/parser.py b/coverage/parser.py
index a2fa1dfe..f488367d 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -9,7 +9,6 @@ import tokenize
from coverage.backward import range # pylint: disable=redefined-builtin
from coverage.backward import bytes_to_ints
from coverage.bytecode import ByteCodes, CodeObjects
-from coverage.files import get_python_source
from coverage.misc import nice_pair, expensive, join_regex
from coverage.misc import CoverageException, NoSource, NotPython
from coverage.phystokens import generate_tokens
@@ -46,6 +45,7 @@ class PythonParser(CodeParser):
self.filename = filename or "<code>"
self.text = text
if not self.text:
+ from coverage.python import get_python_source
try:
self.text = get_python_source(self.filename)
except IOError as err:
diff --git a/coverage/plugin.py b/coverage/plugin.py
index a0a65e44..362e561a 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -1,7 +1,5 @@
"""Plugin management for coverage.py"""
-import sys
-
class CoveragePlugin(object):
"""Base class for coverage.py plugins."""
diff --git a/coverage/python.py b/coverage/python.py
new file mode 100644
index 00000000..62376922
--- /dev/null
+++ b/coverage/python.py
@@ -0,0 +1,136 @@
+"""Python source expertise for coverage.py"""
+
+import os.path
+import sys
+import tokenize
+import zipimport
+
+from coverage.backward import unicode_class
+from coverage.codeunit import CodeUnit
+from coverage.misc import NoSource
+from coverage.parser import PythonParser
+from coverage.phystokens import source_token_lines, source_encoding
+
+
+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)
+ if ext == ".py" and sys.platform == "win32":
+ exts = [".py", ".pyw"]
+ else:
+ exts = [ext]
+
+ for ext in exts:
+ try_filename = base + ext
+ if os.path.exists(try_filename):
+ # A regular text file: open it.
+ 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))
+ break
+ else:
+ # Couldn't find source.
+ raise NoSource("No source for code: '%s'." % filename)
+
+ # Python code should always end with a line with a newline.
+ if source and source[-1] != '\n':
+ source += '\n'
+
+ return source
+
+
+def get_zip_bytes(filename):
+ """Get data from `filename` if it is a zip file path.
+
+ Returns the bytestring data read from the zip file, or None if no zip file
+ could be found or `filename` isn't in it. The data returned will be
+ an empty string if the file is empty.
+
+ """
+ markers = ['.zip'+os.sep, '.egg'+os.sep]
+ for marker in markers:
+ if marker in filename:
+ parts = filename.split(marker)
+ try:
+ zi = zipimport.zipimporter(parts[0]+marker[:-1])
+ except zipimport.ZipImportError:
+ continue
+ try:
+ data = zi.get_data(parts[1])
+ except IOError:
+ continue
+ assert isinstance(data, bytes)
+ return data
+ return None
+
+
+class PythonCodeUnit(CodeUnit):
+ """Represents a Python file."""
+
+ def __init__(self, morf, file_locator=None):
+ super(PythonCodeUnit, self).__init__(morf, file_locator)
+ self._source = None
+
+ def _adjust_filename(self, fname):
+ # .pyc files should always refer to a .py instead.
+ if fname.endswith(('.pyc', '.pyo')):
+ fname = fname[:-1]
+ elif fname.endswith('$py.class'): # Jython
+ fname = fname[:-9] + ".py"
+ return fname
+
+ def source(self):
+ if self._source is None:
+ self._source = get_python_source(self.filename)
+ if sys.version_info < (3, 0):
+ encoding = source_encoding(self._source)
+ self._source = self._source.decode(encoding, "replace")
+ assert isinstance(self._source, unicode_class)
+ return self._source
+
+ def get_parser(self, exclude=None):
+ return PythonParser(filename=self.filename, exclude=exclude)
+
+ def should_be_python(self):
+ """Does it seem like this file should contain Python?
+
+ This is used to decide if a file reported as part of the execution of
+ a program was really likely to have contained Python in the first
+ place.
+
+ """
+ # Get the file extension.
+ _, ext = os.path.splitext(self.filename)
+
+ # Anything named *.py* should be Python.
+ if ext.startswith('.py'):
+ return True
+ # A file with no extension should be Python.
+ if not ext:
+ return True
+ # Everything else is probably not Python.
+ return False
+
+ def source_token_lines(self):
+ return source_token_lines(self.source())
diff --git a/tests/test_codeunit.py b/tests/test_codeunit.py
index c8389870..54e525b9 100644
--- a/tests/test_codeunit.py
+++ b/tests/test_codeunit.py
@@ -3,7 +3,8 @@
import os
import sys
-from coverage.codeunit import CodeUnit, PythonCodeUnit
+from coverage.codeunit import CodeUnit
+from coverage.python import PythonCodeUnit
from tests.coveragetest import CoverageTest
diff --git a/tests/test_files.py b/tests/test_files.py
index d7b8bd47..ae56e728 100644
--- a/tests/test_files.py
+++ b/tests/test_files.py
@@ -2,11 +2,10 @@
import os
import os.path
-import sys
from coverage.files import (
FileLocator, TreeMatcher, FnmatchMatcher, ModuleMatcher, PathAliases,
- find_python_files, abs_file, get_zip_bytes
+ find_python_files, abs_file
)
from coverage.misc import CoverageException
@@ -249,22 +248,3 @@ class FindPythonFilesTest(CoverageTest):
"sub/ssub/__init__.py", "sub/ssub/s.py",
"sub/windows.pyw",
])
-
-
-class GetZipBytesTest(CoverageTest):
- """Tests of `get_zip_bytes`."""
-
- run_in_temp_dir = False
-
- def test_get_encoded_zip_files(self):
- # See igor.py, do_zipmods, for the text of these files.
- zip_file = "tests/zipmods.zip"
- sys.path.append(zip_file) # So we can import the files.
- for encoding in ["utf8", "gb2312", "hebrew", "shift_jis"]:
- filename = zip_file + "/encoded_" + encoding + ".py"
- filename = filename.replace("/", os.sep)
- zip_data = get_zip_bytes(filename)
- zip_text = zip_data.decode(encoding)
- self.assertIn('All OK', zip_text)
- # Run the code to see that we really got it encoded properly.
- __import__("encoded_"+encoding)
diff --git a/tests/test_python.py b/tests/test_python.py
new file mode 100644
index 00000000..f2c18a10
--- /dev/null
+++ b/tests/test_python.py
@@ -0,0 +1,27 @@
+"""Tests of coverage/python.py"""
+
+import os
+import sys
+
+from coverage.python import get_zip_bytes
+
+from tests.coveragetest import CoverageTest
+
+
+class GetZipBytesTest(CoverageTest):
+ """Tests of `get_zip_bytes`."""
+
+ run_in_temp_dir = False
+
+ def test_get_encoded_zip_files(self):
+ # See igor.py, do_zipmods, for the text of these files.
+ zip_file = "tests/zipmods.zip"
+ sys.path.append(zip_file) # So we can import the files.
+ for encoding in ["utf8", "gb2312", "hebrew", "shift_jis"]:
+ filename = zip_file + "/encoded_" + encoding + ".py"
+ filename = filename.replace("/", os.sep)
+ zip_data = get_zip_bytes(filename)
+ zip_text = zip_data.decode(encoding)
+ self.assertIn('All OK', zip_text)
+ # Run the code to see that we really got it encoded properly.
+ __import__("encoded_"+encoding)