You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 16:59:52 +08:00
Compare commits
19 Commits
release-py
...
release-py
Author | SHA1 | Date | |
---|---|---|---|
|
9d425039a2 | ||
|
a5c388c13b | ||
|
c82095e6ac | ||
|
67ad08fd4a | ||
|
fa4f614295 | ||
|
832f04a486 | ||
|
f7f0aa5ea9 | ||
|
083ae5f3fd | ||
|
657d5ef024 | ||
|
e92c2503d1 | ||
|
a01091a46e | ||
|
b5a825f4d8 | ||
|
730b0549d5 | ||
|
e431e49d77 | ||
|
230a38d537 | ||
|
6d29ed9077 | ||
|
bb45be2dc7 | ||
|
f7999d2754 | ||
|
5aeb0424fc |
16
NEWS
16
NEWS
@@ -1,3 +1,19 @@
|
||||
uncompyle6 2.16.0 2018-02-17
|
||||
|
||||
- API additions:
|
||||
- add fragments.op_at_code_loc() and
|
||||
- fragments.deparsed_find()_
|
||||
- Better 2.7 end_if and COME_FROM determination
|
||||
- Fix up 3.6+ CALL_FUNCTION_EX
|
||||
- Misc pydisasm fixes
|
||||
- Wierd comprehension bug seen via new loctraceback
|
||||
- Fix Python 3.5+ CALL_FUNCTION_VAR and BUILD_LIST_UNPACK in call; with this
|
||||
we can can handle 3.5+ f(a, b, *c, *d, *e) now
|
||||
|
||||
uncompyle6 2.15.1 2018-02-05
|
||||
|
||||
- More bug fixes and revert an improper bug fix in 2.15.0
|
||||
|
||||
uncompyle6 2.15.0 2018-02-05 pycon2018.co
|
||||
|
||||
- Bug fixes
|
||||
|
@@ -45,6 +45,8 @@
|
||||
|
||||
$ source admin-tools/setup-python-2.4.sh
|
||||
$ git merge master
|
||||
# Add and fix merge conflicts
|
||||
$ git commit
|
||||
|
||||
# Check against older versions
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import pytest
|
||||
from uncompyle6.semantics.fragments import deparse_code as deparse
|
||||
from uncompyle6.semantics.fragments import deparse_code as deparse, deparsed_find
|
||||
from uncompyle6 import PYTHON_VERSION, PYTHON3
|
||||
|
||||
def map_stmts(x, y):
|
||||
@@ -42,6 +42,7 @@ def check_expect(expect, parsed, fn_name):
|
||||
"%s: ran out if items in testing node" % fn_name)
|
||||
nodeInfo = parsed.offsets[name, offset]
|
||||
node = nodeInfo.node
|
||||
nodeInfo2 = deparsed_find((name, offset), parsed, code)
|
||||
extractInfo = parsed.extract_node_info(node)
|
||||
|
||||
assert expect[i] == extractInfo.selectedLine, \
|
||||
|
BIN
test/bytecode_2.7/03_if_vs_and.pyc
Normal file
BIN
test/bytecode_2.7/03_if_vs_and.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/03_double_star_unpack.pyc
Normal file
BIN
test/bytecode_3.5/03_double_star_unpack.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.6/03_double_star_unpack.pyc
Normal file
BIN
test/bytecode_3.6/03_double_star_unpack.pyc
Normal file
Binary file not shown.
0
test/bytecode_3.6_run/.gitignore
vendored
Normal file
0
test/bytecode_3.6_run/.gitignore
vendored
Normal file
@@ -1,9 +1,16 @@
|
||||
# Bug in Python 3.5 is getting the two star'd arguments right.
|
||||
def sum(a,b,c,d):
|
||||
# Bug in 3.5 is detecting when there is more than
|
||||
# one * in a call. There is a "BUILD_UNPACK_LIST" instruction used
|
||||
# to unpack star arguments
|
||||
def sum(a, b, c, d):
|
||||
return a + b + c + d
|
||||
|
||||
args=(1,2)
|
||||
args, a, b, c = (1, 2), 1, 2, 3
|
||||
sum(*args, *args)
|
||||
sum(*args, *args, *args)
|
||||
|
||||
sum(a, *args, *args)
|
||||
sum(a, b, *args)
|
||||
sum(a, b, *args, *args)
|
||||
|
||||
# FIXME: this is handled incorrectly
|
||||
# (*c,) = (3,4)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# Mode: -*- python -*-
|
||||
#
|
||||
# Copyright (c) 2015-2016 by Rocky Bernstein <rb@dustyfeet.com>
|
||||
# Copyright (c) 2015-2016, 2018 by Rocky Bernstein <rb@dustyfeet.com>
|
||||
#
|
||||
import sys, os, getopt
|
||||
|
||||
@@ -15,17 +15,24 @@ Usage:
|
||||
%s [OPTIONS]... FILE
|
||||
%s [--help | -h | -V | --version]
|
||||
|
||||
Disassemble FILE with the instruction mangling that is done to
|
||||
assist uncompyle6 in parsing the instruction stream. For example
|
||||
instructions with variable-length arguments like CALL_FUNCTION and
|
||||
BUILD_LIST have arguement counts appended to the instruction name, and
|
||||
COME_FROM instructions are inserted into the instruction stream.
|
||||
|
||||
Examples:
|
||||
{0} foo.pyc
|
||||
{0} foo.py # same thing as above but find the file
|
||||
{0} foo.pyc bar.pyc # disassemble foo.pyc and bar.pyc
|
||||
%s foo.pyc
|
||||
%s foo.py # same thing as above but find the file
|
||||
%s foo.pyc bar.pyc # disassemble foo.pyc and bar.pyc
|
||||
|
||||
See also `pydisasm' from the `xdis' package.
|
||||
|
||||
Options:
|
||||
-U | --uncompyle6 show instructions with uncompyle6 mangling
|
||||
-V | --version show version and stop
|
||||
-h | --help show this message
|
||||
|
||||
""" % (program, program)
|
||||
""" % ((program,) * 5)
|
||||
|
||||
PATTERNS = ('*.pyc', '*.pyo')
|
||||
|
||||
@@ -33,8 +40,6 @@ def main():
|
||||
Usage_short = """usage: %s FILE...
|
||||
Type -h for for full help.""" % program
|
||||
|
||||
native = True
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
sys.stderr.write("No file(s) given\n")
|
||||
sys.stderr.write(Usage_short)
|
||||
@@ -54,8 +59,6 @@ Type -h for for full help.""" % program
|
||||
elif opt in ('-V', '--version'):
|
||||
print("%s %s" % (program, VERSION))
|
||||
sys.exit(0)
|
||||
elif opt in ('-U', '--uncompyle6'):
|
||||
native = False
|
||||
else:
|
||||
print(opt)
|
||||
sys.stderr.write(Usage_short)
|
||||
@@ -63,7 +66,7 @@ Type -h for for full help.""" % program
|
||||
|
||||
for file in files:
|
||||
if os.path.exists(files[0]):
|
||||
disassemble_file(file, sys.stdout, native)
|
||||
disassemble_file(file, sys.stdout)
|
||||
else:
|
||||
sys.stderr.write("Can't read %s - skipping\n" % files[0])
|
||||
pass
|
||||
|
@@ -130,6 +130,11 @@ class Python27Parser(Python2Parser):
|
||||
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
else_suitel COME_FROM
|
||||
|
||||
return_stmts ::= _stmts return_stmt
|
||||
return_stmts ::= return_stmt
|
||||
return_stmt ::= return
|
||||
|
||||
ifstmt ::= testexpr return_stmts COME_FROM
|
||||
ifstmt ::= testexpr return_if_stmts COME_FROM
|
||||
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite COME_FROM
|
||||
ifelsestmtc ::= testexpr c_stmts_opt JUMP_ABSOLUTE else_suitec
|
||||
|
@@ -484,33 +484,15 @@ class Python3Parser(PythonParser):
|
||||
|
||||
token.kind = self.call_fn_name(token)
|
||||
uniq_param = args_kw + args_pos
|
||||
if self.version == 3.5 and opname.startswith('CALL_FUNCTION_VAR'):
|
||||
# Python 3.5 changes the stack position of *args. KW args come
|
||||
# after *args.
|
||||
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
|
||||
if opname.endswith('KW'):
|
||||
kw = 'expr '
|
||||
else:
|
||||
kw = ''
|
||||
rule = ('call ::= expr expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) + kw + token.kind)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
|
||||
# Note: 3.5+ have subclassed this method; so we don't handle
|
||||
# 'CALL_FUNCTION_VAR' or 'CALL_FUNCTION_EX' here.
|
||||
rule = ('call ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.kind)
|
||||
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
if self.version >= 3.5 and seen_GET_AWAITABLE_YIELD_FROM:
|
||||
rule = ('async_call ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.kind +
|
||||
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
|
||||
'expr ' * nak + token.kind)
|
||||
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
|
||||
if possible_class_decorator:
|
||||
if next_token == 'CALL_FUNCTION' and next_token.attr == 1:
|
||||
@@ -671,6 +653,11 @@ class Python3Parser(PythonParser):
|
||||
rule = ('build_map_unpack_with_call ::= ' + 'expr1024 ' * int(v//1024) +
|
||||
'expr32 ' * int((v//32) % 32) +
|
||||
'expr ' * (v % 32) + opname)
|
||||
self.addRule(rule, nop_func)
|
||||
elif opname.startswith('BUILD_TUPLE_UNPACK_WITH_CALL'):
|
||||
v = token.attr
|
||||
rule = ('starred ::= %s %s' % ('expr ' * v, opname))
|
||||
self.addRule(rule, nop_func)
|
||||
elif opname_base in ('BUILD_LIST', 'BUILD_SET', 'BUILD_TUPLE'):
|
||||
v = token.attr
|
||||
|
||||
|
@@ -186,6 +186,52 @@ class Python35Parser(Python34Parser):
|
||||
pass
|
||||
return
|
||||
|
||||
def custom_classfunc_rule(self, opname, token, customize,
|
||||
seen_LOAD_BUILD_CLASS,
|
||||
seen_GET_AWAITABLE_YIELD_FROM,
|
||||
*args):
|
||||
args_pos, args_kw = self.get_pos_kw(token)
|
||||
|
||||
# Additional exprs for * and ** args:
|
||||
# 0 if neither
|
||||
# 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
|
||||
# 2 for * and ** args (CALL_FUNCTION_VAR_KW).
|
||||
# Yes, this computation based on instruction name is a little bit hoaky.
|
||||
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
|
||||
uniq_param = args_kw + args_pos
|
||||
|
||||
if seen_GET_AWAITABLE_YIELD_FROM:
|
||||
rule = ('async_call ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.kind +
|
||||
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
|
||||
|
||||
uniq_param = args_kw + args_pos
|
||||
if opname.startswith('CALL_FUNCTION_VAR'):
|
||||
# Python 3.5 changes the stack position of *args. KW args come
|
||||
# after *args.
|
||||
|
||||
# Note: Python 3.6+ replaces CALL_FUNCTION_VAR and
|
||||
# CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
|
||||
|
||||
token.kind = self.call_fn_name(token)
|
||||
if opname.endswith('KW'):
|
||||
kw = 'expr '
|
||||
else:
|
||||
kw = ''
|
||||
rule = ('call ::= expr expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) + kw + token.kind)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
else:
|
||||
super(Python35Parser, self).custom_classfunc_rule(opname, token, customize,
|
||||
seen_LOAD_BUILD_CLASS,
|
||||
seen_GET_AWAITABLE_YIELD_FROM,
|
||||
*args)
|
||||
|
||||
class Python35ParserSingle(Python35Parser, PythonParserSingle):
|
||||
pass
|
||||
|
||||
|
@@ -131,6 +131,26 @@ class Python36Parser(Python35Parser):
|
||||
def custom_classfunc_rule(self, opname, token, customize,
|
||||
possible_class_decorator,
|
||||
seen_GET_AWAITABLE_YIELD_FROM, next_token):
|
||||
|
||||
args_pos, args_kw = self.get_pos_kw(token)
|
||||
|
||||
# Additional exprs for * and ** args:
|
||||
# 0 if neither
|
||||
# 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
|
||||
# 2 for * and ** args (CALL_FUNCTION_VAR_KW).
|
||||
# Yes, this computation based on instruction name is a little bit hoaky.
|
||||
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
|
||||
uniq_param = args_kw + args_pos
|
||||
|
||||
if seen_GET_AWAITABLE_YIELD_FROM:
|
||||
rule = ('async_call ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.kind +
|
||||
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
|
||||
|
||||
if opname.startswith('CALL_FUNCTION_KW'):
|
||||
self.addRule("expr ::= call_kw", nop_func)
|
||||
values = 'expr ' * token.attr
|
||||
|
@@ -962,17 +962,32 @@ class Scanner2(Scanner):
|
||||
'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
|
||||
# compare, then this is an exception clause not an if-then clause
|
||||
# Below, 10 is exception-match. If there is an exception
|
||||
# match in the compare, then this is an exception
|
||||
# clause not an if-then clause
|
||||
if (self.code[self.prev[offset]] != self.opc.COMPARE_OP or
|
||||
self.code[self.prev[offset]+1] != 10):
|
||||
self.structs.append({'type': 'if-then',
|
||||
'start': start,
|
||||
'end': rtarget})
|
||||
self.thens[start] = rtarget
|
||||
if self.version == 2.7 or code[pre_rtarget+1] != self.opc.JUMP_FORWARD:
|
||||
if (self.version == 2.7 or
|
||||
code[pre_rtarget+1] != self.opc.JUMP_FORWARD):
|
||||
# The below is a big hack until we get
|
||||
# better control flow analysis: disallow
|
||||
# END_IF if the instruction before the
|
||||
# END_IF instruction happens to be a jump
|
||||
# target. In this case, probably what's
|
||||
# gone on is that we messed up on the
|
||||
# END_IF location and it should be the
|
||||
# instruction before.
|
||||
self.fixed_jumps[offset] = rtarget
|
||||
self.return_end_ifs.add(pre_rtarget)
|
||||
if (self.version == 2.7 and
|
||||
self.insts[self.offset2inst_index[pre[pre_rtarget]]].is_jump_target):
|
||||
self.return_end_ifs.add(pre[pre_rtarget])
|
||||
pass
|
||||
else:
|
||||
self.return_end_ifs.add(pre_rtarget)
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
|
@@ -33,6 +33,8 @@ class Scanner36(Scanner3):
|
||||
t.kind = 'CALL_FUNCTION_KW_%s' % t.attr
|
||||
elif t.op == self.opc.BUILD_MAP_UNPACK_WITH_CALL:
|
||||
t.kind = 'BUILD_MAP_UNPACK_WITH_CALL_%d' % t.attr
|
||||
elif t.op == self.opc.BUILD_TUPLE_UNPACK_WITH_CALL:
|
||||
t.kind = 'BUILD_TUPLE_UNPACK_WITH_CALL_%d' % t.attr
|
||||
pass
|
||||
return tokens, customize
|
||||
|
||||
|
@@ -43,9 +43,9 @@ def customize_for_version(self, is_pypy, version):
|
||||
if version < 3.0:
|
||||
TABLE_R.update({
|
||||
'STORE_SLICE+0': ( '%c[:]', 0 ),
|
||||
'STORE_SLICE+1': ( '%c[%p:]', 0, (1, 100) ),
|
||||
'STORE_SLICE+2': ( '%c[:%p]', 0, (1, 100) ),
|
||||
'STORE_SLICE+3': ( '%c[%p:%p]', 0, (1, 100), (2, 100) ),
|
||||
'STORE_SLICE+1': ( '%c[%p:]', 0, (1, -1) ),
|
||||
'STORE_SLICE+2': ( '%c[:%p]', 0, (1, -1) ),
|
||||
'STORE_SLICE+3': ( '%c[%p:%p]', 0, (1, -1), (2, -1) ),
|
||||
'DELETE_SLICE+0': ( '%|del %c[:]\n', 0 ),
|
||||
'DELETE_SLICE+1': ( '%|del %c[%c:]\n', 0, 1 ),
|
||||
'DELETE_SLICE+2': ( '%|del %c[:%c]\n', 0, 1 ),
|
||||
@@ -251,8 +251,8 @@ def customize_for_version(self, is_pypy, version):
|
||||
node.kind == 'call'
|
||||
p = self.prec
|
||||
self.prec = 80
|
||||
self.template_engine(('%c(%P)', 0,
|
||||
(1, -4, ', ', 100)), node)
|
||||
self.template_engine(('%c(%P)', 0, (1, -4, ', ',
|
||||
100)), node)
|
||||
self.prec = p
|
||||
node.kind == 'async_call'
|
||||
self.prune()
|
||||
@@ -270,7 +270,7 @@ def customize_for_version(self, is_pypy, version):
|
||||
if key.kind.startswith('CALL_FUNCTION_VAR_KW'):
|
||||
# Python 3.5 changes the stack position of *args. kwargs come
|
||||
# after *args whereas in earlier Pythons, *args is at the end
|
||||
# which simpilfiies things from our perspective.
|
||||
# which simplifies things from our perspective.
|
||||
# Python 3.6+ replaces CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
|
||||
# We will just swap the order to make it look like earlier Python 3.
|
||||
entry = table[key.kind]
|
||||
@@ -282,6 +282,27 @@ def customize_for_version(self, is_pypy, version):
|
||||
node[kwarg_pos], node[args_pos] = node[args_pos], node[kwarg_pos]
|
||||
args_pos = kwarg_pos
|
||||
kwarg_pos += 1
|
||||
elif key.kind.startswith('CALL_FUNCTION_VAR'):
|
||||
nargs = node[-1].attr & 0xFF
|
||||
if nargs > 0:
|
||||
template = ('%c(%C, ', 0, (1, nargs+1, ', '))
|
||||
else:
|
||||
template = ('%c(', 0)
|
||||
self.template_engine(template, node)
|
||||
|
||||
args_node = node[-2]
|
||||
if args_node == 'pos_arg':
|
||||
args_node = args_node[0]
|
||||
if args_node == 'expr':
|
||||
args_node = args_node[0]
|
||||
if args_node == 'build_list_unpack':
|
||||
template = ('*%P)', (0, len(args_node)-1, ', *', 100))
|
||||
self.template_engine(template, args_node)
|
||||
else:
|
||||
template = ('*%c)', -2)
|
||||
self.template_engine(template, node)
|
||||
self.prune()
|
||||
|
||||
self.default(node)
|
||||
self.n_call = n_call
|
||||
|
||||
@@ -326,7 +347,6 @@ def customize_for_version(self, is_pypy, version):
|
||||
'func_args36': ( "%c(**", 0),
|
||||
'try_except36': ( '%|try:\n%+%c%-%c\n\n', 1, 2 ),
|
||||
'unpack_list': ( '*%c', (0, 'list') ),
|
||||
'starred': ( '*%c', (0, 'expr') ),
|
||||
'call_ex' : (
|
||||
'%c(%c)',
|
||||
(0, 'expr'), 1),
|
||||
@@ -577,6 +597,27 @@ def customize_for_version(self, is_pypy, version):
|
||||
return
|
||||
self.n_kwargs_36 = kwargs_36
|
||||
|
||||
def starred(node):
|
||||
l = len(node)
|
||||
assert l > 0
|
||||
pos_args = node[0]
|
||||
if pos_args == 'expr':
|
||||
pos_args = pos_args[0]
|
||||
if pos_args == 'tuple':
|
||||
star_start = 1
|
||||
template = '%C', (0, -1, ', ')
|
||||
self.template_engine(template, pos_args)
|
||||
self.write(', ')
|
||||
else:
|
||||
star_start = 0
|
||||
if l > 1:
|
||||
template = ( '*%C', (star_start, -1, ', *') )
|
||||
else:
|
||||
template = ( '*%c', (star_start, 'expr') )
|
||||
self.template_engine(template, node)
|
||||
self.prune()
|
||||
|
||||
self.n_starred = starred
|
||||
|
||||
def return_closure(node):
|
||||
# Nothing should be output here
|
||||
|
@@ -769,7 +769,8 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.set_pos_info(node[-1], gen_start, len(self.f.getvalue()))
|
||||
|
||||
def listcomprehension_walk2(self, node):
|
||||
"""List comprehensions the way they are done in Python3.
|
||||
"""List comprehensions the way they are done in Python 2 (and
|
||||
some Python 3?).
|
||||
They're more other comprehensions, e.g. set comprehensions
|
||||
See if we can combine code.
|
||||
"""
|
||||
@@ -779,17 +780,24 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
code = Code(node[1].attr, self.scanner, self.currentclass)
|
||||
ast = self.build_ast(code._tokens, code._customize)
|
||||
self.customize(code._customize)
|
||||
ast = ast[0][0][0][0][0]
|
||||
if node == 'set_comp':
|
||||
ast = ast[0][0][0]
|
||||
else:
|
||||
ast = ast[0][0][0][0][0]
|
||||
|
||||
if ast == 'expr':
|
||||
ast = ast[0]
|
||||
|
||||
n = ast[1]
|
||||
collection = node[-3]
|
||||
list_if = None
|
||||
assert n == 'list_iter'
|
||||
|
||||
# find innermost node
|
||||
# Find the list comprehension body. It is the inner-most
|
||||
# node that is not list_.. .
|
||||
while n == 'list_iter':
|
||||
n = n[0] # recurse one step
|
||||
if n == 'list_for':
|
||||
if n == 'list_for':
|
||||
store = n[2]
|
||||
n = n[3]
|
||||
elif n in ('list_if', 'list_if_not'):
|
||||
@@ -1824,29 +1832,66 @@ def deparse_code_around_offset(name, offset, version, co, out=StringIO(),
|
||||
return deparsed
|
||||
|
||||
|
||||
def op_at_code_loc(code, loc, opc):
|
||||
"""Return the instruction name at code[loc] using
|
||||
opc to look up instruction names. Returns 'got IndexError'
|
||||
if code[loc] is invalid.
|
||||
|
||||
`code` is instruction bytecode, `loc` is an offset (integer) and
|
||||
`opc` is an opcode module from `xdis`.
|
||||
"""
|
||||
try:
|
||||
op = code[loc]
|
||||
except IndexError:
|
||||
return 'got IndexError'
|
||||
return opc.opname[op]
|
||||
|
||||
def deparsed_find(tup, deparsed, code):
|
||||
"""Return a NodeInfo nametuple for a fragment-deparsed `deparsed` at `tup`.
|
||||
|
||||
`tup` is a name and offset tuple, `deparsed` is a fragment object
|
||||
and `code` is instruction bytecode.
|
||||
"""
|
||||
nodeInfo = None
|
||||
name, last_i = tup
|
||||
if (name, last_i) in deparsed.offsets.keys():
|
||||
nodeInfo = deparsed.offsets[name, last_i]
|
||||
else:
|
||||
from uncompyle6.scanner import get_scanner
|
||||
scanner = get_scanner(deparsed.version)
|
||||
co = code.co_code
|
||||
if op_at_code_loc(co, last_i, scanner.opc) == 'DUP_TOP':
|
||||
offset = deparsed.scanner.next_offset(co[last_i], last_i)
|
||||
if (name, offset) in deparsed.offsets:
|
||||
nodeInfo = deparsed.offsets[name, offset]
|
||||
|
||||
return nodeInfo
|
||||
|
||||
# if __name__ == '__main__':
|
||||
|
||||
# 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)
|
||||
# deparsed = deparse_code(float_version, co, showasm=False, showast=False,
|
||||
# showgrammar=False, is_pypy=IS_PYPY)
|
||||
# print("deparsed source")
|
||||
# print(walk.text, "\n")
|
||||
# print(deparsed.text, "\n")
|
||||
# print('------------------------')
|
||||
# for name, offset in sorted(walk.offsets.keys(),
|
||||
# for name, offset in sorted(deparsed.offsets.keys(),
|
||||
# key=lambda x: str(x[0])):
|
||||
# print("name %s, offset %s" % (name, offset))
|
||||
# nodeInfo = walk.offsets[name, offset]
|
||||
# nodeInfo = deparsed.offsets[name, offset]
|
||||
# nodeInfo2 = deparsed_find((name, offset), deparsed, co)
|
||||
# assert nodeInfo == nodeInfo2
|
||||
# node = nodeInfo.node
|
||||
# extractInfo = walk.extract_node_info(node)
|
||||
# extractInfo = deparsed.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)
|
||||
# extractInfo, p = deparsed.extract_parent_info(node)
|
||||
|
||||
# if extractInfo:
|
||||
# print("Contained in...")
|
||||
@@ -1862,21 +1907,26 @@ def deparse_code_around_offset(name, offset, version, co, out=StringIO(),
|
||||
# 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)
|
||||
# deparsed = 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(deparsed.text, "\n")
|
||||
# print('------------------------')
|
||||
# for name, offset in sorted(walk.offsets.keys(),
|
||||
# for name, offset in sorted(deparsed.offsets.keys(),
|
||||
# key=lambda x: str(x[0])):
|
||||
# print("name %s, offset %s" % (name, offset))
|
||||
# nodeInfo = walk.offsets[name, offset]
|
||||
# nodeInfo = deparsed.offsets[name, offset]
|
||||
# node = nodeInfo.node
|
||||
# extractInfo = walk.extract_node_info(node)
|
||||
# extractInfo = deparsed.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)
|
||||
# extractInfo, p = deparsed.extract_parent_info(node)
|
||||
# if extractInfo:
|
||||
# print("Contained in...")
|
||||
# print(extractInfo.selectedLine)
|
||||
|
@@ -1620,7 +1620,8 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.prec = p
|
||||
|
||||
def listcomprehension_walk2(self, node):
|
||||
"""List comprehensions the way they are done in Python 2.
|
||||
"""List comprehensions the way they are done in Python 2 (and
|
||||
some Python 3?).
|
||||
They're more other comprehensions, e.g. set comprehensions
|
||||
See if we can combine code.
|
||||
"""
|
||||
@@ -1635,6 +1636,9 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
else:
|
||||
ast = ast[0][0][0][0][0]
|
||||
|
||||
if ast == 'expr':
|
||||
ast = ast[0]
|
||||
|
||||
n = ast[1]
|
||||
collection = node[-3]
|
||||
list_if = None
|
||||
|
@@ -1,3 +1,3 @@
|
||||
# This file is suitable for sourcing inside bash as
|
||||
# well as importing into Python
|
||||
VERSION='2.15.1'
|
||||
VERSION='2.16.0'
|
||||
|
Reference in New Issue
Block a user