make_function1.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. # Copyright (c) 2015-2023 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 typing import List, Tuple
  21. from uncompyle6.scanner import Code
  22. from uncompyle6.semantics.parser_error import ParserError
  23. from uncompyle6.parser import ParserError as ParserError2
  24. from uncompyle6.semantics.helper import (
  25. print_docstring,
  26. find_all_globals,
  27. find_globals_and_nonlocals,
  28. find_none,
  29. )
  30. from xdis import iscode
  31. def make_function1(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(tree, param_names: List[str]) -> Tuple[bool, List[str]]:
  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. args = tree[0]
  44. del tree[0]
  45. params = []
  46. assert args.kind in ("star_args", "args", "varargs")
  47. has_star_arg = args.kind in ("star_args", "varargs")
  48. args_store = args[2]
  49. if args_store == "args_store":
  50. for arg in args_store:
  51. params.append(param_names[arg.attr])
  52. return has_star_arg, params
  53. # MAKE_FUNCTION_... or MAKE_CLOSURE_...
  54. assert node[-1].kind.startswith("BUILD_")
  55. defparams = []
  56. # args_node = node[-1]
  57. # if isinstance(args_node.attr, tuple):
  58. # # positional args are after kwargs
  59. # defparams = node[1 : args_node.attr[0] + 1]
  60. # pos_args, kw_args, annotate_argc = args_node.attr
  61. # else:
  62. # defparams = node[: args_node.attr]
  63. # kw_args = 0
  64. # pass
  65. lambda_index = None
  66. if lambda_index and is_lambda and iscode(node[lambda_index].attr):
  67. assert node[lambda_index].kind == "LOAD_LAMBDA"
  68. code = node[lambda_index].attr
  69. else:
  70. code = code_node.attr
  71. assert iscode(code)
  72. code = Code(code, self.scanner, self.currentclass)
  73. # add defaults values to parameter names
  74. argc = code.co_argcount
  75. paramnames = list(code.co_varnames[:argc])
  76. # defaults are for last n parameters, thus reverse
  77. paramnames.reverse()
  78. defparams.reverse()
  79. try:
  80. tree = self.build_ast(
  81. code._tokens,
  82. code._customize,
  83. code,
  84. is_lambda=is_lambda,
  85. noneInNames=("None" in code.co_names),
  86. )
  87. except (ParserError, ParserError2) as p:
  88. self.write(str(p))
  89. if not self.tolerate_errors:
  90. self.ERROR = p
  91. return
  92. indent = self.indent
  93. # build parameters
  94. has_star_arg, params = build_param(tree, code.co_names)
  95. if has_star_arg:
  96. params[-1] = "*" + params[-1]
  97. # dump parameter list (with default values)
  98. if is_lambda:
  99. self.write("lambda ", ", ".join(params))
  100. # If the last statement is None (which is the
  101. # same thing as "return None" in a lambda) and the
  102. # next to last statement is a "yield". Then we want to
  103. # drop the (return) None since that was just put there
  104. # to have something to after the yield finishes.
  105. # FIXME: this is a bit hoaky and not general
  106. if (
  107. len(tree) > 1
  108. and self.traverse(tree[-1]) == "None"
  109. and self.traverse(tree[-2]).strip().startswith("yield")
  110. ):
  111. del tree[-1]
  112. # Now pick out the expr part of the last statement
  113. tree_expr = tree[-1]
  114. while tree_expr.kind != "expr":
  115. tree_expr = tree_expr[0]
  116. tree[-1] = tree_expr
  117. pass
  118. else:
  119. self.write("(", ", ".join(params))
  120. # if kw_args > 0:
  121. # if not (4 & code.co_flags):
  122. # if argc > 0:
  123. # self.write(", *, ")
  124. # else:
  125. # self.write("*, ")
  126. # pass
  127. # else:
  128. # self.write(", ")
  129. # for n in node:
  130. # if n == "pos_arg":
  131. # continue
  132. # else:
  133. # self.preorder(n)
  134. # break
  135. # pass
  136. # if code_has_star_star_arg(code):
  137. # if argc > 0:
  138. # self.write(", ")
  139. # self.write("**%s" % code.co_varnames[argc + kw_pairs])
  140. if is_lambda:
  141. self.write(": ")
  142. else:
  143. self.println("):")
  144. if (
  145. len(code.co_consts) > 0 and code.co_consts[0] is not None and not is_lambda
  146. ): # ugly
  147. # docstring exists, dump it
  148. print_docstring(self, indent, code.co_consts[0])
  149. if not is_lambda:
  150. assert tree == "stmts"
  151. all_globals = find_all_globals(tree, set())
  152. globals, nonlocals = find_globals_and_nonlocals(
  153. tree, set(), set(), code, self.version
  154. )
  155. # Python 1 doesn't support the "nonlocal" statement
  156. for g in sorted((all_globals & self.mod_globs) | globals):
  157. self.println(self.indent, "global ", g)
  158. self.mod_globs -= all_globals
  159. has_none = "None" in code.co_names
  160. rn = has_none and not find_none(tree)
  161. tree.code = code
  162. self.gen_source(
  163. tree, code.co_name, code._customize, is_lambda=is_lambda, returnNone=rn
  164. )
  165. code._tokens = None # save memory
  166. code._customize = None # save memory