Commit fa628adf authored by Xavier Thompson's avatar Xavier Thompson

WIP

parent c6d14bd0
import logging
def createLogger(filename=None):
logger = logging.getLogger(__name__)
logger.propagate = False
logger.setLevel(logging.DEBUG)
if filename:
handler = logging.FileHandler(filename)
else:
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter()
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
import json
def trace_expr(loc, expr, desc):
data = dict(loc=loc, type=repr(type(expr)), value=repr(expr), desc=desc)
line = json.dumps(data)
logger.info(line)
return expr
import ast
class DepthVisitor(ast.NodeVisitor):
"""
Set node.depth for every node
"""
def __init__(self):
self._depth = 0
def __call__(self, node):
self.visit(node)
return node
def visit(self, node):
node.depth = self._depth
self._depth += 1
try:
self.generic_visit(node)
finally:
self._depth -=1
class FormatVisitor(ast.NodeTransformer):
"""
Replace every Name(id='__N') node with the Nth constructor arg
"""
def __init__(self, *args):
self._args = args
def visit_Name(self, node):
if node.id.startswith('__'):
try:
idx = int(node.id[2:])
except ValueError:
return node
return self._args[idx]
return node
class InstrumentVisitor(ast.NodeTransformer):
"""
Help modify the AST to intrument expression evaluations
"""
def __init__(self, name):
self._name = name
self._trace = False
def __call__(self, node):
return self.visit(node)
def format_tree(self, pos, tree, args):
for node in ast.walk(tree):
ast.copy_location(node, pos)
node.depth = pos.depth
return FormatVisitor(args).visit(tree)
def make_expr(self, pos, fmt, *args):
tree = ast.parse(fmt, mode='eval').body
return self.format_tree(pos, tree, args)
def make_stmt(self, pos, fmt, *args):
tree = ast.parse(fmt).body[0]
return self.format_tree(pos, tree, args)
def make_trace(self, node, desc=None):
self._trace = True
line, col, depth = node.lineno, node.col_offset, node.depth
loc = dict(name=self._name, line=line, col=col, depth=depth)
fmt = "my2to3.runtime.trace_expr(%r, __0, %r)" % (loc, desc)
return self.make_expr(node, fmt, node)
visit_children = ast.NodeTransformer.generic_visit
def visit_Module(self, node):
self.visit_children(node)
if node.body and self._trace:
first = node.body[0]
runtime = self.make_stmt(first, "import my2to3.runtime")
ast.increment_lineno(first)
node.body.insert(0, runtime)
return node
from __future__ import print_function
import ast
import unittest
import sys
import context
from my2to3.hook import Finder
from my2to3.visit import DepthVisitor
from my2to3.visit import InstrumentVisitor
import my2to3.runtime
class BinOpVisitor(InstrumentVisitor):
def visit_BinOp(self, node):
self.visit_children(node)
if isinstance(node.op, ast.Div):
node.left = self.make_trace(node.left)
node.right = self.make_trace(node.right)
return node
class TestVisitBinOp(unittest.TestCase):
def setUp(self):
self._calls = []
self._finder = None
def transform(self, source, name, filename):
t = ast.parse(source, filename)
t = DepthVisitor()(t)
t = BinOpVisitor(name)(t)
return t
def info(self, data):
self._calls.append(data)
def test_visit_sample(self):
self._finder = Finder('sample', self.transform)
sys.meta_path.append(self._finder)
my2to3.runtime.logger = self
import sample.sample
self.assertEqual(len(self._calls), 2)
self.assertEqual(
self._calls,
[
'{"loc": {"depth": 3, "col": 4, "line": 5, "name": "sample.sample"}, "type": "<type \'int\'>", "value": "2", "desc": null}',
'{"loc": {"depth": 3, "col": 8, "line": 5, "name": "sample.sample"}, "type": "<type \'int\'>", "value": "3", "desc": null}'
]
)
def tearDown(self):
del my2to3.runtime.logger
sys.meta_path.remove(self._finder)
del sys.modules['sample']
del sys.modules['sample.sample']
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment