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 nonlocal statement
TODO doc TODO doc
TODO list comprehensions, priority? TODO list comprehensions, priority?
TODO annotations ? how ? TODO annotations ? how ? type evaluation and return?
TODO evaluate asserts (type safety)
TODO generators TODO generators
""" """
from _compatibility import next from _compatibility import next
@@ -184,7 +185,12 @@ class Executable(object):
arrays = follow_call_list(self.scope, [var_arg[1:]]) arrays = follow_call_list(self.scope, [var_arg[1:]])
for array in arrays: for array in arrays:
for key, field in array.get_contents(): 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 yield var_arg
# normal arguments (including key arguments) # normal arguments (including key arguments)
else: else:
@@ -350,6 +356,40 @@ class Execution(Executable):
""" """
cache = {} 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=[]) @memoize_default(default=[])
def get_return_types(self): def get_return_types(self):
""" """
@@ -361,12 +401,15 @@ class Execution(Executable):
# there maybe executions of executions # there maybe executions of executions
stmts = [Instance(self.base, self.var_args)] stmts = [Instance(self.base, self.var_args)]
else: else:
func = self.process_decorators()
# set the callback function to get the var_args # 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 # don't do this with exceptions, as usual, because some deeper
# exceptions could be catched - and I wouldn't know what happened. # exceptions could be catched - and I wouldn't know what happened.
if hasattr(self.base, 'returns'): if hasattr(func, 'returns'):
ret = self.base.returns self.set_param_cb(func)
self.base.is_decorated = True
ret = func.returns
for s in ret: for s in ret:
#temp, s.parent = s.parent, self #temp, s.parent = s.parent, self
stmts += follow_statement(s) stmts += follow_statement(s)
@@ -375,12 +418,17 @@ class Execution(Executable):
# reset the callback function on exit # reset the callback function on exit
# TODO how can we deactivate this again? # TODO how can we deactivate this again?
#self.base.param_cb = None #self.base.param_cb = None
# func could have changed because of decorators, so clear them
# again
self.base.is_decorated = False
else: 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): def __repr__(self):
return "<%s of %s>" % \ return "<%s of %s>" % \
@@ -714,7 +762,10 @@ def follow_call_list(scope, call_list):
if parsing.Array.is_type(call, parsing.Array.NOARRAY): if parsing.Array.is_type(call, parsing.Array.NOARRAY):
result += follow_call_list(scope, call) result += follow_call_list(scope, call)
else: 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.) # The string tokens are just operations (+, -, etc.)
result += follow_call(scope, call) result += follow_call(scope, call)
return set(result) return set(result)
@@ -793,10 +844,8 @@ 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('befexec', scope) debug.dbg('exec', scope)
exe = Execution(scope, current) result = Execution(scope, current).get_return_types()
result = strip_imports(exe.get_return_types())
debug.dbg('exec', result)
else: else:
# curly braces are not allowed, because they make no sense # curly braces are not allowed, because they make no sense
debug.warning('strange function call with {}', current, scope) debug.warning('strange function call with {}', current, scope)

View File

@@ -158,8 +158,6 @@ a = 3; b = ""
b,a=a,b b,a=a,b
a. a.
class C(object): class C(object):
def c_a(self): def c_a(self):
self.c_b = 1 self.c_b = 1
@@ -167,16 +165,16 @@ class C(object):
test = [1,2] test = [1,2]
def fu(a=1, b="", *args, **kwargs): def dec(func):
return a, b, args, kwargs def wrapper(*args, **kwargs):
return func(dict, *args, **kwargs)
return wrapper
exe = fu(arg2=1,arg1=set) @dec
exe = fu(list, 1, "", c=set) def fu(a, b, c, *args, **kwargs):
args_func() #1,"", a=list)[0]. return a, b, c, args, kwargs
args_func(arg1=0, *test + [3], *[4,5], **{'a': 'b'}).
exe = fu(list, c=set, d='')
exe[2][0].
exe[3]['c'].union
exe[0]. exe[0].
exe[4]['d']

View File

@@ -104,6 +104,10 @@ class Scope(Simple):
# print 'push scope: [%s@%s]' % (sub.line_nr, sub.indent) # print 'push scope: [%s@%s]' % (sub.line_nr, sub.indent)
sub.parent = self sub.parent = self
sub.decorators = decorators 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) self.subscopes.append(sub)
return sub return sub
@@ -308,6 +312,9 @@ class Function(Scope):
for p in params: for p in params:
p.parent = self p.parent = self
self.decorators = [] self.decorators = []
# helper variable for the evaluator, maybe remove this, when
# evaluate.py is rewritten, fully functional.
self.is_decorated = False
self.returns = [] self.returns = []
self.is_generator = False self.is_generator = False

View File

@@ -169,3 +169,60 @@ exe[3].items
exe[3]['c'].union exe[3]['c'].union
#? [] #? []
exe[3]['c'].upper 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