diff --git a/uncompyle6/semantics/customize3.py b/uncompyle6/semantics/customize3.py index 70e46889..0bd5c067 100644 --- a/uncompyle6/semantics/customize3.py +++ b/uncompyle6/semantics/customize3.py @@ -21,7 +21,7 @@ from uncompyle6.semantics.consts import TABLE_DIRECT from xdis.code import iscode from uncompyle6.scanner import Code 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.customize36 import customize_for_version36 from uncompyle6.semantics.customize37 import customize_for_version37 diff --git a/uncompyle6/semantics/make_function2.py b/uncompyle6/semantics/make_function2.py new file mode 100644 index 00000000..98b707b9 --- /dev/null +++ b/uncompyle6/semantics/make_function2.py @@ -0,0 +1,209 @@ +# Copyright (c) 2015-2019 by Rocky Bernstein +# Copyright (c) 2000-2002 by hartmut Goebel +# +# 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 . +""" +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 diff --git a/uncompyle6/semantics/make_function.py b/uncompyle6/semantics/make_function3.py similarity index 84% rename from uncompyle6/semantics/make_function.py rename to uncompyle6/semantics/make_function3.py index d8b6684f..708f04a9 100644 --- a/uncompyle6/semantics/make_function.py +++ b/uncompyle6/semantics/make_function3.py @@ -303,179 +303,6 @@ def make_function3_annotate( 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): """Dump function definition, doc string, and function body in Python version 3.0 and above diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index e7307597..6acfffc0 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -137,7 +137,8 @@ from uncompyle6.parsers.treenode import SyntaxTree from spark_parser import GenericASTTraversal, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG from uncompyle6.scanner import Code, get_scanner 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.check_ast import checker from uncompyle6.semantics.customize import customize_for_version