Minimal disassemble, ast compile and deparse work on Python 3.

Some linting
This commit is contained in:
rocky
2015-12-12 09:09:34 -05:00
parent 87a3bf1935
commit 832debeb70
8 changed files with 226 additions and 424 deletions

View File

@@ -3,7 +3,7 @@
[flake8] [flake8]
exclude = .tox,./build,./trepan/processor/command/tmp exclude = .tox,./build,./trepan/processor/command/tmp
filename = *.py filename = *.py
ignore = C901,E113,E121,E122,E123,E125,E126,E127,E128,E129,E201,E202,E203,E221,E222,E225,E226,E241,E242,E251,E261,E271,E272,E302,E401,E501,F401,E701,E702 ignore = C901,E113,E121,E122,E123,E124,E125,E126,E127,E128,E129,E201,E202,E203,E221,E222,E225,E226,E241,E242,E251,E261,E271,E272,E302,E401,E501,F401,E701,E702
[tox] [tox]
envlist = py26, py27, pypy envlist = py26, py27, pypy

View File

@@ -15,188 +15,12 @@ else:
from collections import namedtuple from collections import namedtuple
NodeInfo = namedtuple("NodeInfo", "node start finish") NodeInfo = namedtuple("NodeInfo", "node start finish")
class FindWalker(walker.Walker, object):
stacked_params = ('f', 'indent', 'isLambda', '_globals')
def __init__(self, out, scanner, showast=0):
GenericASTTraversal.__init__(self, ast=None)
params = {
'f': out,
'indent': '',
}
self.showast = showast
self.__params = params
self.__param_stack = []
self.ERROR = None
self.prec = 100
self.return_none = False
self.mod_globs = set()
self.currentclass = None
self.pending_newlines = 0
self.found_offset = False
self.offsets = {}
f = property(lambda s: s.__params['f'],
lambda s, x: s.__params.__setitem__('f', x),
lambda s: s.__params.__delitem__('f'),
None)
indent = property(lambda s: s.__params['indent'],
lambda s, x: s.__params.__setitem__('indent', x),
lambda s: s.__params.__delitem__('indent'),
None)
isLambda = property(lambda s: s.__params['isLambda'],
lambda s, x: s.__params.__setitem__('isLambda', x),
lambda s: s.__params.__delitem__('isLambda'),
None)
_globals = property(lambda s: s.__params['_globals'],
lambda s, x: s.__params.__setitem__('_globals', x),
lambda s: s.__params.__delitem__('_globals'),
None)
def preorder(self, node=None):
if node is None:
node = self.ast
if hasattr(node, 'offset'):
start = len(self.f.getvalue())
if node.offset == self.find_offset:
self.found_offset = True
# print 'BINGO!'
try:
name = 'n_' + self.typestring(node)
if hasattr(self, name):
func = getattr(self, name)
func(node)
else:
self.default(node)
except GenericASTTraversalPruningException:
if hasattr(node, 'offset'):
self.offsets[node.offset] = NodeInfo(node = node,
start = start,
finish = len(self.f.getvalue()))
# print self.offsets[node.offset]
# print self.f.getvalue()[start:]
return
for kid in node:
self.preorder(kid)
name = name + '_exit'
if hasattr(self, name):
func = getattr(self, name)
func(node)
return
def find_source(self, offset, ast, customize, isLambda=0, returnNone=False):
"""convert AST to source code"""
self.find_offset = offset
self.found_offset = False
# FIXME; the below doesn't find self.__params
# work so we duplicate the code.
# self.gen_source(ast, customize, isLambda, returnNone)
rn = self.return_none
self.return_none = returnNone
# if code would be empty, append 'pass'
if len(ast) == 0:
self.print_(self.indent, 'pass')
else:
self.customize(customize)
result = self.traverse(ast, isLambda=isLambda)
if isLambda:
self.write(result)
else:
self.print_(result)
self.return_none = rn
# FIXME; below duplicated the code, since we don't find self.__params
def traverse(self, node, indent=None, isLambda=0):
self.__param_stack.append(self.__params)
if indent is None: indent = self.indent
p = self.pending_newlines
self.pending_newlines = 0
self.__params = {
'_globals': {},
'f': StringIO(),
'indent': indent,
'isLambda': isLambda,
}
self.preorder(node)
self.f.write('\n'*self.pending_newlines)
result = self.f.getvalue()
self.__params = self.__param_stack.pop()
self.pending_newlines = p
return result
pass
def uncompyle_find(version, co, find_offset, out=sys.stdout, showasm=0, showast=0):
assert isinstance(co, types.CodeType)
# store final output stream for case of error
__real_out = out or sys.stdout
if version == 2.7:
import uncompyle6.scanner27 as scan
scanner = scan.Scanner27()
elif version == 2.6:
import scanner26 as scan
scanner = scan.Scanner26()
elif version == 2.5:
import scanner25 as scan
scanner = scan.Scanner25()
scanner.setShowAsm(0, out)
tokens, customize = scanner.disassemble(co)
# Build AST from disassembly.
# walk = walker.Walker(out, scanner, showast=showast)
walk = FindWalker(out, scanner, showast=showast)
try:
ast = walk.build_ast(tokens, customize)
except walker.ParserError as e : # parser failed, dump disassembly
print(e, file=__real_out)
raise
del tokens # save memory
# convert leading '__doc__ = "..." into doc string
assert ast == 'stmts'
try:
if ast[0][0] == walker.ASSIGN_DOC_STRING(co.co_consts[0]):
if find_offset == 0:
walk.print_docstring('', co.co_consts[0])
return
del ast[0]
if ast[-1] == walker.RETURN_NONE:
ast.pop() # remove last node
# todo: if empty, add 'pass'
except:
pass
walk.mod_globs = walker.find_globals(ast, set())
# walk.gen_source(ast, customize)
walk.find_source(find_offset, ast, customize)
for g in walk.mod_globs:
walk.write('global %s ## Warning: Unused global' % g)
if walk.ERROR:
raise walk.ERROR
return
def uncompyle_test(): def uncompyle_test():
frame = inspect.currentframe() frame = inspect.currentframe()
try: try:
co = frame.f_code co = frame.f_code
uncompyle(2.7, co, sys.stdout, 1, 1) uncompyle(2.7, co, sys.stdout, 1, 1)
print() print()
print('------------------------')
uncompyle_find(2.7, co, 33)
finally: finally:
del frame del frame

View File

@@ -42,7 +42,7 @@ class AST(UserList):
def __repr__(self, indent=''): def __repr__(self, indent=''):
rv = str(self.type) rv = str(self.type)
for k in self: for k in self:
rv = rv + '\n' + string.replace(str(k), '\n', '\n ') rv = rv + '\n' + str(k).replace('\n', '\n ')
return rv return rv
@@ -753,7 +753,8 @@ def parse(tokens, customize):
p.customized[k] = None p.customized[k] = None
# nop = lambda self, args: None # nop = lambda self, args: None
op = k[:string.rfind(k, '_')]
op = k[:k.rfind('_')]
if op in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'): if op in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'):
rule = 'build_list ::= ' + 'expr '*v + k rule = 'build_list ::= ' + 'expr '*v + k
elif op in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'): elif op in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
@@ -784,7 +785,7 @@ def parse(tokens, customize):
na = (v & 0xff) # positional parameters na = (v & 0xff) # positional parameters
nk = (v >> 8) & 0xff # keyword parameters nk = (v >> 8) & 0xff # keyword parameters
# number of apply equiv arguments: # number of apply equiv arguments:
nak = ( len(op)-len('CALL_FUNCTION') ) / 3 nak = ( len(op)-len('CALL_FUNCTION') ) // 3
rule = 'call_function ::= expr ' + 'expr '*na + 'kwarg '*nk \ rule = 'call_function ::= expr ' + 'expr '*na + 'kwarg '*nk \
+ 'expr ' * nak + k + 'expr ' * nak + k
else: else:

View File

@@ -42,6 +42,7 @@ class Scanner25(scan.Scanner):
# class and names # class and names
if classname: if classname:
classname = '_' + classname.lstrip('_') + '__' classname = '_' + classname.lstrip('_') + '__'
def unmangle(name): def unmangle(name):
if name.startswith(classname) and name[-2:] != '__': if name.startswith(classname) and name[-2:] != '__':
return name[len(classname) - 2:] return name[len(classname) - 2:]

View File

@@ -55,7 +55,6 @@ class Scanner27(scan.Scanner):
while j < start_byte: while j < start_byte:
self.lines.append(linetuple(prev_line_no, start_byte)) self.lines.append(linetuple(prev_line_no, start_byte))
j += 1 j += 1
last_op = self.code[self.prev[start_byte]]
(prev_start_byte, prev_line_no) = (start_byte, line_no) (prev_start_byte, prev_line_no) = (start_byte, line_no)
while j < n: while j < n:
self.lines.append(linetuple(prev_line_no, n)) self.lines.append(linetuple(prev_line_no, n))
@@ -63,6 +62,7 @@ class Scanner27(scan.Scanner):
# self.lines contains (block,addrLastInstr) # self.lines contains (block,addrLastInstr)
if classname: if classname:
classname = '_' + classname.lstrip('_') + '__' classname = '_' + classname.lstrip('_') + '__'
def unmangle(name): def unmangle(name):
if name.startswith(classname) and name[-2:] != '__': if name.startswith(classname) and name[-2:] != '__':
return name[len(classname) - 2:] return name[len(classname) - 2:]
@@ -126,7 +126,7 @@ class Scanner27(scan.Scanner):
continue continue
if op in hasconst: if op in hasconst:
const = co.co_consts[oparg] const = co.co_consts[oparg]
if type(const) == types.CodeType: if isinstance(const, types.CodeType):
oparg = const oparg = const
if const.co_name == '<lambda>': if const.co_name == '<lambda>':
assert op_name == 'LOAD_CONST' assert op_name == 'LOAD_CONST'
@@ -348,8 +348,6 @@ class Scanner27(scan.Scanner):
start = _start start = _start
end = _end end = _end
parent = s parent = s
# We need to know how many new structures were added in this run
origStructCount = len(self.structs)
if op == SETUP_LOOP: if op == SETUP_LOOP:
start = pos+3 start = pos+3
@@ -444,7 +442,7 @@ class Scanner27(scan.Scanner):
'end': jmp}) 'end': jmp})
i = jmp + 3 i = jmp + 3
## Add the try-else block # Add the try-else block
if end_else != start_else: if end_else != start_else:
r_end_else = self.restrict_to_parent(end_else, parent) r_end_else = self.restrict_to_parent(end_else, parent)
self.structs.append({'type': 'try-else', self.structs.append({'type': 'try-else',
@@ -454,7 +452,6 @@ class Scanner27(scan.Scanner):
else: else:
self.fixed_jumps[i] = i+1 self.fixed_jumps[i] = i+1
elif op in (PJIF, PJIT): elif op in (PJIF, PJIT):
start = pos+3 start = pos+3
target = self.get_target(pos, op) target = self.get_target(pos, op)
@@ -491,9 +488,10 @@ class Scanner27(scan.Scanner):
pass pass
elif code[pre[pre[rtarget]]] == RETURN_VALUE \ elif code[pre[pre[rtarget]]] == RETURN_VALUE \
and self.remove_mid_line_ifs([pos]) \ and self.remove_mid_line_ifs([pos]) \
and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \ and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start,
(PJIF, PJIT), target))) \ pre[pre[rtarget]],
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \ (PJIF, PJIT), target)))
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]],
(PJIF, PJIT, JA), pre[rtarget], True))))): (PJIF, PJIT, JA), pre[rtarget], True))))):
pass pass
else: else:

View File

@@ -8,14 +8,13 @@ from __future__ import print_function
See main module for license. See main module for license.
''' '''
import types import dis, types
from collections import namedtuple from collections import namedtuple
from array import array from array import array
from operator import itemgetter from operator import itemgetter
from uncompyle6.opcodes.opcode_27 import * from uncompyle6.opcodes.opcode_27 import *
import disas as dis import uncompyle6.scanner as scan
import scanner as scan
class Scanner27(scan.Scanner): class Scanner27(scan.Scanner):
def __init__(self): def __init__(self):
@@ -56,7 +55,6 @@ class Scanner27(scan.Scanner):
while j < start_byte: while j < start_byte:
self.lines.append(linetuple(prev_line_no, start_byte)) self.lines.append(linetuple(prev_line_no, start_byte))
j += 1 j += 1
last_op = self.code[self.prev[start_byte]]
(prev_start_byte, prev_line_no) = (start_byte, line_no) (prev_start_byte, prev_line_no) = (start_byte, line_no)
while j < n: while j < n:
self.lines.append(linetuple(prev_line_no, n)) self.lines.append(linetuple(prev_line_no, n))
@@ -64,6 +62,7 @@ class Scanner27(scan.Scanner):
# self.lines contains (block,addrLastInstr) # self.lines contains (block,addrLastInstr)
if classname: if classname:
classname = '_' + classname.lstrip('_') + '__' classname = '_' + classname.lstrip('_') + '__'
def unmangle(name): def unmangle(name):
if name.startswith(classname) and name[-2:] != '__': if name.startswith(classname) and name[-2:] != '__':
return name[len(classname) - 2:] return name[len(classname) - 2:]
@@ -211,7 +210,7 @@ class Scanner27(scan.Scanner):
def build_stmt_indices(self): def build_stmt_indices(self):
code = self.code code = self.code
start = 0; start = 0
end = len(code) end = len(code)
stmt_opcodes = { stmt_opcodes = {
@@ -338,7 +337,7 @@ class Scanner27(scan.Scanner):
if op is None: if op is None:
op = code[pos] op = code[pos]
## Detect parent structure # Detect parent structure
parent = self.structs[0] parent = self.structs[0]
start = parent['start'] start = parent['start']
end = parent['end'] end = parent['end']
@@ -349,7 +348,7 @@ class Scanner27(scan.Scanner):
start = _start start = _start
end = _end end = _end
parent = s parent = s
## We need to know how many new structures were added in this run # We need to know how many new structures were added in this run
origStructCount = len(self.structs) origStructCount = len(self.structs)
if op == SETUP_LOOP: if op == SETUP_LOOP:
@@ -419,14 +418,14 @@ class Scanner27(scan.Scanner):
if target != end: if target != end:
self.fixed_jumps[pos] = end self.fixed_jumps[pos] = end
# print target, end, parent # print target, end, parent
## Add the try block # Add the try block
self.structs.append({'type': 'try', self.structs.append({'type': 'try',
'start': start, 'start': start,
'end': end-4}) 'end': end-4})
## Now isolate the except and else blocks # Now isolate the except and else blocks
end_else = start_else = self.get_target(self.prev[end]) end_else = start_else = self.get_target(self.prev[end])
## Add the except blocks # Add the except blocks
i = end i = end
while self.code[i] != END_FINALLY: while self.code[i] != END_FINALLY:
jmp = self.next_except_jump(i) jmp = self.next_except_jump(i)
@@ -445,7 +444,7 @@ class Scanner27(scan.Scanner):
'end': jmp}) 'end': jmp})
i = jmp + 3 i = jmp + 3
## Add the try-else block # Add the try-else block
if end_else != start_else: if end_else != start_else:
r_end_else = self.restrict_to_parent(end_else, parent) r_end_else = self.restrict_to_parent(end_else, parent)
self.structs.append({'type': 'try-else', self.structs.append({'type': 'try-else',
@@ -455,7 +454,6 @@ class Scanner27(scan.Scanner):
else: else:
self.fixed_jumps[i] = i+1 self.fixed_jumps[i] = i+1
elif op in (PJIF, PJIT): elif op in (PJIF, PJIT):
start = pos+3 start = pos+3
target = self.get_target(pos, op) target = self.get_target(pos, op)
@@ -492,9 +490,10 @@ class Scanner27(scan.Scanner):
pass pass
elif code[pre[pre[rtarget]]] == RETURN_VALUE \ elif code[pre[pre[rtarget]]] == RETURN_VALUE \
and self.remove_mid_line_ifs([pos]) \ and self.remove_mid_line_ifs([pos]) \
and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \ and 1 == (len(set(self.remove_mid_line_ifs(self.rem_or(start,
(PJIF, PJIT), target))) \ pre[pre[rtarget]],
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]], \ (PJIF, PJIT), target)))
| set(self.remove_mid_line_ifs(self.rem_or(start, pre[pre[rtarget]],
(PJIF, PJIT, JA), pre[rtarget], True))))): (PJIF, PJIT, JA), pre[rtarget], True))))):
pass pass
else: else:

View File

@@ -94,11 +94,11 @@ class GenericParser:
for k, v in list(self.edges.items()): for k, v in list(self.edges.items()):
if v is None: if v is None:
state, sym = k state, sym = k
if self.states.has_key(state): if state in self.states:
self.goto(state, sym) self.goto(state, sym)
changes = 1 changes = 1
rv = self.__dict__.copy() rv = self.__dict__.copy()
for s in self.states.values(): for s in list(self.states.values()):
del s.items del s.items
del rv['rule2func'] del rv['rule2func']
del rv['nullable'] del rv['nullable']
@@ -266,7 +266,7 @@ class GenericParser:
self.states = { 0: self.makeState0() } self.states = { 0: self.makeState0() }
self.makeState(0, self._BOF) self.makeState(0, self._BOF)
for i in xrange(len(tokens)): for i in range(len(tokens)):
sets.append([]) sets.append([])
if sets[i] == []: if sets[i] == []:
@@ -315,7 +315,7 @@ class GenericParser:
kitems.append((rule, self.skip(rule, pos+1))) kitems.append((rule, self.skip(rule, pos+1)))
tcore = tuple(sorted(kitems)) tcore = tuple(sorted(kitems))
if self.cores.has_key(tcore): if tcore in self.cores:
return self.cores[tcore] return self.cores[tcore]
# #
# Nope, doesn't exist. Compute it and the associated # Nope, doesn't exist. Compute it and the associated
@@ -339,13 +339,13 @@ class GenericParser:
nextSym = rhs[pos] nextSym = rhs[pos]
key = (X.stateno, nextSym) key = (X.stateno, nextSym)
if not rules.has_key(nextSym): if nextSym not in rules:
if not edges.has_key(key): if key not in edges:
edges[key] = None edges[key] = None
X.T.append(nextSym) X.T.append(nextSym)
else: else:
edges[key] = None edges[key] = None
if not predicted.has_key(nextSym): if nextSym not in predicted:
predicted[nextSym] = 1 predicted[nextSym] = 1
for prule in rules[nextSym]: for prule in rules[nextSym]:
ppos = self.skip(prule) ppos = self.skip(prule)
@@ -370,7 +370,7 @@ class GenericParser:
# to do this without accidentally duplicating states. # to do this without accidentally duplicating states.
# #
tcore = tuple(sorted(predicted.keys())) tcore = tuple(sorted(predicted.keys()))
if self.cores.has_key(tcore): if tcore in self.cores:
self.edges[(k, None)] = self.cores[tcore] self.edges[(k, None)] = self.cores[tcore]
return k return k
@@ -381,7 +381,7 @@ class GenericParser:
def goto(self, state, sym): def goto(self, state, sym):
key = (state, sym) key = (state, sym)
if not self.edges.has_key(key): if key not in self.edges:
# #
# No transitions from state on sym. # No transitions from state on sym.
# #
@@ -579,7 +579,7 @@ class GenericParser:
for i in range(len(rhs)-1, -1, -1): for i in range(len(rhs)-1, -1, -1):
sym = rhs[i] sym = rhs[i]
if not self.newrules.has_key(sym): if sym not in self.newrules:
if sym != self._BOF: if sym != self._BOF:
attr[i] = tokens[k-1] attr[i] = tokens[k-1]
key = (item, k) key = (item, k)
@@ -656,7 +656,7 @@ class GenericASTBuilder(GenericParser):
rv[:len(args)] = args rv[:len(args)] = args
return rv return rv
class GenericASTTraversalPruningException: class GenericASTTraversalPruningException(BaseException):
pass pass
class GenericASTTraversal: class GenericASTTraversal:

View File

@@ -309,9 +309,9 @@ TABLE_DIRECT = {
'kv2': ( '%c: %c', 1, 2 ), 'kv2': ( '%c: %c', 1, 2 ),
'mapexpr': ( '{%[1]C}', (0, maxint, ', ') ), 'mapexpr': ( '{%[1]C}', (0, maxint, ', ') ),
## #######################
## Python 2.5 Additions # Python 2.5 Additions
## #######################
# Import style for 2.5 # Import style for 2.5
'importstmt': ( '%|import %c\n', 2), 'importstmt': ( '%|import %c\n', 2),
@@ -417,7 +417,7 @@ class ParserError(dparser.ParserError):
def __str__(self): def __str__(self):
lines = ['--- This code section failed: ---'] lines = ['--- This code section failed: ---']
lines.extend( map(str, self.tokens) ) lines.extend( list(map(str, self.tokens)) )
lines.extend( ['', str(self.error)] ) lines.extend( ['', str(self.error)] )
return '\n'.join(lines) return '\n'.join(lines)
@@ -446,7 +446,7 @@ def find_none(node):
if not (n == 'return_stmt' or n == 'return_if_stmt'): if not (n == 'return_stmt' or n == 'return_if_stmt'):
if find_none(n): if find_none(n):
return True return True
elif n.type == 'LOAD_CONST' and n.pattr == None: elif n.type == 'LOAD_CONST' and n.pattr is None:
return True return True
return False return False
@@ -461,8 +461,8 @@ class Walker(GenericASTTraversal, object):
'indent': '', 'indent': '',
} }
self.showast = showast self.showast = showast
self.__params = params self.params = params
self.__param_stack = [] self.param_stack = []
self.ERROR = None self.ERROR = None
self.prec = 100 self.prec = 100
self.return_none = False self.return_none = False
@@ -470,37 +470,38 @@ class Walker(GenericASTTraversal, object):
self.currentclass = None self.currentclass = None
self.pending_newlines = 0 self.pending_newlines = 0
f = property(lambda s: s.__params['f'], f = property(lambda s: s.params['f'],
lambda s, x: s.__params.__setitem__('f', x), lambda s, x: s.params.__setitem__('f', x),
lambda s: s.__params.__delitem__('f'), lambda s: s.params.__delitem__('f'),
None) None)
indent = property(lambda s: s.__params['indent'], indent = property(lambda s: s.params['indent'],
lambda s, x: s.__params.__setitem__('indent', x), lambda s, x: s.params.__setitem__('indent', x),
lambda s: s.__params.__delitem__('indent'), lambda s: s.params.__delitem__('indent'),
None) None)
isLambda = property(lambda s: s.__params['isLambda'], isLambda = property(lambda s: s.params['isLambda'],
lambda s, x: s.__params.__setitem__('isLambda', x), lambda s, x: s.params.__setitem__('isLambda', x),
lambda s: s.__params.__delitem__('isLambda'), lambda s: s.params.__delitem__('isLambda'),
None) None)
_globals = property(lambda s: s.__params['_globals'], _globals = property(lambda s: s.params['_globals'],
lambda s, x: s.__params.__setitem__('_globals', x), lambda s, x: s.params.__setitem__('_globals', x),
lambda s: s.__params.__delitem__('_globals'), lambda s: s.params.__delitem__('_globals'),
None) None)
def indentMore(self, indent=TAB): def indentMore(self, indent=TAB):
self.indent += indent self.indent += indent
def indentLess(self, indent=TAB): def indentLess(self, indent=TAB):
self.indent = self.indent[:-len(indent)] self.indent = self.indent[:-len(indent)]
def traverse(self, node, indent=None, isLambda=0): def traverse(self, node, indent=None, isLambda=0):
self.__param_stack.append(self.__params) self.param_stack.append(self.params)
if indent is None: indent = self.indent if indent is None: indent = self.indent
p = self.pending_newlines p = self.pending_newlines
self.pending_newlines = 0 self.pending_newlines = 0
self.__params = { self.params = {
'_globals': {}, '_globals': {},
'f': StringIO(), 'f': StringIO(),
'indent': indent, 'indent': indent,
@@ -509,7 +510,7 @@ class Walker(GenericASTTraversal, object):
self.preorder(node) self.preorder(node)
self.f.write('\n'*self.pending_newlines) self.f.write('\n'*self.pending_newlines)
result = self.f.getvalue() result = self.f.getvalue()
self.__params = self.__param_stack.pop() self.params = self.param_stack.pop()
self.pending_newlines = p self.pending_newlines = p
return result return result
@@ -554,6 +555,7 @@ class Walker(GenericASTTraversal, object):
def print_docstring(self, indent, docstring): def print_docstring(self, indent, docstring):
quote = '"""' quote = '"""'
self.write(indent) self.write(indent)
# FIXME for Python3
if type(docstring) == unicode: if type(docstring) == unicode:
self.write('u') self.write('u')
docstring = repr(docstring.expandtabs())[2:-1] docstring = repr(docstring.expandtabs())[2:-1]
@@ -580,7 +582,8 @@ class Walker(GenericASTTraversal, object):
# restore backslashes unescaped since raw # restore backslashes unescaped since raw
docstring = docstring.replace('\t', '\\') docstring = docstring.replace('\t', '\\')
else: else:
#Escape '"' if it's the last character, so it doesn't ruin the ending triple quote # Escape '"' if it's the last character, so it doesn't
# ruin the ending triple quote
if len(docstring) and docstring[-1] == '"': if len(docstring) and docstring[-1] == '"':
docstring = docstring[:-1] + '\\"' docstring = docstring[:-1] + '\\"'
# Escape triple quote anywhere # Escape triple quote anywhere
@@ -610,9 +613,8 @@ class Walker(GenericASTTraversal, object):
self.print_( indent, line ) self.print_( indent, line )
self.print_(indent, trimmed[-1], quote) self.print_(indent, trimmed[-1], quote)
def n_return_stmt(self, node): def n_return_stmt(self, node):
if self.__params['isLambda']: if self.params['isLambda']:
self.preorder(node[0]) self.preorder(node[0])
self.prune() self.prune()
else: else:
@@ -624,7 +626,7 @@ class Walker(GenericASTTraversal, object):
self.prune() # stop recursing self.prune() # stop recursing
def n_return_if_stmt(self, node): def n_return_if_stmt(self, node):
if self.__params['isLambda']: if self.params['isLambda']:
self.preorder(node[0]) self.preorder(node[0])
self.prune() self.prune()
else: else:
@@ -730,7 +732,6 @@ class Walker(GenericASTTraversal, object):
# LOAD_CONST is a terminal, so stop processing/recursing early # LOAD_CONST is a terminal, so stop processing/recursing early
self.prune() self.prune()
def n_delete_subscr(self, node): def n_delete_subscr(self, node):
if node[-2][0] == 'build_list' and node[-2][0][-1].type.startswith('BUILD_TUPLE'): if node[-2][0] == 'build_list' and node[-2][0][-1].type.startswith('BUILD_TUPLE'):
if node[-2][0][-1] != 'BUILD_TUPLE_0': if node[-2][0][-1] != 'BUILD_TUPLE_0':
@@ -854,13 +855,6 @@ class Walker(GenericASTTraversal, object):
self.preorder(node[1]) self.preorder(node[1])
self.indentLess() self.indentLess()
if_ret_at_end = False
if len(node[2][0]) >= 3:
if node[2][0][-1][0] == 'ifstmt' and node[2][0][-1][0][1][0] == 'return_if_stmts':
if_ret_at_end = True
past_else = False
prev_stmt_is_if_ret = True
for n in node[2][0]: for n in node[2][0]:
n[0].type = 'elifstmt' n[0].type = 'elifstmt'
self.preorder(n) self.preorder(n)
@@ -871,7 +865,7 @@ class Walker(GenericASTTraversal, object):
self.prune() self.prune()
def n_import_as(self, node): def n_import_as(self, node):
iname = node[0].pattr; iname = node[0].pattr
assert node[-1][-1].type.startswith('STORE_') assert node[-1][-1].type.startswith('STORE_')
sname = node[-1][-1].pattr # assume one of STORE_.... here sname = node[-1][-1].pattr # assume one of STORE_.... here
if iname == sname or iname.startswith(sname + '.'): if iname == sname or iname.startswith(sname + '.'):
@@ -893,7 +887,7 @@ class Walker(GenericASTTraversal, object):
self.write(node[-2].attr.co_name) # = code.co_name self.write(node[-2].attr.co_name) # = code.co_name
self.indentMore() self.indentMore()
self.make_function(node, isLambda=0) self.make_function(node, isLambda=0)
if len(self.__param_stack) > 1: if len(self.param_stack) > 1:
self.write('\n\n') self.write('\n\n')
else: else:
self.write('\n\n\n') self.write('\n\n\n')
@@ -916,7 +910,7 @@ class Walker(GenericASTTraversal, object):
elif n == 'list_if': n = n[2] elif n == 'list_if': n = n[2]
elif n == 'list_if_not': n= n[2] elif n == 'list_if_not': n= n[2]
assert n == 'lc_body' assert n == 'lc_body'
self.write( '[ '); self.write( '[ ')
self.preorder(n[0]) # lc_body self.preorder(n[0]) # lc_body
self.preorder(node[-1]) # for/if parts self.preorder(node[-1]) # for/if parts
self.write( ' ]') self.write( ' ]')
@@ -960,7 +954,6 @@ class Walker(GenericASTTraversal, object):
self.write(')') self.write(')')
self.prune() self.prune()
def n_setcomp(self, node): def n_setcomp(self, node):
self.write('{') self.write('{')
self.comprehension_walk(node, 4) self.comprehension_walk(node, 4)
@@ -969,7 +962,6 @@ class Walker(GenericASTTraversal, object):
n_dictcomp = n_setcomp n_dictcomp = n_setcomp
def n_classdef(self, node): def n_classdef(self, node):
# class definition ('class X(A,B,C):') # class definition ('class X(A,B,C):')
cclass = self.currentclass cclass = self.currentclass
@@ -986,14 +978,13 @@ class Walker(GenericASTTraversal, object):
self.indentLess() self.indentLess()
self.currentclass = cclass self.currentclass = cclass
if len(self.__param_stack) > 1: if len(self.param_stack) > 1:
self.write('\n\n') self.write('\n\n')
else: else:
self.write('\n\n\n') self.write('\n\n\n')
self.prune() self.prune()
n_classdefdeco2 = n_classdef n_classdefdeco2 = n_classdef
def print_super_classes(self, node): def print_super_classes(self, node):
@@ -1031,13 +1022,13 @@ class Walker(GenericASTTraversal, object):
# kv2 ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR # kv2 ::= DUP_TOP expr expr ROT_THREE STORE_SUBSCR
# kv3 ::= expr expr STORE_MAP # kv3 ::= expr expr STORE_MAP
if kv == 'kv': if kv == 'kv':
name = self.traverse(kv[-2], indent=''); name = self.traverse(kv[-2], indent='')
value = self.traverse(kv[1], indent=self.indent+(len(name)+2)*' ') value = self.traverse(kv[1], indent=self.indent+(len(name)+2)*' ')
elif kv == 'kv2': elif kv == 'kv2':
name = self.traverse(kv[1], indent=''); name = self.traverse(kv[1], indent='')
value = self.traverse(kv[-3], indent=self.indent+(len(name)+2)*' ') value = self.traverse(kv[-3], indent=self.indent+(len(name)+2)*' ')
elif kv == 'kv3': elif kv == 'kv3':
name = self.traverse(kv[-2], indent=''); name = self.traverse(kv[-2], indent='')
value = self.traverse(kv[0], indent=self.indent+(len(name)+2)*' ') value = self.traverse(kv[0], indent=self.indent+(len(name)+2)*' ')
self.write(sep, name, ': ', value) self.write(sep, name, ': ', value)
sep = line_seperator sep = line_seperator
@@ -1046,7 +1037,6 @@ class Walker(GenericASTTraversal, object):
self.prec = p self.prec = p
self.prune() self.prune()
def n_build_list(self, node): def n_build_list(self, node):
""" """
prettyprint a list or tuple prettyprint a list or tuple
@@ -1113,11 +1103,9 @@ class Walker(GenericASTTraversal, object):
def engine(self, entry, startnode): def engine(self, entry, startnode):
# self.print_("-----") # self.print_("-----")
#self.print_(str(startnode.__dict__)) # self.print(startnode)
fmt = entry[0] fmt = entry[0]
## no longer used, since BUILD_TUPLE_n is pretty printed:
##lastC = 0
arg = 1 arg = 1
i = 0 i = 0
@@ -1139,10 +1127,9 @@ class Walker(GenericASTTraversal, object):
elif typ == '+': self.indentMore() elif typ == '+': self.indentMore()
elif typ == '-': self.indentLess() elif typ == '-': self.indentLess()
elif typ == '|': self.write(self.indent) elif typ == '|': self.write(self.indent)
## no longer used, since BUILD_TUPLE_n is pretty printed: # no longer used, since BUILD_TUPLE_n is pretty printed:
elif typ == ',': elif typ == ',':
if lastC == 1: pass
self.write(',')
elif typ == 'c': elif typ == 'c':
self.preorder(node[entry[arg]]) self.preorder(node[entry[arg]])
arg += 1 arg += 1
@@ -1154,8 +1141,8 @@ class Walker(GenericASTTraversal, object):
arg += 1 arg += 1
elif typ == 'C': elif typ == 'C':
low, high, sep = entry[arg] low, high, sep = entry[arg]
lastC = remaining = len(node[low:high]) remaining = len(node[low:high])
## remaining = len(node[low:high]) # remaining = len(node[low:high])
for subnode in node[low:high]: for subnode in node[low:high]:
self.preorder(subnode) self.preorder(subnode)
remaining -= 1 remaining -= 1
@@ -1165,8 +1152,8 @@ class Walker(GenericASTTraversal, object):
elif typ == 'P': elif typ == 'P':
p = self.prec p = self.prec
low, high, sep, self.prec = entry[arg] low, high, sep, self.prec = entry[arg]
lastC = remaining = len(node[low:high]) remaining = len(node[low:high])
## remaining = len(node[low:high]) # remaining = len(node[low:high])
for subnode in node[low:high]: for subnode in node[low:high]:
self.preorder(subnode) self.preorder(subnode)
remaining -= 1 remaining -= 1
@@ -1192,9 +1179,10 @@ class Walker(GenericASTTraversal, object):
for i in mapping[1:]: for i in mapping[1:]:
key = key[i] key = key[i]
pass
if table.has_key(key): if key.type in table:
self.engine(table[key], node) self.engine(table[key.type], node)
self.prune() self.prune()
def customize(self, customize): def customize(self, customize):
@@ -1202,8 +1190,8 @@ class Walker(GenericASTTraversal, object):
Special handling for opcodes that take a variable number Special handling for opcodes that take a variable number
of arguments -- we add a new entry for each in TABLE_R. of arguments -- we add a new entry for each in TABLE_R.
""" """
for k, v in customize.items(): for k, v in list(customize.items()):
if TABLE_R.has_key(k): if k in TABLE_R:
continue continue
op = k[ :k.rfind('_') ] op = k[ :k.rfind('_') ]
if op == 'CALL_FUNCTION': TABLE_R[k] = ('%c(%P)', 0, (1, -1, ', ', 100)) if op == 'CALL_FUNCTION': TABLE_R[k] = ('%c(%P)', 0, (1, -1, ', ', 100))
@@ -1225,12 +1213,16 @@ class Walker(GenericASTTraversal, object):
str += '*%c, **%c)' str += '*%c, **%c)'
if p2[2]: p2 = (1, -3, ', ') if p2[2]: p2 = (1, -3, ', ')
entry = (str, 0, p2, -3, -2) entry = (str, 0, p2, -3, -2)
pass
TABLE_R[k] = entry TABLE_R[k] = entry
## handled by n_mapexpr: pass
##if op == 'BUILD_SLICE': TABLE_R[k] = ('%C' , (0,-1,':')) # handled by n_mapexpr:
## handled by n_build_list: # if op == 'BUILD_SLICE': TABLE_R[k] = ('%C' , (0,-1,':'))
##if op == 'BUILD_LIST': TABLE_R[k] = ('[%C]' , (0,-1,', ')) # handled by n_build_list:
##elif op == 'BUILD_TUPLE': TABLE_R[k] = ('(%C%,)', (0,-1,', ')) # if op == 'BUILD_LIST': TABLE_R[k] = ('[%C]' , (0,-1,', '))
# elif op == 'BUILD_TUPLE': TABLE_R[k] = ('(%C%,)', (0,-1,', '))
pass
return
def get_tuple_parameter(self, ast, name): def get_tuple_parameter(self, ast, name):
""" """
@@ -1250,8 +1242,8 @@ class Walker(GenericASTTraversal, object):
# search for an assign-statement # search for an assign-statement
assert ast[i][0] == 'stmt' assert ast[i][0] == 'stmt'
node = ast[i][0][0] node = ast[i][0][0]
if node == 'assign' \ if (node == 'assign'
and node[0] == ASSIGN_TUPLE_PARAM(name): and node[0] == ASSIGN_TUPLE_PARAM(name)):
# okay, this assigns '.n' to something # okay, this assigns '.n' to something
del ast[i] del ast[i]
# walk lhs; this # walk lhs; this
@@ -1265,7 +1257,6 @@ class Walker(GenericASTTraversal, object):
# return self.traverse(node[1]) # return self.traverse(node[1])
raise Exception("Can't find tuple parameter " + name) raise Exception("Can't find tuple parameter " + name)
def make_function(self, node, isLambda, nested=1): def make_function(self, node, isLambda, nested=1):
"""Dump function defintion, doc string, and function body.""" """Dump function defintion, doc string, and function body."""
@@ -1342,24 +1333,12 @@ class Walker(GenericASTTraversal, object):
self.print_("(", ", ".join(params), "):") self.print_("(", ", ".join(params), "):")
# self.print_(indent, '#flags:\t', int(code.co_flags)) # self.print_(indent, '#flags:\t', int(code.co_flags))
if len(code.co_consts)>0 and code.co_consts[0] != None and not isLambda: # ugly if len(code.co_consts)>0 and code.co_consts[0] is not None and not isLambda: # ugly
# docstring exists, dump it # docstring exists, dump it
self.print_docstring(indent, code.co_consts[0]) self.print_docstring(indent, code.co_consts[0])
code._tokens = None # save memory code._tokens = None # save memory
assert ast == 'stmts' assert ast == 'stmts'
#if isLambda:
# convert 'return' statement to expression
#assert len(ast[0]) == 1 wrong, see 'lambda (r,b): r,b,g'
#assert ast[-1] == 'stmt'
#assert len(ast[-1]) == 1
# assert ast[-1][0] == 'return_stmt'
# ast[-1][0].type = 'return_lambda'
#else:
# if ast[-1] == RETURN_NONE:
# Python adds a 'return None' to the
# end of any function; remove it
# ast.pop() # remove last node
all_globals = find_all_globals(ast, set()) all_globals = find_all_globals(ast, set())
for g in ((all_globals & self.mod_globs) | find_globals(ast, set())): for g in ((all_globals & self.mod_globs) | find_globals(ast, set())):