On 25 Apr 2005 03:32:38 -0700, "George Sakkis" <[EMAIL PROTECTED]> wrote:
>Is there a general way of injecting code into a function, typically >before and/or after the existing code ? I know that for most purposes, >an OO solution, such as the template pattern, is a cleaner way to get >the same effect, but it's not always applicable (e.g. if you have no >control over the design and you are given a function to start with). In >particular, I want to get access to the function's locals() just before >it exits, i.e. something like: > >def analyzeLocals(func): > func_locals = {} > def probeFunc(): > # insert func's code here > sys._getframe(1).f_locals["func_locals"].update(locals()) > probeFunc() > # func_locals now contains func's locals > >So, how can I add func's code in probeFunc so that the injected code >(the update line here) is always called before the function exits ? >That is, don't just inject it lexically in the end of the function if >there are more than one exit points. I guess a solution will involve a >good deal bytecode hacking, on which i know very little; if there's a >link to a (relatively) simple HOWTO, it would be very useful. > I'm not clear on what your real goal is, but if you just want a snapshot of what locals() is just before exiting func, that could be done with a byte-code-hacking decorator with usage looking something like #func defined before this func_locals = {} @getlocals(func, func_locals) def probefunc(): pass which would make a probefunc function that would be identical to func except that before exiting, it would do func_locals.update(locals()). (you might want func_locals to be a list and do func_locals.append(locals()) in case func is recursive and you are interested in the all the locals). Alternatively, if this is a debugging thing, you might want to look into sys.settrace -- as in this thing I cobbled together (not tested beyond what you see ;-): ----< tracelocals.py >---------------------------------------------------- class TraceLocals(object): from sys import settrace def __init__(self, *names, **kw): self.names = set(names) self.func_locals = kw.get('func_locals', []) # [(name,locals()), ...] tuples def _gwatch(self, frame, event, arg): """ Global scope watcher. When a new scope is entered, returns the local scope watching method _lwatch to do the work for that. """ if event=='call': name = frame.f_code.co_name # name of executing scope if name in self.names: return self._lwatch # name is one whose locals we want def _lwatch(self, frame, event, arg): if event == 'return': self.func_locals.append((frame.f_code.co_name,frame.f_locals)) else: return self._lwatch # keep watching for return event def on(self): """Set the system trace hook to enable tracing. """ self.settrace(self._gwatch) def off(self): """Reset the system trace hook to disable tracing. """ self.settrace(None) def main(modname, entry, *names): print 'main(', modname, entry, names,')' tr = TraceLocals(*names) mod = __import__(modname) try: tr.on() getattr(mod, entry)() finally: tr.off() return tr.func_locals def test(): tr = TraceLocals(*'t1 t2 t3'.split()) def t1(): x ='t1' def t2(y=123): y*=2 def t3(): t1() t2() t2('hello ') try: tr.on() t3() finally: tr.off() for name, loc in tr.func_locals: print '%5s: %s' %(name, loc) if __name__=='__main__': import sys args = sys.argv[1:] if not args: raise SystemExit( 'Usage: python tracelocals.py (-test | module entry name+)\n' ) if args[0]=='-test': test() else: print args func_locals = main(args[0], args[1], *args[2:]) for name, loc in func_locals: print '%5s: %s' %(name, loc) -------------------------------------------------------------------------- Test result: [22:37] C:\pywk\clp\sakkis\tracelocals>py24 tracelocals.py -test t1: {'x': 't1'} t2: {'y': 246} t2: {'y': 'hello hello '} t3: {'t2': <function t2 at 0x02EE8ED4>, 't1': <function t1 at 0x02EE8E9C>} Note that t3 is seeing t1 and t2 in its locals -- I think because they're visible as cell vars in test. If you put t1-t3 in a separate module, you don't see it: ----< tmod.py >--------------- def t1(): print '-- t1' x ='t1' def t2(y=123): print '-- t2' y*=2 def t3(): print '-- t3' t1() t2() t2('hello ') ----------------------------- [22:42] C:\pywk\clp\sakkis\tracelocals>py24 tracelocals.py tmod t3 t1 t2 t3 ['tmod', 't3', 't1', 't2', 't3'] main( tmod t3 ('t1', 't2', 't3') ) -- t3 -- t1 -- t2 -- t2 t1: {'x': 't1'} t2: {'y': 246} t2: {'y': 'hello hello '} t3: {} [22:46] C:\pywk\clp\sakkis\tracelocals>py24 tracelocals.py tmod t3 t2 ['tmod', 't3', 't2'] main( tmod t3 ('t2',) ) -- t3 -- t1 -- t2 -- t2 t2: {'y': 246} t2: {'y': 'hello hello '} Notice that the -- side effects from all being called in the last, but only t2 being captured. Maybe this will give you something to expand to your needs. Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list