views:

105

answers:

4

I'm implementing a event system: Various pieces of code will post events to a central place where they will be distributed to all listeners. The main problem with this approach: When an exception happens during event processing, I can't tell anymore who posted the event.

So my question is: Is there an efficient way to figure out who called the constructor and remember that in Python 2.5?

More info: The simple way would be to use the traceback module to get a copy of the stack in the constructor and remember that. Alas, I only need this information rarely, so I'm wondering if there was a way to cache this or whether I could just remember the topmost stack frame and work my way back in the rare case that I actually need this data.

A: 

It may be worthwhile to attach a hash of the stack trace to the constructor of your event and to store the actual contents in memcache with the hash as the key.

Jeremy Stanley
But to create the hash, I still need to process the whole stacktrace at the time the constructor is called :/
Aaron Digulla
+1  A: 
import sys
def get_caller(ext=False):
    """ Get the caller of the caller of this function. If the optional ext parameter is given, returns the line's text as well. """
    f=sys._getframe(2)
    s=(f.f_code.co_filename, f.f_lineno)
    del f
    if ext:
     import linecache
     s=(s[0], s[1], linecache.getline(s[0], s[1]))

    return s

def post_event(e):
    caller=get_caller(True)
    print "Event %r posted from %r"%(e, caller)

## Testing the functions.

def q():
    post_event("baz")

post_event("foo")
print "Hello!"
q()

results in

Event 'foo' posted from ('getcaller.py', 20, 'post_event("foo")\n')
Hello!
Event 'baz' posted from ('getcaller.py', 17, '\tpost_event("baz")\n')
AKX
Thanks but this doesn't work reliably for classes when __init__ is overloaded.
Aaron Digulla
Ah, okay. Didn't test that case, but I suppose it could be amended to find `self' in f.f_locals and save the value of that too...
AKX
+1  A: 

You could simply store a reference to the caller's frame object, but this is probably a bad idea. This keeps the frames alive, and also holds references to all the local variables used, so it may impact performance if they happen to be using large chunks of memory, and could have even worse effects if they're relying (incorrectly) on finalization to destroy resources like locks and filehandles when they go out of scope.

That means you'd need to hold a string representation of the stacktrace instead, which is not ideal for your purposes (need to actually do some processing to get it, even though it's rarely needed). Unfortunately, there doesn't seem to be much way around this, though you could consider disabling it until you set some configuration option. That way you'd get better performance for the common case, but could still enable the setting when trying to diagnose failures.

If your calling function alone (or some small number of parent callers) is enough to distinguish the route (ie. the trace is always the same when called via func1(), and there's no func2 -> func1() vs func3() -> func1() to distinguish between ), you could maintain a hash based on filename and line number of the calling frame (or the last two calling frames etc). However this probably doesn't match your situation, and where it doesn't, you'd end up with bogus stack traces.

Note that if you do want the caller's frame, using inspect.currentframe(depth) is probably a better way to get it.

Brian
+1 for mentioning the issues in keeping references to frames. Original dire warning from the horse's mouth is here: http://docs.python.org/library/inspect.html#the-interpreter-stack
Jarret Hardie
+1  A: 

I'd think that the simplest method would be to add an ID field to the event(s) in question, and to have each event source (by whatever definition of 'event source' is appropriate here) provide a unique identifier when it posts the event. You do get slightly more overhead, but probably not enough to be problematic, and I'd suspect that you'll find other ways that knowing an event's source would be helpful.

Jeff Shannon