1# -*- coding: utf-8 -*-
2# Copyright: Damien Elmes <anki@ichi2.net>
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
16from .decorator import 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            func(*args)
29
30def runFilter(hook, arg, *args):
31    hook = _hooks.get(hook, None)
32    if hook:
33        for func in hook:
34            arg = func(arg, *args)
35    return arg
36
37def addHook(hook, func):
38    "Add a function to hook. Ignore if already on hook."
39    if not _hooks.get(hook, None):
40        _hooks[hook] = []
41    if func not in _hooks[hook]:
42        _hooks[hook].append(func)
43
44def remHook(hook, func):
45    "Remove a function if is on hook."
46    hook = _hooks.get(hook, [])
47    if func in hook:
48        hook.remove(func)
49
50# Instrumenting
51##############################################################################
52
53def wrap(old, new, pos="after"):
54    "Override an existing function."
55    def repl(*args, **kwargs):
56        if pos == "after":
57            old(*args, **kwargs)
58            return new(*args, **kwargs)
59        elif pos == "before":
60            new(*args, **kwargs)
61            return old(*args, **kwargs)
62        else:
63            return new(_old=old, *args, **kwargs)
64
65    def decorator_wrapper(f, *args, **kwargs):
66        return repl(*args, **kwargs)
67
68    return decorator.decorator(decorator_wrapper)(old)
69