[Tutor] function signatures for callbacks

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Tue Mar 21 22:44:22 CET 2006


> > It seems backwards to me because because if I get the function
> > definition wrong in the client site then I get a traceback to the
> > callback site - which is meant to be an opaque library that the client
> > should not have to know about.
>
> Yes, what you're asking makes sense: we want to "blame" the right party,
> and if it's the callback provider that messed up, we should only
> attribute them.  (The same kind of problem happens in Tkinter, where
> bugs in callbacks end up exposing Tkinter internals.)


Hi Don,

As a quick-and-dirty hack, we can do a kind of wrapped exception handling
to hide the internals of the system.

Here's an example of this:

#####################################################################
"""A small example of attaching callbacks and wrapping exceptions."""

class CallbackError(Exception): pass

class Announcer(object):
    def __init__(self):
        self.listeners = []

    def add_listener(self, listener):
        self.listeners.append(listener)

    def shout(self, msg):
        try:
            self._internal_shout(msg)
        except Exception, e:
            raise CallbackError, str(e)

    def _internal_shout(self, msg):
        for l in self.listeners:
            l(msg)
#####################################################################


Here's a small demonstration:

######
>>> announcer = Announcer()
>>> def f1(x):
...     print "f1 sees", x
...
>>> def f2(x):
...     1 / 0
...
>>> announcer.add_listener(f1)
>>> announcer.add_listener(f2)
>>> announcer.shout("foo")
f1 sees foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/tmp/python-213928MGH.py", line 16, in shout
__main__.CallbackError: integer division or modulo by zero
######


The traceback we see here starts off at the entry point --- at shout()
--- and because we're returning a new exception object at that point, we
don't show that we were at _internal_shout().


If we look at the implementation of shout():

    def shout(self, msg):
        try:
            self._internal_shout(msg)
        except Exception, e:
            raise CallbackError, str(e)

then we can probably make this a little more sophisticated by properly
extracting more information out of the original exception to make error
debugging a little easier.


However, if we're going to go through this sophisticated route, we'd
better make extra sure not to discard useful debugging information.  One
thing that beginners will often do is something like:

    try:
        ...
    except:
        print "Something bad happened"

which is just silly.  *grin*

There's a happy medium between error messages being too detailed and not
detailed enough; I'd personally lean toward being too detailed at first,
but maybe that's just me.



I hope this helps!



More information about the Tutor mailing list