"""A module for inferring objects For more information see the documentation in `rope.base.oi` package. """ import rope.base.builtins import rope.base.pynames import rope.base.pyobjects from rope.base import evaluate, utils, arguments _ignore_inferred = utils.ignore_exception( rope.base.pyobjects.IsBeingInferredError) @_ignore_inferred def infer_returned_object(pyfunction, args): """Infer the `PyObject` this `PyFunction` returns after calling""" object_info = pyfunction.pycore.object_info result = object_info.get_exact_returned(pyfunction, args) if result is not None: return result result = _infer_returned(pyfunction, args) if result is not None: if args and pyfunction.get_module().get_resource() is not None: params = args.get_arguments( pyfunction.get_param_names(special_args=False)) object_info.function_called(pyfunction, params, result) return result return object_info.get_returned(pyfunction, args) @_ignore_inferred def infer_parameter_objects(pyfunction): """Infer the `PyObject`\s of parameters of this `PyFunction`""" object_info = pyfunction.pycore.object_info result = object_info.get_parameter_objects(pyfunction) if result is None: result = _parameter_objects(pyfunction) _handle_first_parameter(pyfunction, result) return result def _handle_first_parameter(pyobject, parameters): kind = pyobject.get_kind() if parameters is None or kind not in ['method', 'classmethod']: pass if not parameters: if not pyobject.get_param_names(special_args=False): return parameters.append(rope.base.pyobjects.get_unknown()) if kind == 'method': parameters[0] = rope.base.pyobjects.PyObject(pyobject.parent) if kind == 'classmethod': parameters[0] = pyobject.parent @_ignore_inferred def infer_assigned_object(pyname): if not pyname.assignments: return for assignment in reversed(pyname.assignments): result = _infer_assignment(assignment, pyname.module) if result is not None: return result def get_passed_objects(pyfunction, parameter_index): object_info = pyfunction.pycore.object_info result = object_info.get_passed_objects(pyfunction, parameter_index) if not result: statically_inferred = _parameter_objects(pyfunction) if len(statically_inferred) > parameter_index: result.append(statically_inferred[parameter_index]) return result def _infer_returned(pyobject, args): if args: # HACK: Setting parameter objects manually # This is not thread safe and might cause problems if `args` # does not come from a good call site pyobject.get_scope().invalidate_data() pyobject._set_parameter_pyobjects( args.get_arguments(pyobject.get_param_names(special_args=False))) scope = pyobject.get_scope() if not scope._get_returned_asts(): return maxtries = 3 for returned_node in reversed(scope._get_returned_asts()[-maxtries:]): try: resulting_pyname = evaluate.eval_node(scope, returned_node) if resulting_pyname is None: continue pyobject = resulting_pyname.get_object() if pyobject == rope.base.pyobjects.get_unknown(): continue if not scope._is_generator(): return pyobject else: return rope.base.builtins.get_generator(pyobject) except rope.base.pyobjects.IsBeingInferredError: pass def _parameter_objects(pyobject): params = pyobject.get_param_names(special_args=False) return [rope.base.pyobjects.get_unknown()] * len(params) # handling `rope.base.pynames.AssignmentValue` @_ignore_inferred def _infer_assignment(assignment, pymodule): result = _follow_pyname(assignment, pymodule) if result is None: return None pyname, pyobject = result pyobject = _follow_evaluations(assignment, pyname, pyobject) if pyobject is None: return None return _follow_levels(assignment, pyobject) def _follow_levels(assignment, pyobject): for index in assignment.levels: if isinstance(pyobject.get_type(), rope.base.builtins.Tuple): holdings = pyobject.get_type().get_holding_objects() if holdings: pyobject = holdings[min(len(holdings) - 1, index)] else: pyobject = None elif isinstance(pyobject.get_type(), rope.base.builtins.List): pyobject = pyobject.get_type().holding else: pyobject = None if pyobject is None: break return pyobject @_ignore_inferred def _follow_pyname(assignment, pymodule, lineno=None): assign_node = assignment.ast_node if lineno is None: lineno = _get_lineno_for_node(assign_node) holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno) pyname = evaluate.eval_node(holding_scope, assign_node) if pyname is not None: result = pyname.get_object() if isinstance(result.get_type(), rope.base.builtins.Property) and \ holding_scope.get_kind() == 'Class': arg = rope.base.pynames.UnboundName( rope.base.pyobjects.PyObject(holding_scope.pyobject)) return pyname, result.get_type().get_property_object( arguments.ObjectArguments([arg])) return pyname, result @_ignore_inferred def _follow_evaluations(assignment, pyname, pyobject): new_pyname = pyname tokens = assignment.evaluation.split('.') for token in tokens: call = token.endswith('()') if call: token = token[:-2] if token: pyname = new_pyname new_pyname = _get_attribute(pyobject, token) if new_pyname is not None: pyobject = new_pyname.get_object() if pyobject is not None and call: if isinstance(pyobject, rope.base.pyobjects.AbstractFunction): args = arguments.ObjectArguments([pyname]) pyobject = pyobject.get_returned_object(args) else: pyobject = None if pyobject is None: break if pyobject is not None and assignment.assign_type: return rope.base.pyobjects.PyObject(pyobject) return pyobject def _get_lineno_for_node(assign_node): if hasattr(assign_node, 'lineno') and \ assign_node.lineno is not None: return assign_node.lineno return 1 def _get_attribute(pyobject, name): if pyobject is not None and name in pyobject: return pyobject[name]