You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 01:09:52 +08:00
Split out make_function.py into v2 and v3 versions
A custom 3.3 make_function will be coming soon.
This commit is contained in:
@@ -21,7 +21,7 @@ from uncompyle6.semantics.consts import TABLE_DIRECT
|
|||||||
from xdis.code import iscode
|
from xdis.code import iscode
|
||||||
from uncompyle6.scanner import Code
|
from uncompyle6.scanner import Code
|
||||||
from uncompyle6.semantics.helper import gen_function_parens_adjust
|
from uncompyle6.semantics.helper import gen_function_parens_adjust
|
||||||
from uncompyle6.semantics.make_function import make_function3_annotate
|
from uncompyle6.semantics.make_function3 import make_function3_annotate
|
||||||
from uncompyle6.semantics.customize35 import customize_for_version35
|
from uncompyle6.semantics.customize35 import customize_for_version35
|
||||||
from uncompyle6.semantics.customize36 import customize_for_version36
|
from uncompyle6.semantics.customize36 import customize_for_version36
|
||||||
from uncompyle6.semantics.customize37 import customize_for_version37
|
from uncompyle6.semantics.customize37 import customize_for_version37
|
||||||
|
209
uncompyle6/semantics/make_function2.py
Normal file
209
uncompyle6/semantics/make_function2.py
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# Copyright (c) 2015-2019 by Rocky Bernstein
|
||||||
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
All the crazy things we have to do to handle Python functions
|
||||||
|
"""
|
||||||
|
from xdis.code import iscode, code_has_star_arg, code_has_star_star_arg
|
||||||
|
from uncompyle6.scanner import Code
|
||||||
|
from uncompyle6.parsers.treenode import SyntaxTree
|
||||||
|
from uncompyle6 import PYTHON3
|
||||||
|
from uncompyle6.semantics.parser_error import ParserError
|
||||||
|
from uncompyle6.parser import ParserError as ParserError2
|
||||||
|
from uncompyle6.semantics.helper import (
|
||||||
|
print_docstring,
|
||||||
|
find_all_globals,
|
||||||
|
find_globals_and_nonlocals,
|
||||||
|
find_none,
|
||||||
|
)
|
||||||
|
|
||||||
|
if PYTHON3:
|
||||||
|
from itertools import zip_longest
|
||||||
|
else:
|
||||||
|
from itertools import izip_longest as zip_longest
|
||||||
|
|
||||||
|
from uncompyle6.show import maybe_show_tree_param_default
|
||||||
|
|
||||||
|
def make_function2(self, node, is_lambda, nested=1, code_node=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_tree_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].kind.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_argc = args_node.attr
|
||||||
|
else:
|
||||||
|
defparams = node[: args_node.attr]
|
||||||
|
kw_args = 0
|
||||||
|
pass
|
||||||
|
|
||||||
|
lambda_index = None
|
||||||
|
|
||||||
|
if lambda_index and is_lambda and iscode(node[lambda_index].attr):
|
||||||
|
assert node[lambda_index].kind == "LOAD_LAMBDA"
|
||||||
|
code = node[lambda_index].attr
|
||||||
|
else:
|
||||||
|
code = code_node.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,
|
||||||
|
is_lambda=is_lambda,
|
||||||
|
noneInNames=("None" in code.co_names),
|
||||||
|
)
|
||||||
|
except (ParserError, ParserError2) as p:
|
||||||
|
self.write(str(p))
|
||||||
|
if not self.tolerate_errors:
|
||||||
|
self.ERROR = p
|
||||||
|
return
|
||||||
|
|
||||||
|
kw_pairs = 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 code_has_star_arg(code):
|
||||||
|
params.append("*%s" % code.co_varnames[argc])
|
||||||
|
argc += 1
|
||||||
|
|
||||||
|
# dump parameter list (with default values)
|
||||||
|
if is_lambda:
|
||||||
|
self.write("lambda ", ", ".join(params))
|
||||||
|
# If the last statement is None (which is the
|
||||||
|
# same thing as "return None" in a lambda) and the
|
||||||
|
# next to last statement is a "yield". Then we want to
|
||||||
|
# drop the (return) None since that was just put there
|
||||||
|
# to have something to after the yield finishes.
|
||||||
|
# FIXME: this is a bit hoaky and not general
|
||||||
|
if (
|
||||||
|
len(ast) > 1
|
||||||
|
and self.traverse(ast[-1]) == "None"
|
||||||
|
and self.traverse(ast[-2]).strip().startswith("yield")
|
||||||
|
):
|
||||||
|
del ast[-1]
|
||||||
|
# Now pick out the expr part of the last statement
|
||||||
|
ast_expr = ast[-1]
|
||||||
|
while ast_expr.kind != "expr":
|
||||||
|
ast_expr = ast_expr[0]
|
||||||
|
ast[-1] = ast_expr
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.write("(", ", ".join(params))
|
||||||
|
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
self.preorder(n)
|
||||||
|
break
|
||||||
|
pass
|
||||||
|
|
||||||
|
if code_has_star_star_arg(code):
|
||||||
|
if argc > 0:
|
||||||
|
self.write(", ")
|
||||||
|
self.write("**%s" % code.co_varnames[argc + kw_pairs])
|
||||||
|
|
||||||
|
if is_lambda:
|
||||||
|
self.write(": ")
|
||||||
|
else:
|
||||||
|
self.println("):")
|
||||||
|
|
||||||
|
if (
|
||||||
|
len(code.co_consts) > 0 and code.co_consts[0] is not None and not is_lambda
|
||||||
|
): # ugly
|
||||||
|
# docstring exists, dump it
|
||||||
|
print_docstring(self, indent, code.co_consts[0])
|
||||||
|
|
||||||
|
code._tokens = None # save memory
|
||||||
|
if not is_lambda:
|
||||||
|
assert ast == "stmts"
|
||||||
|
|
||||||
|
all_globals = find_all_globals(ast, set())
|
||||||
|
|
||||||
|
globals, nonlocals = find_globals_and_nonlocals(
|
||||||
|
ast, set(), set(), code, self.version
|
||||||
|
)
|
||||||
|
|
||||||
|
# Python 2 doesn't support the "nonlocal" statement
|
||||||
|
assert self.version >= 3.0 or not nonlocals
|
||||||
|
|
||||||
|
for g in sorted((all_globals & self.mod_globs) | globals):
|
||||||
|
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, is_lambda=is_lambda, returnNone=rn
|
||||||
|
)
|
||||||
|
code._tokens = None
|
||||||
|
code._customize = None # save memory
|
@@ -303,179 +303,6 @@ def make_function3_annotate(
|
|||||||
code._tokens = code._customize = None # save memory
|
code._tokens = code._customize = None # save memory
|
||||||
|
|
||||||
|
|
||||||
def make_function2(self, node, is_lambda, nested=1, code_node=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_tree_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].kind.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_argc = args_node.attr
|
|
||||||
else:
|
|
||||||
defparams = node[: args_node.attr]
|
|
||||||
kw_args = 0
|
|
||||||
pass
|
|
||||||
|
|
||||||
lambda_index = None
|
|
||||||
|
|
||||||
if lambda_index and is_lambda and iscode(node[lambda_index].attr):
|
|
||||||
assert node[lambda_index].kind == "LOAD_LAMBDA"
|
|
||||||
code = node[lambda_index].attr
|
|
||||||
else:
|
|
||||||
code = code_node.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,
|
|
||||||
is_lambda=is_lambda,
|
|
||||||
noneInNames=("None" in code.co_names),
|
|
||||||
)
|
|
||||||
except (ParserError, ParserError2) as p:
|
|
||||||
self.write(str(p))
|
|
||||||
if not self.tolerate_errors:
|
|
||||||
self.ERROR = p
|
|
||||||
return
|
|
||||||
|
|
||||||
kw_pairs = 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 code_has_star_arg(code):
|
|
||||||
params.append("*%s" % code.co_varnames[argc])
|
|
||||||
argc += 1
|
|
||||||
|
|
||||||
# dump parameter list (with default values)
|
|
||||||
if is_lambda:
|
|
||||||
self.write("lambda ", ", ".join(params))
|
|
||||||
# If the last statement is None (which is the
|
|
||||||
# same thing as "return None" in a lambda) and the
|
|
||||||
# next to last statement is a "yield". Then we want to
|
|
||||||
# drop the (return) None since that was just put there
|
|
||||||
# to have something to after the yield finishes.
|
|
||||||
# FIXME: this is a bit hoaky and not general
|
|
||||||
if (
|
|
||||||
len(ast) > 1
|
|
||||||
and self.traverse(ast[-1]) == "None"
|
|
||||||
and self.traverse(ast[-2]).strip().startswith("yield")
|
|
||||||
):
|
|
||||||
del ast[-1]
|
|
||||||
# Now pick out the expr part of the last statement
|
|
||||||
ast_expr = ast[-1]
|
|
||||||
while ast_expr.kind != "expr":
|
|
||||||
ast_expr = ast_expr[0]
|
|
||||||
ast[-1] = ast_expr
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.write("(", ", ".join(params))
|
|
||||||
|
|
||||||
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
|
|
||||||
else:
|
|
||||||
self.preorder(n)
|
|
||||||
break
|
|
||||||
pass
|
|
||||||
|
|
||||||
if code_has_star_star_arg(code):
|
|
||||||
if argc > 0:
|
|
||||||
self.write(", ")
|
|
||||||
self.write("**%s" % code.co_varnames[argc + kw_pairs])
|
|
||||||
|
|
||||||
if is_lambda:
|
|
||||||
self.write(": ")
|
|
||||||
else:
|
|
||||||
self.println("):")
|
|
||||||
|
|
||||||
if (
|
|
||||||
len(code.co_consts) > 0 and code.co_consts[0] is not None and not is_lambda
|
|
||||||
): # ugly
|
|
||||||
# docstring exists, dump it
|
|
||||||
print_docstring(self, indent, code.co_consts[0])
|
|
||||||
|
|
||||||
code._tokens = None # save memory
|
|
||||||
if not is_lambda:
|
|
||||||
assert ast == "stmts"
|
|
||||||
|
|
||||||
all_globals = find_all_globals(ast, set())
|
|
||||||
|
|
||||||
globals, nonlocals = find_globals_and_nonlocals(
|
|
||||||
ast, set(), set(), code, self.version
|
|
||||||
)
|
|
||||||
|
|
||||||
# Python 2 doesn't support the "nonlocal" statement
|
|
||||||
assert self.version >= 3.0 or not nonlocals
|
|
||||||
|
|
||||||
for g in sorted((all_globals & self.mod_globs) | globals):
|
|
||||||
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, is_lambda=is_lambda, returnNone=rn
|
|
||||||
)
|
|
||||||
code._tokens = None
|
|
||||||
code._customize = None # save memory
|
|
||||||
|
|
||||||
|
|
||||||
def make_function3(self, node, is_lambda, nested=1, code_node=None):
|
def make_function3(self, node, is_lambda, nested=1, code_node=None):
|
||||||
"""Dump function definition, doc string, and function body in
|
"""Dump function definition, doc string, and function body in
|
||||||
Python version 3.0 and above
|
Python version 3.0 and above
|
@@ -137,7 +137,8 @@ from uncompyle6.parsers.treenode import SyntaxTree
|
|||||||
from spark_parser import GenericASTTraversal, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
from spark_parser import GenericASTTraversal, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||||
from uncompyle6.scanner import Code, get_scanner
|
from uncompyle6.scanner import Code, get_scanner
|
||||||
import uncompyle6.parser as python_parser
|
import uncompyle6.parser as python_parser
|
||||||
from uncompyle6.semantics.make_function import make_function2, make_function3
|
from uncompyle6.semantics.make_function2 import make_function2
|
||||||
|
from uncompyle6.semantics.make_function3 import make_function3
|
||||||
from uncompyle6.semantics.parser_error import ParserError
|
from uncompyle6.semantics.parser_error import ParserError
|
||||||
from uncompyle6.semantics.check_ast import checker
|
from uncompyle6.semantics.check_ast import checker
|
||||||
from uncompyle6.semantics.customize import customize_for_version
|
from uncompyle6.semantics.customize import customize_for_version
|
||||||
|
Reference in New Issue
Block a user