2016-08-20 01:48:00 +03:00
|
|
|
from __future__ import absolute_import
|
2014-10-09 06:42:29 +03:00
|
|
|
|
|
|
|
from functools import wraps
|
|
|
|
|
2016-08-20 01:48:00 +03:00
|
|
|
from flask_sqlalchemy import DeclarativeMeta
|
|
|
|
|
|
|
|
|
2014-10-09 06:42:29 +03:00
|
|
|
|
|
|
|
def hook_func(name, fn):
|
|
|
|
@wraps(fn)
|
|
|
|
def wrapper(self, *args, **kwargs):
|
|
|
|
for hook, a, kw in self.__class__._pre_hooks.get(name) or []:
|
2016-07-08 09:10:26 +03:00
|
|
|
hook(self, *args, **kwargs)
|
2014-10-09 06:42:29 +03:00
|
|
|
|
|
|
|
rv = fn(self, *args, **kwargs)
|
|
|
|
|
2014-11-10 18:54:46 +02:00
|
|
|
# Attach return value for post hooks
|
|
|
|
kwargs.update(dict(rv=rv))
|
|
|
|
|
2014-10-09 06:42:29 +03:00
|
|
|
for hook, a, kw in self.__class__._post_hooks.get(name) or []:
|
2016-07-08 09:10:26 +03:00
|
|
|
hook(self, *args, **kwargs)
|
2014-10-09 06:42:29 +03:00
|
|
|
|
|
|
|
return rv
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
|
|
|
class HookMixinMeta(type):
|
|
|
|
def __new__(cls, name, bases, attrs):
|
|
|
|
super_new = super(HookMixinMeta, cls).__new__
|
|
|
|
|
2016-07-09 04:30:20 +03:00
|
|
|
hookable = []
|
2014-10-09 06:42:29 +03:00
|
|
|
for key, value in attrs.items():
|
2016-07-14 04:05:49 +03:00
|
|
|
# Disallow hooking methods which start with an underscore (allow __init__ etc. still)
|
|
|
|
if key.startswith('_') and not key.startswith('__'):
|
|
|
|
continue
|
2014-10-09 06:42:29 +03:00
|
|
|
if callable(value):
|
|
|
|
attrs[key] = hook_func(key, value)
|
2016-07-09 04:30:20 +03:00
|
|
|
hookable.append(key)
|
|
|
|
attrs['_hookable'] = hookable
|
2014-10-09 06:42:29 +03:00
|
|
|
|
|
|
|
return super_new(cls, name, bases, attrs)
|
|
|
|
|
|
|
|
|
|
|
|
class HookMixin(object):
|
|
|
|
__metaclass__ = HookMixinMeta
|
|
|
|
|
|
|
|
_pre_hooks = {}
|
|
|
|
_post_hooks = {}
|
2016-07-09 04:30:20 +03:00
|
|
|
_hookable = []
|
2014-10-09 06:42:29 +03:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def after(cls, method_name):
|
2016-07-09 04:30:20 +03:00
|
|
|
assert method_name in cls._hookable, "'%s' not a hookable method of '%s'" % (method_name, cls.__name__)
|
|
|
|
|
2014-10-09 06:42:29 +03:00
|
|
|
def outer(f, *args, **kwargs):
|
|
|
|
cls._post_hooks.setdefault(method_name, []).append((f, args, kwargs))
|
|
|
|
return f
|
|
|
|
return outer
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def before(cls, method_name):
|
2016-07-09 04:30:20 +03:00
|
|
|
assert method_name in cls._hookable, "'%s' not a hookable method of '%s'" % (method_name, cls.__name__)
|
|
|
|
|
2014-10-09 06:42:29 +03:00
|
|
|
def outer(f, *args, **kwargs):
|
|
|
|
cls._pre_hooks.setdefault(method_name, []).append((f, args, kwargs))
|
|
|
|
return f
|
|
|
|
return outer
|
|
|
|
|
|
|
|
|
|
|
|
class HookModelMeta(DeclarativeMeta, HookMixinMeta):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|