summaryrefslogtreecommitdiff
path: root/coverage/parser.py
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2011-08-02 22:35:48 -0400
committerNed Batchelder <ned@nedbatchelder.com>2011-08-02 22:35:48 -0400
commit3447a59bd5570dde4e1ae355aca06a4b9bf4413b (patch)
tree5ee8ca7fe830d5da8241e855af0b580b8675e9d6 /coverage/parser.py
parent37850e04b59675a7292f8b6b810c932be0b4939d (diff)
downloadpython-coveragepy-git-3447a59bd5570dde4e1ae355aca06a4b9bf4413b.tar.gz
Split out and improve the ad-hoc parsing and disassembly tool
Diffstat (limited to 'coverage/parser.py')
-rw-r--r--coverage/parser.py160
1 files changed, 5 insertions, 155 deletions
diff --git a/coverage/parser.py b/coverage/parser.py
index b65689c4..d380eda1 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -1,6 +1,6 @@
"""Code parsing for Coverage."""
-import glob, opcode, os, re, sys, token, tokenize
+import opcode, re, sys, token, tokenize
from coverage.backward import set, sorted, StringIO # pylint: disable=W0622
from coverage.backward import open_source
@@ -314,6 +314,7 @@ class ByteParser(object):
def __init__(self, code=None, text=None, filename=None):
if code:
self.code = code
+ self.text = text
else:
if not text:
assert filename, "If no code or text, need a filename"
@@ -322,6 +323,7 @@ class ByteParser(object):
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
@@ -350,7 +352,8 @@ class ByteParser(object):
The iteration includes `self` as its first value.
"""
- return map(lambda c: ByteParser(code=c), CodeObjects(self.code))
+ children = CodeObjects(self.code)
+ return [ByteParser(code=c, text=self.text) for c in children]
# Getting numbers from the lnotab value changed in Py3.0.
if sys.version_info >= (3, 0):
@@ -402,18 +405,6 @@ class ByteParser(object):
stmts.add(l)
return stmts
- def _disassemble(self): # pragma: no cover
- """Disassemble code, for ad-hoc experimenting."""
-
- import dis
-
- for bp in self.child_parsers():
- print("\n%s: " % bp.code)
- dis.dis(bp.code)
- print("Bytes lines: %r" % bp._bytes_lines())
-
- print("")
-
def _split_into_chunks(self):
"""Split the code object into a list of `Chunk` objects.
@@ -657,144 +648,3 @@ class Chunk(object):
return "<%d+%d @%d %r>" % (
self.byte, self.length, self.line, list(self.exits)
)
-
-
-class AdHocMain(object): # pragma: no cover
- """An ad-hoc main for code parsing experiments."""
-
- def main(self, args):
- """A main function for trying the code from the command line."""
-
- from optparse import OptionParser
-
- parser = OptionParser()
- parser.add_option(
- "-c", action="store_true", dest="chunks",
- help="Show basic block chunks"
- )
- parser.add_option(
- "-d", action="store_true", dest="dis",
- help="Disassemble"
- )
- parser.add_option(
- "-R", action="store_true", dest="recursive",
- help="Recurse to find source files"
- )
- parser.add_option(
- "-s", action="store_true", dest="source",
- help="Show analyzed source"
- )
- parser.add_option(
- "-t", action="store_true", dest="tokens",
- help="Show tokens"
- )
-
- options, args = parser.parse_args()
- if options.recursive:
- if args:
- root = args[0]
- else:
- root = "."
- for root, _, _ in os.walk(root):
- for f in glob.glob(root + "/*.py"):
- self.adhoc_one_file(options, f)
- else:
- self.adhoc_one_file(options, args[0])
-
- def adhoc_one_file(self, options, filename):
- """Process just one file."""
-
- if options.dis or options.chunks:
- try:
- bp = ByteParser(filename=filename)
- except CoverageException:
- _, err, _ = sys.exc_info()
- print("%s" % (err,))
- return
-
- if options.dis:
- print("Main code:")
- bp._disassemble()
-
- if options.chunks:
- chunks = bp._all_chunks()
- if options.recursive:
- print("%6d: %s" % (len(chunks), filename))
- else:
- print("Chunks: %r" % chunks)
- arcs = bp._all_arcs()
- print("Arcs: %r" % sorted(arcs))
-
- if options.source or options.tokens:
- cp = CodeParser(filename=filename, exclude=r"no\s*cover")
- cp.show_tokens = options.tokens
- cp._raw_parse()
-
- if options.source:
- if options.chunks:
- arc_width, arc_chars = self.arc_ascii_art(arcs)
- else:
- arc_width, arc_chars = 0, {}
-
- exit_counts = cp.exit_counts()
-
- for i, ltext in enumerate(cp.lines):
- lineno = i+1
- m0 = m1 = m2 = m3 = a = ' '
- if lineno in cp.statement_starts:
- m0 = '-'
- exits = exit_counts.get(lineno, 0)
- if exits > 1:
- m1 = str(exits)
- if lineno in cp.docstrings:
- m2 = '"'
- if lineno in cp.classdefs:
- m2 = 'C'
- if lineno in cp.excluded:
- m3 = 'x'
- a = arc_chars.get(lineno, '').ljust(arc_width)
- print("%4d %s%s%s%s%s %s" %
- (lineno, m0, m1, m2, m3, a, ltext)
- )
-
- def arc_ascii_art(self, arcs):
- """Draw arcs as ascii art.
-
- Returns a width of characters needed to draw all the arcs, and a
- dictionary mapping line numbers to ascii strings to draw for that line.
-
- """
- arc_chars = {}
- for lfrom, lto in sorted(arcs):
- if lfrom < 0:
- arc_chars[lto] = arc_chars.get(lto, '') + 'v'
- elif lto < 0:
- arc_chars[lfrom] = arc_chars.get(lfrom, '') + '^'
- else:
- if lfrom == lto - 1:
- # Don't show obvious arcs.
- continue
- if lfrom < lto:
- l1, l2 = lfrom, lto
- else:
- l1, l2 = lto, lfrom
- w = max([len(arc_chars.get(l, '')) for l in range(l1, l2+1)])
- for l in range(l1, l2+1):
- if l == lfrom:
- ch = '<'
- elif l == lto:
- ch = '>'
- else:
- ch = '|'
- arc_chars[l] = arc_chars.get(l, '').ljust(w) + ch
- arc_width = 0
-
- if arc_chars:
- arc_width = max([len(a) for a in arc_chars.values()])
- else:
- arc_width = 0
-
- return arc_width, arc_chars
-
-if __name__ == '__main__':
- AdHocMain().main(sys.argv[1:])