You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 01:09:52 +08:00
Fix Python 3 list comprehansion closure bug
This commit is contained in:
BIN
test/bytecode_3.2/11-list-if.pyc
Normal file
BIN
test/bytecode_3.2/11-list-if.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.5/11-list-if.pyc
Normal file
BIN
test/bytecode_3.5/11-list-if.pyc
Normal file
Binary file not shown.
8
test/simple_source/comprehension/11-list-if.py
Normal file
8
test/simple_source/comprehension/11-list-if.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Test Python 3.x test semantic handling of
|
||||||
|
#
|
||||||
|
# load_closure ::= LOAD_CLOSURE BUILD_TUPLE_1
|
||||||
|
# ...
|
||||||
|
# listcomp ::= load_closure LOAD_LISTCOMP LOAD_CONST MAKE_CLOSURE_0 expr GET_ITER CALL_FUNCTION_1
|
||||||
|
|
||||||
|
def long_has_args(opt, longopts):
|
||||||
|
return [o for o in longopts if o.startswith(opt)]
|
@@ -53,6 +53,8 @@ class Python3Parser(PythonParser):
|
|||||||
list_for ::= expr FOR_ITER designator list_iter JUMP_BACK
|
list_for ::= expr FOR_ITER designator list_iter JUMP_BACK
|
||||||
|
|
||||||
# See also common Python p_list_comprehension
|
# See also common Python p_list_comprehension
|
||||||
|
|
||||||
|
load_closure ::= LOAD_CLOSURE BUILD_TUPLE_1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def p_setcomp3(self, args):
|
def p_setcomp3(self, args):
|
||||||
@@ -426,7 +428,7 @@ class Python3Parser(PythonParser):
|
|||||||
listcomp ::= LOAD_LISTCOMP MAKE_FUNCTION_0 expr
|
listcomp ::= LOAD_LISTCOMP MAKE_FUNCTION_0 expr
|
||||||
GET_ITER CALL_FUNCTION_1
|
GET_ITER CALL_FUNCTION_1
|
||||||
|
|
||||||
build_class (see load_build_class)
|
# build_class (see load_build_class)
|
||||||
|
|
||||||
build_list ::= {expr}^n BUILD_LIST_n
|
build_list ::= {expr}^n BUILD_LIST_n
|
||||||
build_list ::= {expr}^n BUILD_TUPLE_n
|
build_list ::= {expr}^n BUILD_TUPLE_n
|
||||||
@@ -438,7 +440,9 @@ class Python3Parser(PythonParser):
|
|||||||
mklambda ::= {expr}^n LOAD_LAMBDA MAKE_FUNCTION_n
|
mklambda ::= {expr}^n LOAD_LAMBDA MAKE_FUNCTION_n
|
||||||
mkfunc ::= {expr}^n load_closure LOAD_CONST MAKE_FUNCTION_n
|
mkfunc ::= {expr}^n load_closure LOAD_CONST MAKE_FUNCTION_n
|
||||||
|
|
||||||
call_function (see custom_classfunc_rule)
|
listcomp ::= load_closure expr GET_ITER CALL_FUNCTION_1"
|
||||||
|
|
||||||
|
# call_function (see custom_classfunc_rule)
|
||||||
"""
|
"""
|
||||||
# from trepan.api import debug
|
# from trepan.api import debug
|
||||||
# debug(start_opts={'startup-profile': True})
|
# debug(start_opts={'startup-profile': True})
|
||||||
@@ -503,6 +507,19 @@ class Python3Parser(PythonParser):
|
|||||||
'expr GET_ITER CALL_FUNCTION_1' %
|
'expr GET_ITER CALL_FUNCTION_1' %
|
||||||
('expr ' * token.attr, opname),
|
('expr ' * token.attr, opname),
|
||||||
opname, token.attr, customize)
|
opname, token.attr, customize)
|
||||||
|
if self.version >= 3.4:
|
||||||
|
self.add_unique_rule('listcomp ::= %s load_closure '
|
||||||
|
'LOAD_LISTCOMP LOAD_CONST %s expr '
|
||||||
|
'GET_ITER CALL_FUNCTION_1' %
|
||||||
|
('expr ' * token.attr, opname),
|
||||||
|
opname, token.attr, customize)
|
||||||
|
else:
|
||||||
|
self.add_unique_rule('listcomp ::= %s load_closure '
|
||||||
|
'LOAD_LISTCOMP %s expr '
|
||||||
|
'GET_ITER CALL_FUNCTION_1' %
|
||||||
|
('expr ' * token.attr, opname),
|
||||||
|
opname, token.attr, customize)
|
||||||
|
|
||||||
self.add_unique_rule('setcomp ::= %s load_closure LOAD_SETCOMP %s expr '
|
self.add_unique_rule('setcomp ::= %s load_closure LOAD_SETCOMP %s expr '
|
||||||
'GET_ITER CALL_FUNCTION_1' %
|
'GET_ITER CALL_FUNCTION_1' %
|
||||||
('expr ' * token.attr, opname),
|
('expr ' * token.attr, opname),
|
||||||
|
@@ -1053,16 +1053,12 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
if n == 'list_for':
|
if n == 'list_for':
|
||||||
designator = n[2]
|
designator = n[2]
|
||||||
n = n[3]
|
n = n[3]
|
||||||
elif n == 'list_if':
|
elif n in ['list_if', 'list_if_not']:
|
||||||
# FIXME: just a guess
|
|
||||||
designator = n[1]
|
|
||||||
|
|
||||||
n = n[2]
|
|
||||||
elif n == 'list_if_not':
|
|
||||||
# FIXME: just a guess
|
# FIXME: just a guess
|
||||||
designator = n[1]
|
designator = n[1]
|
||||||
n = n[2]
|
n = n[2]
|
||||||
|
pass
|
||||||
|
pass
|
||||||
assert n == 'lc_body', ast
|
assert n == 'lc_body', ast
|
||||||
|
|
||||||
self.preorder(n[0])
|
self.preorder(n[0])
|
||||||
@@ -1070,12 +1066,59 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.preorder(designator)
|
self.preorder(designator)
|
||||||
self.write(' in ')
|
self.write(' in ')
|
||||||
self.preorder(node[-3])
|
self.preorder(node[-3])
|
||||||
# self.preorder(ast[iter_index])
|
self.prec = p
|
||||||
|
|
||||||
|
def listcomprehension_walk2(self, node):
|
||||||
|
"""List comprehensions the way they are done in Python3.
|
||||||
|
They're more other comprehensions, e.g. set comprehensions
|
||||||
|
See if we can combine code.
|
||||||
|
"""
|
||||||
|
p = self.prec
|
||||||
|
self.prec = 27
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
n = ast[1]
|
||||||
|
collection = node[-3]
|
||||||
|
list_if = None
|
||||||
|
assert n == 'list_iter'
|
||||||
|
|
||||||
|
# find innermost node
|
||||||
|
while n == 'list_iter': # list_iter
|
||||||
|
n = n[0] # recurse one step
|
||||||
|
if n == 'list_for':
|
||||||
|
designator = n[2]
|
||||||
|
n = n[3]
|
||||||
|
elif n in ('list_if', 'list_if_not'):
|
||||||
|
# FIXME: just a guess
|
||||||
|
if n[0].type == 'expr':
|
||||||
|
list_if = n
|
||||||
|
else:
|
||||||
|
list_if = n[1]
|
||||||
|
n = n[2]
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
|
||||||
|
assert n == 'lc_body', ast
|
||||||
|
|
||||||
|
self.preorder(n[0])
|
||||||
|
self.write(' for ')
|
||||||
|
self.preorder(designator)
|
||||||
|
self.write(' in ')
|
||||||
|
self.preorder(collection)
|
||||||
|
if list_if:
|
||||||
|
self.preorder(list_if)
|
||||||
self.prec = p
|
self.prec = p
|
||||||
|
|
||||||
def n_listcomp(self, node):
|
def n_listcomp(self, node):
|
||||||
self.write('[')
|
self.write('[')
|
||||||
self.listcomprehension_walk3(node, iter_index=1, code_index=0)
|
if node[0].type == 'load_closure':
|
||||||
|
self.listcomprehension_walk2(node)
|
||||||
|
else:
|
||||||
|
self.listcomprehension_walk3(node, 1, 0)
|
||||||
self.write(']')
|
self.write(']')
|
||||||
self.prune()
|
self.prune()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user