467,081 Members | 982 Online
Bytes | Developer Community
Ask Question

Home New Posts Topics Members FAQ

Post your question to a community of 467,081 developers. It's quick & easy.

Re-raising exceptions with modified message

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. My naive solution was this:

try:
...
except Exception, e:
raise e.__class__, str(e) + ", sorry!"

This works pretty well for most exceptions, e.g.
>>try:
.... 1/0
.... except Exception, e:
.... raise e.__class__, str(e) + ", sorry!"
....
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ZeroDivisionError: integer division or modulo by zero, sorry!

But it fails for some exceptions that cannot be instantiated with a
single string argument, like UnicodeDecodeError which gets "converted"
to a TypeError:
>>try:
.... unicode('\xe4')
.... except Exception, e:
.... raise e.__class__, str(e) + ", sorry!"
....
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
TypeError: function takes exactly 5 arguments (1 given)

Another approach is using a wrapper Extension class:

class SorryEx(Exception):
def __init__(self, e):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
return str(self._e) + ", sorry!"

try:
unicode('\xe4')
except Exception, e:
raise SorryEx(e)

But then I get the name of the wrapper class in the message:

__main__.SorryEx: 'ascii' codec can't decode byte 0xe4 in position 0:
ordinal not in range(128), sorry!

Yet another approach would be to replace the __str__ method of e, but
this does not work for new style Exceptions (Python 2.5).

Any suggestions?

-- Chris
Jul 5 '07 #1
  • viewed: 5182
Share:
28 Replies
Christoph Zwerschke schrieb:
What is the best way to re-raise any exception with a message
supplemented with additional information (e.g. line number in a
template)?
I have the impression that you do NOT want to change the exceptions,
instead you want to print the traceback in a customized way. But I may be wrong...

Thomas

Jul 5 '07 #2
Thomas Heller wrote:
I have the impression that you do NOT want to change the exceptions,
instead you want to print the traceback in a customized way. But I may be wrong...
No, I really want to modify the exception, supplementing its message
with additional information about the state of the program.

The use case are compiled Kid templates (http://kid-templating.org). If
an error occurs, I want to add the corresponding line number of the XML
file as information for the template developer. Since the final error
handling may happen somewhere else (e.g. by TurboGears importing a Kid
template), I do not want to modify trackeback handling or something.

-- Chris
Jul 5 '07 #3
On Jul 5, 3:53 pm, Christoph Zwerschke <c...@online.dewrote:
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. My naive solution was this:

try:
...
except Exception, e:
raise e.__class__, str(e) + ", sorry!"

This works pretty well for most exceptions, e.g.
>>try:
... 1/0
... except Exception, e:
... raise e.__class__, str(e) + ", sorry!"
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ZeroDivisionError: integer division or modulo by zero, sorry!

But it fails for some exceptions that cannot be instantiated with a
single string argument, like UnicodeDecodeError which gets "converted"
to a TypeError:
>>try:
... unicode('\xe4')
... except Exception, e:
... raise e.__class__, str(e) + ", sorry!"
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
TypeError: function takes exactly 5 arguments (1 given)

Another approach is using a wrapper Extension class:

class SorryEx(Exception):
def __init__(self, e):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
return str(self._e) + ", sorry!"

try:
unicode('\xe4')
except Exception, e:
raise SorryEx(e)

But then I get the name of the wrapper class in the message:

__main__.SorryEx: 'ascii' codec can't decode byte 0xe4 in position 0:
ordinal not in range(128), sorry!

Yet another approach would be to replace the __str__ method of e, but
this does not work for new style Exceptions (Python 2.5).

Any suggestions?

-- Chris
If you are sure that the exception isn't caught on another level just
use the following showtraceback() function, manipulate it's output
slightly and terminate your program with sys.exit()

def showtraceback():
'''
(Copied from code.py)
'''
try:
type, value, tb = sys.exc_info()
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
tblist = traceback.extract_tb(tb)
del tblist[:1]
lst = traceback.format_list(tblist)
if lst:
lst.insert(0, "Traceback (most recent call last):\n")
lst[len(lst):] = traceback.format_exception_only(type,
value)
finally:
tblist = tb = None
sys.stderr.write("".join(lst))
Jul 5 '07 #4
Kay Schluehr wrote:
If you are sure that the exception isn't caught on another level just
use the following showtraceback() function, manipulate it's output
slightly and terminate your program with sys.exit()
That's what I want to avoid. In my case the error is displayed and
evaluated in a completly different piece of software.

-- Chris
Jul 5 '07 #5
Seems that no simple solution exists,
so for now, I will be using something like this:

class PoliteException(Exception):
def __init__(self, e):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
if isinstance(self._e, PoliteException):
return str(self._e)
else:
return '\n%s: %s, I am sorry!' % (
self._e.__class__.__name__, str(self._e))

try:
unicode('\xe4')
except Exception, e:
raise PoliteException(e)
Jul 5 '07 #6
On Jul 6, 12:21 am, Christoph Zwerschke <c...@online.dewrote:
Kay Schluehr wrote:
If you are sure that the exception isn't caught on another level just
use the following showtraceback() function, manipulate it's output
slightly and terminate your program with sys.exit()

That's what I want to avoid. In my case the error is displayed and
evaluated in a completly different piece of software.

-- Chris
Probably the simplest solution would be to create a new exception and
wrapping the old one and the additional info. Unfortunately,
this may have a huge impact on 3rd party code that was catching the
original exception.

So, I think you should create an utility factory-like function that is
either creating a new exception instance as the one caught
and with the additional information, or an utility that knows how to
modify the caught exception according to its type.
In the first case you will need somehow to tell to the new instance
exception the real stack trace, because by simply raising
a new one the original stack trace may get lost.

bests,

../alex
--
..w( the_mindstorm )p.

Jul 5 '07 #7
Sorry for the soliloquy, but what I am really using is the following so
that the re-raised excpetion has the same type:

def PoliteException(e):
class PoliteException(e.__class__):
def __init__(self, e):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
if isinstance(self._e, PoliteException):
return str(self._e)
else:
return '\n%s: %s, I am sorry!' % (
self._e.__class__.__name__, str(self._e))
return PoliteException(e)

try:
unicode('\xe4')
except Exception, e:
raise PoliteException(e)

Jul 5 '07 #8
Alex Popescu wrote:
Probably the simplest solution would be to create a new exception and
wrapping the old one and the additional info. Unfortunately, this
may have a huge impact on 3rd party code that was catching the
original exception. So, I think you should create an utility
factory-like function that is either creating a new exception
instance as the one caught and with the additional information,
Right, I have gone with that (see the example with the PoliteException
class somewhere below).
or an utility that knows how to modify the caught exception according
to its type.
I guess you mean something like this (simplified):

except Exception, e:
if getattr(e, 'reason'):
e.reason += "sorry"
else:
e.message += "sorry"

The problem is that these attribute names are not standardized and can
change between Python versions. Not even "args" is sure, and if a class
has "message" it does not mean that it is displayed. Therefore I think
the first approach is better.
In the first case you will need somehow to tell to the new instance
exception the real stack trace, because by simply raising
a new one the original stack trace may get lost.
Yes, but thats a different problem that is easy to solve.

-- Chris
Jul 6 '07 #9
On Jul 6, 4:20 am, Christoph Zwerschke <c...@online.dewrote:
Alex Popescu wrote:
Jul 6 '07 #10
On Jul 6, 4:20 am, Christoph Zwerschke <c...@online.dewrote:
Alex Popescu wrote:
Probably the simplest solution would be to create a new exception and
wrapping the old one and the additional info. Unfortunately, this
may have a huge impact on 3rd party code that was catching the
original exception. So, I think you should create an utility
factory-like function that is either creating a new exception
instance as the one caught and with the additional information,

Right, I have gone with that (see the example with the PoliteException
class somewhere below).
or an utility that knows how to modify the caught exception according
to its type.

I guess you mean something like this (simplified):

except Exception, e:
if getattr(e, 'reason'):
e.reason += "sorry"
else:
e.message += "sorry"

The problem is that these attribute names are not standardized and can
change between Python versions. Not even "args" is sure, and if a class
has "message" it does not mean that it is displayed. Therefore I think
the first approach is better.
In the first case you will need somehow to tell to the new instance
exception the real stack trace, because by simply raising
a new one the original stack trace may get lost.

Yes, but thats a different problem that is easy to solve.
Yeah maybe for a python guy, but I am a newbie. I would really
appreciate if you can show in this thread how this can be done in
Python.

tia,

../alex
--
..w( the_mindstorm )p.

PS: sorry for reposting, but it looks like my previous message hasn't
gone through :-(.

Jul 6 '07 #11
On 2007-07-06, Alex Popescu <th***********************@gmail.comwrote:
On Jul 6, 4:20 am, Christoph Zwerschke <c...@online.dewrote:
>Alex Popescu wrote:
Probably the simplest solution would be to create a new exception and
wrapping the old one and the additional info. Unfortunately, this
may have a huge impact on 3rd party code that was catching the
original exception. So, I think you should create an utility
factory-like function that is either creating a new exception
instance as the one caught and with the additional information,

Right, I have gone with that (see the example with the PoliteException
class somewhere below).
or an utility that knows how to modify the caught exception according
to its type.

I guess you mean something like this (simplified):

except Exception, e:
if getattr(e, 'reason'):
e.reason += "sorry"
else:
e.message += "sorry"

The problem is that these attribute names are not standardized and can
change between Python versions. Not even "args" is sure, and if a class
has "message" it does not mean that it is displayed. Therefore I think
the first approach is better.
In the first case you will need somehow to tell to the new instance
exception the real stack trace, because by simply raising
a new one the original stack trace may get lost.

Yes, but thats a different problem that is easy to solve.

Yeah maybe for a python guy, but I am a newbie. I would really
appreciate if you can show in this thread how this can be done
in Python.
Chech out the docs for sys.exc_info(), and for the raise
statement.

When handling an exception, you can rethrow a different
exception, but with the same traceback, by using the three-arg
version of raise.

See one of my earlier posts in this thread for a working example
(although it didn't solve Chris's problem).

--
Neil Cerutti
Jul 6 '07 #12
On Jul 6, 12:18 am, Christoph Zwerschke <c...@online.dewrote:
Sorry for the soliloquy, but what I am really using is the following so
that the re-raised excpetion has the same type:

def PoliteException(e):
class PoliteException(e.__class__):
def __init__(self, e):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
if isinstance(self._e, PoliteException):
return str(self._e)
else:
return '\n%s: %s, I am sorry!' % (
self._e.__class__.__name__, str(self._e))
return PoliteException(e)

try:
unicode('\xe4')
except Exception, e:
raise PoliteException(e)
Would a decorator work here?
class PoliteException(Exception):
def __init__(self, e):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
return '\n%s: %s, I am sorry!' % (
self._e.__class__.__name__, str(self._e))

def politefail(fn):
def wrapper(*args, **kwargs):
try:
return fn(*args, **kwargs)
except Exception, e:
raise PoliteException(e)
return wrapper

@politefail
def funktion():
unicode('\xe4')

funktion()

@politefail
def raise_exception(err, *args):
raise err(*args)
def funktion():
if 1 != 2:
raise_exception(ArithmeticError, '1 is not equal to 2.')

print
funktion()

Jul 6 '07 #13
Gerard Flanagan wrote:
Would a decorator work here?
Depends on how you want to use that functionality. In my use case I only
need to catch the excpetion once.

Note that in your code the exception has not the right type which is
what I targeted in my last posting. I.e. the following will raise an
Exception if function is decorated:

try:
print funktion()
except ArithmeticError:
pass

-- Chris
Jul 7 '07 #14
On Jul 5, 8:53 am, Christoph Zwerschke <c...@online.dewrote:
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. My naive solution was this:

try:
...
except Exception, e:
raise e.__class__, str(e) + ", sorry!"

This works pretty well for most exceptions, e.g.
>>try:
... 1/0
... except Exception, e:
... raise e.__class__, str(e) + ", sorry!"
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ZeroDivisionError: integer division or modulo by zero, sorry!

But it fails for some exceptions that cannot be instantiated with a
single string argument, like UnicodeDecodeError which gets "converted"
to a TypeError:
>>try:
... unicode('\xe4')
... except Exception, e:
... raise e.__class__, str(e) + ", sorry!"
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
TypeError: function takes exactly 5 arguments (1 given)

Another approach is using a wrapper Extension class:

class SorryEx(Exception):
def __init__(self, e):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
return str(self._e) + ", sorry!"

try:
unicode('\xe4')
except Exception, e:
raise SorryEx(e)

But then I get the name of the wrapper class in the message:

__main__.SorryEx: 'ascii' codec can't decode byte 0xe4 in position 0:
ordinal not in range(128), sorry!

Yet another approach would be to replace the __str__ method of e, but
this does not work for new style Exceptions (Python 2.5).

Any suggestions?

-- Chris
Can "try" statements be used in "except" clauses? It appears so, thus
a hybrid approach might work well enough.

try:
...
except Exception, e:
try:
raise e.__class__, str(e) + ", sorry!"
except TypeError:
raise SorryEx(e)

That leaves the issue of the name being changed for
UnicodeDecodeError, which might be fixable by diddling with __name__
properties. Or perhaps SorryEx needs to be a factory that returns
exception classes; the last line would be "SorryEx(e)()". I'll have
to play with this a bit.

Jul 7 '07 #15
On Jul 7, 4:13 pm, samwyse <samw...@gmail.comwrote:
On Jul 5, 8:53 am, Christoph Zwerschke <c...@online.dewrote:
What is the best way to re-raise any exception with a message
supplemented with additional information (e.g. line number in a
template)?
[...]
That leaves the issue of the name being changed for
UnicodeDecodeError, which might be fixable by diddling with __name__
properties. Or perhaps SorryEx needs to be a factory that returns
exception classes; the last line would be "SorryEx(e)()". I'll have
to play with this a bit.
OK, the following mostly works. You probably want the factory to copy
more of the original class into the SorryEx class each time, since
someone catching an exception may expect to look at things besides its
string representation.

def SorryFactory(e):
class SorryEx(Exception):
def __init__(self):
self._e = e
def __getattr__(self, name):
return getattr(self._e, name)
def __str__(self):
return str(self._e) + ", sorry!"
SorryEx.__name__ = e.__class__.__name__
return SorryEx

def test(code):
try:
code()
except Exception, e:
try:
raise e.__class__, str(e) + ", sorry!"
except TypeError:
raise SorryFactory(e)()

test(lambda: unicode('\xe4'))
Jul 7 '07 #16
Did you run this?
With Py < 2.5 I get a syntax error, and with Py 2.5 I get:

new.__class__ = old.__class__
TypeError: __class__ must be set to a class

-- Chris
Jul 8 '07 #17
samwyse wrote:
def test(code):
try:
code()
except Exception, e:
try:
raise e.__class__, str(e) + ", sorry!"
except TypeError:
raise SorryFactory(e)()
Ok, you're suggestig the naive approach if it works and the factory
approach I came up with last as a fallback. Maybe a suitable compromize.

-- Chris
Jul 8 '07 #18
On Jul 12, 6:31 am, samwyse <samw...@gmail.comwrote:
On Jul 8, 8:50 am, Christoph Zwerschke <c...@online.dewrote:
With Py 2.5 I get:
new.__class__ = old.__class__
TypeError: __class__ must be set to a class

Hmmm, under Python 2.4.X, printing repr(old.__class__) gives me this:
<class exceptions.UnicodeDecodeError at 0x00A24F00>
while under 2.5.X, I get this:
<type 'exceptions.UnicodeDecodeError'>
So, let's try sub-classing the type:

def modify_message(old, f):
class Empty: pass
new = Empty()
print "old.__class__ =", repr(old.__class__)
print "Empty =", repr(Empty)
new.__class__ = Empty

class Excpt(old.__class__): pass
print "Excpt =", repr(Excpt)
print "Excpt.__class__ =", repr(Excpt.__class__)
new.__class__ = Excpt

new.__dict__ = old.__dict__.copy()
new.__str__ = f
return new

Nope, that gives us the same message:

old.__class__ = <type 'exceptions.UnicodeDecodeError'>
Empty = <class __main__.Empty at 0x00AB0AB0>
Excpt = <class '__main__.Excpt'>
Excpt.__class__ = <type 'type'>
Traceback (most recent call last):
[...]
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?

Jul 12 '07 #19
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
Jul 13 '07 #20
On Jul 12, 11:48 am, samwyse <samw...@gmail.comwrote:
On Jul 12, 6:31 am,samwyse<samw...@gmail.comwrote:
On Jul 8, 8:50 am, Christoph Zwerschke <c...@online.dewrote:
With Py 2.5 I get:
new.__class__ = old.__class__
TypeError: __class__ must be set to a class

Hmmm, under Python 2.4.X, printing repr(old.__class__) gives me this:
<class exceptions.UnicodeDecodeError at 0x00A24F00>
while under 2.5.X, I get this:
<type 'exceptions.UnicodeDecodeError'>

So, let's try sub-classing the type:

def modify_message(old, f):
class Empty: pass
new = Empty()
print "old.__class__ =", repr(old.__class__)
print "Empty =", repr(Empty)
new.__class__ = Empty

class Excpt(old.__class__): pass
print "Excpt =", repr(Excpt)
print "Excpt.__class__ =", repr(Excpt.__class__)
new.__class__ = Excpt

new.__dict__ = old.__dict__.copy()
new.__str__ = f
return new

Nope, that gives us the same message:

old.__class__ = <type 'exceptions.UnicodeDecodeError'>
Empty = <class __main__.Empty at 0x00AB0AB0>
Excpt = <class '__main__.Excpt'>
Excpt.__class__ = <type 'type'>
Traceback (most recent call last):
[...]
TypeError: __class__ must be set to a class

Excpt certainly appears to be a class. Does anyone smarter than me
know what's going on here?
OK, in classobject.h, we find this:

#define PyClass_Check(op) ((op)->ob_type == &PyClass_Type)

That seems straightforward enough. And the relevant message appears
in classobject.c here:

static int
instance_setattr(PyInstanceObject *inst, PyObject *name, PyObject *v)
[...]
if (strcmp(sname, "__class__") == 0) {
if (v == NULL || !PyClass_Check(v)) {
PyErr_SetString(PyExc_TypeError,
"__class__ must be set to a class");
return -1;
}

Back in our test code, we got these:
Empty = <class __main__.Empty at 0x00AB0AB0>
Excpt = <class '__main__.Excpt'>
The first class (Empty) passes the PyClass_Check macro, the second one
(Excpt) evidently fails. I'll need to dig deeper. Meanwhile, I still
have to wonder why the code doesn't allow __class_ to be assigned a
type instead of a class. Why can't we do this in the C code (assuming
the appropriate PyType_Check macro):

if (v == NULL || !(PyClass_Check(v) || PyType_Check(v))) {

Jul 13 '07 #21
(Yes, I probably should have said CPython in my subject, not Python.
Sorry.)

On Jul 13, 12:56 am, samwyse <samw...@gmail.comwrote:
OK, in classobject.h, we find this:

#define PyClass_Check(op) ((op)->ob_type == &PyClass_Type)

That seems straightforward enough. And the relevant message appears
in classobject.c here:

static int
instance_setattr(PyInstanceObject *inst, PyObject *name, PyObject *v)
[...]
if (strcmp(sname, "__class__") == 0) {
if (v == NULL || !PyClass_Check(v)) {
PyErr_SetString(PyExc_TypeError,
"__class__ must be set to a class");
return -1;
}

Back in our test code, we got these:
Empty = <class __main__.Empty at 0x00AB0AB0>
Excpt = <class '__main__.Excpt'>

The first class (Empty) passes the PyClass_Check macro, the second one
(Excpt) evidently fails. I'll need to dig deeper. Meanwhile, I still
have to wonder why the code doesn't allow __class_ to be assigned a
type instead of a class. Why can't we do this in the C code (assuming
the appropriate PyType_Check macro):

if (v == NULL || !(PyClass_Check(v) || PyType_Check(v))) {
After a good night's sleep, I can see that Empty is a "real" class;
i.e. its repr() is handled by class_repr() in classobject.c. Excpt,
on the other hand, is a type; i.e. its repr is handled by type_repr()
in typeobject.c. (You can tell because class_repr() returns a value
formatted as "<class %s.%s at %p>" whereas type_repr returns a value
formatted as "<%s '%s.%s'>", where the first %s gets filled with
either "type" or "class".)

This is looking more and more like a failure to abide by PEP 252/253.
I think that the patch is simple, but I'm unusre of the
ramifications. I also haven't looked at the 2.4 source to see how
things used to work. Still, I think that I've got a work-around for
OP's problem, I just need to test it under both 2.4 and 2.5.

Jul 13 '07 #22
On Jul 13, 12:45 am, Christoph Zwerschke <c...@online.dewrote:
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 <sa*****@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...@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()

Jul 13 '07 #23
samwyse wrote:
NewStyle.__name__ = old.__class__.__name__
Simple, but that does the trick!
new.__dict__ = old.__dict__.copy()
Unfortunately, that does not work, since the attributes are not
writeable and thus do not appear in __dict__.

But my __getattr__ solution does not work either, since the attributes
are set to None when initialized, so __getattr__ is never called.

Need to think about this point some more...

Anyway, the beer is on me ;-)

-- Chris
Jul 15 '07 #24
Christoph Zwerschke wrote:
But my __getattr__ solution does not work either, since the attributes
are set to None when initialized, so __getattr__ is never called.
Here is a simple solution, but it depends on the existence of the args
attribute that "will eventually be deprecated" according to the docs:

def PoliteException(e):
E = e.__class__
class PoliteException(E):
def __str__(self):
return str(e) + ", sorry!"
PoliteException.__name__ = E.__name__
return PoliteException(*e.args)

try:
unicode('\xe4')
except Exception, e:
p = PoliteException(e)
assert p.reason == e.reason
raise p
Jul 15 '07 #25
Christoph Zwerschke wrote:
Here is a simple solution, but it depends on the existence of the args
attribute that "will eventually be deprecated" according to the docs:
Ok, here is another solution that does not depend on args:

def PoliteException(e):
E = e.__class__
class PoliteException(E):
def __init__(self):
for arg in dir(e):
if not arg.startswith('_'):
setattr(self, arg, getattr(e, arg))
def __str__(self):
return str(e) + ", sorry!"
PoliteException.__name__ = E.__name__
return PoliteException()

try:
unicode('\xe4')
except Exception, e:
p = PoliteException(e)
assert p.reason == e.reason
raise p
Jul 15 '07 #26
Christoph Zwerschke wrote:
Here is a simple solution, but it depends on the existence of the args
attribute that "will eventually be deprecated" according to the docs:
Just found another amazingly simple solution that does neither use teh
..args (docs: "will eventually be deprecated") attribute nor the dir()
function (docs: "its detailed behavior may change across releases").
Instead it relies on the fact that the exception itselfs behaves like
its args tuple (checked with Py 2.3, 2.4 and 2.5).

As another twist, I set the wrapper exception module to the module of
the original exception so that the output looks more like the output of
the original exception (i.e. simply "UnicodeDecodeError" instead of
"__main__.UnicodeDecodeError").

The code now looks like this:

def PoliteException(e):
E = e.__class__
class PoliteException(E):
def __str__(self):
return str(e) + ", sorry!"
PoliteException.__name__ = E.__name__
PoliteException.__module__ = E.__module__
return PoliteException(*e)

try:
unicode('\xe4')
except Exception, e:
p = PoliteException(e)
assert p.reason == e.reason
raise p
Jul 15 '07 #27
On Jul 15, 2:55 am, Christoph Zwerschke <c...@online.dewrote:
Here is a simple solution, but it depends
on the existence of the args attribute that
"will eventually be deprecated" according
to the docs
If you don't mind using .args, then the solution is usually as simple
as:
try:
Thing.do(arg1, arg2)
except Exception, e:
e.args += (Thing.state, arg1, arg2)
raise
No over-engineering needed. ;)
Robert Brewer
System Architect
Amor Ministries
fu******@amor.org

Jul 16 '07 #28
En Mon, 16 Jul 2007 13:50:50 -0300, fumanchu <fu******@amor.orgescribió:
On Jul 15, 2:55 am, Christoph Zwerschke <c...@online.dewrote:
>Here is a simple solution, but it depends
on the existence of the args attribute that
"will eventually be deprecated" according
to the docs

If you don't mind using .args, then the solution is usually as simple
as:
try:
Thing.do(arg1, arg2)
except Exception, e:
e.args += (Thing.state, arg1, arg2)
raise
No over-engineering needed. ;)
If you read enough of this long thread, you'll see that the original
requirement was to enhance the *message* displayed by a normal traceback -
the OP has no control over the callers, but wants to add useful
information to any exception.
Your code does not qualify:

pytry:
.... open("a file that does not exist")
.... except Exception,e:
.... e.args += ("Sorry",)
.... raise
....
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IOError: [Errno 2] No such file or directory: 'a file that does not exist'
pytry:
.... x = u"á".encode("ascii")
.... except Exception,e:
.... e.args += ("Sorry",)
.... raise
....
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in
position 0:
ordinal not in range(128)

--
Gabriel Genellina

Jul 16 '07 #29

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

1 post views Thread by Nel | last post: by
4 posts views Thread by Craig Bailey | last post: by
1 post views Thread by Chris | last post: by
11 posts views Thread by James | last post: by
4 posts views Thread by Alan Walkington | last post: by
1 post views Thread by John Ryan | last post: by
10 posts views Thread by James | last post: by
8 posts views Thread by Lothar Scholz | last post: by
1 post views Thread by joost | last post: by
2 posts views Thread by sky2070 | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.