| OLD | NEW |
| (Empty) |
| 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, na
me)): | |
| 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)) | |
| OLD | NEW |