summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.pylintrc4
-rw-r--r--coverage/__init__.py12
-rw-r--r--coverage/html.py22
-rw-r--r--coverage/misc.py1
-rw-r--r--coverage/templite.py38
-rw-r--r--test/coverage_coverage.py2
-rw-r--r--test/coveragetest.py18
7 files changed, 76 insertions, 21 deletions
diff --git a/.pylintrc b/.pylintrc
index e7d76c31..834dcc3c 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -69,7 +69,7 @@ load-plugins=
# Messages that are noisy for now, eventually maybe we'll turn them on:
# C0111:169:coverage.analyze_morf: Missing docstring
# C0103:256:coverage.morf_filename: Invalid name "f" (should match [a-z_][a-z0-9_]{2,30}$)
-disable-msg=I0011,W0122,W0142,W0232,C0323,C0324,W0603, R0201,R0401,W0403,E1103, C0111,C0103
+disable-msg=I0011,W0122,W0142,W0232,C0323,C0324,W0603, R0201,R0401,W0403,E1103, C0103
[REPORTS]
@@ -123,7 +123,7 @@ required-attributes=
# Regular expression which should only match functions or classes name which do
# not require a docstring
-no-docstring-rgx=__.*__
+no-docstring-rgx=__.*__|test[A-Z_].*|setUp|tearDown
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
diff --git a/coverage/__init__.py b/coverage/__init__.py
index c1b95874..c787b341 100644
--- a/coverage/__init__.py
+++ b/coverage/__init__.py
@@ -23,12 +23,20 @@ from coverage.misc import CoverageException
_the_coverage = None
def _singleton_method(name):
- def func(*args, **kwargs):
+ """Return a function to the `name` method on a singleton `coverage` object.
+
+ The singleton object is created the first time one of these functions is
+ called.
+
+ """
+ def wrapper(*args, **kwargs):
+ """Singleton wrapper around a coverage method."""
global _the_coverage
if not _the_coverage:
_the_coverage = coverage()
return getattr(_the_coverage, name)(*args, **kwargs)
- return func
+ return wrapper
+
# Define the module-level functions.
use_cache = _singleton_method('use_cache')
diff --git a/coverage/html.py b/coverage/html.py
index 6fc49ff5..d8f98c45 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -16,9 +16,7 @@ def data(fname):
class HtmlReporter(Reporter):
- """HTML reporting.
-
- """
+ """HTML reporting."""
def __init__(self, coverage, ignore_errors=False):
super(HtmlReporter, self).__init__(coverage, ignore_errors)
@@ -27,7 +25,14 @@ class HtmlReporter(Reporter):
self.files = []
- def report(self, morfs, directory=None, omit_prefixes=None):
+ def report(self, morfs, directory, omit_prefixes=None):
+ """Generate an HTML report for `morfs`.
+
+ `morfs` is a list of modules or filenames. `directory` is where to put
+ the HTML files. `omit_prefixes` is a list of strings, prefixes of
+ modules to omit from the report.
+
+ """
assert directory, "must provide a directory for html reporting"
# Process all the files.
@@ -147,10 +152,15 @@ def not_empty(t):
return t or " "
def format_pct(p):
+ """Format a percentage value for the HTML reports."""
return "%.0f" % p
def spaceless(html):
- """Squeeze out some of that annoying extra space that comes from
- nicely-formatted templates."""
+ """Squeeze out some annoying extra space from an HTML string.
+
+ Nicely-formatted templates mean lots of extra space in the result. Get
+ rid of some.
+
+ """
html = re.sub(">\s+<p ", ">\n<p ", html)
return html
diff --git a/coverage/misc.py b/coverage/misc.py
index 398f9b27..8a8b5117 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -47,4 +47,5 @@ def format_lines(statements, lines):
class CoverageException(Exception):
+ """An exception specific to Coverage."""
pass
diff --git a/coverage/templite.py b/coverage/templite.py
index f75e2a2d..fabc3dbb 100644
--- a/coverage/templite.py
+++ b/coverage/templite.py
@@ -8,9 +8,26 @@ import re
class Templite(object):
"""A simple template renderer, for a nano-subset of Django syntax.
+
+ Supported constructs are extended variable access::
+
+ {{var.modifer.modifier|filter|filter}}
+
+ and loops::
+
+ {% for var in list %}...{% endfor %}
+
+ Construct a Templite with the template text, then use `render` against a
+ dictionary context to create a finished string.
"""
def __init__(self, text, *contexts):
+ """Construct a Templite with the given `text`.
+
+ `contexts` are dictionaries of values to use for future renderings.
+ These are good for filters and global values.
+
+ """
self.loops = []
self.text = self._prepare(text)
self.context = {}
@@ -18,12 +35,17 @@ class Templite(object):
self.context.update(context)
def render(self, context=None):
+ """Render this template by applying it to `context`.
+
+ `context` is a dictionary of values to use in this rendering.
+
+ """
# Make the complete context we'll use.
ctx = dict(self.context)
if context:
ctx.update(context)
- ctxaccess = ContextAccess(ctx)
+ ctxaccess = _ContextAccess(ctx)
# Render the loops.
for iloop, (loopvar, listvar, loopbody) in enumerate(self.loops):
@@ -41,7 +63,7 @@ class Templite(object):
# Pull out loops.
text = re.sub(
r"(?s){% for ([a-z0-9_]+) in ([a-z0-9_.|]+) %}(.*?){% endfor %}",
- self._loop_repl, text
+ self._loop_prepare, text
)
# Protect actual percent signs in the text.
text = text.replace("%", "%%")
@@ -49,7 +71,8 @@ class Templite(object):
text = re.sub(r"{{([^}]+)}}", r"%(\1)s", text)
return text
- def _loop_repl(self, match):
+ def _loop_prepare(self, match):
+ """Prepare a loop body for `_prepare`."""
nloop = len(self.loops)
# Append (loopvar, listvar, loopbody) to self.loops
loopvar, listvar, loopbody = match.groups()
@@ -58,8 +81,13 @@ class Templite(object):
return "{{loop:%d}}" % nloop
-class ContextAccess(object):
+class _ContextAccess(object):
+ """A mediator for a context.
+ Implements __getitem__ on a context for Templite, so that string formatting
+ references can pull data from the context.
+
+ """
def __init__(self, context):
self.context = context
@@ -70,7 +98,7 @@ class ContextAccess(object):
for func in pipes[1:]:
value = self[func](value)
elif "." in key:
- dots = key.split('.')
+ dots = key.split('.')
value = self[dots[0]]
for dot in dots[1:]:
try:
diff --git a/test/coverage_coverage.py b/test/coverage_coverage.py
index 0ab0e2ff..ca10edc3 100644
--- a/test/coverage_coverage.py
+++ b/test/coverage_coverage.py
@@ -1,4 +1,4 @@
-# Coverage-test Coverage itself.
+"""Coverage-test Coverage itself."""
import coverage
import os, shutil, sys
diff --git a/test/coveragetest.py b/test/coveragetest.py
index 5e2c18dc..46572c56 100644
--- a/test/coveragetest.py
+++ b/test/coveragetest.py
@@ -14,14 +14,14 @@ import coverage
class Tee(object):
- """A file-like that writes to all the file-likes it was constructed with.
-
- """
+ """A file-like that writes to all the file-likes it has."""
def __init__(self, *files):
+ """Make a Tee that writes to all the files in `files.`"""
self.files = files
def write(self, data):
+ """Write `data` to all the files."""
for f in self.files:
f.write(data)
@@ -77,8 +77,7 @@ class CoverageTest(unittest.TestCase):
f.close()
def importModule(self, modname):
- """ Import the module named modname, and return the module object.
- """
+ """Import the module named modname, and return the module object."""
modfile = modname + '.py'
f = open(modfile, 'r')
@@ -94,6 +93,7 @@ class CoverageTest(unittest.TestCase):
return mod
def getModuleName(self):
+ """Return the module name to use for this test run."""
# We append self.n because otherwise two calls in one test will use the
# same filename and whether the test works or not depends on the
# timestamps in the .pyc file, so it becomes random whether the second
@@ -103,6 +103,14 @@ class CoverageTest(unittest.TestCase):
return modname
def checkCoverage(self, text, lines, missing="", excludes=None, report=""):
+ """Check the coverage measurement of `text`.
+
+ The source `text` is run and measured. `lines` are the line numbers
+ that are executable, `missing` are the lines not executed, `excludes`
+ are regexes to match against for excluding lines, and `report` is
+ the text of the measurement report.
+
+ """
# We write the code into a file so that we can import it.
# Coverage wants to deal with things as modules with file names.
modname = self.getModuleName()