summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2013-12-13 22:45:10 -0500
committerNed Batchelder <ned@nedbatchelder.com>2013-12-13 22:45:10 -0500
commit2df9b1c35cbb5c92204fc5923368a3d619a34f6d (patch)
treed1ede8ffef812ba4e345b08f698f001ebe69cb56 /coverage
parent84221611890880b749dbb650e8d07ac8918dba46 (diff)
parent7c66441eab3af17539c478a2cb4e19cd93ba0cf4 (diff)
downloadpython-coveragepy-git-2df9b1c35cbb5c92204fc5923368a3d619a34f6d.tar.gz
Merged 4.0 to default
Diffstat (limited to 'coverage')
-rw-r--r--coverage/annotate.py1
-rw-r--r--coverage/backward.py43
-rw-r--r--coverage/cmdline.py31
-rw-r--r--coverage/codeunit.py2
-rw-r--r--coverage/config.py2
-rw-r--r--coverage/control.py16
-rw-r--r--coverage/data.py16
-rw-r--r--coverage/debug.py2
-rw-r--r--coverage/execfile.py64
-rw-r--r--coverage/files.py2
-rw-r--r--coverage/html.py29
-rw-r--r--coverage/misc.py10
-rw-r--r--coverage/parser.py50
-rw-r--r--coverage/phystokens.py10
-rw-r--r--coverage/results.py43
-rw-r--r--coverage/templite.py2
-rw-r--r--coverage/version.py2
-rw-r--r--coverage/xmlreport.py7
18 files changed, 116 insertions, 216 deletions
diff --git a/coverage/annotate.py b/coverage/annotate.py
index 5c396784..19777eaf 100644
--- a/coverage/annotate.py
+++ b/coverage/annotate.py
@@ -2,7 +2,6 @@
import os, re
-from coverage.backward import sorted # pylint: disable=W0622
from coverage.report import Reporter
class AnnotateReporter(Reporter):
diff --git a/coverage/backward.py b/coverage/backward.py
index 7d268545..8237d01b 100644
--- a/coverage/backward.py
+++ b/coverage/backward.py
@@ -8,47 +8,6 @@
import os, re, sys
-# Python 2.3 doesn't have `set`
-try:
- set = set # new in 2.4
-except NameError:
- from sets import Set as set
-
-# Python 2.3 doesn't have `sorted`.
-try:
- sorted = sorted
-except NameError:
- def sorted(iterable):
- """A 2.3-compatible implementation of `sorted`."""
- lst = list(iterable)
- lst.sort()
- return lst
-
-# Python 2.3 doesn't have `reversed`.
-try:
- reversed = reversed
-except NameError:
- def reversed(iterable):
- """A 2.3-compatible implementation of `reversed`."""
- lst = list(iterable)
- return lst[::-1]
-
-# rpartition is new in 2.5
-try:
- "".rpartition
-except AttributeError:
- def rpartition(s, sep):
- """Implement s.rpartition(sep) for old Pythons."""
- i = s.rfind(sep)
- if i == -1:
- return ('', '', s)
- else:
- return (s[:i], sep, s[i+len(sep):])
-else:
- def rpartition(s, sep):
- """A common interface for new Pythons."""
- return s.rpartition(sep)
-
# Pythons 2 and 3 differ on where to get StringIO
try:
from cStringIO import StringIO
@@ -164,7 +123,7 @@ else:
def binary_bytes(byte_values):
"""Produce a byte string with the ints from `byte_values`."""
- return "".join([chr(b) for b in byte_values])
+ return "".join(chr(b) for b in byte_values)
def byte_to_int(byte_value):
"""Turn an element of a bytes object into an int."""
diff --git a/coverage/cmdline.py b/coverage/cmdline.py
index ea112a8b..c311976d 100644
--- a/coverage/cmdline.py
+++ b/coverage/cmdline.py
@@ -2,7 +2,6 @@
import optparse, os, sys, time, traceback
-from coverage.backward import sorted # pylint: disable=W0622
from coverage.execfile import run_python_file, run_python_module
from coverage.misc import CoverageException, ExceptionDuringRun, NoSource
from coverage.debug import info_formatter
@@ -565,17 +564,16 @@ class CoverageScript(object):
self.coverage.start()
code_ran = True
try:
- try:
- if options.module:
- sys.path[0] = ''
- self.run_python_module(args[0], args)
- else:
- filename = args[0]
- sys.path[0] = os.path.abspath(os.path.dirname(filename))
- self.run_python_file(filename, args)
- except NoSource:
- code_ran = False
- raise
+ if options.module:
+ sys.path[0] = ''
+ self.run_python_module(args[0], args)
+ else:
+ filename = args[0]
+ sys.path[0] = os.path.abspath(os.path.dirname(filename))
+ self.run_python_file(filename, args)
+ except NoSource:
+ code_ran = False
+ raise
finally:
self.coverage.stop()
if code_ran:
@@ -722,21 +720,18 @@ def main(argv=None):
end = time.clock()
if 0:
print("time: %.3fs" % (end - start))
- except ExceptionDuringRun:
+ except ExceptionDuringRun as err:
# An exception was caught while running the product code. The
# sys.exc_info() return tuple is packed into an ExceptionDuringRun
# exception.
- _, err, _ = sys.exc_info()
traceback.print_exception(*err.args)
status = ERR
- except CoverageException:
+ except CoverageException as err:
# A controlled error inside coverage.py: print the message to the user.
- _, err, _ = sys.exc_info()
print(err)
status = ERR
- except SystemExit:
+ except SystemExit as err:
# The user called `sys.exit()`. Exit with their argument, if any.
- _, err, _ = sys.exc_info()
if err.args:
status = err.args[0]
else:
diff --git a/coverage/codeunit.py b/coverage/codeunit.py
index ca1ae5c5..c58e237b 100644
--- a/coverage/codeunit.py
+++ b/coverage/codeunit.py
@@ -52,7 +52,7 @@ class CodeUnit(object):
else:
f = morf
# .pyc files should always refer to a .py instead.
- if f.endswith('.pyc') or f.endswith('.pyo'):
+ if f.endswith(('.pyc', '.pyo')):
f = f[:-1]
elif f.endswith('$py.class'): # Jython
f = f[:-9] + ".py"
diff --git a/coverage/config.py b/coverage/config.py
index 87318ff1..6223afda 100644
--- a/coverage/config.py
+++ b/coverage/config.py
@@ -25,7 +25,7 @@ class HandyConfigParser(configparser.RawConfigParser):
def dollar_replace(m):
"""Called for each $replacement."""
# Only one of the groups will have matched, just get its text.
- word = [w for w in m.groups() if w is not None][0]
+ word = next(w for w in m.groups() if w is not None)
if word == "$":
return "$"
else:
diff --git a/coverage/control.py b/coverage/control.py
index f75a3dda..fa6fec74 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -3,7 +3,7 @@
import atexit, os, random, socket, sys
from coverage.annotate import AnnotateReporter
-from coverage.backward import string_class, iitems, sorted # pylint: disable=W0622
+from coverage.backward import string_class, iitems
from coverage.codeunit import code_unit_factory, CodeUnit
from coverage.collector import Collector
from coverage.config import CoverageConfig
@@ -98,8 +98,7 @@ class coverage(object):
config_file = ".coveragerc"
try:
self.config.from_file(config_file)
- except ValueError:
- _, err, _ = sys.exc_info()
+ except ValueError as err:
raise CoverageException(
"Couldn't read config file %s: %s" % (config_file, err)
)
@@ -689,12 +688,11 @@ class coverage(object):
outfile = open(self.config.xml_output, "w")
file_to_close = outfile
try:
- try:
- reporter = XmlReporter(self, self.config)
- return reporter.report(morfs, outfile=outfile)
- except CoverageException:
- delete_file = True
- raise
+ reporter = XmlReporter(self, self.config)
+ return reporter.report(morfs, outfile=outfile)
+ except CoverageException:
+ delete_file = True
+ raise
finally:
if file_to_close:
file_to_close.close()
diff --git a/coverage/data.py b/coverage/data.py
index fb88c5b1..042b6405 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -2,7 +2,7 @@
import os
-from coverage.backward import iitems, pickle, sorted # pylint: disable=W0622
+from coverage.backward import iitems, pickle
from coverage.files import PathAliases
from coverage.misc import file_be_gone
@@ -101,13 +101,13 @@ class CoverageData(object):
def line_data(self):
"""Return the map from filenames to lists of line numbers executed."""
return dict(
- [(f, sorted(lmap.keys())) for f, lmap in iitems(self.lines)]
+ (f, sorted(lmap.keys())) for f, lmap in iitems(self.lines)
)
def arc_data(self):
"""Return the map from filenames to lists of line number pairs."""
return dict(
- [(f, sorted(amap.keys())) for f, amap in iitems(self.arcs)]
+ (f, sorted(amap.keys())) for f, amap in iitems(self.arcs)
)
def write_file(self, filename):
@@ -128,11 +128,8 @@ class CoverageData(object):
self.debug.write("Writing data to %r" % (filename,))
# Write the pickle to the file.
- fdata = open(filename, 'wb')
- try:
+ with open(filename, 'wb') as fdata:
pickle.dump(data, fdata, 2)
- finally:
- fdata.close()
def read_file(self, filename):
"""Read the coverage data from `filename`."""
@@ -142,11 +139,8 @@ class CoverageData(object):
"""Return the raw pickled data from `filename`."""
if self.debug and self.debug.should('dataio'):
self.debug.write("Reading data from %r" % (filename,))
- fdata = open(filename, 'rb')
- try:
+ with open(filename, 'rb') as fdata:
data = pickle.load(fdata)
- finally:
- fdata.close()
return data
def _read_file(self, filename):
diff --git a/coverage/debug.py b/coverage/debug.py
index 104f3b1d..6908383d 100644
--- a/coverage/debug.py
+++ b/coverage/debug.py
@@ -41,7 +41,7 @@ def info_formatter(info):
nicely formatted, ready to print.
"""
- label_len = max([len(l) for l, _d in info])
+ label_len = max(len(l) for l, _d in info)
for label, data in info:
if data == []:
data = "-none-"
diff --git a/coverage/execfile.py b/coverage/execfile.py
index f6ebdf79..f90096e9 100644
--- a/coverage/execfile.py
+++ b/coverage/execfile.py
@@ -31,35 +31,33 @@ def run_python_module(modulename, args):
openfile = None
glo, loc = globals(), locals()
try:
- try:
- # Search for the module - inside its parent package, if any - using
- # standard import mechanics.
- if '.' in modulename:
- packagename, name = rsplit1(modulename, '.')
- package = __import__(packagename, glo, loc, ['__path__'])
- searchpath = package.__path__
- else:
- packagename, name = None, modulename
- searchpath = None # "top-level search" in imp.find_module()
+ # Search for the module - inside its parent package, if any - using
+ # standard import mechanics.
+ if '.' in modulename:
+ packagename, name = rsplit1(modulename, '.')
+ package = __import__(packagename, glo, loc, ['__path__'])
+ searchpath = package.__path__
+ else:
+ packagename, name = None, modulename
+ searchpath = None # "top-level search" in imp.find_module()
+ openfile, pathname, _ = imp.find_module(name, searchpath)
+
+ # Complain if this is a magic non-file module.
+ if openfile is None and pathname is None:
+ raise NoSource(
+ "module does not live in a file: %r" % modulename
+ )
+
+ # If `modulename` is actually a package, not a mere module, then we
+ # pretend to be Python 2.7 and try running its __main__.py script.
+ if openfile is None:
+ packagename = modulename
+ name = '__main__'
+ package = __import__(packagename, glo, loc, ['__path__'])
+ searchpath = package.__path__
openfile, pathname, _ = imp.find_module(name, searchpath)
-
- # Complain if this is a magic non-file module.
- if openfile is None and pathname is None:
- raise NoSource(
- "module does not live in a file: %r" % modulename
- )
-
- # If `modulename` is actually a package, not a mere module, then we
- # pretend to be Python 2.7 and try running its __main__.py script.
- if openfile is None:
- packagename = modulename
- name = '__main__'
- package = __import__(packagename, glo, loc, ['__path__'])
- searchpath = package.__path__
- openfile, pathname, _ = imp.find_module(name, searchpath)
- except ImportError:
- _, err, _ = sys.exc_info()
- raise NoSource(str(err))
+ except ImportError as err:
+ raise NoSource(str(err))
finally:
if openfile:
openfile.close()
@@ -94,7 +92,7 @@ def run_python_file(filename, args, package=None):
try:
# Make a code object somehow.
- if filename.endswith(".pyc") or filename.endswith(".pyo"):
+ if filename.endswith((".pyc", ".pyo")):
code = make_code_from_pyc(filename)
else:
code = make_code_from_py(filename)
@@ -129,10 +127,8 @@ def make_code_from_py(filename):
except IOError:
raise NoSource("No file to run: %r" % filename)
- try:
+ with source_file:
source = source_file.read()
- finally:
- source_file.close()
# 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.
@@ -150,7 +146,7 @@ def make_code_from_pyc(filename):
except IOError:
raise NoCode("No file to run: %r" % filename)
- try:
+ with fpyc:
# First four bytes are a version-specific magic number. It has to
# match or we won't run the file.
magic = fpyc.read(4)
@@ -165,7 +161,5 @@ def make_code_from_pyc(filename):
# The rest of the file is the code object we want.
code = marshal.load(fpyc)
- finally:
- fpyc.close()
return code
diff --git a/coverage/files.py b/coverage/files.py
index 464535a8..94388f96 100644
--- a/coverage/files.py
+++ b/coverage/files.py
@@ -137,7 +137,7 @@ def prep_patterns(patterns):
"""
prepped = []
for p in patterns or []:
- if p.startswith("*") or p.startswith("?"):
+ if p.startswith(("*", "?")):
prepped.append(p)
else:
prepped.append(abs_file(p))
diff --git a/coverage/html.py b/coverage/html.py
index 5242236c..d168e351 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -43,11 +43,8 @@ def data_filename(fname, pkgdir=""):
def data(fname):
"""Return the contents of a data file of ours."""
- data_file = open(data_filename(fname))
- try:
+ with open(data_filename(fname)) as data_file:
return data_file.read()
- finally:
- data_file.close()
class HtmlReporter(Reporter):
@@ -140,11 +137,8 @@ class HtmlReporter(Reporter):
def write_html(self, fname, html):
"""Write `html` to `fname`, properly encoded."""
- fout = open(fname, "wb")
- try:
+ with open(fname, "wb") as fout:
fout.write(html.encode('ascii', 'xmlcharrefreplace'))
- finally:
- fout.close()
def file_hash(self, source, cu):
"""Compute a hash that changes if the file needs to be re-reported."""
@@ -156,10 +150,8 @@ class HtmlReporter(Reporter):
def html_file(self, cu, analysis):
"""Generate an HTML file for one source file."""
source_file = cu.source_file()
- try:
+ with source_file:
source = source_file.read()
- finally:
- source_file.close()
# Find out if the file on disk is already correct.
flat_rootname = cu.flat_rootname()
@@ -195,8 +187,7 @@ class HtmlReporter(Reporter):
lines = []
- for lineno, line in enumerate(source_token_lines(source)):
- lineno += 1 # 1-based line numbers.
+ for lineno, line in enumerate(source_token_lines(source), start=1):
# Figure out how to mark this line.
line_class = []
annotate_html = ""
@@ -271,7 +262,7 @@ class HtmlReporter(Reporter):
data("index.html"), self.template_globals
)
- self.totals = sum([f['nums'] for f in self.files])
+ self.totals = sum(f['nums'] for f in self.files)
html = index_tmpl.render({
'arcs': self.arcs,
@@ -310,11 +301,8 @@ class HtmlStatus(object):
usable = False
try:
status_file = os.path.join(directory, self.STATUS_FILE)
- fstatus = open(status_file, "rb")
- try:
+ with open(status_file, "rb") as fstatus:
status = pickle.load(fstatus)
- finally:
- fstatus.close()
except (IOError, ValueError):
usable = False
else:
@@ -339,11 +327,8 @@ class HtmlStatus(object):
'settings': self.settings,
'files': self.files,
}
- fout = open(status_file, "wb")
- try:
+ with open(status_file, "wb") as fout:
pickle.dump(status, fout)
- finally:
- fout.close()
def settings_hash(self):
"""Get the hash of the coverage.py settings."""
diff --git a/coverage/misc.py b/coverage/misc.py
index 0378173f..c88d4ecd 100644
--- a/coverage/misc.py
+++ b/coverage/misc.py
@@ -3,9 +3,8 @@
import errno
import inspect
import os
-import sys
-from coverage.backward import md5, sorted # pylint: disable=W0622
+from coverage.backward import md5
from coverage.backward import string_class, to_bytes
@@ -59,7 +58,7 @@ def format_lines(statements, lines):
def short_stack():
"""Return a string summarizing the call stack."""
stack = inspect.stack()[:0:-1]
- return "\n".join(["%30s : %s @%d" % (t[3],t[1],t[2]) for t in stack])
+ return "\n".join("%30s : %s @%d" % (t[3],t[1],t[2]) for t in stack)
def expensive(fn):
@@ -88,7 +87,7 @@ def bool_or_none(b):
def join_regex(regexes):
"""Combine a list of regexes into one that matches any of them."""
if len(regexes) > 1:
- return "|".join(["(%s)" % r for r in regexes])
+ return "|".join("(%s)" % r for r in regexes)
elif regexes:
return regexes[0]
else:
@@ -99,8 +98,7 @@ def file_be_gone(path):
"""Remove a file, and don't get annoyed if it doesn't exist."""
try:
os.remove(path)
- except OSError:
- _, e, _ = sys.exc_info()
+ except OSError as e:
if e.errno != errno.ENOENT:
raise
diff --git a/coverage/parser.py b/coverage/parser.py
index 7a145a2a..de6590aa 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -1,10 +1,9 @@
"""Code parsing for Coverage."""
-import dis, re, sys, token, tokenize
+import collections, dis, re, token, tokenize
-from coverage.backward import set, sorted, StringIO # pylint: disable=W0622
+from coverage.backward import StringIO
from coverage.backward import open_source, range # pylint: disable=W0622
-from coverage.backward import reversed # pylint: disable=W0622
from coverage.backward import bytes_to_ints
from coverage.bytecode import ByteCodes, CodeObjects
from coverage.misc import nice_pair, expensive, join_regex
@@ -26,13 +25,9 @@ class CodeParser(object):
self.text = text
if not self.text:
try:
- sourcef = open_source(self.filename)
- try:
+ with open_source(self.filename) as sourcef:
self.text = sourcef.read()
- finally:
- sourcef.close()
- except IOError:
- _, err, _ = sys.exc_info()
+ except IOError as err:
raise NoSource(
"No source for code: '%s': %s" % (self.filename, err)
)
@@ -66,13 +61,13 @@ class CodeParser(object):
# Lazily-created ByteParser
self._byte_parser = None
- def _get_byte_parser(self):
+ @property
+ def byte_parser(self):
"""Create a ByteParser on demand."""
if not self._byte_parser:
self._byte_parser = \
ByteParser(text=self.text, filename=self.filename)
return self._byte_parser
- byte_parser = property(_get_byte_parser)
def lines_matching(self, *regexes):
"""Find the lines matching one of a list of regexes.
@@ -84,9 +79,9 @@ class CodeParser(object):
"""
regex_c = re.compile(join_regex(regexes))
matches = set()
- for i, ltext in enumerate(self.lines):
+ for i, ltext in enumerate(self.lines, start=1):
if regex_c.search(ltext):
- matches.add(i+1)
+ matches.add(i)
return matches
def _raw_parse(self):
@@ -208,8 +203,7 @@ class CodeParser(object):
"""
try:
self._raw_parse()
- except (tokenize.TokenError, IndentationError):
- _, tokerr, _ = sys.exc_info()
+ except (tokenize.TokenError, IndentationError) as tokerr:
msg, lineno = tokerr.args
raise NotPython(
"Couldn't parse '%s' as Python source: '%s' at %s" %
@@ -225,6 +219,7 @@ class CodeParser(object):
return lines, excluded_lines
+ @expensive
def arcs(self):
"""Get information about the arcs available in the code.
@@ -239,8 +234,8 @@ class CodeParser(object):
if fl1 != fl2:
all_arcs.append((fl1, fl2))
return sorted(all_arcs)
- arcs = expensive(arcs)
+ @expensive
def exit_counts(self):
"""Get a mapping from line numbers to count of exits from that line.
@@ -248,7 +243,7 @@ class CodeParser(object):
"""
excluded_lines = self.first_lines(self.excluded)
- exit_counts = {}
+ exit_counts = collections.defaultdict(int)
for l1, l2 in self.arcs():
if l1 < 0:
# Don't ever report -1 as a line number
@@ -259,8 +254,6 @@ class CodeParser(object):
if l2 in excluded_lines:
# Arcs to excluded lines shouldn't count.
continue
- if l1 not in exit_counts:
- exit_counts[l1] = 0
exit_counts[l1] += 1
# Class definitions have one extra exit, so remove one for each:
@@ -270,7 +263,6 @@ class CodeParser(object):
exit_counts[l] -= 1
return exit_counts
- exit_counts = expensive(exit_counts)
## Opcodes that guide the ByteParser.
@@ -336,19 +328,15 @@ class ByteParser(object):
else:
if not text:
assert filename, "If no code or text, need a filename"
- sourcef = open_source(filename)
- try:
+ with open_source(filename) as sourcef:
text = sourcef.read()
- finally:
- sourcef.close()
self.text = text
try:
# Python 2.3 and 2.4 don't like partial last lines, so be sure
# the text ends nicely for them.
self.code = compile(text + '\n', filename, "exec")
- except SyntaxError:
- _, synerr, _ = sys.exc_info()
+ except SyntaxError as synerr:
raise NotPython(
"Couldn't parse '%s' as Python source: '%s' at line %d" %
(filename, synerr.msg, synerr.lineno)
@@ -371,7 +359,7 @@ class ByteParser(object):
"""
children = CodeObjects(self.code)
- return [ByteParser(code=c, text=self.text) for c in children]
+ return (ByteParser(code=c, text=self.text) for c in children)
def _bytes_lines(self):
"""Map byte offsets to line numbers in `code`.
@@ -415,7 +403,7 @@ class ByteParser(object):
def _block_stack_repr(self, block_stack):
"""Get a string version of `block_stack`, for debugging."""
blocks = ", ".join(
- ["(%s, %r)" % (dis.opname[b[0]], b[1]) for b in block_stack]
+ "(%s, %r)" % (dis.opname[b[0]], b[1]) for b in block_stack
)
return "[" + blocks + "]"
@@ -554,9 +542,9 @@ class ByteParser(object):
def validate_chunks(self, chunks):
"""Validate the rule that chunks have a single entrance."""
# starts is the entrances to the chunks
- starts = set([ch.byte for ch in chunks])
+ starts = set(ch.byte for ch in chunks)
for ch in chunks:
- assert all([(ex in starts or ex < 0) for ex in ch.exits])
+ assert all((ex in starts or ex < 0) for ex in ch.exits)
def _arcs(self):
"""Find the executable arcs in the code.
@@ -569,7 +557,7 @@ class ByteParser(object):
chunks = self._split_into_chunks()
# A map from byte offsets to chunks jumped into.
- byte_chunks = dict([(c.byte, c) for c in chunks])
+ byte_chunks = dict((c.byte, c) for c in chunks)
# There's always an entrance at the first chunk.
yield (-1, byte_chunks[0].line)
diff --git a/coverage/phystokens.py b/coverage/phystokens.py
index 99b1d5ba..e79ce01f 100644
--- a/coverage/phystokens.py
+++ b/coverage/phystokens.py
@@ -1,7 +1,6 @@
"""Better tokenizing for coverage.py."""
import codecs, keyword, re, sys, token, tokenize
-from coverage.backward import set # pylint: disable=W0622
from coverage.parser import generate_tokens
@@ -143,13 +142,8 @@ def source_encoding(source):
# invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found,
# 'utf-8-sig' is returned.
- # If no encoding is specified, then the default will be returned. The
- # default varied with version.
-
- if sys.version_info <= (2, 4):
- default = 'iso-8859-1'
- else:
- default = 'ascii'
+ # If no encoding is specified, then the default will be returned.
+ default = 'ascii'
bom_found = False
encoding = None
diff --git a/coverage/results.py b/coverage/results.py
index db6df0d3..0576ae1f 100644
--- a/coverage/results.py
+++ b/coverage/results.py
@@ -1,8 +1,9 @@
"""Results of coverage measurement."""
+import collections
import os
-from coverage.backward import iitems, set, sorted # pylint: disable=W0622
+from coverage.backward import iitems
from coverage.misc import format_lines, join_regex, NoSource
from coverage.parser import CodeParser
@@ -36,9 +37,9 @@ class Analysis(object):
n_branches = self.total_branches()
mba = self.missing_branch_arcs()
n_partial_branches = sum(
- [len(v) for k,v in iitems(mba) if k not in self.missing]
+ len(v) for k,v in iitems(mba) if k not in self.missing
)
- n_missing_branches = sum([len(v) for k,v in iitems(mba)])
+ n_missing_branches = sum(len(v) for k,v in iitems(mba))
else:
n_branches = n_partial_branches = n_missing_branches = 0
self.no_branch = set()
@@ -112,18 +113,18 @@ class Analysis(object):
"""Returns a sorted list of the arcs actually executed in the code."""
executed = self.coverage.data.executed_arcs(self.filename)
m2fl = self.parser.first_line
- executed = [(m2fl(l1), m2fl(l2)) for (l1,l2) in executed]
+ executed = ((m2fl(l1), m2fl(l2)) for (l1,l2) in executed)
return sorted(executed)
def arcs_missing(self):
"""Returns a sorted list of the arcs in the code not executed."""
possible = self.arc_possibilities()
executed = self.arcs_executed()
- missing = [
+ missing = (
p for p in possible
if p not in executed
and p[0] not in self.no_branch
- ]
+ )
return sorted(missing)
def arcs_unpredicted(self):
@@ -133,11 +134,11 @@ class Analysis(object):
# Exclude arcs here which connect a line to itself. They can occur
# in executed data in some cases. This is where they can cause
# trouble, and here is where it's the least burden to remove them.
- unpredicted = [
+ unpredicted = (
e for e in executed
if e not in possible
and e[0] != e[1]
- ]
+ )
return sorted(unpredicted)
def branch_lines(self):
@@ -148,7 +149,7 @@ class Analysis(object):
def total_branches(self):
"""How many total branches are there?"""
exit_counts = self.parser.exit_counts()
- return sum([count for count in exit_counts.values() if count > 1])
+ return sum(count for count in exit_counts.values() if count > 1)
def missing_branch_arcs(self):
"""Return arcs that weren't executed from branch lines.
@@ -158,11 +159,9 @@ class Analysis(object):
"""
missing = self.arcs_missing()
branch_lines = set(self.branch_lines())
- mba = {}
+ mba = collections.defaultdict(list)
for l1, l2 in missing:
if l1 in branch_lines:
- if l1 not in mba:
- mba[l1] = []
mba[l1].append(l2)
return mba
@@ -210,25 +209,26 @@ class Numbers(object):
self.n_partial_branches = n_partial_branches
self.n_missing_branches = n_missing_branches
+ @classmethod
def set_precision(cls, precision):
"""Set the number of decimal places used to report percentages."""
assert 0 <= precision < 10
cls._precision = precision
cls._near0 = 1.0 / 10**precision
cls._near100 = 100.0 - cls._near0
- set_precision = classmethod(set_precision)
- def _get_n_executed(self):
+ @property
+ def n_executed(self):
"""Returns the number of executed statements."""
return self.n_statements - self.n_missing
- n_executed = property(_get_n_executed)
- def _get_n_executed_branches(self):
+ @property
+ def n_executed_branches(self):
"""Returns the number of executed branches."""
return self.n_branches - self.n_missing_branches
- n_executed_branches = property(_get_n_executed_branches)
- def _get_pc_covered(self):
+ @property
+ def pc_covered(self):
"""Returns a single percentage value for coverage."""
if self.n_statements > 0:
pc_cov = (100.0 * (self.n_executed + self.n_executed_branches) /
@@ -236,9 +236,9 @@ class Numbers(object):
else:
pc_cov = 100.0
return pc_cov
- pc_covered = property(_get_pc_covered)
- def _get_pc_covered_str(self):
+ @property
+ def pc_covered_str(self):
"""Returns the percent covered, as a string, without a percent sign.
Note that "0" is only returned when the value is truly zero, and "100"
@@ -254,15 +254,14 @@ class Numbers(object):
else:
pc = round(pc, self._precision)
return "%.*f" % (self._precision, pc)
- pc_covered_str = property(_get_pc_covered_str)
+ @classmethod
def pc_str_width(cls):
"""How many characters wide can pc_covered_str be?"""
width = 3 # "100"
if cls._precision > 0:
width += 1 + cls._precision
return width
- pc_str_width = classmethod(pc_str_width)
def __add__(self, other):
nums = Numbers()
diff --git a/coverage/templite.py b/coverage/templite.py
index e5c0bafe..429a5ccc 100644
--- a/coverage/templite.py
+++ b/coverage/templite.py
@@ -4,8 +4,6 @@
import re
-from coverage.backward import set # pylint: disable=W0622
-
class CodeBuilder(object):
"""Build source code conveniently."""
diff --git a/coverage/version.py b/coverage/version.py
index a43bde80..27c2f6b1 100644
--- a/coverage/version.py
+++ b/coverage/version.py
@@ -1,7 +1,7 @@
"""The version and URL for coverage.py"""
# This file is exec'ed in setup.py, don't import anything!
-__version__ = "3.7.1" # see detailed history in CHANGES.txt
+__version__ = "4.0a0" # see detailed history in CHANGES.txt
__url__ = "http://nedbatchelder.com/code/coverage"
if max(__version__).isalpha():
diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py
index 26ac02ad..f5a4c1ba 100644
--- a/coverage/xmlreport.py
+++ b/coverage/xmlreport.py
@@ -4,7 +4,6 @@ import os, sys, time
import xml.dom.minidom
from coverage import __url__, __version__
-from coverage.backward import sorted, rpartition # pylint: disable=W0622
from coverage.report import Reporter
def rate(hit, num):
@@ -97,7 +96,7 @@ class XmlReporter(Reporter):
# Create the 'lines' and 'package' XML elements, which
# are populated later. Note that a package == a directory.
- package_name = rpartition(cu.name, ".")[0]
+ package_name = cu.name.rpartition(".")[0]
className = cu.name
package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
@@ -138,8 +137,8 @@ class XmlReporter(Reporter):
class_hits = class_lines - len(analysis.missing)
if self.arcs:
- class_branches = sum([t for t,k in branch_stats.values()])
- missing_branches = sum([t-k for t,k in branch_stats.values()])
+ class_branches = sum(t for t, k in branch_stats.values())
+ missing_branches = sum(t - k for t, k in branch_stats.values())
class_br_hits = class_branches - missing_branches
else:
class_branches = 0.0