See More

from __future__ import absolute_import, division, print_function, unicode_literals from .. import source, lexer, diagnostic import os, codecs _buf = None with codecs.open(os.path.join(os.path.dirname(__file__), "..", "parser.py"), encoding="utf-8") as f: _buf = source.Buffer(f.read(), f.name) # Inject the grammar with locations of rules, because Python's # builtin tracebacks don't include column numbers. # This would really be more elegant if it used the parser, # but the parser doesn't work yet at the time of writing. def instrument(): rewriter = source.Rewriter(_buf) lex = lexer.Lexer(_buf, (3, 4), diagnostic.Engine()) in_grammar = False paren_stack = [] stmt_stack = [None] all_stmt_list = [] in_square = 0 stmt_init = None for token in lex: if token.kind == "from": token = lex.next() if token.kind == ".": rewriter.replace(token.loc, "..") if token.kind == "class": token = lex.next() if token.kind == "ident" and token.value == "Parser": in_grammar = True if token.kind == "ident" and token.value == "_all_stmts": lex.next() # = stmt_init = lex.next().loc # { if not in_grammar: continue if token.kind == ".": # skip ast.List lex.next() if token.kind == "ident" and \ token.value in ("action", "Eps", "Tok", "Loc", "Rule", "Expect", "Seq", "SeqN", "Alt", "Opt", "Star", "Plus", "List", "Newline", "Oper", "BinOper", "BeginEnd"): lparen = lex.next() if lparen.kind == "(": rparen = lex.peek() if rparen.kind == ")": lex.next() rewriter.insert_before(rparen.loc, "loc=(%d,%d)" % (token.loc.begin_pos, token.loc.end_pos)) else: paren_stack.append(", loc=(%d,%d)" % (token.loc.begin_pos, token.loc.end_pos)) if token.kind == "(": paren_stack.append(None) if token.kind == ")": data = paren_stack.pop() if data is not None: rewriter.insert_before(token.loc, data) if token.kind == "[": in_square += 1 elif token.kind == "]": in_square -= 1 if token.kind in ("def", "if", "elif", "else", "for") and in_square == 0: all_stmt_list.append((token.loc.begin_pos, token.loc.end_pos)) stmt_stack.append("_all_stmts[(%d,%d)] = True\n" % (token.loc.begin_pos, token.loc.end_pos)) elif token.kind == "lambda": stmt_stack.append(None) if token.kind == "indent": data = stmt_stack.pop() if data is not None: rewriter.insert_after(token.loc, data + " " * token.loc.column()) rewriter.insert_after(stmt_init, ", ".join(["(%d,%d): False" % x for x in all_stmt_list])) with codecs.open(os.path.join(os.path.dirname(__file__), "parser.py"), "w", encoding="utf-8") as f: f.write(rewriter.rewrite().source) # Produce an HTML report for test coverage of parser rules. def report(parser, name="parser"): all_locs = dict() for rule in parser._all_rules: pts = len(rule.covered) covered = len(list(filter(lambda x: x, rule.covered))) if rule.loc in all_locs: pts_, covered_, covered_bool_ = all_locs[rule.loc] if covered > covered_: all_locs[rule.loc] = pts, covered, rule.covered else: all_locs[rule.loc] = pts, covered, rule.covered rewriter = source.Rewriter(_buf) total_pts = 0 total_covered = 0 for loc in all_locs: pts, covered, covered_bool = all_locs[loc] if covered == 0: klass, hint = "uncovered", None elif covered < pts: klass, hint = "partial", ", ".join(map(lambda x: "yes" if x else "no", covered_bool)) else: klass, hint = "covered", None sloc = source.Range(_buf, *loc) if hint: rewriter.insert_before(sloc, r"" % (klass, hint)) else: rewriter.insert_before(sloc, r"" % klass) rewriter.insert_after(sloc, r"") total_pts += pts total_covered += covered for stmt in parser._all_stmts: loc = source.Range(_buf, *stmt) if parser._all_stmts[stmt]: rewriter.insert_before(loc, r"") else: rewriter.insert_before(loc, r"") rewriter.insert_after(loc, r"") total_pts += 1 total_covered += (1 if parser._all_stmts[stmt] else 0) print("GRAMMAR COVERAGE: %.2f%%" % (total_covered / total_pts * 100)) content = rewriter.rewrite().source content = "\n".join(map( lambda x: r"{1}".format(*x), enumerate(content.split("\n"), 1))) with codecs.open(os.path.join(os.path.dirname(__file__), "..", "..", "doc", "coverage", name + ".html"), "w", encoding="utf-8") as f: f.write(r""" {percentage:.2f}%: {file} coverage report

{percentage:.2f}% ({covered}/{pts}): {file} coverage report

{content}
""".format(percentage=total_covered / total_pts * 100, pts=total_pts, covered=total_covered, file=os.path.basename(_buf.name), content=content)) # Create the instrumented parser when `import pythonparser.coverage.parser` # is invoked. Not intended for any use except running the internal testsuite. instrument()