aboutsummaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/python/hook.py
blob: 4142b0a006ad6b66af286e033b40624d5fc613da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.



"""
I define support for hookable instance methods.

These are methods which you can register pre-call and post-call external
functions to augment their functionality.  People familiar with more esoteric
languages may think of these as \"method combinations\".

This could be used to add optional preconditions, user-extensible callbacks
(a-la emacs) or a thread-safety mechanism.

The four exported calls are:

   - L{addPre}
   - L{addPost}
   - L{removePre}
   - L{removePost}

All have the signature (class, methodName, callable), and the callable they
take must always have the signature (instance, *args, **kw) unless the
particular signature of the method they hook is known.

Hooks should typically not throw exceptions, however, no effort will be made by
this module to prevent them from doing so.  Pre-hooks will always be called,
but post-hooks will only be called if the pre-hooks do not raise any exceptions
(they will still be called if the main method raises an exception).  The return
values and exception status of the main method will be propogated (assuming
none of the hooks raise an exception).  Hooks will be executed in the order in
which they are added.
"""


### Public Interface

class HookError(Exception):
    "An error which will fire when an invariant is violated."

def addPre(klass, name, func):
    """hook.addPre(klass, name, func) -> None

    Add a function to be called before the method klass.name is invoked.
    """

    _addHook(klass, name, PRE, func)

def addPost(klass, name, func):
    """hook.addPost(klass, name, func) -> None

    Add a function to be called after the method klass.name is invoked.
    """
    _addHook(klass, name, POST, func)

def removePre(klass, name, func):
    """hook.removePre(klass, name, func) -> None

    Remove a function (previously registered with addPre) so that it
    is no longer executed before klass.name.
    """

    _removeHook(klass, name, PRE, func)

def removePost(klass, name, func):
    """hook.removePre(klass, name, func) -> None

    Remove a function (previously registered with addPost) so that it
    is no longer executed after klass.name.
    """
    _removeHook(klass, name, POST, func)

### "Helper" functions.

hooked_func = """

import %(module)s

def %(name)s(*args, **kw):
    klazz = %(module)s.%(klass)s
    for preMethod in klazz.%(preName)s:
        preMethod(*args, **kw)
    try:
        return klazz.%(originalName)s(*args, **kw)
    finally:
        for postMethod in klazz.%(postName)s:
            postMethod(*args, **kw)
"""

_PRE = '__hook_pre_%s_%s_%s__'
_POST = '__hook_post_%s_%s_%s__'
_ORIG = '__hook_orig_%s_%s_%s__'


def _XXX(k,n,s):
    """
    String manipulation garbage.
    """
    x = s % (k.__module__.replace('.', '_'), k.__name__, n)
    return x

def PRE(k,n):
    "(private) munging to turn a method name into a pre-hook-method-name"
    return _XXX(k,n,_PRE)

def POST(k,n):
    "(private) munging to turn a method name into a post-hook-method-name"
    return _XXX(k,n,_POST)

def ORIG(k,n):
    "(private) munging to turn a method name into an `original' identifier"
    return _XXX(k,n,_ORIG)


def _addHook(klass, name, phase, func):
    "(private) adds a hook to a method on a class"
    _enhook(klass, name)

    if not hasattr(klass, phase(klass, name)):
        setattr(klass, phase(klass, name), [])

    phaselist = getattr(klass, phase(klass, name))
    phaselist.append(func)


def _removeHook(klass, name, phase, func):
    "(private) removes a hook from a method on a class"
    phaselistname = phase(klass, name)
    if not hasattr(klass, ORIG(klass,name)):
        raise HookError("no hooks present!")

    phaselist = getattr(klass, phase(klass, name))
    try: phaselist.remove(func)
    except ValueError:
        raise HookError("hook %s not found in removal list for %s"%
                    (name,klass))

    if not getattr(klass, PRE(klass,name)) and not getattr(klass, POST(klass, name)):
        _dehook(klass, name)

def _enhook(klass, name):
    "(private) causes a certain method name to be hooked on a class"
    if hasattr(klass, ORIG(klass, name)):
        return

    def newfunc(*args, **kw):
        for preMethod in getattr(klass, PRE(klass, name)):
            preMethod(*args, **kw)
        try:
            return getattr(klass, ORIG(klass, name))(*args, **kw)
        finally:
            for postMethod in getattr(klass, POST(klass, name)):
                postMethod(*args, **kw)
    try:
        newfunc.func_name = name
    except TypeError:
        # Older python's don't let you do this
        pass

    oldfunc = getattr(klass, name).im_func
    setattr(klass, ORIG(klass, name), oldfunc)
    setattr(klass, PRE(klass, name), [])
    setattr(klass, POST(klass, name), [])
    setattr(klass, name, newfunc)

def _dehook(klass, name):
    "(private) causes a certain method name no longer to be hooked on a class"

    if not hasattr(klass, ORIG(klass, name)):
        raise HookError("Cannot unhook!")
    setattr(klass, name, getattr(klass, ORIG(klass,name)))
    delattr(klass, PRE(klass,name))
    delattr(klass, POST(klass,name))
    delattr(klass, ORIG(klass,name))