make_function2.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. # Copyright (c) 2015-2021 2024 by Rocky Bernstein
  2. # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. """
  17. All the crazy things we have to do to handle Python functions in Python before 3.0.
  18. The saga of changes continues in 3.0 and above and in other files.
  19. """
  20. from itertools import zip_longest
  21. from xdis import code_has_star_arg, code_has_star_star_arg, iscode
  22. from uncompyle6.parser import ParserError as ParserError2
  23. from uncompyle6.scanner import Code
  24. from uncompyle6.semantics.helper import (
  25. find_all_globals,
  26. find_globals_and_nonlocals,
  27. find_none,
  28. print_docstring,
  29. )
  30. from uncompyle6.semantics.parser_error import ParserError
  31. def make_function2(self, node, is_lambda, nested=1, code_node=None):
  32. """
  33. Dump function definition, doc string, and function body.
  34. This code is specialied for Python 2.
  35. """
  36. def build_param(ast, name, default):
  37. """build parameters:
  38. - handle defaults
  39. - handle format tuple parameters
  40. """
  41. # if formal parameter is a tuple, the parameter name
  42. # starts with a dot (eg. '.1', '.2')
  43. if name.startswith("."):
  44. # replace the name with the tuple-string
  45. name = self.get_tuple_parameter(ast, name)
  46. pass
  47. if default:
  48. value = self.traverse(default, indent="")
  49. result = "%s=%s" % (name, value)
  50. if result[-2:] == "= ": # default was 'LOAD_CONST None'
  51. result += "None"
  52. return result
  53. else:
  54. return name
  55. # MAKE_FUNCTION_... or MAKE_CLOSURE_...
  56. assert node[-1].kind.startswith("MAKE_")
  57. args_node = node[-1]
  58. if isinstance(args_node.attr, tuple):
  59. # positional args are after kwargs
  60. defparams = node[1 : args_node.attr[0] + 1]
  61. pos_args, kw_args, annotate_argc = args_node.attr
  62. else:
  63. defparams = node[: args_node.attr]
  64. kw_args = 0
  65. pass
  66. lambda_index = None
  67. if lambda_index and is_lambda and iscode(node[lambda_index].attr):
  68. assert node[lambda_index].kind == "LOAD_LAMBDA"
  69. code = node[lambda_index].attr
  70. else:
  71. code = code_node.attr
  72. assert iscode(code)
  73. code = Code(code, self.scanner, self.currentclass)
  74. # add defaults values to parameter names
  75. argc = code.co_argcount
  76. paramnames = list(code.co_varnames[:argc])
  77. # defaults are for last n parameters, thus reverse
  78. paramnames.reverse()
  79. defparams.reverse()
  80. try:
  81. ast = self.build_ast(
  82. code._tokens,
  83. code._customize,
  84. code,
  85. is_lambda=is_lambda,
  86. noneInNames=("None" in code.co_names),
  87. )
  88. except (ParserError, ParserError2) as p:
  89. self.write(str(p))
  90. if not self.tolerate_errors:
  91. self.ERROR = p
  92. return
  93. kw_pairs = 0
  94. indent = self.indent
  95. # build parameters
  96. params = [
  97. build_param(ast, name, default)
  98. for name, default in zip_longest(paramnames, defparams, fillvalue=None)
  99. ]
  100. params.reverse() # back to correct order
  101. if code_has_star_arg(code):
  102. params.append("*%s" % code.co_varnames[argc])
  103. argc += 1
  104. # dump parameter list (with default values)
  105. if is_lambda:
  106. self.write("lambda ", ", ".join(params))
  107. # If the last statement is None (which is the
  108. # same thing as "return None" in a lambda) and the
  109. # next to last statement is a "yield". Then we want to
  110. # drop the (return) None since that was just put there
  111. # to have something to after the yield finishes.
  112. # FIXME: this is a bit hoaky and not general
  113. if (
  114. len(ast) > 1
  115. and self.traverse(ast[-1]) == "None"
  116. and self.traverse(ast[-2]).strip().startswith("yield")
  117. ):
  118. del ast[-1]
  119. # Now pick out the expr part of the last statement
  120. ast_expr = ast[-1]
  121. while ast_expr.kind != "expr":
  122. ast_expr = ast_expr[0]
  123. ast[-1] = ast_expr
  124. pass
  125. else:
  126. self.write("(", ", ".join(params))
  127. if kw_args > 0:
  128. if not (4 & code.co_flags):
  129. if argc > 0:
  130. self.write(", *, ")
  131. else:
  132. self.write("*, ")
  133. pass
  134. else:
  135. self.write(", ")
  136. for n in node:
  137. if n == "pos_arg":
  138. continue
  139. else:
  140. self.preorder(n)
  141. break
  142. pass
  143. if code_has_star_star_arg(code):
  144. if argc > 0:
  145. self.write(", ")
  146. self.write("**%s" % code.co_varnames[argc + kw_pairs])
  147. if is_lambda:
  148. self.write(": ")
  149. else:
  150. self.println("):")
  151. if (
  152. len(code.co_consts) > 0 and code.co_consts[0] is not None and not is_lambda
  153. ): # ugly
  154. # docstring exists, dump it
  155. print_docstring(self, indent, code.co_consts[0])
  156. if not is_lambda:
  157. assert ast == "stmts"
  158. all_globals = find_all_globals(ast, set())
  159. globals, nonlocals = find_globals_and_nonlocals(
  160. ast, set(), set(), code, self.version
  161. )
  162. # Python 2 doesn't support the "nonlocal" statement
  163. assert self.version >= (3, 0) or not nonlocals
  164. for g in sorted((all_globals & self.mod_globs) | globals):
  165. self.println(self.indent, "global ", g)
  166. self.mod_globs -= all_globals
  167. has_none = "None" in code.co_names
  168. rn = has_none and not find_none(ast)
  169. self.gen_source(
  170. ast, code.co_name, code._customize, is_lambda=is_lambda, returnNone=rn
  171. )
  172. code._tokens = None # save memory
  173. code._customize = None # save memory