Re-raising exceptions with modified message

samwyse samwyse at gmail.com
Fri Jul 13 18:53:08 CEST 2007


On Jul 13, 12:45 am, Christoph Zwerschke <c... at online.de> wrote:
> samwyse wrote:
> > TypeError: __class__ must be set to a class
>
> > Excpt ceratinly appears to be a class.  Does anyone smarter than me
> > know what's going on here?
>
> Not that I want to appear smarter, but I think the problem here is that
> exceptions are new-style classes now, whereas Empty is an old-style
> class. But even if you define Empty as a new-style class, it will not
> work, you get:
>
> TypeError: __class__ assignment: only for heap types
>
> This tells us that we cannot change the attributes of a built-in
> exception. If it would be possible, I simply would have overridden the
> __str__ method of the original exception in the first place.
>
> -- Chris

Chris, you owe me a beer if you're ever in St. Louis, or I'm ever in
Germany.

# ----- CUT HERE -----

# Written by Sam Denton <samwyse at gmail.com>
# You may use, copy, or distribute this work,
# as long as you give credit to the original author.

# tested successfully under Python 2.4.1, 2.4.3, 2.5.1

"""
On Jul 5, 2007, at 8:53 am, Christoph Zwerschke <c... at online.de>
wrote:
> What is the best way to re-raise any exception with a message
> supplemented with additional information (e.g. line number in a
> template)? Let's say for simplicity I just want to add "sorry" to
> every exception message.

Here is an example of typical usage:

>>> def typical_usage(code):
...     try:
...         code()
...     except Exception, e:
...         simplicity = lambda self: str(e) + ", sorry!"
...         raise modify_message(e, simplicity)

Note that if we want to re-cycle the original exception's message,
then we need our re-formatter (here called 'simplicity') to be
defined inside the exception handler.  I tried verious approaches
to defining the re-formater, but all of them eventually needed a
closure; I decided that I liked this approach best.

This harness wraps the example so that doctest doesn't get upset.

>>> def test_harness(code):
...     try:
...         typical_usage(code)
...     except Exception, e:
...         print "%s: %s" % (e.__class__.__name__, str(e))

Now for some test cases:

>>> test_harness(lambda: 1/0)
ZeroDivisionError: integer division or modulo by zero, sorry!

>>> test_harness(lambda: unicode('\xe4'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position
0: ordinal not in range(128), sorry!

"""

def modify_message(old, f):
    """modify_message(exception, mutator) --> exception

    Modifies the string representation of an exception.
    """
    class NewStyle(old.__class__):
        def __init__(self): pass
    NewStyle.__name__ = old.__class__.__name__
    NewStyle.__str__ = f
    new = NewStyle()
    new.__dict__ = old.__dict__.copy()
    return new

def _test():
    import doctest
    return doctest.testmod(verbose=True)

if __name__ == "__main__":
    _test()




More information about the Python-list mailing list