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)
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
def execute(self, obj, arguments=(), trailer=None):
if not isinstance(arguments, param.Arguments):

View File

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

View File

@@ -211,14 +211,14 @@ class NameFinder(object):
# str is important, because it shouldn't be `Name`!
name = compiled.create(self._evaluator, str(self.name_str))
with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattr__', [name])
result = inst.execute_subscope_by_name('__getattr__', name)
if not result:
# this is a little bit special. `__getattribute__` is executed
# before anything else. But: I know no use case, where this
# could be practical and the jedi would return wrong types. If
# you ever have something, let me know!
with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattribute__', [name])
result = inst.execute_subscope_by_name('__getattribute__', name)
return result
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
if isinstance(func, InstanceElement):
# 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))
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,
param, [], values))
if not (non_matching_keys or had_multiple_value_error
or param.stars or param.default):
# add a warning only if there's not another one.

View File

@@ -97,8 +97,12 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
else:
# Need to execute the __init__ function, because the dynamic param
# searching needs it.
with common.ignored(KeyError):
self.execute_subscope_by_name('__init__', self.var_args)
try:
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
# (No var_args) used.
self.is_generated = is_generated
@@ -180,16 +184,16 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
sub = self.base.get_subscope_by_name(name)
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)
return self._evaluator.execute(method, args)
return self._evaluator.execute_evaluated(method, *args)
def get_descriptor_return(self, obj):
""" Throws a KeyError if there's no method. """
# Arguments in __get__ descriptors are obj, class.
# `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]
return self.execute_subscope_by_name('__get__', args)
return self.execute_subscope_by_name('__get__', *args)
def scope_names_generator(self, position=None):
"""
@@ -211,7 +215,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
indexes = []
try:
return self.execute_subscope_by_name('__getitem__', [indexes])
return self.execute_subscope_by_name('__getitem__', indexes)
except KeyError:
debug.warning('No __getitem__, cannot access the array.')
return []
@@ -494,8 +498,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)):
# Create param array.
old_func = Function(self._evaluator, f, is_decorated=True)
arg = iterable.AlreadyEvaluated([old_func])
wrappers = self._evaluator.execute(decorator, (arg,))
wrappers = self._evaluator.execute_evaluated(decorator, old_func)
if not len(wrappers):
debug.warning('no wrappers found %s', self.base_func)
return self

View File

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