~midokura/nova/midostack-oneiric

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/hook.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
 
6
 
 
7
"""
 
8
I define support for hookable instance methods.
 
9
 
 
10
These are methods which you can register pre-call and post-call external
 
11
functions to augment their functionality.  People familiar with more esoteric
 
12
languages may think of these as \"method combinations\".
 
13
 
 
14
This could be used to add optional preconditions, user-extensible callbacks
 
15
(a-la emacs) or a thread-safety mechanism.
 
16
 
 
17
The four exported calls are:
 
18
 
 
19
   - L{addPre}
 
20
   - L{addPost}
 
21
   - L{removePre}
 
22
   - L{removePost}
 
23
 
 
24
All have the signature (class, methodName, callable), and the callable they
 
25
take must always have the signature (instance, *args, **kw) unless the
 
26
particular signature of the method they hook is known.
 
27
 
 
28
Hooks should typically not throw exceptions, however, no effort will be made by
 
29
this module to prevent them from doing so.  Pre-hooks will always be called,
 
30
but post-hooks will only be called if the pre-hooks do not raise any exceptions
 
31
(they will still be called if the main method raises an exception).  The return
 
32
values and exception status of the main method will be propogated (assuming
 
33
none of the hooks raise an exception).  Hooks will be executed in the order in
 
34
which they are added.
 
35
 
 
36
"""
 
37
 
 
38
# System Imports
 
39
import string
 
40
 
 
41
### Public Interface
 
42
 
 
43
class HookError(Exception):
 
44
    "An error which will fire when an invariant is violated."
 
45
 
 
46
def addPre(klass, name, func):
 
47
    """hook.addPre(klass, name, func) -> None
 
48
 
 
49
    Add a function to be called before the method klass.name is invoked.
 
50
    """
 
51
 
 
52
    _addHook(klass, name, PRE, func)
 
53
 
 
54
def addPost(klass, name, func):
 
55
    """hook.addPost(klass, name, func) -> None
 
56
 
 
57
    Add a function to be called after the method klass.name is invoked.
 
58
    """
 
59
    _addHook(klass, name, POST, func)
 
60
 
 
61
def removePre(klass, name, func):
 
62
    """hook.removePre(klass, name, func) -> None
 
63
 
 
64
    Remove a function (previously registered with addPre) so that it
 
65
    is no longer executed before klass.name.
 
66
    """
 
67
 
 
68
    _removeHook(klass, name, PRE, func)
 
69
 
 
70
def removePost(klass, name, func):
 
71
    """hook.removePre(klass, name, func) -> None
 
72
 
 
73
    Remove a function (previously registered with addPost) so that it
 
74
    is no longer executed after klass.name.
 
75
    """
 
76
    _removeHook(klass, name, POST, func)
 
77
 
 
78
### "Helper" functions.
 
79
 
 
80
hooked_func = """
 
81
 
 
82
import %(module)s
 
83
 
 
84
def %(name)s(*args, **kw):
 
85
    klazz = %(module)s.%(klass)s
 
86
    for preMethod in klazz.%(preName)s:
 
87
        preMethod(*args, **kw)
 
88
    try:
 
89
        return klazz.%(originalName)s(*args, **kw)
 
90
    finally:
 
91
        for postMethod in klazz.%(postName)s:
 
92
            postMethod(*args, **kw)
 
93
"""
 
94
 
 
95
_PRE = '__hook_pre_%s_%s_%s__'
 
96
_POST = '__hook_post_%s_%s_%s__'
 
97
_ORIG = '__hook_orig_%s_%s_%s__'
 
98
 
 
99
 
 
100
def _XXX(k,n,s):
 
101
    "string manipulation garbage"
 
102
    x = s % (string.replace(k.__module__,'.','_'), k.__name__, n)
 
103
    return x
 
104
 
 
105
def PRE(k,n):
 
106
    "(private) munging to turn a method name into a pre-hook-method-name"
 
107
    return _XXX(k,n,_PRE)
 
108
 
 
109
def POST(k,n):
 
110
    "(private) munging to turn a method name into a post-hook-method-name"
 
111
    return _XXX(k,n,_POST)
 
112
 
 
113
def ORIG(k,n):
 
114
    "(private) munging to turn a method name into an `original' identifier"
 
115
    return _XXX(k,n,_ORIG)
 
116
 
 
117
 
 
118
def _addHook(klass, name, phase, func):
 
119
    "(private) adds a hook to a method on a class"
 
120
    _enhook(klass, name)
 
121
 
 
122
    if not hasattr(klass, phase(klass, name)):
 
123
        setattr(klass, phase(klass, name), [])
 
124
 
 
125
    phaselist = getattr(klass, phase(klass, name))
 
126
    phaselist.append(func)
 
127
 
 
128
 
 
129
def _removeHook(klass, name, phase, func):
 
130
    "(private) removes a hook from a method on a class"
 
131
    phaselistname = phase(klass, name)
 
132
    if not hasattr(klass, ORIG(klass,name)):
 
133
        raise HookError("no hooks present!")
 
134
 
 
135
    phaselist = getattr(klass, phase(klass, name))
 
136
    try: phaselist.remove(func)
 
137
    except ValueError:
 
138
        raise HookError("hook %s not found in removal list for %s"%
 
139
                    (name,klass))
 
140
 
 
141
    if not getattr(klass, PRE(klass,name)) and not getattr(klass, POST(klass, name)):
 
142
        _dehook(klass, name)
 
143
 
 
144
def _enhook(klass, name):
 
145
    "(private) causes a certain method name to be hooked on a class"
 
146
    if hasattr(klass, ORIG(klass, name)):
 
147
        return
 
148
 
 
149
    def newfunc(*args, **kw):
 
150
        for preMethod in getattr(klass, PRE(klass, name)):
 
151
            preMethod(*args, **kw)
 
152
        try:
 
153
            return getattr(klass, ORIG(klass, name))(*args, **kw)
 
154
        finally:
 
155
            for postMethod in getattr(klass, POST(klass, name)):
 
156
                postMethod(*args, **kw)
 
157
    try:
 
158
        newfunc.func_name = name
 
159
    except TypeError:
 
160
        # Older python's don't let you do this
 
161
        pass
 
162
 
 
163
    oldfunc = getattr(klass, name).im_func
 
164
    setattr(klass, ORIG(klass, name), oldfunc)
 
165
    setattr(klass, PRE(klass, name), [])
 
166
    setattr(klass, POST(klass, name), [])
 
167
    setattr(klass, name, newfunc)
 
168
 
 
169
def _dehook(klass, name):
 
170
    "(private) causes a certain method name no longer to be hooked on a class"
 
171
 
 
172
    if not hasattr(klass, ORIG(klass, name)):
 
173
        raise HookError("Cannot unhook!")
 
174
    setattr(klass, name, getattr(klass, ORIG(klass,name)))
 
175
    delattr(klass, PRE(klass,name))
 
176
    delattr(klass, POST(klass,name))
 
177
    delattr(klass, ORIG(klass,name))