Function for evaluating functions with already executed arguments.

This commit is contained in:
Dave Halter
2014-11-23 19:12:25 +01:00
parent 8adfc47297
commit 267016f533
6 changed files with 35 additions and 16 deletions

View File

@@ -256,6 +256,13 @@ class Evaluator(object):
new_types += get(self, node) new_types += get(self, node)
return new_types return new_types
def execute_evaluated(self, obj, *args):
"""
Execute a function with already executed arguments.
"""
args = [iterable.AlreadyEvaluated([arg]) for arg in args]
return self.execute(obj, args)
@debug.increase_indent @debug.increase_indent
def execute(self, obj, arguments=(), trailer=None): def execute(self, obj, arguments=(), trailer=None):
if not isinstance(arguments, param.Arguments): if not isinstance(arguments, param.Arguments):

View File

@@ -20,6 +20,7 @@ It works as follows:
from jedi._compatibility import unicode from jedi._compatibility import unicode
from jedi.parser import representation as pr from jedi.parser import representation as pr
from jedi import settings from jedi import settings
from jedi import debug
from jedi.evaluate import helpers from jedi.evaluate import helpers
from jedi.evaluate.cache import memoize_default from jedi.evaluate.cache import memoize_default
from jedi.evaluate import imports from jedi.evaluate import imports
@@ -40,6 +41,7 @@ class ParamListener(object):
self.param_possibilities.append(params) self.param_possibilities.append(params)
@debug.increase_indent
@memoize_default([], evaluator_is_first_arg=True) @memoize_default([], evaluator_is_first_arg=True)
def search_params(evaluator, param): def search_params(evaluator, param):
""" """
@@ -57,6 +59,7 @@ def search_params(evaluator, param):
if not settings.dynamic_params: if not settings.dynamic_params:
return [] return []
from jedi.evaluate import representation as er from jedi.evaluate import representation as er
debug.dbg('Dynamic param search for %s', param)
def get_params_for_module(module): def get_params_for_module(module):
""" """
@@ -71,7 +74,7 @@ def search_params(evaluator, param):
for name in names: for name in names:
stmt = name.get_definition() stmt = name.get_definition()
if not isinstance(stmt, (pr.ExprStmt, pr.CompFor)): if not isinstance(stmt, (pr.ExprStmt, pr.CompFor, pr.ReturnStmt)):
continue continue
parent = name.parent parent = name.parent
if pr.is_node(parent, 'trailer'): if pr.is_node(parent, 'trailer'):
@@ -179,4 +182,5 @@ def search_params(evaluator, param):
# cleanup: remove the listener; important: should not stick. # cleanup: remove the listener; important: should not stick.
func.listeners.remove(listener) func.listeners.remove(listener)
debug.dbg('Dynamic param result %s', result)
return result return result

View File

@@ -211,14 +211,14 @@ class NameFinder(object):
# str is important, because it shouldn't be `Name`! # str is important, because it shouldn't be `Name`!
name = compiled.create(self._evaluator, str(self.name_str)) name = compiled.create(self._evaluator, str(self.name_str))
with common.ignored(KeyError): with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattr__', [name]) result = inst.execute_subscope_by_name('__getattr__', name)
if not result: if not result:
# this is a little bit special. `__getattribute__` is executed # this is a little bit special. `__getattribute__` is executed
# before anything else. But: I know no use case, where this # before anything else. But: I know no use case, where this
# could be practical and the jedi would return wrong types. If # could be practical and the jedi would return wrong types. If
# you ever have something, let me know! # you ever have something, let me know!
with common.ignored(KeyError): with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattribute__', [name]) result = inst.execute_subscope_by_name('__getattribute__', name)
return result return result
def _is_name_break_scope(self, stmt): def _is_name_break_scope(self, stmt):

View File

@@ -200,7 +200,7 @@ def get_params(evaluator, func, var_args):
from jedi.evaluate.representation import InstanceElement from jedi.evaluate.representation import InstanceElement
if isinstance(func, InstanceElement): if isinstance(func, InstanceElement):
# Include self at this place. # Include self at this place.
unpacked_va.insert(0, (None, [func.instance])) unpacked_va.insert(0, (None, [iterable.AlreadyEvaluated([func.instance])]))
var_arg_iterator = common.PushBackIterator(iter(unpacked_va)) var_arg_iterator = common.PushBackIterator(iter(unpacked_va))
non_matching_keys = defaultdict(lambda: []) non_matching_keys = defaultdict(lambda: [])
@@ -289,6 +289,7 @@ def get_params(evaluator, func, var_args):
result.append(_gen_param_name_copy(evaluator, func, var_args, result.append(_gen_param_name_copy(evaluator, func, var_args,
param, [], values)) param, [], values))
if not (non_matching_keys or had_multiple_value_error if not (non_matching_keys or had_multiple_value_error
or param.stars or param.default): or param.stars or param.default):
# add a warning only if there's not another one. # add a warning only if there's not another one.

View File

@@ -97,8 +97,12 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
else: else:
# Need to execute the __init__ function, because the dynamic param # Need to execute the __init__ function, because the dynamic param
# searching needs it. # searching needs it.
with common.ignored(KeyError): try:
self.execute_subscope_by_name('__init__', self.var_args) method = self.get_subscope_by_name('__init__')
except KeyError:
pass
else:
evaluator.execute(method, self.var_args)
# Generated instances are classes that are just generated by self # Generated instances are classes that are just generated by self
# (No var_args) used. # (No var_args) used.
self.is_generated = is_generated self.is_generated = is_generated
@@ -180,16 +184,16 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
sub = self.base.get_subscope_by_name(name) sub = self.base.get_subscope_by_name(name)
return get_instance_el(self._evaluator, self, sub, True) return get_instance_el(self._evaluator, self, sub, True)
def execute_subscope_by_name(self, name, args=()): def execute_subscope_by_name(self, name, *args):
method = self.get_subscope_by_name(name) method = self.get_subscope_by_name(name)
return self._evaluator.execute(method, args) return self._evaluator.execute_evaluated(method, *args)
def get_descriptor_return(self, obj): def get_descriptor_return(self, obj):
""" Throws a KeyError if there's no method. """ """ Throws a KeyError if there's no method. """
# Arguments in __get__ descriptors are obj, class. # Arguments in __get__ descriptors are obj, class.
# `method` is the new parent of the array, don't know if that's good. # `method` is the new parent of the array, don't know if that's good.
args = [obj, obj.base] if isinstance(obj, Instance) else [compiled.none_obj, obj] args = [obj, obj.base] if isinstance(obj, Instance) else [compiled.none_obj, obj]
return self.execute_subscope_by_name('__get__', args) return self.execute_subscope_by_name('__get__', *args)
def scope_names_generator(self, position=None): def scope_names_generator(self, position=None):
""" """
@@ -211,7 +215,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
indexes = [] indexes = []
try: try:
return self.execute_subscope_by_name('__getitem__', [indexes]) return self.execute_subscope_by_name('__getitem__', indexes)
except KeyError: except KeyError:
debug.warning('No __getitem__, cannot access the array.') debug.warning('No __getitem__, cannot access the array.')
return [] return []
@@ -494,8 +498,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)):
# Create param array. # Create param array.
old_func = Function(self._evaluator, f, is_decorated=True) old_func = Function(self._evaluator, f, is_decorated=True)
arg = iterable.AlreadyEvaluated([old_func]) wrappers = self._evaluator.execute_evaluated(decorator, old_func)
wrappers = self._evaluator.execute(decorator, (arg,))
if not len(wrappers): if not len(wrappers):
debug.warning('no wrappers found %s', self.base_func) debug.warning('no wrappers found %s', self.base_func)
return self return self

View File

@@ -358,11 +358,13 @@ def nested_kw(**kwargs1):
def nested_kw2(**kwargs2): def nested_kw2(**kwargs2):
return nested_kw(**kwargs2) return nested_kw(**kwargs2)
#? int() # invalid command, doesn't need to return anything
#?
nested_kw(b=1, c=1.0, list) nested_kw(b=1, c=1.0, list)
#? int() #? int()
nested_kw(b=1) nested_kw(b=1)
#? int() # invalid command, doesn't need to return anything
#?
nested_kw(d=1.0, b=1, list) nested_kw(d=1.0, b=1, list)
#? int() #? int()
nested_kw(a=3.0, b=1) nested_kw(a=3.0, b=1)
@@ -395,10 +397,12 @@ def nested_both(*args, **kwargs):
def nested_both2(*args, **kwargs): def nested_both2(*args, **kwargs):
return nested_both(*args, **kwargs) return nested_both(*args, **kwargs)
#? int() # invalid commands, may return whatever.
#? list
nested_both('', b=1, c=1.0, list) nested_both('', b=1, c=1.0, list)
#? int() #? list
nested_both('', c=1.0, b=1, list) nested_both('', c=1.0, b=1, list)
#? [] #? []
nested_both('') nested_both('')