summaryrefslogtreecommitdiff
path: root/coverage/plugin.py
diff options
context:
space:
mode:
Diffstat (limited to 'coverage/plugin.py')
-rw-r--r--coverage/plugin.py116
1 files changed, 63 insertions, 53 deletions
diff --git a/coverage/plugin.py b/coverage/plugin.py
index f4182b0f..1b098364 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -3,11 +3,8 @@
"""Plugin interfaces for coverage.py"""
-import os
-import re
-
from coverage import files
-from coverage.misc import _needs_to_implement
+from coverage.misc import contract, _needs_to_implement
class CoveragePlugin(object):
@@ -154,42 +151,42 @@ class FileTracer(object):
class FileReporter(object):
"""Support needed for files during the reporting phase."""
+
def __init__(self, filename):
- # TODO: document that this init happens.
+ """Simple initialization of a `FileReporter`.
+
+ The `filename` argument is the path to the file being reported. This
+ will be available as the `.filename` attribute on the object. Other
+ method implementations on this base class rely on this attribute.
+
+ """
self.filename = filename
def __repr__(self):
return "<{0.__class__.__name__} filename={0.filename!r}>".format(self)
def relative_filename(self):
- return files.relative_filename(self.filename)
-
- # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
- # of them defined.
-
- def __eq__(self, other):
- return isinstance(other, FileReporter) and self.filename == other.filename
+ """Return the relative filename for this file.
- def __ne__(self, other):
- return not (self == other)
+ This file path will be displayed in reports. You only need to supply
+ this method if you have an unusual syntax for file paths. The default
+ implementation will supply the actual project-relative file path.
- def __lt__(self, other):
- return self.filename < other.filename
-
- def __le__(self, other):
- return self.filename <= other.filename
+ """
+ return files.relative_filename(self.filename)
- def __gt__(self, other):
- return self.filename > other.filename
+ def lines(self):
+ """Return a set of executable lines"""
+ _needs_to_implement(self, "lines")
- def __ge__(self, other):
- return self.filename >= other.filename
+ def excluded_lines(self):
+ return set()
- def statements(self):
- _needs_to_implement(self, "statements")
+ def arcs(self):
+ return []
- def excluded_statements(self):
- return set([])
+ def no_branch_lines(self):
+ return set()
def translate_lines(self, lines):
return set(lines)
@@ -197,47 +194,60 @@ class FileReporter(object):
def translate_arcs(self, arcs):
return arcs
- def no_branch_lines(self):
- return set()
-
def exit_counts(self):
return {}
- def arcs(self):
- return []
-
+ @contract(returns='unicode')
def source(self):
"""Return the source for the code, a Unicode string."""
# A generic implementation: read the text of self.filename
- with open(self.filename) as f:
- return f.read()
+ with open(self.filename, "rb") as f:
+ return f.read().decode("utf8")
def source_token_lines(self):
- """Return the 'tokenized' text for the code."""
+ """Generate a series of tokenized lines, one for each line in `source`.
+
+ These tokens are used for syntax-colored reports.
+
+ Each line is a list of pairs, each pair is a token::
+
+ [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
+
+ Each pair has a token class, and the token text. The token classes are:
+
+ * `'com'`: a comment
+ * `'key'`: a keyword
+ * `'nam'`: a name, or identifier
+ * `'num'`: a number
+ * `'op'`: an operator
+ * `'str'`: a string literal
+ * `'txt'`: some other kind of text
+
+ If you concatenate all the token texts, and then join them with newlines,
+ you should have your original `source` back.
+
+ """
# A generic implementation, each line is one "txt" token.
for line in self.source().splitlines():
yield [('txt', line)]
- def should_be_python(self):
- """Does it seem like this file should contain Python?
+ # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
+ # of them defined.
- 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.
- """
- return False
+ def __eq__(self, other):
+ return isinstance(other, FileReporter) and self.filename == other.filename
- def flat_rootname(self):
- """A base for a flat filename to correspond to this file.
+ def __ne__(self, other):
+ return not (self == other)
- Useful for writing files about the code where you want all the files in
- the same directory, but need to differentiate same-named files from
- different directories.
+ def __lt__(self, other):
+ return self.filename < other.filename
- For example, the file a/b/c.py will return 'a_b_c_py'
+ def __le__(self, other):
+ return self.filename <= other.filename
- You should not need to override this method.
+ def __gt__(self, other):
+ return self.filename > other.filename
- """
- name = os.path.splitdrive(self.relative_filename())[1]
- return re.sub(r"[\\/.:]", "_", name)
+ def __ge__(self, other):
+ return self.filename >= other.filename