mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-15 01:57:06 +08:00
class decorators with __call__ are now properly working
This commit is contained in:
65
evaluate.py
65
evaluate.py
@@ -15,6 +15,7 @@ TODO nonlocal statement
|
|||||||
|
|
||||||
TODO getattr / __getattr__ / __getattribute__ ?
|
TODO getattr / __getattr__ / __getattribute__ ?
|
||||||
TODO descriptors
|
TODO descriptors
|
||||||
|
TODO @staticmethod @classmethod
|
||||||
"""
|
"""
|
||||||
from _compatibility import next, property
|
from _compatibility import next, property
|
||||||
|
|
||||||
@@ -157,7 +158,7 @@ class Instance(Executable):
|
|||||||
class_names = self.base.get_defined_names()
|
class_names = self.base.get_defined_names()
|
||||||
for var in class_names:
|
for var in class_names:
|
||||||
# functions are also instance elements
|
# functions are also instance elements
|
||||||
if isinstance(var.parent, (parsing.Function, Function)):
|
if isinstance(var.parent, (Function, parsing.Function)):
|
||||||
var = InstanceElement(self, var)
|
var = InstanceElement(self, var)
|
||||||
names.append(var)
|
names.append(var)
|
||||||
|
|
||||||
@@ -187,8 +188,12 @@ class InstanceElement(object):
|
|||||||
self.var = var
|
self.var = var
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@memoize_default()
|
||||||
def parent(self):
|
def parent(self):
|
||||||
return InstanceElement(self.instance, self.var.parent)
|
par = self.var.parent
|
||||||
|
if isinstance(par, parsing.Function):
|
||||||
|
par = Function(par)
|
||||||
|
return InstanceElement(self.instance, par)
|
||||||
|
|
||||||
def get_parent_until(self, *classes):
|
def get_parent_until(self, *classes):
|
||||||
scope = self.var.get_parent_until(*classes)
|
scope = self.var.get_parent_until(*classes)
|
||||||
@@ -261,19 +266,21 @@ class Function(object):
|
|||||||
def __init__(self, func, is_decorated=False):
|
def __init__(self, func, is_decorated=False):
|
||||||
""" This should not be called directly """
|
""" This should not be called directly """
|
||||||
self.base_func = func
|
self.base_func = func
|
||||||
self.func = self.process_decorators(is_decorated)
|
self.is_decorated = is_decorated
|
||||||
|
|
||||||
def __getattr__(self, name):
|
@property
|
||||||
return getattr(self.func, name)
|
@memoize_default()
|
||||||
|
def func(self):
|
||||||
def process_decorators(self, is_decorated):
|
"""
|
||||||
""" Returns the function, that is to be executed in the end """
|
Returns the function, that is to be executed in the end.
|
||||||
func = self.base_func
|
This is also the places where the decorators are processed.
|
||||||
|
"""
|
||||||
|
f = self.base_func
|
||||||
|
|
||||||
# only enter it, if has not already been processed
|
# only enter it, if has not already been processed
|
||||||
if not is_decorated:
|
if not self.is_decorated:
|
||||||
for dec in reversed(self.base_func.decorators):
|
for dec in reversed(self.base_func.decorators):
|
||||||
debug.dbg('decorator:', dec, func)
|
debug.dbg('decorator:', dec, f)
|
||||||
dec_results = follow_statement(dec)
|
dec_results = follow_statement(dec)
|
||||||
if not len(dec_results):
|
if not len(dec_results):
|
||||||
debug.warning('decorator func not found', self.base_func)
|
debug.warning('decorator func not found', self.base_func)
|
||||||
@@ -283,7 +290,7 @@ class Function(object):
|
|||||||
dec_results)
|
dec_results)
|
||||||
decorator = dec_results.pop()
|
decorator = dec_results.pop()
|
||||||
# create param array
|
# create param array
|
||||||
old_func = Function(func, is_decorated=True)
|
old_func = Function(f, is_decorated=True)
|
||||||
params = parsing.Array(parsing.Array.NOARRAY, old_func)
|
params = parsing.Array(parsing.Array.NOARRAY, old_func)
|
||||||
params.values = [[old_func]]
|
params.values = [[old_func]]
|
||||||
wrappers = Execution(decorator, params).get_return_types()
|
wrappers = Execution(decorator, params).get_return_types()
|
||||||
@@ -294,16 +301,18 @@ class Function(object):
|
|||||||
debug.warning('multiple wrappers found', self.base_func,
|
debug.warning('multiple wrappers found', self.base_func,
|
||||||
wrappers)
|
wrappers)
|
||||||
# this is here, that the wrapper gets executed
|
# this is here, that the wrapper gets executed
|
||||||
func = wrappers[0]
|
f = wrappers[0]
|
||||||
|
|
||||||
debug.dbg('decorator end')
|
debug.dbg('decorator end')
|
||||||
if func != self.base_func:
|
if f != self.base_func and isinstance(f, parsing.Function):
|
||||||
return Function(func)
|
f = Function(f)
|
||||||
else:
|
return f
|
||||||
return func
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(self.func, name)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<e%s of %s>" % (self.__class__.__name__, self.func)
|
return "<e%s of %s>" % (self.__class__.__name__, self.base_func)
|
||||||
|
|
||||||
|
|
||||||
class Execution(Executable):
|
class Execution(Executable):
|
||||||
@@ -313,8 +322,6 @@ class Execution(Executable):
|
|||||||
This is the most complicated class, because it contains the logic to
|
This is the most complicated class, because it contains the logic to
|
||||||
transfer parameters. This is even more complicated, because there may be
|
transfer parameters. This is even more complicated, because there may be
|
||||||
multiple call to functions and recursion has to be avoided.
|
multiple call to functions and recursion has to be avoided.
|
||||||
|
|
||||||
TODO InstantElements ?
|
|
||||||
"""
|
"""
|
||||||
cache = {}
|
cache = {}
|
||||||
|
|
||||||
@@ -342,10 +349,15 @@ class Execution(Executable):
|
|||||||
except (AttributeError, KeyError):
|
except (AttributeError, KeyError):
|
||||||
debug.warning("no execution possible", self.base)
|
debug.warning("no execution possible", self.base)
|
||||||
else:
|
else:
|
||||||
|
debug.dbg('__call__', call_method, self.base)
|
||||||
|
base = self.base
|
||||||
|
if isinstance(self.base, Function):
|
||||||
|
base = self.base.func
|
||||||
|
call_method = InstanceElement(base, call_method)
|
||||||
exe = Execution(call_method, self.var_args)
|
exe = Execution(call_method, self.var_args)
|
||||||
stmts = exe.get_return_types()
|
stmts = exe.get_return_types()
|
||||||
|
|
||||||
debug.dbg('exec results:', stmts, self.base, repr(self))
|
debug.dbg('exec result: %s in %s' % (stmts, self))
|
||||||
|
|
||||||
return strip_imports(stmts)
|
return strip_imports(stmts)
|
||||||
|
|
||||||
@@ -844,7 +856,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
|
|||||||
# if there are results, ignore the other scopes
|
# if there are results, ignore the other scopes
|
||||||
if result:
|
if result:
|
||||||
break
|
break
|
||||||
debug.dbg('sfn filter', name_str, result)
|
debug.dbg('sfn filter "%s" in %s: %s' % (name_str, scope, result))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
if search_global:
|
if search_global:
|
||||||
@@ -932,10 +944,9 @@ def follow_statement(stmt, scope=None, seek_name=None):
|
|||||||
scope = stmt.get_parent_until(parsing.Function, Function, Execution,
|
scope = stmt.get_parent_until(parsing.Function, Function, Execution,
|
||||||
parsing.Class, Class, Instance,
|
parsing.Class, Class, Instance,
|
||||||
InstanceElement)
|
InstanceElement)
|
||||||
debug.dbg('follow_stmt', stmt, stmt.parent, 'in', scope, seek_name)
|
debug.dbg('follow_stmt %s in %s (%s)' % (stmt, scope, seek_name))
|
||||||
|
|
||||||
call_list = stmt.get_assignment_calls()
|
call_list = stmt.get_assignment_calls()
|
||||||
debug.dbg('calls', call_list, call_list.values)
|
debug.dbg('calls: %s' % call_list)
|
||||||
result = follow_call_list(scope, call_list)
|
result = follow_call_list(scope, call_list)
|
||||||
|
|
||||||
# assignment checking is only important if the statement defines multiple
|
# assignment checking is only important if the statement defines multiple
|
||||||
@@ -1039,7 +1050,7 @@ def follow_path(path, scope, position=None):
|
|||||||
current = next(path)
|
current = next(path)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return None
|
return None
|
||||||
debug.dbg('follow', current, scope)
|
debug.dbg('follow %s in scope %s' % (current, scope))
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
if isinstance(current, parsing.Array):
|
if isinstance(current, parsing.Array):
|
||||||
@@ -1048,7 +1059,7 @@ def follow_path(path, scope, position=None):
|
|||||||
result = scope.get_index_types(current)
|
result = scope.get_index_types(current)
|
||||||
elif current.type not in [parsing.Array.DICT]:
|
elif current.type not in [parsing.Array.DICT]:
|
||||||
# scope must be a class or func - make an instance or execution
|
# scope must be a class or func - make an instance or execution
|
||||||
debug.dbg('exec', scope)
|
debug.dbg('exe', scope)
|
||||||
result = Execution(scope, current).get_return_types()
|
result = Execution(scope, current).get_return_types()
|
||||||
else:
|
else:
|
||||||
# curly braces are not allowed, because they make no sense
|
# curly braces are not allowed, because they make no sense
|
||||||
|
|||||||
@@ -183,9 +183,27 @@ class B():
|
|||||||
def r(self):
|
def r(self):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@r.setter
|
#@r.setter
|
||||||
def r(self, value):
|
#def r(self, value):
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
|
#? []
|
||||||
|
B().r.
|
||||||
#? []
|
#? []
|
||||||
B().r()
|
B().r()
|
||||||
|
|
||||||
|
class Decorator(object):
|
||||||
|
def __init__(self, func):
|
||||||
|
self.func = func
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
return self.func(1, *args, **kwargs)
|
||||||
|
|
||||||
|
@Decorator
|
||||||
|
def nothing(a,b,c):
|
||||||
|
return a,b,c
|
||||||
|
|
||||||
|
#? int()
|
||||||
|
nothing("")[0]
|
||||||
|
#? str()
|
||||||
|
nothing("")[1]
|
||||||
|
|||||||
@@ -54,12 +54,15 @@ def run_definition_test(correct, source, line_nr, line):
|
|||||||
else:
|
else:
|
||||||
should_be = set()
|
should_be = set()
|
||||||
for index in re.finditer('(?: +|$)', correct):
|
for index in re.finditer('(?: +|$)', correct):
|
||||||
|
if correct == ' ':
|
||||||
|
continue
|
||||||
# -1 for the comment, +3 because of the comment start `#? `
|
# -1 for the comment, +3 because of the comment start `#? `
|
||||||
start = index.start() + 3
|
start = index.start() + 3
|
||||||
try:
|
try:
|
||||||
should_be |= defs(line_nr-1, start)
|
should_be |= defs(line_nr-1, start)
|
||||||
except Exception:
|
except Exception:
|
||||||
print 'could not resolve %s indent %' % (line_nr - 1, start)
|
print 'could not resolve %s indent %s' % (line_nr - 1, start)
|
||||||
|
return 1
|
||||||
# because the objects have different ids, `repr` it, then compare it.
|
# because the objects have different ids, `repr` it, then compare it.
|
||||||
should_str = sorted(str(r) for r in should_be)
|
should_str = sorted(str(r) for r in should_be)
|
||||||
is_str = sorted(str(r) for r in result)
|
is_str = sorted(str(r) for r in result)
|
||||||
|
|||||||
Reference in New Issue
Block a user