Merge branch 'master' into python-2.4

This commit is contained in:
rocky
2018-02-05 06:27:33 -05:00
14 changed files with 330 additions and 213 deletions

13
NEWS
View File

@@ -1,4 +1,11 @@
uncompyle6 2.15.0 2017-01-27
uncompyle6 2.15.0 2018-02-05 pycon2018.co
- Bug fixes
- Code fragment improvements
- Code cleanups
- Expand testing
uncompyle6 2.15.1 2018-01-27
- Add --linemap option to give line correspondences
between original source lines and reconstructed line sources.
@@ -10,7 +17,7 @@ uncompyle6 2.15.0 2017-01-27
- Correct 3.6+ calls with kwargs
- Describe the difficulty of 3.6 in README
uncompyle6 2.14.3 2017-01-19
uncompyle6 2.14.3 2018-01-19
- Fix bug in 3.5+ await stmt
- Better version to magic handling; handle 3.5.2 .. 3.5.4 versions
@@ -22,7 +29,7 @@ uncompyle6 2.14.3 2017-01-19
- better tests in setup.py for running the right version of Python
- Fix 2.6- parsing of "for .. try/else" ... with "continue" inside
uncompyle6 2.14.2 2017-01-09 Samish
uncompyle6 2.14.2 2018-01-09 Samish
Decompilation bug fixes, mostly 3.6 and pre 2.7

View File

@@ -74,8 +74,8 @@ def check_expect(expect, parsed, fn_name):
pass
@pytest.mark.skip(reason='needs reworking')
def test_stuff():
return
parsed = get_parsed_for_fn(map_stmts)
expect = """
-1

View File

@@ -29,6 +29,9 @@ def test_if_in_for():
bytecode = Bytecode(code, scan.opc)
scan.build_lines_data(code, n)
scan.insts = list(bytecode)
scan.offset2inst_index = {}
for i, inst in enumerate(scan.insts):
scan.offset2inst_index[inst.offset] = i
scan.build_prev_op(n)
fjt = scan.find_jump_targets(False)
@@ -45,8 +48,13 @@ def test_if_in_for():
code = bug_loop.__code__
n = scan.setup_code(code)
bytecode = Bytecode(code, scan.opc)
scan.build_lines_data(code, n)
scan.insts = list(bytecode)
scan.build_prev_op(n)
scan.offset2inst_index = {}
for i, inst in enumerate(scan.insts):
scan.offset2inst_index[inst.offset] = i
fjt = scan.find_jump_targets(False)
assert{64: [42], 67: [42, 42], 42: [16, 41], 19: [6]} == fjt
assert scan.structs == [

Binary file not shown.

View File

@@ -0,0 +1,10 @@
# Issue #149. Bug in Python 2.7 was handling a return stmt at the end
# of a while with so no jump back, confusing which block the
# return should be part of
def test(a):
while True:
if a:
pass
else:
continue
return

View File

@@ -170,8 +170,9 @@ def main_bin():
try:
result = main(src_base, out_base, files, codes, outfile,
**options)
result = list(result) + [options.get('do_verify', None)]
if len(files) > 1:
mess = status_msg(do_verify, result, do_verify)
mess = status_msg(do_verify, *result)
print('# ' + mess)
pass
except (KeyboardInterrupt):

View File

@@ -207,7 +207,7 @@ def main(in_base, out_base, files, codes, outfile=None,
for d in deparsed:
last_mod = None
offsets = d.offsets
for e in sorted(offsets.keys()):
for e in sorted([k for k in offsets.keys() if isinstance(k[1], int)]):
if e[0] != last_mod:
line = '=' * len(e[0])
outstream.write("%s\n%s\n%s\n" % (line, e[0], line))

View File

@@ -119,7 +119,8 @@ class Python27Parser(Python2Parser):
POP_BLOCK LOAD_CONST COME_FROM_WITH
WITH_CLEANUP END_FINALLY
whilestmt ::= SETUP_LOOP testexpr returns POP_BLOCK COME_FROM
whilestmt ::= SETUP_LOOP testexpr returns
_come_froms POP_BLOCK COME_FROM
while1stmt ::= SETUP_LOOP returns bp_come_from
while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2017 by Rocky Bernstein
# Copyright (c) 2015-2018 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
"""
@@ -113,7 +113,11 @@ class Scanner2(Scanner):
self.build_lines_data(co, codelen)
self.build_prev_op(codelen)
self.insts = list(bytecode)
self.offset2inst_index = {}
for i, inst in enumerate(self.insts):
self.offset2inst_index[inst.offset] = i
free, names, varnames = self.unmangle_code_names(co, classname)
self.names = names
@@ -510,50 +514,55 @@ class Scanner2(Scanner):
# Try to find the jump_back instruction of the loop.
# It could be a return instruction.
inst = self.insts[self.offset2inst_index[offset]]
start += instruction_size(op, self.opc)
target = self.get_target(offset) + extended_arg
end = self.restrict_to_parent(target, parent)
self.setup_loop_targets[offset] = target
self.setup_loops[target] = offset
setup_target = inst.argval
loop_end_offset = self.restrict_to_parent(setup_target, parent)
self.setup_loop_targets[offset] = setup_target
self.setup_loops[setup_target] = offset
if target != end:
self.fixed_jumps[offset] = end
if setup_target != loop_end_offset:
self.fixed_jumps[offset] = loop_end_offset
(line_no, next_line_byte) = self.lines[offset]
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE,
# jump_back_offset is the instruction after the SETUP_LOOP
# where we iterate back to.
jump_back_offset = self.last_instr(start, loop_end_offset, self.opc.JUMP_ABSOLUTE,
next_line_byte, False)
if jump_back:
if jump_back_offset:
# Account for the fact that < 2.7 has an explicit
# POP_TOP instruction in the equivalate POP_JUMP_IF
# construct
if self.version < 2.7:
jump_forward_offset = jump_back+4
return_val_offset1 = self.prev[self.prev[self.prev[end]]]
jump_forward_offset = jump_back_offset+4
return_val_offset1 = self.prev[self.prev[self.prev[loop_end_offset]]]
# Is jump back really "back"?
jump_target = self.get_target(jump_back, code[jump_back])
if (jump_target > jump_back or
code[jump_back+3] in [self.opc.JUMP_FORWARD, self.opc.JUMP_ABSOLUTE]):
jump_back = None
jump_target = self.get_target(jump_back_offset, code[jump_back_offset])
if (jump_target > jump_back_offset or
code[jump_back_offset+3] in [self.opc.JUMP_FORWARD, self.opc.JUMP_ABSOLUTE]):
jump_back_offset = None
pass
else:
jump_forward_offset = jump_back+3
return_val_offset1 = self.prev[self.prev[end]]
jump_forward_offset = jump_back_offset+3
return_val_offset1 = self.prev[self.prev[loop_end_offset]]
if (jump_back and jump_back != self.prev[end]
if (jump_back_offset and jump_back_offset != self.prev[loop_end_offset]
and code[jump_forward_offset] in self.jump_forward):
if (code[self.prev[end]] == self.opc.RETURN_VALUE or
(code[self.prev[end]] == self.opc.POP_BLOCK
if (code[self.prev[loop_end_offset]] == self.opc.RETURN_VALUE or
(code[self.prev[loop_end_offset]] == self.opc.POP_BLOCK
and code[return_val_offset1] == self.opc.RETURN_VALUE)):
jump_back = None
if not jump_back:
jump_back_offset = None
if not jump_back_offset:
# loop suite ends in return
# scanner26 of wbiti had:
# jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False)
jump_back = self.last_instr(start, end, self.opc.RETURN_VALUE)
if not jump_back:
# jump_back_offset = self.last_instr(start, loop_end_offset, self.opc.JUMP_ABSOLUTE, start, False)
jump_back_offset = self.last_instr(start, loop_end_offset, self.opc.RETURN_VALUE)
if not jump_back_offset:
return
jump_back += 1
jump_back_offset += 1
if_offset = None
if self.version < 2.7:
@@ -569,71 +578,76 @@ class Scanner2(Scanner):
loop_type = 'while'
self.ignore_if.add(if_offset)
if self.version < 2.7 and (
code[self.prev[jump_back]] == self.opc.RETURN_VALUE):
self.ignore_if.add(self.prev[jump_back])
code[self.prev[jump_back_offset]] == self.opc.RETURN_VALUE):
self.ignore_if.add(self.prev[jump_back_offset])
pass
pass
else:
loop_type = 'for'
target = next_line_byte
end = jump_back + 3
setup_target = next_line_byte
loop_end_offset = jump_back_offset + 3
else:
if self.get_target(jump_back) >= next_line_byte:
jump_back = self.last_instr(start, end, self.opc.JUMP_ABSOLUTE, start, False)
if end > jump_back+4 and code[end] in self.jump_forward:
if code[jump_back+4] in self.jump_forward:
if self.get_target(jump_back+4) == self.get_target(end):
self.fixed_jumps[offset] = jump_back+4
end = jump_back+4
elif target < offset:
self.fixed_jumps[offset] = jump_back+4
end = jump_back+4
# We have a loop with a jump-back instruction
if self.get_target(jump_back_offset) >= next_line_byte:
jump_back_offset = self.last_instr(start, loop_end_offset, self.opc.JUMP_ABSOLUTE, start, False)
if loop_end_offset > jump_back_offset+4 and code[loop_end_offset] in self.jump_forward:
if code[jump_back_offset+4] in self.jump_forward:
if self.get_target(jump_back_offset+4) == self.get_target(loop_end_offset):
self.fixed_jumps[offset] = jump_back_offset+4
loop_end_offset = jump_back_offset+4
elif setup_target < offset:
self.fixed_jumps[offset] = jump_back_offset+4
loop_end_offset = jump_back_offset+4
target = self.get_target(jump_back, self.opc.JUMP_ABSOLUTE)
setup_target = self.get_target(jump_back_offset, self.opc.JUMP_ABSOLUTE)
if (self.version > 2.1 and
code[target] in (self.opc.FOR_ITER, self.opc.GET_ITER)):
code[setup_target] in (self.opc.FOR_ITER, self.opc.GET_ITER)):
loop_type = 'for'
else:
loop_type = 'while'
# Look for a test condition immediately after the
# SETUP_LOOP while
if (self.version < 2.7
and self.code[self.prev[next_line_byte]] == self.opc.POP_TOP):
test = self.prev[self.prev[next_line_byte]]
test_op_offset = self.prev[self.prev[next_line_byte]]
else:
test = self.prev[next_line_byte]
test_op_offset = self.prev[next_line_byte]
if test == offset:
if test_op_offset == offset:
loop_type = 'while 1'
elif self.code[test] in self.opc.JUMP_OPs:
self.ignore_if.add(test)
test_target = self.get_target(test)
if test_target > (jump_back+3):
jump_back = test_target
self.not_continue.add(jump_back)
self.loops.append(target)
elif self.code[test_op_offset] in self.opc.JUMP_OPs:
test_target = self.get_target(test_op_offset)
self.ignore_if.add(test_op_offset)
if test_target > (jump_back_offset+3):
jump_back_offset = test_target
self.not_continue.add(jump_back_offset)
self.loops.append(setup_target)
self.structs.append({'type': loop_type + '-loop',
'start': target,
'end': jump_back})
if jump_back+3 != end:
'start': setup_target,
'end': jump_back_offset})
if jump_back_offset+3 != loop_end_offset:
self.structs.append({'type': loop_type + '-else',
'start': jump_back+3,
'end': end})
'start': jump_back_offset+3,
'end': loop_end_offset})
elif op == self.opc.SETUP_EXCEPT:
start = offset + op_size(op, self.opc)
target = self.get_target(offset, op)
end = self.restrict_to_parent(target, parent)
if target != end:
self.fixed_jumps[offset] = end
end_offset = self.restrict_to_parent(target, parent)
if target != end_offset:
self.fixed_jumps[offset] = end_offset
# print target, end, parent
# Add the try block
self.structs.append({'type': 'try',
'start': start-3,
'end': end-4})
'end': end_offset-4})
# 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_offset])
end_finally_offset = end
end_finally_offset = end_offset
setup_except_nest = 0
while end_finally_offset < len(self.code):
if self.code[end_finally_offset] == self.opc.END_FINALLY:
@@ -647,7 +661,7 @@ class Scanner2(Scanner):
pass
# Add the except blocks
i = end
i = end_offset
while i < len(self.code) and i < end_finally_offset:
jmp = self.next_except_jump(i)
if jmp is None: # check
@@ -705,7 +719,7 @@ class Scanner2(Scanner):
test_target = target
if self.version < 2.7:
# Before 2.6 we have to deal with the fact that there is an extra
# Before 2.7 we have to deal with the fact that there is an extra
# POP_TOP that is logically associated with the JUMP_IF's (even though
# the instance set is called "self.pop_jump_if")
if code[pre[test_target]] == self.opc.POP_TOP:
@@ -715,22 +729,29 @@ class Scanner2(Scanner):
test_set = self.pop_jump_if_or_pop | self.pop_jump_if
if ( code[pre[test_target]] in test_set and target > offset ):
# We have POP_JUMP_IF... target
# ...
# pre: POP_JUMP_IF ...
# target: ...
#
# We will take that as either as "and" or "or".
self.fixed_jumps[offset] = pre[target]
self.structs.append({'type': 'and/or',
'start': start,
'end': pre[target]})
return
# The op offset just before the target jump offset is important
# The instruction offset just before the target jump offset is important
# in making a determination of what we have. Save that.
pre_rtarget = pre[rtarget]
# Is it an "and" inside an "if" or "while" block
if op == self.opc.PJIF:
# Search for other POP_JUMP_IF_FALSE targetting the same op,
# in current statement, starting from current offset, and filter
# everything inside inner 'or' jumps and midline ifs
# Search for other POP_JUMP_IF_...'s targetting the
# same target, of the current POP_JUMP_... instruction,
# starting from current offset, and filter everything inside inner 'or'
# jumps and mid-line ifs
match = self.rem_or(start, self.next_stmt[offset], self.opc.PJIF, target)
# If we still have any offsets in set, start working on it
@@ -829,7 +850,7 @@ class Scanner2(Scanner):
# JUMP_FORWARD
# HERE:
#
# If so, this can be block inside an "if" statement
# If so, this can be a block inside an "if" statement
# or a conditional assignment like:
# x = 1 if x else 2
#
@@ -860,7 +881,7 @@ class Scanner2(Scanner):
self.fixed_jumps[jump_if_offset] = jump_target
return
end = self.restrict_to_parent(if_end, parent)
end_offset = self.restrict_to_parent(if_end, parent)
if_then_maybe = None
@@ -899,7 +920,7 @@ class Scanner2(Scanner):
self.structs.append({'type': 'if-then',
'start': start-3,
'end': pre_rtarget})
self.thens[start] = end
self.thens[start] = end_offset
elif jump_op == 'JUMP_ABSOLUTE':
if_then_maybe = {'type': 'if-then',
'start': start-3,
@@ -914,7 +935,7 @@ class Scanner2(Scanner):
if pre_rtarget not in self.linestartoffsets or self.version < 2.7:
self.not_continue.add(pre_rtarget)
if rtarget < end:
if rtarget < end_offset:
# We have an "else" block of some kind.
# Is it associated with "if_then_maybe" seen above?
# These will be linked in this funny way:
@@ -930,15 +951,15 @@ class Scanner2(Scanner):
# 256
if if_then_maybe and jump_op == 'JUMP_ABSOLUTE':
jump_target = self.get_target(jump_inst, code[jump_inst])
if self.opname_for_offset(end) == 'JUMP_FORWARD':
end_target = self.get_target(end, code[end])
if self.opname_for_offset(end_offset) == 'JUMP_FORWARD':
end_target = self.get_target(end_offset, code[end_offset])
if jump_target == end_target:
self.structs.append(if_then_maybe)
self.thens[start] = end
self.thens[start] = end_offset
self.structs.append({'type': 'else',
'start': rtarget,
'end': end})
'end': end_offset})
elif code_pre_rtarget == self.opc.RETURN_VALUE:
if self.version == 2.7 or pre_rtarget not in self.ignore_if:
# 10 is exception-match. If there is an exception match in the

View File

@@ -110,7 +110,11 @@ class Scanner26(scan.Scanner2):
self.build_lines_data(co, codelen)
self.build_prev_op(codelen)
self.insts = list(bytecode)
self.offset2inst_index = {}
for i, inst in enumerate(self.insts):
self.offset2inst_index[inst.offset] = i
free, names, varnames = self.unmangle_code_names(co, classname)
self.names = names

View File

@@ -258,13 +258,13 @@ TABLE_DIRECT = {
'while1elsestmt': ( '%|while 1:\n%+%c%-%|else:\n%+%c%-\n\n', 1, -2 ),
'whileelsestmt': ( '%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n', 1, 2, -2 ),
'whileelselaststmt': ( '%|while %c:\n%+%c%-%|else:\n%+%c%-', 1, 2, -2 ),
'for': ( '%|for %c in %c:\n%+%c%-\n\n', 3, 1, 4 ),
'for': ( '%|for %c in %c:\n%+%c%-\n\n', (3, 'store'), 1, 4 ),
'forelsestmt': (
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, 1, 4, -2 ),
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', (3, 'store'), 1, 4, -2 ),
'forelselaststmt': (
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-', 3, 1, 4, -2 ),
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-', (3, 'store'), 1, 4, -2 ),
'forelselaststmtl': (
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, 1, 4, -2 ),
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', (3, 'store'), 1, 4, -2 ),
'try_except': ( '%|try:\n%+%c%-%c\n\n', 1, 3 ),
'tryelsestmt': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-\n\n', 1, 3, 4 ),
'tryelsestmtc': ( '%|try:\n%+%c%-%c%|else:\n%+%c%-', 1, 3, 4 ),

View File

@@ -1,5 +1,4 @@
# Copyright (c) 2015-2018 by Rocky Bernstein
# Copyright (c) 1999 John Aycock
"""
Creates Python source code from an uncompyle6 abstract syntax tree,
@@ -15,12 +14,13 @@ We add some format specifiers here not used in pysource
-----
%x takes an argument (src, (dest...)) and copies all of the range attributes
from src to dest.
from src to all nodes under dest.
For example in:
'import': ( '%|import %c%x\n', 2, (2,(0,1)), ),
node 2 range information, it in %c, is copied to nodes 0 and 1.
node 2 range information, it in %c, is copied to nodes 0 and 1. If
1. is a nonterminal, all the nodes under it get node2 range information.
2. %r
-----
@@ -43,7 +43,7 @@ do it recursively which is where offsets are probably located.
For example in:
'importmultiple': ( '%|import%b %c%c\n', 0, 2, 3 ),
n
The node position 0 will be associated with "import".
"""
@@ -99,17 +99,22 @@ TABLE_DIRECT_FRAGMENT = {
'importmultiple': ( '%|import%b %c%c\n', 0, 2, 3 ),
'list_for': (' for %c%x in %c%c', 2, (2, (1, )), 0, 3 ),
'forelsestmt': (
'%|for %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, (3, (2,)), 1, 4, -2),
'forelselaststmt': (
'%|for %c%x in %c:\n%+%c%-%|else:\n%+%c%-', 3, (3, (2,)), 1, 4, -2),
'forelselaststmtl': (
'%|for %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n', 3, (3, (2, )), 1, 4, -2),
'for':
('%|for%b %c%x in %c:\n%+%c%-\n\n',
0, (3, 'store'), (3, (2,)), 1, 4 ),
'forelsestmt':
('%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
0, (3, 'store'), (3, (2,)), 1, 4, -2),
'forelselaststmt':
('%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-',
0, (3, 'store'), (3, (2,)), 1, 4, -2),
'forelselaststmtl':
('%|for%b %c%x in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
0, (3, 'store'), (3, (2,)), 1, 4, -2),
'whilestmt': ( '%|while%b %c:\n%+%c%-\n\n', 0, 1, 2 ),
'whileelsestmt': ( '%|while%b %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 2, -2 ),
'whileelselaststmt': ( '%|while%b %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 2, -2 ),
'for': ( '%|for%b %c in %c:\n%+%c%-\n\n', 0, 3, 1, 4 ),
}
@@ -207,12 +212,55 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.default(node)
def n_try_except(self, node):
# Note: we could also do this via modifying the
# 5 or so template rules. That is change:
# 'try_except': ( '%|try%:\n%+%c%-%c\n\n', 1, 3 ),
# to:
# 'try_except': ( '%|try%b:\n%+%c%-%c\n\n', 0, 1, 3 ),
start = len(self.f.getvalue()) + len(self.indent)
self.set_pos_info(node[0], start, start+len("try:"))
self.default(node)
n_tryelsestmt = n_tryelsestmtc = n_tryelsestmtl = n_tryfinallystmt = n_try_except
def n_raise_stmt0(self, node):
assert node[0] == 'RAISE_VARARGS_0'
start = len(self.f.getvalue()) + len(self.indent)
try:
self.default(node)
except GenericASTTraversalPruningException:
self.set_pos_info(node[0], start, len(self.f.getvalue()))
self.prune()
def n_raise_stmt1(self, node):
assert node[1] == 'RAISE_VARARGS_1'
start = len(self.f.getvalue()) + len(self.indent)
try:
self.default(node)
except GenericASTTraversalPruningException:
self.set_pos_info(node[1], start, len(self.f.getvalue()))
self.prune()
def n_raise_stmt2(self, node):
assert node[2] == 'RAISE_VARARGS_2'
start = len(self.f.getvalue()) + len(self.indent)
try:
self.default(node)
except GenericASTTraversalPruningException:
self.set_pos_info(node[2], start, len(self.f.getvalue()))
self.prune()
# FIXME: Isolate: only in Python 2.x.
def n_raise_stmt3(self, node):
assert node[3] == 'RAISE_VARARGS_3'
start = len(self.f.getvalue()) + len(self.indent)
try:
self.default(node)
except GenericASTTraversalPruningException:
self.set_pos_info(node[3], start, len(self.f.getvalue()))
self.prune()
def n_return(self, node):
start = len(self.f.getvalue()) + len(self.indent)
if self.params['is_lambda']:
@@ -224,7 +272,8 @@ class FragmentsWalker(pysource.SourceWalker, object):
else:
start = len(self.f.getvalue()) + len(self.indent)
self.write(self.indent, 'return')
if self.return_none or node != AST('return', [AST('ret_expr', [NONE]), Token('RETURN_VALUE')]):
if self.return_none or node != AST('return', [AST('ret_expr', [NONE]),
Token('RETURN_VALUE')]):
self.write(' ')
self.last_finish = len(self.f.getvalue())
self.preorder(node[0])
@@ -339,7 +388,8 @@ class FragmentsWalker(pysource.SourceWalker, object):
def n_binary_expr(self, node):
start = len(self.f.getvalue())
node[0].parent = node
for n in node:
n.parent = node
self.last_finish = len(self.f.getvalue())
try:
super(FragmentsWalker, self).n_binary_expr(node)
@@ -602,13 +652,17 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.prec = 27
code = node[code_index].attr
assert iscode(code)
assert iscode(code), node[code_index]
code_name = code.co_name
code = Code(code, self.scanner, self.currentclass)
ast = self.build_ast(code._tokens, code._customize)
self.customize(code._customize)
# skip over stmts sstmt smt
if ast[0] == 'sstmt':
ast = ast[0]
# skip over stmt return ret_expr
ast = ast[0][0][0]
store = None
if ast in ['setcomp_func', 'dictcomp_func']:
@@ -626,11 +680,12 @@ class FragmentsWalker(pysource.SourceWalker, object):
else:
ast = ast[0][0]
n = ast[iter_index]
assert n == 'list_iter'
assert n == 'list_iter', n
# FIXME: I'm not totally sure this is right.
# find innermost node
# Find the list comprehension body. It is the inner-most
# node that is not list_.. .
if_node = None
comp_for = None
comp_store = None
@@ -640,12 +695,12 @@ class FragmentsWalker(pysource.SourceWalker, object):
have_not = False
while n in ('list_iter', 'comp_iter'):
n = n[0] # recurse one step
if n == 'list_for':
n = n[0] # iterate one nesting deeper
if n in ('list_for', 'comp_for'):
if n[2] == 'store':
store = n[2]
n = n[3]
elif n in ['list_if', 'list_if_not', 'comp_if']:
elif n in ('list_if', 'list_if_not', 'comp_if', 'comp_ifnot'):
have_not = n in ('list_if_not', 'comp_ifnot')
if_node = n[0]
if n[1] == 'store':
@@ -661,12 +716,29 @@ class FragmentsWalker(pysource.SourceWalker, object):
old_name = self.name
self.name = code_name
# Issue created with later Python code generation is that there
# is a lamda set up with a dummy argument name that is then called
# So we can't just translate that as is but need to replace the
# dummy name. Below we are picking out the variable name as seen
# in the code. And trying to generate code for the other parts
# that don't have the dummy argument name in it.
# Another approach might be to be able to pass in the source name
# for the dummy argument.
self.preorder(n[0])
gen_start = len(self.f.getvalue()) + 1
self.write(' for ')
start = len(self.f.getvalue())
self.preorder(store)
if comp_store:
self.preorder(comp_store)
else:
self.preorder(store)
self.set_pos_info(store, start, len(self.f.getvalue()))
# FIXME this is all merely approximate
# from trepan.api import debug; debug()
self.write(' in ')
start = len(self.f.getvalue())
node[-3].parent = node
@@ -674,6 +746,15 @@ class FragmentsWalker(pysource.SourceWalker, object):
fin = len(self.f.getvalue())
self.set_pos_info(node[-3], start, fin, old_name)
if ast == 'list_comp':
list_iter = ast[1]
assert list_iter == 'list_iter'
if list_iter == 'list_for':
self.preorder(list_iter[3])
self.prec = p
return
pass
if comp_store:
self.preorder(comp_for)
elif if_node:
@@ -681,6 +762,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
if have_not:
self.write('not ')
self.preorder(if_node)
pass
self.prec = p
self.name = old_name
if node[-1].kind.startswith('CALL_FUNCTION'):
@@ -1530,17 +1612,6 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.preorder(node[index])
finish = len(self.f.getvalue())
# FIXME rocky: figure out how to get this to be table driven
# for loops have two positions that correspond to a single text
# location. In "for i in ..." there is the initialization "i" code as well
# as the iteration code with "i"
match = re.search(r'^for', startnode.kind)
if match and entry[arg] == 3:
self.set_pos_info(node[0], start, finish)
for n in node[2]:
self.set_pos_info(n, start, finish)
self.set_pos_info(node, start, finish)
arg += 1
elif typ == 'p':
@@ -1578,18 +1649,9 @@ class FragmentsWalker(pysource.SourceWalker, object):
pass
arg += 1
elif typ == 'x':
assert isinstance(entry[arg], tuple)
src, dest = entry[arg]
for d in dest:
if hasattr(node[d], 'offset'):
self.set_pos_info(node[d], node[src].start, node[src].finish)
else:
for n in node[d]:
if hasattr(n, 'offset'):
self.set_pos_info(n, node[src].start, node[src].finish)
pass
pass
pass
self.set_pos_info_recurse(node[d], node[src].start, node[src].finish)
pass
arg += 1
elif typ == 'P':
@@ -1762,91 +1824,93 @@ def deparse_code_around_offset(name, offset, version, co, out=StringIO(),
return deparsed
if __name__ == '__main__':
# if __name__ == '__main__':
from uncompyle6 import IS_PYPY
def deparse_test(co, is_pypy=IS_PYPY):
sys_version = sys.version_info.major + (sys.version_info.minor / 10.0)
walk = deparse_code(sys_version, co, showasm=False, showast=False,
showgrammar=False, is_pypy=IS_PYPY)
print("deparsed source")
print(walk.text, "\n")
print('------------------------')
for name, offset in sorted(walk.offsets.keys(),
key=lambda x: str(x[0])):
print("name %s, offset %s" % (name, offset))
nodeInfo = walk.offsets[name, offset]
node = nodeInfo.node
extractInfo = walk.extract_node_info(node)
print("code: %s" % node.kind)
# print extractInfo
print(extractInfo.selectedText)
print(extractInfo.selectedLine)
print(extractInfo.markerLine)
extractInfo, p = walk.extract_parent_info(node)
if extractInfo:
print("Contained in...")
print(extractInfo.selectedLine)
print(extractInfo.markerLine)
print("code: %s" % p.kind)
print('=' * 40)
pass
pass
return
# from uncompyle6 import IS_PYPY
# def deparse_test(co, is_pypy=IS_PYPY):
# from xdis.magics import sysinfo2float
# float_version = sysinfo2float()
# walk = deparse_code(float_version, co, showasm=False, showast=False,
# showgrammar=False, is_pypy=IS_PYPY)
# print("deparsed source")
# print(walk.text, "\n")
# print('------------------------')
# for name, offset in sorted(walk.offsets.keys(),
# key=lambda x: str(x[0])):
# print("name %s, offset %s" % (name, offset))
# nodeInfo = walk.offsets[name, offset]
# node = nodeInfo.node
# extractInfo = walk.extract_node_info(node)
# print("code: %s" % node.kind)
# # print extractInfo
# print(extractInfo.selectedText)
# print(extractInfo.selectedLine)
# print(extractInfo.markerLine)
# extractInfo, p = walk.extract_parent_info(node)
def deparse_test_around(offset, name, co, is_pypy=IS_PYPY):
sys_version = sys.version_info[0] + (sys.version_info[1] / 10.0)
walk = deparse_code_around_offset(name, offset, sys_version, co, showasm=False, showast=False,
showgrammar=False, is_pypy=IS_PYPY)
print("deparsed source")
print(walk.text, "\n")
print('------------------------')
for name, offset in sorted(walk.offsets.keys(),
key=lambda x: str(x[0])):
print("name %s, offset %s" % (name, offset))
nodeInfo = walk.offsets[name, offset]
node = nodeInfo.node
extractInfo = walk.extract_node_info(node)
print("code: %s" % node.kind)
# print extractInfo
print(extractInfo.selectedText)
print(extractInfo.selectedLine)
print(extractInfo.markerLine)
extractInfo, p = walk.extract_parent_info(node)
if extractInfo:
print("Contained in...")
print(extractInfo.selectedLine)
print(extractInfo.markerLine)
print("code: %s" % p.kind)
print('=' * 40)
pass
pass
return
# if extractInfo:
# print("Contained in...")
# print(extractInfo.selectedLine)
# print(extractInfo.markerLine)
# print("code: %s" % p.kind)
# print('=' * 40)
# pass
# pass
# return
def get_code_for_fn(fn):
if hasattr(fn, 'func_code'):
return fn.func_code
return fn.__code__
# def deparse_test_around(offset, name, co, is_pypy=IS_PYPY):
# sys_version = sys.version_info[0] + (sys.version_info[1] / 10.0)
# walk = deparse_code_around_offset(name, offset, sys_version, co, showasm=False, showast=False,
# showgrammar=False, is_pypy=IS_PYPY)
# print("deparsed source")
# print(walk.text, "\n")
# print('------------------------')
# for name, offset in sorted(walk.offsets.keys(),
# key=lambda x: str(x[0])):
# print("name %s, offset %s" % (name, offset))
# nodeInfo = walk.offsets[name, offset]
# node = nodeInfo.node
# extractInfo = walk.extract_node_info(node)
# print("code: %s" % node.kind)
# # print extractInfo
# print(extractInfo.selectedText)
# print(extractInfo.selectedLine)
# print(extractInfo.markerLine)
# extractInfo, p = walk.extract_parent_info(node)
# if extractInfo:
# print("Contained in...")
# print(extractInfo.selectedLine)
# print(extractInfo.markerLine)
# print("code: %s" % p.kind)
# print('=' * 40)
# pass
# pass
# return
def test():
import os, sys
# def get_code_for_fn(fn):
# if hasattr(fn, 'func_code'):
# return fn.func_code
# return fn.__code__
def gcd(a, b):
if a > b:
(a, b) = (b, a)
pass
# def test():
# import os, sys
if a <= 0:
return None
if a == 1 or a == b:
return a
return gcd(b-a, a)
# def gcd(a, b):
# if a > b:
# (a, b) = (b, a)
# pass
# check_args(['3', '5'])
# deparse_test(get_code_for_fn(gcd))
# deparse_test(get_code_for_fn(test))
# deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets))
# deparse_test(get_code_for_fn(FragmentsWalker.n_list))
print('=' * 30)
deparse_test_around(408, 'n_list', get_code_for_fn(FragmentsWalker.n_list))
# deparse_test(inspect.currentframe().f_code)
# if a <= 0:
# return None
# if a == 1 or a == b:
# return a
# return gcd(b-a, a)
# # check_args(['3', '5'])
# # deparse_test(get_code_for_fn(gcd))
# # deparse_test(get_code_for_fn(test))
# # deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets))
# # deparse_test(get_code_for_fn(FragmentsWalker.n_list))
# print('=' * 30)
# deparse_test_around(408, 'n_list', get_code_for_fn(FragmentsWalker.n_list))
# # deparse_test(inspect.currentframe().f_code)

View File

@@ -1529,7 +1529,8 @@ class SourceWalker(GenericASTTraversal, object):
ast = self.build_ast(code._tokens, code._customize)
self.customize(code._customize)
# skip over stmts sstmt smt
# skip over stmt return ret_expr
ast = ast[0][0][0]
store = None
if ast in ['setcomp_func', 'dictcomp_func']:

View File

@@ -1,3 +1,3 @@
# This file is suitable for sourcing inside bash as
# well as importing into Python
VERSION='2.15.0'
VERSION='2.15.1'