'''
Created on Jul 25, 2016
@author: Alex
'''
import six
from inspect import isfunction
[docs]class GenericDecorator(six.with_metaclass(DecortorMeta)):
"""
tidies up shell game of decorators.
arguments passed to the decorator at creation
end up as self.args and self.kwargs.
kwargs are also added to self.
if args_map is populated - will also add those to self
args_map = ("foo",)
populates self.foo with args[0]
if there is no args_map can be used bare (without initalisation)
default_kwargs sets the default value of any kwargs that might be passed in
(also added to self)
override self.gateway if it's a choice between using this function and
a different one
overide self.arg_decorator to adjust arguments being passed in
expects self, function, then delivers any args and kwargs passed to
the function
"""
args_map = []
default_kwargs = {}
abstract = True # disappears for children,
def __call__(self, *args, **kwargs):
"""
grabs the function and all arguments being passed to it
"""
func = args[0]
"""
if this can be used bare (but isn't) - reinit with the correct defaults
"""
if isfunction(func) == False or kwargs:
return self.__class__(*args, **kwargs)
# this is a bare class that's already been used, create new child
if self.name:
return self.__class__()(func)
self.function = func
self.name = func.__name__
def inner(*args, **kwargs):
self.function_args = args
self.function_kwargs = kwargs
return self.gateway()
inner.__name__ = self.name
new_inner = self.post_creation(inner)
if new_inner:
return new_inner
else:
return inner
[docs] @classmethod
def allow_bare(cls):
"""
can this decorator be used without initialisation?
only if no args_map - or all values in args_map are
already provided by default_kwargs
"""
if cls.args_map:
non_default = [
x for x in cls.args_map if x not in cls.default_kwargs]
if non_default:
return False
else:
return True
else:
return True
def __init__(self, *args, **kwargs):
"""
uses the args_map and kwargs to load details in
"""
self.args = args
self.name = ""
self.kwargs = {}
if self.__class__.default_kwargs:
self.kwargs = dict(self.__class__.default_kwargs)
self.kwargs.update(kwargs)
self.__dict__.update(self.kwargs)
for x, a in enumerate(self.__class__.args_map):
try:
setattr(self, a, args[x])
except IndexError:
if a in self.kwargs:
continue
else:
raise ValueError("Value {0} not provided".format(a))
[docs] def gateway(self):
"""
override if this is a "return this or something else" decorator
Use super or call self.raw_decorator() to proceed
"""
return self.raw_decorator()
[docs] def raw_decorator(self):
"""
accesses properties passed to decorator through self reference
"""
obj = self.arg_decorator(
self.function, *self.function_args, **self.function_kwargs)
nobj = self.post_run(obj)
# post-run may or may not return an new version of the object
if nobj:
return nobj
else:
return obj
[docs] def arg_decorator(self, function, *args, **kwargs):
""""
accesses properties passed to decorated object through arguments
"""
return function(*args, **kwargs)
[docs] def post_run(self, obj):
"""
passed self and the result of the function
return final object when done
"""
return obj
[docs] def post_creation(self, obj):
"""
passed self and the created object
return final object when done
"""
return obj