function decorator support (only written with functions)

This commit is contained in:
David Halter
2012-05-10 11:08:08 +02:00
parent 17f059e853
commit 49ef3dcace
4 changed files with 135 additions and 24 deletions

View File

@@ -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)

View File

@@ -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']

View File

@@ -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

View File

@@ -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