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