soi.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. """A module for inferring objects
  2. For more information see the documentation in `rope.base.oi`
  3. package.
  4. """
  5. import rope.base.builtins
  6. import rope.base.pynames
  7. import rope.base.pyobjects
  8. from rope.base import evaluate, utils, arguments
  9. _ignore_inferred = utils.ignore_exception(
  10. rope.base.pyobjects.IsBeingInferredError)
  11. @_ignore_inferred
  12. def infer_returned_object(pyfunction, args):
  13. """Infer the `PyObject` this `PyFunction` returns after calling"""
  14. object_info = pyfunction.pycore.object_info
  15. result = object_info.get_exact_returned(pyfunction, args)
  16. if result is not None:
  17. return result
  18. result = _infer_returned(pyfunction, args)
  19. if result is not None:
  20. if args and pyfunction.get_module().get_resource() is not None:
  21. params = args.get_arguments(
  22. pyfunction.get_param_names(special_args=False))
  23. object_info.function_called(pyfunction, params, result)
  24. return result
  25. return object_info.get_returned(pyfunction, args)
  26. @_ignore_inferred
  27. def infer_parameter_objects(pyfunction):
  28. """Infer the `PyObject`\s of parameters of this `PyFunction`"""
  29. object_info = pyfunction.pycore.object_info
  30. result = object_info.get_parameter_objects(pyfunction)
  31. if result is None:
  32. result = _parameter_objects(pyfunction)
  33. _handle_first_parameter(pyfunction, result)
  34. return result
  35. def _handle_first_parameter(pyobject, parameters):
  36. kind = pyobject.get_kind()
  37. if parameters is None or kind not in ['method', 'classmethod']:
  38. pass
  39. if not parameters:
  40. if not pyobject.get_param_names(special_args=False):
  41. return
  42. parameters.append(rope.base.pyobjects.get_unknown())
  43. if kind == 'method':
  44. parameters[0] = rope.base.pyobjects.PyObject(pyobject.parent)
  45. if kind == 'classmethod':
  46. parameters[0] = pyobject.parent
  47. @_ignore_inferred
  48. def infer_assigned_object(pyname):
  49. if not pyname.assignments:
  50. return
  51. for assignment in reversed(pyname.assignments):
  52. result = _infer_assignment(assignment, pyname.module)
  53. if result is not None:
  54. return result
  55. def get_passed_objects(pyfunction, parameter_index):
  56. object_info = pyfunction.pycore.object_info
  57. result = object_info.get_passed_objects(pyfunction,
  58. parameter_index)
  59. if not result:
  60. statically_inferred = _parameter_objects(pyfunction)
  61. if len(statically_inferred) > parameter_index:
  62. result.append(statically_inferred[parameter_index])
  63. return result
  64. def _infer_returned(pyobject, args):
  65. if args:
  66. # HACK: Setting parameter objects manually
  67. # This is not thread safe and might cause problems if `args`
  68. # does not come from a good call site
  69. pyobject.get_scope().invalidate_data()
  70. pyobject._set_parameter_pyobjects(
  71. args.get_arguments(pyobject.get_param_names(special_args=False)))
  72. scope = pyobject.get_scope()
  73. if not scope._get_returned_asts():
  74. return
  75. maxtries = 3
  76. for returned_node in reversed(scope._get_returned_asts()[-maxtries:]):
  77. try:
  78. resulting_pyname = evaluate.eval_node(scope, returned_node)
  79. if resulting_pyname is None:
  80. continue
  81. pyobject = resulting_pyname.get_object()
  82. if pyobject == rope.base.pyobjects.get_unknown():
  83. continue
  84. if not scope._is_generator():
  85. return pyobject
  86. else:
  87. return rope.base.builtins.get_generator(pyobject)
  88. except rope.base.pyobjects.IsBeingInferredError:
  89. pass
  90. def _parameter_objects(pyobject):
  91. params = pyobject.get_param_names(special_args=False)
  92. return [rope.base.pyobjects.get_unknown()] * len(params)
  93. # handling `rope.base.pynames.AssignmentValue`
  94. @_ignore_inferred
  95. def _infer_assignment(assignment, pymodule):
  96. result = _follow_pyname(assignment, pymodule)
  97. if result is None:
  98. return None
  99. pyname, pyobject = result
  100. pyobject = _follow_evaluations(assignment, pyname, pyobject)
  101. if pyobject is None:
  102. return None
  103. return _follow_levels(assignment, pyobject)
  104. def _follow_levels(assignment, pyobject):
  105. for index in assignment.levels:
  106. if isinstance(pyobject.get_type(), rope.base.builtins.Tuple):
  107. holdings = pyobject.get_type().get_holding_objects()
  108. if holdings:
  109. pyobject = holdings[min(len(holdings) - 1, index)]
  110. else:
  111. pyobject = None
  112. elif isinstance(pyobject.get_type(), rope.base.builtins.List):
  113. pyobject = pyobject.get_type().holding
  114. else:
  115. pyobject = None
  116. if pyobject is None:
  117. break
  118. return pyobject
  119. @_ignore_inferred
  120. def _follow_pyname(assignment, pymodule, lineno=None):
  121. assign_node = assignment.ast_node
  122. if lineno is None:
  123. lineno = _get_lineno_for_node(assign_node)
  124. holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno)
  125. pyname = evaluate.eval_node(holding_scope, assign_node)
  126. if pyname is not None:
  127. result = pyname.get_object()
  128. if isinstance(result.get_type(), rope.base.builtins.Property) and \
  129. holding_scope.get_kind() == 'Class':
  130. arg = rope.base.pynames.UnboundName(
  131. rope.base.pyobjects.PyObject(holding_scope.pyobject))
  132. return pyname, result.get_type().get_property_object(
  133. arguments.ObjectArguments([arg]))
  134. return pyname, result
  135. @_ignore_inferred
  136. def _follow_evaluations(assignment, pyname, pyobject):
  137. new_pyname = pyname
  138. tokens = assignment.evaluation.split('.')
  139. for token in tokens:
  140. call = token.endswith('()')
  141. if call:
  142. token = token[:-2]
  143. if token:
  144. pyname = new_pyname
  145. new_pyname = _get_attribute(pyobject, token)
  146. if new_pyname is not None:
  147. pyobject = new_pyname.get_object()
  148. if pyobject is not None and call:
  149. if isinstance(pyobject, rope.base.pyobjects.AbstractFunction):
  150. args = arguments.ObjectArguments([pyname])
  151. pyobject = pyobject.get_returned_object(args)
  152. else:
  153. pyobject = None
  154. if pyobject is None:
  155. break
  156. if pyobject is not None and assignment.assign_type:
  157. return rope.base.pyobjects.PyObject(pyobject)
  158. return pyobject
  159. def _get_lineno_for_node(assign_node):
  160. if hasattr(assign_node, 'lineno') and \
  161. assign_node.lineno is not None:
  162. return assign_node.lineno
  163. return 1
  164. def _get_attribute(pyobject, name):
  165. if pyobject is not None and name in pyobject:
  166. return pyobject[name]