You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 09:22:40 +08:00
Break out make_function() into its own file.
It is already too complex and will get worse in Python 3.6. Note: make_function in fragments.py is still inside and probably needs fixup.
This commit is contained in:
@@ -67,7 +67,9 @@ from uncompyle6.show import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from uncompyle6.semantics.pysource import AST, INDENT_PER_LEVEL, NONE, PRECEDENCE, \
|
from uncompyle6.semantics.pysource import AST, INDENT_PER_LEVEL, NONE, PRECEDENCE, \
|
||||||
ParserError, TABLE_DIRECT, escape, find_all_globals, find_globals, find_none, minint, MAP
|
ParserError, TABLE_DIRECT, escape, find_globals, minint, MAP
|
||||||
|
|
||||||
|
from uncompyle6.semantics.make_function import find_all_globals, find_none
|
||||||
|
|
||||||
if PYTHON3:
|
if PYTHON3:
|
||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
@@ -77,8 +79,7 @@ else:
|
|||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
from spark_parser import GenericASTTraversalPruningException, \
|
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||||
DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
NodeInfo = namedtuple("NodeInfo", "node start finish")
|
NodeInfo = namedtuple("NodeInfo", "node start finish")
|
||||||
|
520
uncompyle6/semantics/make_function.py
Normal file
520
uncompyle6/semantics/make_function.py
Normal file
@@ -0,0 +1,520 @@
|
|||||||
|
# Copyright (c) 2015, 2016 by Rocky Bernstein
|
||||||
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
|
"""
|
||||||
|
All the crazy things we have to do to handle Python functions
|
||||||
|
"""
|
||||||
|
from xdis.code import iscode
|
||||||
|
from uncompyle6.scanner import Code
|
||||||
|
from uncompyle6.parsers.astnode import AST
|
||||||
|
from uncompyle6 import PYTHON3
|
||||||
|
from uncompyle6.semantics.parser_error import ParserError
|
||||||
|
|
||||||
|
if PYTHON3:
|
||||||
|
from itertools import zip_longest
|
||||||
|
else:
|
||||||
|
from itertools import izip_longest as zip_longest
|
||||||
|
|
||||||
|
from uncompyle6.show import maybe_show_ast_param_default
|
||||||
|
|
||||||
|
def find_all_globals(node, globs):
|
||||||
|
"""Find globals in this statement."""
|
||||||
|
for n in node:
|
||||||
|
if isinstance(n, AST):
|
||||||
|
globs = find_all_globals(n, globs)
|
||||||
|
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL', 'LOAD_GLOBAL'):
|
||||||
|
globs.add(n.pattr)
|
||||||
|
return globs
|
||||||
|
|
||||||
|
def find_globals(node, globs):
|
||||||
|
"""Find globals in this statement."""
|
||||||
|
for n in node:
|
||||||
|
if isinstance(n, AST):
|
||||||
|
globs = find_globals(n, globs)
|
||||||
|
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL'):
|
||||||
|
globs.add(n.pattr)
|
||||||
|
return globs
|
||||||
|
|
||||||
|
def find_none(node):
|
||||||
|
for n in node:
|
||||||
|
if isinstance(n, AST):
|
||||||
|
if not n in ('return_stmt', 'return_if_stmt'):
|
||||||
|
if find_none(n):
|
||||||
|
return True
|
||||||
|
elif n.type == 'LOAD_CONST' and n.pattr is None:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# FIXME: DRY the below code...
|
||||||
|
|
||||||
|
def make_function3_annotate(self, node, isLambda, nested=1,
|
||||||
|
codeNode=None, annotate=None):
|
||||||
|
"""
|
||||||
|
Dump function defintion, doc string, and function
|
||||||
|
body. This code is specialized for Python 3"""
|
||||||
|
|
||||||
|
def build_param(ast, name, default):
|
||||||
|
"""build parameters:
|
||||||
|
- handle defaults
|
||||||
|
- handle format tuple parameters
|
||||||
|
"""
|
||||||
|
if default:
|
||||||
|
value = self.traverse(default, indent='')
|
||||||
|
maybe_show_ast_param_default(self.showast, name, value)
|
||||||
|
result = '%s=%s' % (name, value)
|
||||||
|
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
||||||
|
result += 'None'
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return name
|
||||||
|
|
||||||
|
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
||||||
|
assert node[-1].type.startswith('MAKE_')
|
||||||
|
|
||||||
|
args_node = node[-1]
|
||||||
|
if isinstance(args_node.attr, tuple):
|
||||||
|
# positional args are before kwargs
|
||||||
|
defparams = node[:args_node.attr[0]]
|
||||||
|
pos_args, kw_args, annotate_args = args_node.attr
|
||||||
|
else:
|
||||||
|
defparams = node[:args_node.attr]
|
||||||
|
kw_args = 0
|
||||||
|
pass
|
||||||
|
|
||||||
|
if 3.0 <= self.version <= 3.2:
|
||||||
|
lambda_index = -2
|
||||||
|
elif 3.03 <= self.version:
|
||||||
|
lambda_index = -3
|
||||||
|
else:
|
||||||
|
lambda_index = None
|
||||||
|
|
||||||
|
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
||||||
|
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
||||||
|
code = node[lambda_index].attr
|
||||||
|
else:
|
||||||
|
code = codeNode.attr
|
||||||
|
|
||||||
|
assert iscode(code)
|
||||||
|
code = Code(code, self.scanner, self.currentclass)
|
||||||
|
|
||||||
|
# add defaults values to parameter names
|
||||||
|
argc = code.co_argcount
|
||||||
|
paramnames = list(code.co_varnames[:argc])
|
||||||
|
|
||||||
|
try:
|
||||||
|
ast = self.build_ast(code._tokens,
|
||||||
|
code._customize,
|
||||||
|
isLambda = isLambda,
|
||||||
|
noneInNames = ('None' in code.co_names))
|
||||||
|
except ParserError as p:
|
||||||
|
self.write(str(p))
|
||||||
|
self.ERROR = p
|
||||||
|
return
|
||||||
|
|
||||||
|
kw_pairs = args_node.attr[1]
|
||||||
|
indent = self.indent
|
||||||
|
|
||||||
|
if isLambda:
|
||||||
|
self.write("lambda ")
|
||||||
|
else:
|
||||||
|
self.write("(")
|
||||||
|
|
||||||
|
last_line = self.f.getvalue().split("\n")[-1]
|
||||||
|
l = len(last_line)
|
||||||
|
indent = ' ' * l
|
||||||
|
line_number = self.line_number
|
||||||
|
|
||||||
|
|
||||||
|
if 4 & code.co_flags: # flag 2 -> variable number of args
|
||||||
|
self.write('*%s' % code.co_varnames[argc + kw_pairs])
|
||||||
|
argc += 1
|
||||||
|
|
||||||
|
i = len(paramnames) - len(defparams)
|
||||||
|
self.write(", ".join(paramnames[:i]))
|
||||||
|
suffix = ', ' if i > 0 else ''
|
||||||
|
for n in node:
|
||||||
|
if n == 'pos_arg':
|
||||||
|
self.write(suffix)
|
||||||
|
self.write(paramnames[i] + '=')
|
||||||
|
i += 1
|
||||||
|
self.preorder(n)
|
||||||
|
if (line_number != self.line_number):
|
||||||
|
suffix = ",\n" + indent
|
||||||
|
line_number = self.line_number
|
||||||
|
else:
|
||||||
|
suffix = ', '
|
||||||
|
|
||||||
|
# self.println(indent, '#flags:\t', int(code.co_flags))
|
||||||
|
if kw_args > 0:
|
||||||
|
if not (4 & code.co_flags):
|
||||||
|
if argc > 0:
|
||||||
|
self.write(", *, ")
|
||||||
|
else:
|
||||||
|
self.write("*, ")
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.write(", ")
|
||||||
|
|
||||||
|
kwargs = node[0]
|
||||||
|
last = len(kwargs)-1
|
||||||
|
i = 0
|
||||||
|
for n in node[0]:
|
||||||
|
if n == 'kwarg':
|
||||||
|
self.write('%s=' % n[0].pattr)
|
||||||
|
self.preorder(n[1])
|
||||||
|
if i < last:
|
||||||
|
self.write(', ')
|
||||||
|
i += 1
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
|
||||||
|
if 8 & code.co_flags: # flag 3 -> keyword args
|
||||||
|
if argc > 0:
|
||||||
|
self.write(', ')
|
||||||
|
self.write('**%s' % code.co_varnames[argc + kw_pairs])
|
||||||
|
|
||||||
|
if isLambda:
|
||||||
|
self.write(": ")
|
||||||
|
else:
|
||||||
|
self.write(')')
|
||||||
|
if annotate:
|
||||||
|
self.write(' -> "%s"' % annotate)
|
||||||
|
self.println(":")
|
||||||
|
|
||||||
|
if (len(code.co_consts) > 0 and
|
||||||
|
code.co_consts[0] is not None and not isLambda): # ugly
|
||||||
|
# docstring exists, dump it
|
||||||
|
self.print_docstring(indent, code.co_consts[0])
|
||||||
|
|
||||||
|
code._tokens = None # save memory
|
||||||
|
assert ast == 'stmts'
|
||||||
|
|
||||||
|
all_globals = find_all_globals(ast, set())
|
||||||
|
for g in ((all_globals & self.mod_globs) | find_globals(ast, set())):
|
||||||
|
self.println(self.indent, 'global ', g)
|
||||||
|
self.mod_globs -= all_globals
|
||||||
|
has_none = 'None' in code.co_names
|
||||||
|
rn = has_none and not find_none(ast)
|
||||||
|
self.gen_source(ast, code.co_name, code._customize, isLambda=isLambda,
|
||||||
|
returnNone=rn)
|
||||||
|
code._tokens = code._customize = None # save memory
|
||||||
|
|
||||||
|
def make_function2(self, node, isLambda, nested=1, codeNode=None):
|
||||||
|
"""
|
||||||
|
Dump function defintion, doc string, and function body.
|
||||||
|
This code is specialied for Python 2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# FIXME: call make_function3 if we are self.version >= 3.0
|
||||||
|
# and then simplify the below.
|
||||||
|
|
||||||
|
def build_param(ast, name, default):
|
||||||
|
"""build parameters:
|
||||||
|
- handle defaults
|
||||||
|
- handle format tuple parameters
|
||||||
|
"""
|
||||||
|
# if formal parameter is a tuple, the paramater name
|
||||||
|
# starts with a dot (eg. '.1', '.2')
|
||||||
|
if name.startswith('.'):
|
||||||
|
# replace the name with the tuple-string
|
||||||
|
name = self.get_tuple_parameter(ast, name)
|
||||||
|
pass
|
||||||
|
|
||||||
|
if default:
|
||||||
|
value = self.traverse(default, indent='')
|
||||||
|
maybe_show_ast_param_default(self.showast, name, value)
|
||||||
|
result = '%s=%s' % (name, value)
|
||||||
|
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
||||||
|
result += 'None'
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return name
|
||||||
|
|
||||||
|
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
||||||
|
assert node[-1].type.startswith('MAKE_')
|
||||||
|
|
||||||
|
args_node = node[-1]
|
||||||
|
if isinstance(args_node.attr, tuple):
|
||||||
|
# positional args are after kwargs
|
||||||
|
defparams = node[1:args_node.attr[0]+1]
|
||||||
|
pos_args, kw_args, annotate_args = args_node.attr
|
||||||
|
else:
|
||||||
|
defparams = node[:args_node.attr]
|
||||||
|
kw_args = 0
|
||||||
|
pass
|
||||||
|
|
||||||
|
lambda_index = None
|
||||||
|
|
||||||
|
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
||||||
|
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
||||||
|
code = node[lambda_index].attr
|
||||||
|
else:
|
||||||
|
code = codeNode.attr
|
||||||
|
|
||||||
|
assert iscode(code)
|
||||||
|
code = Code(code, self.scanner, self.currentclass)
|
||||||
|
|
||||||
|
# add defaults values to parameter names
|
||||||
|
argc = code.co_argcount
|
||||||
|
paramnames = list(code.co_varnames[:argc])
|
||||||
|
|
||||||
|
# defaults are for last n parameters, thus reverse
|
||||||
|
paramnames.reverse(); defparams.reverse()
|
||||||
|
|
||||||
|
try:
|
||||||
|
ast = self.build_ast(code._tokens,
|
||||||
|
code._customize,
|
||||||
|
isLambda = isLambda,
|
||||||
|
noneInNames = ('None' in code.co_names))
|
||||||
|
except ParserError as p:
|
||||||
|
self.write(str(p))
|
||||||
|
self.ERROR = p
|
||||||
|
return
|
||||||
|
|
||||||
|
kw_pairs = args_node.attr[1] if self.version >= 3.0 else 0
|
||||||
|
indent = self.indent
|
||||||
|
|
||||||
|
# build parameters
|
||||||
|
params = [build_param(ast, name, default) for
|
||||||
|
name, default in zip_longest(paramnames, defparams, fillvalue=None)]
|
||||||
|
params.reverse() # back to correct order
|
||||||
|
|
||||||
|
if 4 & code.co_flags: # flag 2 -> variable number of args
|
||||||
|
if self.version > 3.0:
|
||||||
|
params.append('*%s' % code.co_varnames[argc + kw_pairs])
|
||||||
|
else:
|
||||||
|
params.append('*%s' % code.co_varnames[argc])
|
||||||
|
argc += 1
|
||||||
|
|
||||||
|
# dump parameter list (with default values)
|
||||||
|
if isLambda:
|
||||||
|
self.write("lambda ", ", ".join(params))
|
||||||
|
else:
|
||||||
|
self.write("(", ", ".join(params))
|
||||||
|
# self.println(indent, '#flags:\t', int(code.co_flags))
|
||||||
|
|
||||||
|
if kw_args > 0:
|
||||||
|
if not (4 & code.co_flags):
|
||||||
|
if argc > 0:
|
||||||
|
self.write(", *, ")
|
||||||
|
else:
|
||||||
|
self.write("*, ")
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.write(", ")
|
||||||
|
|
||||||
|
for n in node:
|
||||||
|
if n == 'pos_arg':
|
||||||
|
continue
|
||||||
|
elif self.version >= 3.4 and n.type != 'kwargs':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
self.preorder(n)
|
||||||
|
break
|
||||||
|
pass
|
||||||
|
|
||||||
|
if 8 & code.co_flags: # flag 3 -> keyword args
|
||||||
|
if argc > 0:
|
||||||
|
self.write(', ')
|
||||||
|
self.write('**%s' % code.co_varnames[argc + kw_pairs])
|
||||||
|
|
||||||
|
if isLambda:
|
||||||
|
self.write(": ")
|
||||||
|
else:
|
||||||
|
self.println("):")
|
||||||
|
|
||||||
|
if len(code.co_consts) > 0 and code.co_consts[0] is not None and not isLambda: # ugly
|
||||||
|
# docstring exists, dump it
|
||||||
|
self.print_docstring(indent, code.co_consts[0])
|
||||||
|
|
||||||
|
code._tokens = None # save memory
|
||||||
|
assert ast == 'stmts'
|
||||||
|
|
||||||
|
all_globals = find_all_globals(ast, set())
|
||||||
|
for g in ((all_globals & self.mod_globs) | find_globals(ast, set())):
|
||||||
|
self.println(self.indent, 'global ', g)
|
||||||
|
self.mod_globs -= all_globals
|
||||||
|
has_none = 'None' in code.co_names
|
||||||
|
rn = has_none and not find_none(ast)
|
||||||
|
self.gen_source(ast, code.co_name, code._customize, isLambda=isLambda,
|
||||||
|
returnNone=rn)
|
||||||
|
code._tokens = None; code._customize = None # save memory
|
||||||
|
|
||||||
|
|
||||||
|
def make_function3(self, node, isLambda, nested=1, codeNode=None):
|
||||||
|
"""Dump function definition, doc string, and function body."""
|
||||||
|
|
||||||
|
# FIXME: call make_function3 if we are self.version >= 3.0
|
||||||
|
# and then simplify the below.
|
||||||
|
|
||||||
|
def build_param(ast, name, default):
|
||||||
|
"""build parameters:
|
||||||
|
- handle defaults
|
||||||
|
- handle format tuple parameters
|
||||||
|
"""
|
||||||
|
if default:
|
||||||
|
value = self.traverse(default, indent='')
|
||||||
|
maybe_show_ast_param_default(self.showast, name, value)
|
||||||
|
result = '%s=%s' % (name, value)
|
||||||
|
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
||||||
|
result += 'None'
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return name
|
||||||
|
|
||||||
|
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
||||||
|
assert node[-1].type.startswith('MAKE_')
|
||||||
|
|
||||||
|
args_node = node[-1]
|
||||||
|
if isinstance(args_node.attr, tuple):
|
||||||
|
if self.version <= 3.3:
|
||||||
|
# positional args are after kwargs
|
||||||
|
defparams = node[1:args_node.attr[0]+1]
|
||||||
|
else:
|
||||||
|
# positional args are before kwargs
|
||||||
|
defparams = node[:args_node.attr[0]]
|
||||||
|
pos_args, kw_args, annotate_args = args_node.attr
|
||||||
|
else:
|
||||||
|
defparams = node[:args_node.attr]
|
||||||
|
kw_args = 0
|
||||||
|
pass
|
||||||
|
|
||||||
|
if 3.0 <= self.version <= 3.2:
|
||||||
|
lambda_index = -2
|
||||||
|
elif 3.03 <= self.version:
|
||||||
|
lambda_index = -3
|
||||||
|
else:
|
||||||
|
lambda_index = None
|
||||||
|
|
||||||
|
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
||||||
|
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
||||||
|
code = node[lambda_index].attr
|
||||||
|
else:
|
||||||
|
code = codeNode.attr
|
||||||
|
|
||||||
|
assert iscode(code)
|
||||||
|
code = Code(code, self.scanner, self.currentclass)
|
||||||
|
|
||||||
|
# add defaults values to parameter names
|
||||||
|
argc = code.co_argcount
|
||||||
|
paramnames = list(code.co_varnames[:argc])
|
||||||
|
|
||||||
|
# defaults are for last n parameters, thus reverse
|
||||||
|
if not 3.0 <= self.version <= 3.2:
|
||||||
|
paramnames.reverse(); defparams.reverse()
|
||||||
|
|
||||||
|
try:
|
||||||
|
ast = self.build_ast(code._tokens,
|
||||||
|
code._customize,
|
||||||
|
isLambda = isLambda,
|
||||||
|
noneInNames = ('None' in code.co_names))
|
||||||
|
except ParserError as p:
|
||||||
|
self.write(str(p))
|
||||||
|
self.ERROR = p
|
||||||
|
return
|
||||||
|
|
||||||
|
kw_pairs = args_node.attr[1] if self.version >= 3.0 else 0
|
||||||
|
indent = self.indent
|
||||||
|
|
||||||
|
# build parameters
|
||||||
|
if self.version != 3.2:
|
||||||
|
params = [build_param(ast, name, default) for
|
||||||
|
name, default in zip_longest(paramnames, defparams, fillvalue=None)]
|
||||||
|
params.reverse() # back to correct order
|
||||||
|
|
||||||
|
if 4 & code.co_flags: # flag 2 -> variable number of args
|
||||||
|
if self.version > 3.0:
|
||||||
|
params.append('*%s' % code.co_varnames[argc + kw_pairs])
|
||||||
|
else:
|
||||||
|
params.append('*%s' % code.co_varnames[argc])
|
||||||
|
argc += 1
|
||||||
|
|
||||||
|
# dump parameter list (with default values)
|
||||||
|
if isLambda:
|
||||||
|
self.write("lambda ", ", ".join(params))
|
||||||
|
else:
|
||||||
|
self.write("(", ", ".join(params))
|
||||||
|
# self.println(indent, '#flags:\t', int(code.co_flags))
|
||||||
|
|
||||||
|
else:
|
||||||
|
if isLambda:
|
||||||
|
self.write("lambda ")
|
||||||
|
else:
|
||||||
|
self.write("(")
|
||||||
|
|
||||||
|
if 4 & code.co_flags: # flag 2 -> variable number of args
|
||||||
|
self.write('*%s' % code.co_varnames[argc + kw_pairs])
|
||||||
|
argc += 1
|
||||||
|
|
||||||
|
i = len(paramnames) - len(defparams)
|
||||||
|
self.write(", ".join(paramnames[:i]))
|
||||||
|
suffix = ', ' if i > 0 else ''
|
||||||
|
for n in node:
|
||||||
|
if n == 'pos_arg':
|
||||||
|
self.write(suffix)
|
||||||
|
self.write(paramnames[i] + '=')
|
||||||
|
i += 1
|
||||||
|
self.preorder(n)
|
||||||
|
suffix = ', '
|
||||||
|
|
||||||
|
if kw_args > 0:
|
||||||
|
if not (4 & code.co_flags):
|
||||||
|
if argc > 0:
|
||||||
|
self.write(", *, ")
|
||||||
|
else:
|
||||||
|
self.write("*, ")
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.write(", ")
|
||||||
|
|
||||||
|
if not 3.0 <= self.version <= 3.2:
|
||||||
|
for n in node:
|
||||||
|
if n == 'pos_arg':
|
||||||
|
continue
|
||||||
|
elif self.version >= 3.4 and n.type != 'kwargs':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
self.preorder(n)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
kwargs = node[0]
|
||||||
|
last = len(kwargs)-1
|
||||||
|
i = 0
|
||||||
|
for n in node[0]:
|
||||||
|
if n == 'kwarg':
|
||||||
|
self.write('%s=' % n[0].pattr)
|
||||||
|
self.preorder(n[1])
|
||||||
|
if i < last:
|
||||||
|
self.write(', ')
|
||||||
|
i += 1
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
pass
|
||||||
|
|
||||||
|
if 8 & code.co_flags: # flag 3 -> keyword args
|
||||||
|
if argc > 0:
|
||||||
|
self.write(', ')
|
||||||
|
self.write('**%s' % code.co_varnames[argc + kw_pairs])
|
||||||
|
|
||||||
|
if isLambda:
|
||||||
|
self.write(": ")
|
||||||
|
else:
|
||||||
|
self.println("):")
|
||||||
|
|
||||||
|
if len(code.co_consts) > 0 and code.co_consts[0] is not None and not isLambda: # ugly
|
||||||
|
# docstring exists, dump it
|
||||||
|
self.print_docstring(indent, code.co_consts[0])
|
||||||
|
|
||||||
|
code._tokens = None # save memory
|
||||||
|
assert ast == 'stmts'
|
||||||
|
|
||||||
|
all_globals = find_all_globals(ast, set())
|
||||||
|
for g in ((all_globals & self.mod_globs) | find_globals(ast, set())):
|
||||||
|
self.println(self.indent, 'global ', g)
|
||||||
|
self.mod_globs -= all_globals
|
||||||
|
has_none = 'None' in code.co_names
|
||||||
|
rn = has_none and not find_none(ast)
|
||||||
|
self.gen_source(ast, code.co_name, code._customize, isLambda=isLambda,
|
||||||
|
returnNone=rn)
|
||||||
|
code._tokens = None; code._customize = None # save memory
|
11
uncompyle6/semantics/parser_error.py
Normal file
11
uncompyle6/semantics/parser_error.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import uncompyle6.parser as python_parser
|
||||||
|
class ParserError(python_parser.ParserError):
|
||||||
|
def __init__(self, error, tokens):
|
||||||
|
self.error = error # previous exception
|
||||||
|
self.tokens = tokens
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
lines = ['--- This code section failed: ---']
|
||||||
|
lines.extend([str(i) for i in self.tokens])
|
||||||
|
lines.extend( ['', str(self.error)] )
|
||||||
|
return '\n'.join(lines)
|
@@ -79,10 +79,13 @@ from spark_parser import GenericASTTraversal, DEFAULT_DEBUG as PARSER_DEFAULT_DE
|
|||||||
from uncompyle6.scanner import Code, get_scanner
|
from uncompyle6.scanner import Code, get_scanner
|
||||||
from uncompyle6.scanners.tok import Token, NoneToken
|
from uncompyle6.scanners.tok import Token, NoneToken
|
||||||
import uncompyle6.parser as python_parser
|
import uncompyle6.parser as python_parser
|
||||||
|
from uncompyle6.semantics.make_function import (
|
||||||
|
make_function2, make_function3, make_function3_annotate, find_globals)
|
||||||
|
from uncompyle6.semantics.parser_error import ParserError
|
||||||
|
|
||||||
from uncompyle6.show import (
|
from uncompyle6.show import (
|
||||||
maybe_show_asm,
|
maybe_show_asm,
|
||||||
maybe_show_ast,
|
maybe_show_ast,
|
||||||
maybe_show_ast_param_default,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if PYTHON3:
|
if PYTHON3:
|
||||||
@@ -430,45 +433,6 @@ def is_docstring(node):
|
|||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class ParserError(python_parser.ParserError):
|
|
||||||
def __init__(self, error, tokens):
|
|
||||||
self.error = error # previous exception
|
|
||||||
self.tokens = tokens
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
lines = ['--- This code section failed: ---']
|
|
||||||
lines.extend([str(i) for i in self.tokens])
|
|
||||||
lines.extend( ['', str(self.error)] )
|
|
||||||
return '\n'.join(lines)
|
|
||||||
|
|
||||||
def find_globals(node, globs):
|
|
||||||
"""Find globals in this statement."""
|
|
||||||
for n in node:
|
|
||||||
if isinstance(n, AST):
|
|
||||||
globs = find_globals(n, globs)
|
|
||||||
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL'):
|
|
||||||
globs.add(n.pattr)
|
|
||||||
return globs
|
|
||||||
|
|
||||||
def find_all_globals(node, globs):
|
|
||||||
"""Find globals in this statement."""
|
|
||||||
for n in node:
|
|
||||||
if isinstance(n, AST):
|
|
||||||
globs = find_all_globals(n, globs)
|
|
||||||
elif n.type in ('STORE_GLOBAL', 'DELETE_GLOBAL', 'LOAD_GLOBAL'):
|
|
||||||
globs.add(n.pattr)
|
|
||||||
return globs
|
|
||||||
|
|
||||||
def find_none(node):
|
|
||||||
for n in node:
|
|
||||||
if isinstance(n, AST):
|
|
||||||
if not n in ('return_stmt', 'return_if_stmt'):
|
|
||||||
if find_none(n):
|
|
||||||
return True
|
|
||||||
elif n.type == 'LOAD_CONST' and n.pattr is None:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
class SourceWalkerError(Exception):
|
class SourceWalkerError(Exception):
|
||||||
def __init__(self, errmsg):
|
def __init__(self, errmsg):
|
||||||
self.errmsg = errmsg
|
self.errmsg = errmsg
|
||||||
@@ -617,160 +581,6 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
'store_locals': ( '%|# inspect.currentframe().f_locals = __locals__\n', ),
|
'store_locals': ( '%|# inspect.currentframe().f_locals = __locals__\n', ),
|
||||||
})
|
})
|
||||||
|
|
||||||
def make_function3(node, isLambda, nested=1,
|
|
||||||
codeNode=None, annotate=None):
|
|
||||||
"""Dump function defintion, doc string, and function
|
|
||||||
body. This code is specialzed for Python 3"""
|
|
||||||
|
|
||||||
def build_param(ast, name, default):
|
|
||||||
"""build parameters:
|
|
||||||
- handle defaults
|
|
||||||
- handle format tuple parameters
|
|
||||||
"""
|
|
||||||
if default:
|
|
||||||
value = self.traverse(default, indent='')
|
|
||||||
maybe_show_ast_param_default(self.showast, name, value)
|
|
||||||
result = '%s=%s' % (name, value)
|
|
||||||
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
|
||||||
result += 'None'
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return name
|
|
||||||
|
|
||||||
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
|
||||||
assert node[-1].type.startswith('MAKE_')
|
|
||||||
|
|
||||||
args_node = node[-1]
|
|
||||||
if isinstance(args_node.attr, tuple):
|
|
||||||
# positional args are before kwargs
|
|
||||||
defparams = node[:args_node.attr[0]]
|
|
||||||
pos_args, kw_args, annotate_args = args_node.attr
|
|
||||||
else:
|
|
||||||
defparams = node[:args_node.attr]
|
|
||||||
kw_args = 0
|
|
||||||
pass
|
|
||||||
|
|
||||||
if 3.0 <= self.version <= 3.2:
|
|
||||||
lambda_index = -2
|
|
||||||
elif 3.03 <= self.version:
|
|
||||||
lambda_index = -3
|
|
||||||
else:
|
|
||||||
lambda_index = None
|
|
||||||
|
|
||||||
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
|
||||||
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
|
||||||
code = node[lambda_index].attr
|
|
||||||
else:
|
|
||||||
code = codeNode.attr
|
|
||||||
|
|
||||||
assert iscode(code)
|
|
||||||
code = Code(code, self.scanner, self.currentclass)
|
|
||||||
|
|
||||||
# add defaults values to parameter names
|
|
||||||
argc = code.co_argcount
|
|
||||||
paramnames = list(code.co_varnames[:argc])
|
|
||||||
|
|
||||||
try:
|
|
||||||
ast = self.build_ast(code._tokens,
|
|
||||||
code._customize,
|
|
||||||
isLambda = isLambda,
|
|
||||||
noneInNames = ('None' in code.co_names))
|
|
||||||
except ParserError as p:
|
|
||||||
self.write(str(p))
|
|
||||||
self.ERROR = p
|
|
||||||
return
|
|
||||||
|
|
||||||
kw_pairs = args_node.attr[1]
|
|
||||||
indent = self.indent
|
|
||||||
|
|
||||||
if isLambda:
|
|
||||||
self.write("lambda ")
|
|
||||||
else:
|
|
||||||
self.write("(")
|
|
||||||
|
|
||||||
last_line = self.f.getvalue().split("\n")[-1]
|
|
||||||
l = len(last_line)
|
|
||||||
indent = ' ' * l
|
|
||||||
line_number = self.line_number
|
|
||||||
|
|
||||||
|
|
||||||
if 4 & code.co_flags: # flag 2 -> variable number of args
|
|
||||||
self.write('*%s' % code.co_varnames[argc + kw_pairs])
|
|
||||||
argc += 1
|
|
||||||
|
|
||||||
i = len(paramnames) - len(defparams)
|
|
||||||
self.write(",".join(paramnames[:i]))
|
|
||||||
suffix = ', ' if i > 0 else ''
|
|
||||||
for n in node:
|
|
||||||
if n == 'pos_arg':
|
|
||||||
self.write(suffix)
|
|
||||||
self.write(paramnames[i] + '=')
|
|
||||||
i += 1
|
|
||||||
self.preorder(n)
|
|
||||||
if (line_number != self.line_number):
|
|
||||||
suffix = ",\n" + indent
|
|
||||||
line_number = self.line_number
|
|
||||||
else:
|
|
||||||
suffix = ', '
|
|
||||||
|
|
||||||
# self.println(indent, '#flags:\t', int(code.co_flags))
|
|
||||||
if kw_args > 0:
|
|
||||||
if not (4 & code.co_flags):
|
|
||||||
if argc > 0:
|
|
||||||
self.write(", *, ")
|
|
||||||
else:
|
|
||||||
self.write("*, ")
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.write(", ")
|
|
||||||
|
|
||||||
kwargs = node[0]
|
|
||||||
last = len(kwargs)-1
|
|
||||||
i = 0
|
|
||||||
for n in node[0]:
|
|
||||||
if n == 'kwarg':
|
|
||||||
self.write('%s=' % n[0].pattr)
|
|
||||||
self.preorder(n[1])
|
|
||||||
if i < last:
|
|
||||||
self.write(', ')
|
|
||||||
i += 1
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
|
|
||||||
if 8 & code.co_flags: # flag 3 -> keyword args
|
|
||||||
if argc > 0:
|
|
||||||
self.write(', ')
|
|
||||||
self.write('**%s' % code.co_varnames[argc + kw_pairs])
|
|
||||||
|
|
||||||
if isLambda:
|
|
||||||
self.write(": ")
|
|
||||||
else:
|
|
||||||
self.write(')')
|
|
||||||
if annotate:
|
|
||||||
self.write(' -> "%s"' % annotate)
|
|
||||||
self.println(":")
|
|
||||||
|
|
||||||
if (len(code.co_consts) > 0 and
|
|
||||||
code.co_consts[0] is not None and not isLambda): # ugly
|
|
||||||
# docstring exists, dump it
|
|
||||||
self.print_docstring(indent, code.co_consts[0])
|
|
||||||
|
|
||||||
code._tokens = None # save memory
|
|
||||||
assert ast == 'stmts'
|
|
||||||
|
|
||||||
all_globals = find_all_globals(ast, set())
|
|
||||||
for g in ((all_globals & self.mod_globs) | find_globals(ast, set())):
|
|
||||||
self.println(self.indent, 'global ', g)
|
|
||||||
self.mod_globs -= all_globals
|
|
||||||
has_none = 'None' in code.co_names
|
|
||||||
rn = has_none and not find_none(ast)
|
|
||||||
self.gen_source(ast, code.co_name, code._customize, isLambda=isLambda,
|
|
||||||
returnNone=rn)
|
|
||||||
code._tokens = code._customize = None # save memory
|
|
||||||
|
|
||||||
self.make_function3 = make_function3
|
|
||||||
|
|
||||||
def n_mkfunc_annotate(node):
|
def n_mkfunc_annotate(node):
|
||||||
|
|
||||||
if self.version >= 3.3 or node[-2] == 'kwargs':
|
if self.version >= 3.3 or node[-2] == 'kwargs':
|
||||||
@@ -799,7 +609,7 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
annotate_return = node[annotate_last-1][0].attr
|
annotate_return = node[annotate_last-1][0].attr
|
||||||
pass
|
pass
|
||||||
# FIXME: handle and pass full annotate args
|
# FIXME: handle and pass full annotate args
|
||||||
self.make_function3(node, isLambda=False,
|
make_function3_annotate(self, node, isLambda=False,
|
||||||
codeNode=code, annotate=annotate_return)
|
codeNode=code, annotate=annotate_return)
|
||||||
|
|
||||||
if len(self.param_stack) > 1:
|
if len(self.param_stack) > 1:
|
||||||
@@ -1349,6 +1159,13 @@ class SourceWalker(GenericASTTraversal, object):
|
|||||||
self.indentLess()
|
self.indentLess()
|
||||||
self.prune() # stop recursing
|
self.prune() # stop recursing
|
||||||
|
|
||||||
|
def make_function(self, node, isLambda, nested=1,
|
||||||
|
codeNode=None, annotate=None):
|
||||||
|
if self.version >= 3.0:
|
||||||
|
make_function3(self, node, isLambda, nested, codeNode)
|
||||||
|
else:
|
||||||
|
make_function2(self, node, isLambda, nested, codeNode)
|
||||||
|
|
||||||
def n_mklambda(self, node):
|
def n_mklambda(self, node):
|
||||||
self.make_function(node, isLambda=True, codeNode=node[-2])
|
self.make_function(node, isLambda=True, codeNode=node[-2])
|
||||||
self.prune() # stop recursing
|
self.prune() # stop recursing
|
||||||
@@ -2295,193 +2112,6 @@ class SourceWalker(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, codeNode=None):
|
|
||||||
"""Dump function defintion, doc string, and function body."""
|
|
||||||
|
|
||||||
# FIXME: call make_function3 if we are self.version >= 3.0
|
|
||||||
# and then simplify the below.
|
|
||||||
|
|
||||||
def build_param(ast, name, default):
|
|
||||||
"""build parameters:
|
|
||||||
- handle defaults
|
|
||||||
- handle format tuple parameters
|
|
||||||
"""
|
|
||||||
if self.version < 3.0:
|
|
||||||
# if formal parameter is a tuple, the paramater name
|
|
||||||
# starts with a dot (eg. '.1', '.2')
|
|
||||||
if name.startswith('.'):
|
|
||||||
# replace the name with the tuple-string
|
|
||||||
name = self.get_tuple_parameter(ast, name)
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
|
|
||||||
if default:
|
|
||||||
value = self.traverse(default, indent='')
|
|
||||||
maybe_show_ast_param_default(self.showast, name, value)
|
|
||||||
result = '%s=%s' % (name, value)
|
|
||||||
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
|
||||||
result += 'None'
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return name
|
|
||||||
|
|
||||||
# MAKE_FUNCTION_... or MAKE_CLOSURE_...
|
|
||||||
assert node[-1].type.startswith('MAKE_')
|
|
||||||
|
|
||||||
args_node = node[-1]
|
|
||||||
if isinstance(args_node.attr, tuple):
|
|
||||||
if self.version <= 3.3:
|
|
||||||
# positional args are after kwargs
|
|
||||||
defparams = node[1:args_node.attr[0]+1]
|
|
||||||
else:
|
|
||||||
# positional args are before kwargs
|
|
||||||
defparams = node[:args_node.attr[0]]
|
|
||||||
pos_args, kw_args, annotate_args = args_node.attr
|
|
||||||
else:
|
|
||||||
defparams = node[:args_node.attr]
|
|
||||||
kw_args = 0
|
|
||||||
pass
|
|
||||||
|
|
||||||
if 3.0 <= self.version <= 3.2:
|
|
||||||
lambda_index = -2
|
|
||||||
elif 3.03 <= self.version:
|
|
||||||
lambda_index = -3
|
|
||||||
else:
|
|
||||||
lambda_index = None
|
|
||||||
|
|
||||||
if lambda_index and isLambda and iscode(node[lambda_index].attr):
|
|
||||||
assert node[lambda_index].type == 'LOAD_LAMBDA'
|
|
||||||
code = node[lambda_index].attr
|
|
||||||
else:
|
|
||||||
code = codeNode.attr
|
|
||||||
|
|
||||||
assert iscode(code)
|
|
||||||
code = Code(code, self.scanner, self.currentclass)
|
|
||||||
|
|
||||||
# add defaults values to parameter names
|
|
||||||
argc = code.co_argcount
|
|
||||||
paramnames = list(code.co_varnames[:argc])
|
|
||||||
|
|
||||||
# defaults are for last n parameters, thus reverse
|
|
||||||
if not 3.0 <= self.version <= 3.2:
|
|
||||||
paramnames.reverse(); defparams.reverse()
|
|
||||||
|
|
||||||
try:
|
|
||||||
ast = self.build_ast(code._tokens,
|
|
||||||
code._customize,
|
|
||||||
isLambda = isLambda,
|
|
||||||
noneInNames = ('None' in code.co_names))
|
|
||||||
except ParserError as p:
|
|
||||||
self.write(str(p))
|
|
||||||
self.ERROR = p
|
|
||||||
return
|
|
||||||
|
|
||||||
kw_pairs = args_node.attr[1] if self.version >= 3.0 else 0
|
|
||||||
indent = self.indent
|
|
||||||
|
|
||||||
# build parameters
|
|
||||||
if self.version != 3.2:
|
|
||||||
params = [build_param(ast, name, default) for
|
|
||||||
name, default in zip_longest(paramnames, defparams, fillvalue=None)]
|
|
||||||
params.reverse() # back to correct order
|
|
||||||
|
|
||||||
if 4 & code.co_flags: # flag 2 -> variable number of args
|
|
||||||
if self.version > 3.0:
|
|
||||||
params.append('*%s' % code.co_varnames[argc + kw_pairs])
|
|
||||||
else:
|
|
||||||
params.append('*%s' % code.co_varnames[argc])
|
|
||||||
argc += 1
|
|
||||||
|
|
||||||
# dump parameter list (with default values)
|
|
||||||
if isLambda:
|
|
||||||
self.write("lambda ", ", ".join(params))
|
|
||||||
else:
|
|
||||||
self.write("(", ", ".join(params))
|
|
||||||
# self.println(indent, '#flags:\t', int(code.co_flags))
|
|
||||||
|
|
||||||
else:
|
|
||||||
if isLambda:
|
|
||||||
self.write("lambda ")
|
|
||||||
else:
|
|
||||||
self.write("(")
|
|
||||||
|
|
||||||
if 4 & code.co_flags: # flag 2 -> variable number of args
|
|
||||||
self.write('*%s' % code.co_varnames[argc + kw_pairs])
|
|
||||||
argc += 1
|
|
||||||
|
|
||||||
i = len(paramnames) - len(defparams)
|
|
||||||
self.write(",".join(paramnames[:i]))
|
|
||||||
suffix = ', ' if i > 0 else ''
|
|
||||||
for n in node:
|
|
||||||
if n == 'pos_arg':
|
|
||||||
self.write(suffix)
|
|
||||||
self.write(paramnames[i] + '=')
|
|
||||||
i += 1
|
|
||||||
self.preorder(n)
|
|
||||||
suffix = ', '
|
|
||||||
|
|
||||||
if kw_args > 0:
|
|
||||||
if not (4 & code.co_flags):
|
|
||||||
if argc > 0:
|
|
||||||
self.write(", *, ")
|
|
||||||
else:
|
|
||||||
self.write("*, ")
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.write(", ")
|
|
||||||
|
|
||||||
if not 3.0 <= self.version <= 3.2:
|
|
||||||
for n in node:
|
|
||||||
if n == 'pos_arg':
|
|
||||||
continue
|
|
||||||
elif self.version >= 3.4 and n.type != 'kwargs':
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
self.preorder(n)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
kwargs = node[0]
|
|
||||||
last = len(kwargs)-1
|
|
||||||
i = 0
|
|
||||||
for n in node[0]:
|
|
||||||
if n == 'kwarg':
|
|
||||||
self.write('%s=' % n[0].pattr)
|
|
||||||
self.preorder(n[1])
|
|
||||||
if i < last:
|
|
||||||
self.write(', ')
|
|
||||||
i += 1
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
pass
|
|
||||||
|
|
||||||
if 8 & code.co_flags: # flag 3 -> keyword args
|
|
||||||
if argc > 0:
|
|
||||||
self.write(', ')
|
|
||||||
self.write('**%s' % code.co_varnames[argc + kw_pairs])
|
|
||||||
|
|
||||||
if isLambda:
|
|
||||||
self.write(": ")
|
|
||||||
else:
|
|
||||||
self.println("):")
|
|
||||||
|
|
||||||
if len(code.co_consts)>0 and code.co_consts[0] is not None and not isLambda: # ugly
|
|
||||||
# docstring exists, dump it
|
|
||||||
self.print_docstring(indent, code.co_consts[0])
|
|
||||||
|
|
||||||
code._tokens = None # save memory
|
|
||||||
assert ast == 'stmts'
|
|
||||||
|
|
||||||
all_globals = find_all_globals(ast, set())
|
|
||||||
for g in ((all_globals & self.mod_globs) | find_globals(ast, set())):
|
|
||||||
self.println(self.indent, 'global ', g)
|
|
||||||
self.mod_globs -= all_globals
|
|
||||||
has_none = 'None' in code.co_names
|
|
||||||
rn = has_none and not find_none(ast)
|
|
||||||
self.gen_source(ast, code.co_name, code._customize, isLambda=isLambda,
|
|
||||||
returnNone=rn)
|
|
||||||
code._tokens = None; code._customize = None # save memory
|
|
||||||
|
|
||||||
def build_class(self, code):
|
def build_class(self, code):
|
||||||
"""Dump class definition, doc string and class body."""
|
"""Dump class definition, doc string and class body."""
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user