functionutils.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import rope.base.exceptions
  2. import rope.base.pyobjects
  3. from rope.base.builtins import Lambda
  4. from rope.base import worder
  5. class DefinitionInfo(object):
  6. def __init__(self, function_name, is_method, args_with_defaults,
  7. args_arg, keywords_arg):
  8. self.function_name = function_name
  9. self.is_method = is_method
  10. self.args_with_defaults = args_with_defaults
  11. self.args_arg = args_arg
  12. self.keywords_arg = keywords_arg
  13. def to_string(self):
  14. return '%s(%s)' % (self.function_name, self.arguments_to_string())
  15. def arguments_to_string(self, from_index=0):
  16. params = []
  17. for arg, default in self.args_with_defaults:
  18. if default is not None:
  19. params.append('%s=%s' % (arg, default))
  20. else:
  21. params.append(arg)
  22. if self.args_arg is not None:
  23. params.append('*' + self.args_arg)
  24. if self.keywords_arg:
  25. params.append('**' + self.keywords_arg)
  26. return ', '.join(params[from_index:])
  27. @staticmethod
  28. def _read(pyfunction, code):
  29. scope = pyfunction.get_scope()
  30. parent = scope.parent
  31. parameter_names = pyfunction.get_param_names()
  32. kind = pyfunction.get_kind()
  33. is_method = kind == 'method'
  34. is_lambda = kind == 'lambda'
  35. info = _FunctionParser(code, is_method, is_lambda)
  36. args, keywords = info.get_parameters()
  37. args_arg = None
  38. keywords_arg = None
  39. if args and args[-1].startswith('**'):
  40. keywords_arg = args[-1][2:]
  41. del args[-1]
  42. if args and args[-1].startswith('*'):
  43. args_arg = args[-1][1:]
  44. del args[-1]
  45. args_with_defaults = [(name, None) for name in args]
  46. args_with_defaults.extend(keywords)
  47. return DefinitionInfo(info.get_function_name(), is_method,
  48. args_with_defaults, args_arg, keywords_arg)
  49. @staticmethod
  50. def read(pyfunction):
  51. pymodule = pyfunction.get_module()
  52. word_finder = worder.Worder(pymodule.source_code)
  53. lineno = pyfunction.get_ast().lineno
  54. start = pymodule.lines.get_line_start(lineno)
  55. if isinstance(pyfunction, Lambda):
  56. call = word_finder.get_lambda_and_args(start)
  57. else:
  58. call = word_finder.get_function_and_args_in_header(start)
  59. return DefinitionInfo._read(pyfunction, call)
  60. class CallInfo(object):
  61. def __init__(self, function_name, args, keywords, args_arg,
  62. keywords_arg, implicit_arg, constructor):
  63. self.function_name = function_name
  64. self.args = args
  65. self.keywords = keywords
  66. self.args_arg = args_arg
  67. self.keywords_arg = keywords_arg
  68. self.implicit_arg = implicit_arg
  69. self.constructor = constructor
  70. def to_string(self):
  71. function = self.function_name
  72. if self.implicit_arg:
  73. function = self.args[0] + '.' + self.function_name
  74. params = []
  75. start = 0
  76. if self.implicit_arg or self.constructor:
  77. start = 1
  78. if self.args[start:]:
  79. params.extend(self.args[start:])
  80. if self.keywords:
  81. params.extend(['%s=%s' % (name, value) for name, value in self.keywords])
  82. if self.args_arg is not None:
  83. params.append('*' + self.args_arg)
  84. if self.keywords_arg:
  85. params.append('**' + self.keywords_arg)
  86. return '%s(%s)' % (function, ', '.join(params))
  87. @staticmethod
  88. def read(primary, pyname, definition_info, code):
  89. is_method_call = CallInfo._is_method_call(primary, pyname)
  90. is_constructor = CallInfo._is_class(pyname)
  91. is_classmethod = CallInfo._is_classmethod(pyname)
  92. info = _FunctionParser(code, is_method_call or is_classmethod)
  93. args, keywords = info.get_parameters()
  94. args_arg = None
  95. keywords_arg = None
  96. if args and args[-1].startswith('**'):
  97. keywords_arg = args[-1][2:]
  98. del args[-1]
  99. if args and args[-1].startswith('*'):
  100. args_arg = args[-1][1:]
  101. del args[-1]
  102. if is_constructor:
  103. args.insert(0, definition_info.args_with_defaults[0][0])
  104. return CallInfo(info.get_function_name(), args, keywords, args_arg,
  105. keywords_arg, is_method_call or is_classmethod,
  106. is_constructor)
  107. @staticmethod
  108. def _is_method_call(primary, pyname):
  109. return primary is not None and \
  110. isinstance(primary.get_object().get_type(),
  111. rope.base.pyobjects.PyClass) and \
  112. CallInfo._is_method(pyname)
  113. @staticmethod
  114. def _is_class(pyname):
  115. return pyname is not None and \
  116. isinstance(pyname.get_object(),
  117. rope.base.pyobjects.PyClass)
  118. @staticmethod
  119. def _is_method(pyname):
  120. if pyname is not None and \
  121. isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction):
  122. return pyname.get_object().get_kind() == 'method'
  123. return False
  124. @staticmethod
  125. def _is_classmethod(pyname):
  126. if pyname is not None and \
  127. isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction):
  128. return pyname.get_object().get_kind() == 'classmethod'
  129. return False
  130. class ArgumentMapping(object):
  131. def __init__(self, definition_info, call_info):
  132. self.call_info = call_info
  133. self.param_dict = {}
  134. self.keyword_args = []
  135. self.args_arg = []
  136. for index, value in enumerate(call_info.args):
  137. if index < len(definition_info.args_with_defaults):
  138. name = definition_info.args_with_defaults[index][0]
  139. self.param_dict[name] = value
  140. else:
  141. self.args_arg.append(value)
  142. for name, value in call_info.keywords:
  143. index = -1
  144. for pair in definition_info.args_with_defaults:
  145. if pair[0] == name:
  146. self.param_dict[name] = value
  147. break
  148. else:
  149. self.keyword_args.append((name, value))
  150. def to_call_info(self, definition_info):
  151. args = []
  152. keywords = []
  153. for index in range(len(definition_info.args_with_defaults)):
  154. name = definition_info.args_with_defaults[index][0]
  155. if name in self.param_dict:
  156. args.append(self.param_dict[name])
  157. else:
  158. for i in range(index, len(definition_info.args_with_defaults)):
  159. name = definition_info.args_with_defaults[i][0]
  160. if name in self.param_dict:
  161. keywords.append((name, self.param_dict[name]))
  162. break
  163. args.extend(self.args_arg)
  164. keywords.extend(self.keyword_args)
  165. return CallInfo(self.call_info.function_name, args, keywords,
  166. self.call_info.args_arg, self.call_info.keywords_arg,
  167. self.call_info.implicit_arg, self.call_info.constructor)
  168. class _FunctionParser(object):
  169. def __init__(self, call, implicit_arg, is_lambda=False):
  170. self.call = call
  171. self.implicit_arg = implicit_arg
  172. self.word_finder = worder.Worder(self.call)
  173. if is_lambda:
  174. self.last_parens = self.call.rindex(':')
  175. else:
  176. self.last_parens = self.call.rindex(')')
  177. self.first_parens = self.word_finder._find_parens_start(self.last_parens)
  178. def get_parameters(self):
  179. args, keywords = self.word_finder.get_parameters(self.first_parens,
  180. self.last_parens)
  181. if self.is_called_as_a_method():
  182. instance = self.call[:self.call.rindex('.', 0, self.first_parens)]
  183. args.insert(0, instance.strip())
  184. return args, keywords
  185. def get_instance(self):
  186. if self.is_called_as_a_method():
  187. return self.word_finder.get_primary_at(
  188. self.call.rindex('.', 0, self.first_parens) - 1)
  189. def get_function_name(self):
  190. if self.is_called_as_a_method():
  191. return self.word_finder.get_word_at(self.first_parens - 1)
  192. else:
  193. return self.word_finder.get_primary_at(self.first_parens - 1)
  194. def is_called_as_a_method(self):
  195. return self.implicit_arg and '.' in self.call[:self.first_parens]