1# -*- coding: utf-8 -*- 2# Copyright: Ankitects Pty Ltd and contributors 3# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html 4 5"""\ 6Hooks - hook management and tools for extending Anki 7============================================================================== 8 9To find available hooks, grep for runHook and runFilter in the source code. 10 11Instrumenting allows you to modify functions that don't have hooks available. 12If you call wrap() with pos='around', the original function will not be called 13automatically but can be called with _old(). 14""" 15 16import decorator 17 18# Hooks 19############################################################################## 20 21_hooks = {} 22 23def runHook(hook, *args): 24 "Run all functions on hook." 25 hook = _hooks.get(hook, None) 26 if hook: 27 for func in hook: 28 try: 29 func(*args) 30 except: 31 hook.remove(func) 32 raise 33 34def runFilter(hook, arg, *args): 35 hook = _hooks.get(hook, None) 36 if hook: 37 for func in hook: 38 try: 39 arg = func(arg, *args) 40 except: 41 hook.remove(func) 42 raise 43 return arg 44 45def addHook(hook, func): 46 "Add a function to hook. Ignore if already on hook." 47 if not _hooks.get(hook, None): 48 _hooks[hook] = [] 49 if func not in _hooks[hook]: 50 _hooks[hook].append(func) 51 52def remHook(hook, func): 53 "Remove a function if is on hook." 54 hook = _hooks.get(hook, []) 55 if func in hook: 56 hook.remove(func) 57 58# Instrumenting 59############################################################################## 60 61def wrap(old, new, pos="after"): 62 "Override an existing function." 63 def repl(*args, **kwargs): 64 if pos == "after": 65 old(*args, **kwargs) 66 return new(*args, **kwargs) 67 elif pos == "before": 68 new(*args, **kwargs) 69 return old(*args, **kwargs) 70 else: 71 return new(_old=old, *args, **kwargs) 72 73 def decorator_wrapper(f, *args, **kwargs): 74 return repl(*args, **kwargs) 75 76 return decorator.decorator(decorator_wrapper)(old) 77