| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- """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]
|