Source

coverage.py / lab / parser.py

"""Parser.py: a main for invoking code in coverage/parser.py"""

import glob, os, sys
from optparse import OptionParser

import disgen

from coverage.misc import CoverageException
from coverage.parser import ByteParser, CodeParser


class ParserMain(object):
    """A main for code parsing experiments."""

    def main(self, args):
        """A main function for trying the code from the command line."""

        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.one_file(options, f)
        elif not args:
            parser.print_help()
        else:
            self.one_file(options, args[0])

    def 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:")
            self.disassemble(bp)

        arcs = bp._all_arcs()
        if options.chunks and not options.dis:
            chunks = bp._all_chunks()
            if options.recursive:
                print("%6d: %s" % (len(chunks), filename))
            else:
                print("Chunks: %r" % chunks)
                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 disassemble(self, byte_parser):
        """Disassemble code, for ad-hoc experimenting."""

        for bp in byte_parser.child_parsers():
            chunks = bp._split_into_chunks()
            chunkd = dict((chunk.byte, chunk) for chunk in chunks)
            if bp.text:
                srclines = bp.text.splitlines()
            else:
                srclines = None
            print("\n%s: " % bp.code)
            upto = None
            for disline in disgen.disgen(bp.code):
                if disline.first:
                    if srclines:
                        upto = upto or disline.lineno-1
                        while upto <= disline.lineno-1:
                            print("%100s%s" % ("", srclines[upto]))
                            upto += 1
                    elif disline.offset > 0:
                        print("")
                line = disgen.format_dis_line(disline)
                chunk = chunkd.get(disline.offset)
                if chunk:
                    chunkstr = ":: %r" % chunk
                else:
                    chunkstr = ""
                print("%-70s%s" % (line, chunkstr))

        print("")

    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__':
    ParserMain().main(sys.argv[1:])
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.