You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 09:22:40 +08:00
Python 3 class deparsing. stop earlier in uncompyle6 on a syntax error.
This commit is contained in:
@@ -11,3 +11,7 @@
|
|||||||
import io
|
import io
|
||||||
class BZ2File(io.BufferedIOBase):
|
class BZ2File(io.BufferedIOBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ABC(metaclass=BZ2File):
|
||||||
|
pass
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Copyright (c) 2000 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
|
||||||
# Copyright (c) 2015 by Rocky Bernstein
|
# Copyright (c) 2015 by Rocky Bernstein
|
||||||
|
# Copyright (c) 2000 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import imp, marshal, os, py_compile, sys, tempfile
|
import imp, marshal, os, py_compile, sys, tempfile
|
||||||
@@ -26,7 +26,7 @@ def check_object_path(path):
|
|||||||
spath = path if PYTHON3 else path.decode('utf-8')
|
spath = path if PYTHON3 else path.decode('utf-8')
|
||||||
path = tempfile.mkstemp(prefix=basename + '-',
|
path = tempfile.mkstemp(prefix=basename + '-',
|
||||||
suffix='.pyc', text=False)[1]
|
suffix='.pyc', text=False)[1]
|
||||||
py_compile.compile(spath, cfile=path)
|
py_compile.compile(spath, cfile=path, doraise=True)
|
||||||
|
|
||||||
if not path.endswith(".pyc") and not path.endswith(".pyo"):
|
if not path.endswith(".pyc") and not path.endswith(".pyo"):
|
||||||
raise ValueError("path %s must point to a .py or .pyc file\n" %
|
raise ValueError("path %s must point to a .py or .pyc file\n" %
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import datetime, inspect, os, sys
|
import datetime, os, sys
|
||||||
|
|
||||||
from uncompyle6 import verify, PYTHON_VERSION
|
from uncompyle6 import verify, PYTHON_VERSION
|
||||||
from uncompyle6.code import iscode
|
from uncompyle6.code import iscode
|
||||||
@@ -108,7 +108,7 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
try:
|
try:
|
||||||
uncompyle_file(infile, outstream, showasm, showast, showgrammar)
|
uncompyle_file(infile, outstream, showasm, showast, showgrammar)
|
||||||
tot_files += 1
|
tot_files += 1
|
||||||
except ValueError as e:
|
except (ValueError, SyntaxError) as e:
|
||||||
sys.stderr.write("\n# %s" % e)
|
sys.stderr.write("\n# %s" % e)
|
||||||
failed_files += 1
|
failed_files += 1
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
@@ -368,9 +368,9 @@ class Python3Parser(PythonParser):
|
|||||||
|
|
||||||
kwarg ::= LOAD_CONST expr
|
kwarg ::= LOAD_CONST expr
|
||||||
|
|
||||||
classdef ::= buildclass designator
|
classdef ::= build_class designator
|
||||||
# Python3 introduced LOAD_BUILD_CLASS
|
# Python3 introduced LOAD_BUILD_CLASS
|
||||||
# the definition of buildclass is a custom rule
|
# the definition of build_class is a custom rule
|
||||||
|
|
||||||
stmt ::= classdefdeco
|
stmt ::= classdefdeco
|
||||||
classdefdeco ::= classdefdeco1 designator
|
classdefdeco ::= classdefdeco1 designator
|
||||||
@@ -547,6 +547,7 @@ class Python3Parser(PythonParser):
|
|||||||
expr ::= LOAD_GLOBAL
|
expr ::= LOAD_GLOBAL
|
||||||
expr ::= LOAD_DEREF
|
expr ::= LOAD_DEREF
|
||||||
expr ::= LOAD_LOCALS
|
expr ::= LOAD_LOCALS
|
||||||
|
expr ::= LOAD_CLASSNAME
|
||||||
expr ::= load_attr
|
expr ::= load_attr
|
||||||
expr ::= binary_expr
|
expr ::= binary_expr
|
||||||
expr ::= binary_expr_na
|
expr ::= binary_expr_na
|
||||||
@@ -677,72 +678,61 @@ class Python3Parser(PythonParser):
|
|||||||
nullexprlist ::=
|
nullexprlist ::=
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def custom_buildclass_rule(self, opname, i, token, tokens, customize):
|
@staticmethod
|
||||||
"""
|
def call_fn_name(token):
|
||||||
Python >= 3.3:
|
"""Customize CALL_FUNCTION to add the number of positional arguments"""
|
||||||
buildclass ::= LOAD_BUILD_CLASS mkfunc
|
return 'CALL_FUNCTION_%i' % token.attr
|
||||||
LOAD_CLASSNAME expr CALL_FUNCTION_3
|
|
||||||
or
|
|
||||||
buildclass ::= LOAD_BUILD_CLASS mkfunc
|
|
||||||
LOAD_CONST CALL_FUNCTION_3
|
|
||||||
Python < 3.3
|
|
||||||
buildclass ::= LOAD_BUILD_CLASS LOAD_CONST MAKE_FUNCTION_0 LOAD_CONST
|
|
||||||
CALL_FUNCTION_n
|
|
||||||
|
|
||||||
|
def custom_build_class_rule(self, opname, i, token, tokens, customize):
|
||||||
"""
|
"""
|
||||||
|
build_class ::= LOAD_BUILD_CLASS mkfunc
|
||||||
|
LOAD_CLASSNAME {expr}^n CALL_FUNCTION_n+2
|
||||||
|
LOAD_CONST CALL_FUNCTION_n
|
||||||
|
"""
|
||||||
|
# FIXME: I bet this can be simplified
|
||||||
# look for next MAKE_FUNCTION
|
# look for next MAKE_FUNCTION
|
||||||
for i in range(i+1, len(tokens)):
|
for i in range(i+1, len(tokens)):
|
||||||
if tokens[i].type.startswith('MAKE_FUNCTION'):
|
if tokens[i].type.startswith('MAKE_FUNCTION'):
|
||||||
break
|
break
|
||||||
pass
|
pass
|
||||||
assert i < len(tokens)
|
assert i < len(tokens), "build_class needs to find MAKE_FUNCTION"
|
||||||
assert tokens[i+1].type == 'LOAD_CONST'
|
assert tokens[i+1].type == 'LOAD_CONST', \
|
||||||
# find load names
|
"build_class expecing CONST after MAKE_FUNCTION"
|
||||||
have_loadname = False
|
for i in range(i, len(tokens)):
|
||||||
for i in range(i+1, len(tokens)):
|
if tokens[i].type == 'CALL_FUNCTION':
|
||||||
if tokens[i].type == 'LOAD_NAME':
|
call_fn_tok = tokens[i]
|
||||||
if tokens[i+1].type != 'LOAD_ATTR':
|
|
||||||
tokens[i].type = 'LOAD_CLASSNAME'
|
|
||||||
have_loadname = True
|
|
||||||
break
|
break
|
||||||
if tokens[i].type in 'CALL_FUNCTION':
|
assert call_fn_tok, "build_class custom rule needs to find CALL_FUNCTION"
|
||||||
break
|
|
||||||
pass
|
|
||||||
assert i < len(tokens)
|
|
||||||
have_load_attr = False
|
|
||||||
if have_loadname:
|
|
||||||
j = 1
|
|
||||||
for i in range(i+1, len(tokens)):
|
|
||||||
if tokens[i].type in ['CALL_FUNCTION', 'LOAD_ATTR']:
|
|
||||||
if tokens[i].type == 'LOAD_ATTR':
|
|
||||||
have_load_attr = True
|
|
||||||
break
|
|
||||||
assert tokens[i].type == 'LOAD_NAME', \
|
|
||||||
'Expecting LOAD_NAME after CALL_FUNCTION'
|
|
||||||
tokens[i].type = 'LOAD_CLASSNAME'
|
|
||||||
j += 1
|
|
||||||
pass
|
|
||||||
load_names = 'LOAD_CLASSNAME ' * j
|
|
||||||
else:
|
|
||||||
j = 0
|
|
||||||
load_names = ''
|
|
||||||
# customize CALL_FUNCTION
|
# customize CALL_FUNCTION
|
||||||
if self.version >= 3.3:
|
call_function = self.call_fn_name(call_fn_tok)
|
||||||
if not have_load_attr:
|
args_pos = call_fn_tok.attr & 0xff
|
||||||
call_function = 'CALL_FUNCTION_%d' % (j + 2)
|
args_kw = (call_fn_tok.attr >> 8) & 0xff
|
||||||
rule = ("buildclass ::= LOAD_BUILD_CLASS mkfunc LOAD_CONST "
|
rule = ("build_class ::= LOAD_BUILD_CLASS mkfunc %s"
|
||||||
"%s%s" % (load_names, call_function))
|
"%s" % (('expr ' * (args_pos - 1) + ('kwarg ' * args_kw)),
|
||||||
else:
|
call_function))
|
||||||
rule = ("buildclass ::= LOAD_BUILD_CLASS mkfunc LOAD_CONST expr "
|
|
||||||
"CALL_FUNCTION_3")
|
|
||||||
else:
|
|
||||||
call_function = 'CALL_FUNCTION_%d' % (j + 1)
|
|
||||||
rule = ("buildclass ::= LOAD_BUILD_CLASS mkfunc %s%s" %
|
|
||||||
(load_names, call_function))
|
|
||||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def custom_classfunc_rule(self, opname, token, customize):
|
||||||
|
"""
|
||||||
|
call_function ::= expr {expr}^n CALL_FUNCTION_n
|
||||||
|
call_function ::= expr {expr}^n CALL_FUNCTION_VAR_n POP_TOP
|
||||||
|
call_function ::= expr {expr}^n CALL_FUNCTION_VAR_KW_n POP_TOP
|
||||||
|
call_function ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP
|
||||||
|
"""
|
||||||
|
# Low byte indicates number of positional paramters,
|
||||||
|
# high byte number of positional parameters
|
||||||
|
args_pos = token.attr & 0xff
|
||||||
|
args_kw = (token.attr >> 8) & 0xff
|
||||||
|
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
|
||||||
|
token.type = self.call_fn_name(token)
|
||||||
|
rule = ('call_function ::= expr '
|
||||||
|
+ ('expr ' * args_pos)
|
||||||
|
+ ('kwarg ' * args_kw)
|
||||||
|
+ 'expr ' * nak + token.type)
|
||||||
|
self.add_unique_rule(rule, token.type, args_pos, customize)
|
||||||
|
|
||||||
def add_custom_rules(self, tokens, customize):
|
def add_custom_rules(self, tokens, customize):
|
||||||
"""
|
"""
|
||||||
Special handling for opcodes that take a variable number
|
Special handling for opcodes that take a variable number
|
||||||
@@ -755,7 +745,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
|
||||||
|
|
||||||
buildclass (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
|
||||||
@@ -766,10 +756,8 @@ class Python3Parser(PythonParser):
|
|||||||
mkfunc ::= {expr}^n LOAD_CONST MAKE_FUNCTION_n
|
mkfunc ::= {expr}^n LOAD_CONST MAKE_FUNCTION_n
|
||||||
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
|
||||||
expr ::= expr {expr}^n CALL_FUNCTION_n
|
|
||||||
expr ::= expr {expr}^n CALL_FUNCTION_VAR_n POP_TOP
|
call_function (see custom_classfunc_rule)
|
||||||
expr ::= expr {expr}^n CALL_FUNCTION_VAR_KW_n POP_TOP
|
|
||||||
expr ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP
|
|
||||||
"""
|
"""
|
||||||
# from trepan.api import debug
|
# from trepan.api import debug
|
||||||
# debug(start_opts={'startup-profile': True})
|
# debug(start_opts={'startup-profile': True})
|
||||||
@@ -779,17 +767,7 @@ class Python3Parser(PythonParser):
|
|||||||
|
|
||||||
if opname in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR',
|
if opname in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR',
|
||||||
'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'):
|
'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'):
|
||||||
# Low byte indicates number of positional paramters,
|
self.custom_classfunc_rule(opname, token, customize)
|
||||||
# high byte number of positional parameters
|
|
||||||
args_pos = token.attr & 0xff
|
|
||||||
args_kw = (token.attr >> 8) & 0xff
|
|
||||||
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
|
|
||||||
token.type = 'CALL_FUNCTION_%i' % token.attr
|
|
||||||
rule = ('call_function ::= expr '
|
|
||||||
+ ('expr ' * args_pos)
|
|
||||||
+ ('kwarg ' * args_kw)
|
|
||||||
+ 'expr ' * nak + token.type)
|
|
||||||
self.add_unique_rule(rule, token.type, args_pos, customize)
|
|
||||||
elif opname == 'LOAD_LISTCOMP':
|
elif opname == 'LOAD_LISTCOMP':
|
||||||
if self.version >= 3.4:
|
if self.version >= 3.4:
|
||||||
rule = ("listcomp ::= LOAD_LISTCOMP LOAD_CONST MAKE_FUNCTION_0 expr "
|
rule = ("listcomp ::= LOAD_LISTCOMP LOAD_CONST MAKE_FUNCTION_0 expr "
|
||||||
@@ -799,7 +777,7 @@ class Python3Parser(PythonParser):
|
|||||||
"GET_ITER CALL_FUNCTION_1")
|
"GET_ITER CALL_FUNCTION_1")
|
||||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||||
elif opname == 'LOAD_BUILD_CLASS':
|
elif opname == 'LOAD_BUILD_CLASS':
|
||||||
self.custom_buildclass_rule(opname, i, token, tokens, customize)
|
self.custom_build_class_rule(opname, i, token, tokens, customize)
|
||||||
elif opname_base in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'):
|
elif opname_base in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'):
|
||||||
rule = 'build_list ::= ' + 'expr ' * token.attr + opname
|
rule = 'build_list ::= ' + 'expr ' * token.attr + opname
|
||||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||||
|
@@ -1118,22 +1118,20 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
|
|
||||||
def print_super_classes3(self, node):
|
def print_super_classes3(self, node):
|
||||||
|
|
||||||
# FIXME: wrap superclasses onto a node
|
|
||||||
# as a custom rule
|
|
||||||
n = len(node)-1
|
n = len(node)-1
|
||||||
assert node[n].type.startswith('CALL_FUNCTION')
|
assert node[n].type.startswith('CALL_FUNCTION')
|
||||||
|
|
||||||
for i in range(n-1, 0, -1):
|
for i in range(n-2, 0, -1):
|
||||||
if not node[i].type in ['expr', 'LOAD_CLASSNAME']:
|
if not node[i].type in ['expr', 'LOAD_CLASSNAME']:
|
||||||
break
|
break
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if i == n-1:
|
if i == n-2:
|
||||||
return
|
return
|
||||||
self.write('(')
|
self.write('(')
|
||||||
line_separator = ', '
|
line_separator = ', '
|
||||||
sep = ''
|
sep = ''
|
||||||
i += 1
|
i += 2
|
||||||
while i < n:
|
while i < n:
|
||||||
value = self.traverse(node[i])
|
value = self.traverse(node[i])
|
||||||
i += 1
|
i += 1
|
||||||
|
Reference in New Issue
Block a user