mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-09 23:34:45 +08:00
function decorator support (only written with functions)
This commit is contained in:
75
evaluate.py
75
evaluate.py
@@ -7,7 +7,8 @@ follow_statement -> follow_call -> follow_paths -> follow_path
|
||||
TODO nonlocal statement
|
||||
TODO doc
|
||||
TODO list comprehensions, priority?
|
||||
TODO annotations ? how ?
|
||||
TODO annotations ? how ? type evaluation and return?
|
||||
TODO evaluate asserts (type safety)
|
||||
TODO generators
|
||||
"""
|
||||
from _compatibility import next
|
||||
@@ -184,7 +185,12 @@ class Executable(object):
|
||||
arrays = follow_call_list(self.scope, [var_arg[1:]])
|
||||
for array in arrays:
|
||||
for key, field in array.get_contents():
|
||||
yield key[0].name, field
|
||||
# take the first index
|
||||
if isinstance(key, parsing.Name):
|
||||
name = key
|
||||
else:
|
||||
name = key[0].name
|
||||
yield name, field
|
||||
yield var_arg
|
||||
# normal arguments (including key arguments)
|
||||
else:
|
||||
@@ -350,6 +356,40 @@ class Execution(Executable):
|
||||
"""
|
||||
cache = {}
|
||||
|
||||
def process_decorators(self):
|
||||
""" Returns the function, that is to be executed in the end """
|
||||
func = self.base
|
||||
|
||||
# only enter it, if has not already been processed
|
||||
if hasattr(func, 'is_decorated') and not func.is_decorated:
|
||||
for dec in reversed(self.base.decorators):
|
||||
print '\n\ndecorator:', dec, func
|
||||
dec_results = follow_statement(dec)
|
||||
if not len(dec_results):
|
||||
debug.warning('decorator func not found', self.base)
|
||||
return []
|
||||
if len(dec_results) > 1:
|
||||
debug.warning('multiple decorators found', self.base,
|
||||
dec_results)
|
||||
decorator = dec_results.pop()
|
||||
# create param array
|
||||
params = parsing.Array(parsing.Array.NOARRAY, func)
|
||||
params.values = [[func]]
|
||||
wrappers = Execution(decorator, params).get_return_types()
|
||||
if not len(wrappers):
|
||||
debug.warning('no wrappers found', self.base)
|
||||
return []
|
||||
if len(wrappers) > 1:
|
||||
debug.warning('multiple wrappers found', self.base,
|
||||
wrappers)
|
||||
# this is here, that the wrapper gets executed
|
||||
func = wrappers[0]
|
||||
|
||||
print 'dece\n\n'
|
||||
#print dec.parent
|
||||
return func
|
||||
|
||||
|
||||
@memoize_default(default=[])
|
||||
def get_return_types(self):
|
||||
"""
|
||||
@@ -361,12 +401,15 @@ class Execution(Executable):
|
||||
# there maybe executions of executions
|
||||
stmts = [Instance(self.base, self.var_args)]
|
||||
else:
|
||||
func = self.process_decorators()
|
||||
|
||||
# set the callback function to get the var_args
|
||||
self.set_param_cb(self.base)
|
||||
# don't do this with exceptions, as usual, because some deeper
|
||||
# exceptions could be catched - and I wouldn't know what happened.
|
||||
if hasattr(self.base, 'returns'):
|
||||
ret = self.base.returns
|
||||
if hasattr(func, 'returns'):
|
||||
self.set_param_cb(func)
|
||||
self.base.is_decorated = True
|
||||
ret = func.returns
|
||||
for s in ret:
|
||||
#temp, s.parent = s.parent, self
|
||||
stmts += follow_statement(s)
|
||||
@@ -375,12 +418,17 @@ class Execution(Executable):
|
||||
# reset the callback function on exit
|
||||
# TODO how can we deactivate this again?
|
||||
#self.base.param_cb = None
|
||||
|
||||
# func could have changed because of decorators, so clear them
|
||||
# again
|
||||
self.base.is_decorated = False
|
||||
else:
|
||||
debug.warning("no execution possible", self.base)
|
||||
debug.warning("no execution possible", func)
|
||||
|
||||
debug.dbg('exec stmts=', stmts, self.base, repr(self))
|
||||
|
||||
return stmts
|
||||
debug.dbg('exec results:', stmts, self.base, repr(self))
|
||||
|
||||
return strip_imports(stmts)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s of %s>" % \
|
||||
@@ -714,7 +762,10 @@ def follow_call_list(scope, call_list):
|
||||
if parsing.Array.is_type(call, parsing.Array.NOARRAY):
|
||||
result += follow_call_list(scope, call)
|
||||
else:
|
||||
if not isinstance(call, str):
|
||||
# with things like params, these can also be functions, etc
|
||||
if isinstance(call, (parsing.Function, parsing.Class)):
|
||||
result.append(call)
|
||||
elif not isinstance(call, str):
|
||||
# The string tokens are just operations (+, -, etc.)
|
||||
result += follow_call(scope, call)
|
||||
return set(result)
|
||||
@@ -793,10 +844,8 @@ def follow_path(path, scope, position=None):
|
||||
result = scope.get_index_types(current)
|
||||
elif current.type not in [parsing.Array.DICT]:
|
||||
# scope must be a class or func - make an instance or execution
|
||||
debug.dbg('befexec', scope)
|
||||
exe = Execution(scope, current)
|
||||
result = strip_imports(exe.get_return_types())
|
||||
debug.dbg('exec', result)
|
||||
debug.dbg('exec', scope)
|
||||
result = Execution(scope, current).get_return_types()
|
||||
else:
|
||||
# curly braces are not allowed, because they make no sense
|
||||
debug.warning('strange function call with {}', current, scope)
|
||||
|
||||
20
parsetest.py
20
parsetest.py
@@ -158,8 +158,6 @@ a = 3; b = ""
|
||||
b,a=a,b
|
||||
a.
|
||||
|
||||
|
||||
|
||||
class C(object):
|
||||
def c_a(self):
|
||||
self.c_b = 1
|
||||
@@ -167,16 +165,16 @@ class C(object):
|
||||
test = [1,2]
|
||||
|
||||
|
||||
def fu(a=1, b="", *args, **kwargs):
|
||||
return a, b, args, kwargs
|
||||
def dec(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(dict, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
exe = fu(arg2=1,arg1=set)
|
||||
exe = fu(list, 1, "", c=set)
|
||||
args_func() #1,"", a=list)[0].
|
||||
args_func(arg1=0, *test + [3], *[4,5], **{'a': 'b'}).
|
||||
@dec
|
||||
def fu(a, b, c, *args, **kwargs):
|
||||
return a, b, c, args, kwargs
|
||||
|
||||
exe = fu(list, c=set, d='')
|
||||
|
||||
|
||||
exe[2][0].
|
||||
exe[3]['c'].union
|
||||
exe[0].
|
||||
exe[4]['d']
|
||||
|
||||
@@ -104,6 +104,10 @@ class Scope(Simple):
|
||||
# print 'push scope: [%s@%s]' % (sub.line_nr, sub.indent)
|
||||
sub.parent = self
|
||||
sub.decorators = decorators
|
||||
for d in decorators:
|
||||
# the parent is the same, because the decorator has not the scope
|
||||
# of the function
|
||||
d.parent = sub.parent
|
||||
self.subscopes.append(sub)
|
||||
return sub
|
||||
|
||||
@@ -308,6 +312,9 @@ class Function(Scope):
|
||||
for p in params:
|
||||
p.parent = self
|
||||
self.decorators = []
|
||||
# helper variable for the evaluator, maybe remove this, when
|
||||
# evaluate.py is rewritten, fully functional.
|
||||
self.is_decorated = False
|
||||
self.returns = []
|
||||
self.is_generator = False
|
||||
|
||||
|
||||
@@ -169,3 +169,60 @@ exe[3].items
|
||||
exe[3]['c'].union
|
||||
#? []
|
||||
exe[3]['c'].upper
|
||||
|
||||
# -----------------
|
||||
# decorators
|
||||
# -----------------
|
||||
|
||||
def decorator(func):
|
||||
def wrapper(*args):
|
||||
return func(1, *args)
|
||||
return wrapper
|
||||
|
||||
@decorator
|
||||
def decorated(a,b):
|
||||
return a,b
|
||||
|
||||
exe = decorated(set, '')
|
||||
|
||||
#? []
|
||||
exe[0].union
|
||||
#? ['union']
|
||||
exe[1].union
|
||||
|
||||
#? ['real']
|
||||
exe[0].real
|
||||
#? []
|
||||
exe[1].real
|
||||
|
||||
# more complicated with args/kwargs
|
||||
def dec(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
@dec
|
||||
def fu(a, b, c, *args, **kwargs):
|
||||
return a, b, c, args, kwargs
|
||||
|
||||
exe = fu(list, c=set, b=3, d='')
|
||||
|
||||
#? ['append']
|
||||
exe[0].append
|
||||
#? ['real']
|
||||
exe[1].real
|
||||
#? ['union']
|
||||
exe[2].union
|
||||
|
||||
#? []
|
||||
exe[4]['d'].union
|
||||
#? ['upper']
|
||||
exe[4]['d'].upper
|
||||
|
||||
|
||||
exe = fu(list, set, 3, '', d='')
|
||||
|
||||
#? ['upper']
|
||||
exe[3][0].upper
|
||||
#? []
|
||||
exe[3][0].real
|
||||
|
||||
Reference in New Issue
Block a user