30

Python 3 has the neat

try:
    raise OneException('sorry')
except OneException as e:
    # after a failed attempt of mitigation:
    raise AnotherException('I give up') from e

syntax which allows raising a followup exception without loosing context. The best analogy I could come up with in Python 2 is

raise AnotherException((e,'I give up')), None, sys.exc_info()[2]

where the (e,'') is an ugly hack to have the original exception's name included in the message. But isn't there a better way?

Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • You could do just `raise` without any argument, but I don't think you could change the type to `AnotherException` if you did that. – Kevin Dec 05 '14 at 14:47
  • @Kevin I know, this is just a simplification - in a more realistic case this would be something like a file not found exception and its failsafe also failing, thus yielding a different exception with some other message as well, but the original exception should be conserved – Tobias Kienzler Dec 05 '14 at 14:49
  • 1
    I don't think Python 2 provides proper support for anything like this. I suppose you could wrap your ugly hack in a `reraise()` function or something. Given the [release schedule for 2.8](https://www.python.org/dev/peps/pep-0404), you might consider switching to 3.x. Do you have any 2.x-only dependencies? – Kevin Dec 05 '14 at 16:21
  • 1
    @Kevin https://hg.python.org/peps/rev/76d43e52d978?utm_content=buffer55d59&utm_medium=social&utm_source=facebook.com&utm_campaign=buffer :P Currently the major reason I use Python 2.7 is PythonXY (Windows), but I'm also asking out of curiosity – Tobias Kienzler Dec 08 '14 at 12:44

2 Answers2

29

There's a raise_from in python-future; simply install it

pip install future

and import to use

from future.utils import raise_from
# or: from six import reraise as raise_from

class FileDatabase:
    def __init__(self, filename):
        try:
            self.file = open(filename)
        except IOError as exc:
            raise_from(DatabaseError('failed to open'), exc)

UPDATE

The compatibility package six also supports raise_from, from version 1.9 (released in 2015). It is used in the same manner as above.

tutuDajuju
  • 10,307
  • 6
  • 65
  • 88
  • 2
    Hi. raise_from in `six` actually imitates plain raise(exec) in python2, so it doesn't get python 3 behavior :( – bbaja42 Jan 16 '17 at 13:06
  • 2
    Yes, `raise_from` in `six` imitates plain raise. But `reraise`, also from `six`, worked. See the example in this link: http://python-future.org/compatible_idioms.html or in my suggested solution. – waltersantosf Mar 20 '17 at 13:42
9

Instead of using six.raise_from, try to use six.reraise, as explained in this page:

http://python-future.org/compatible_idioms.html

from six import reraise as raise_ 
# or from future.utils import raise_

traceback = sys.exc_info()[2]
raise_(ValueError, "dodgy value", traceback)
waltersantosf
  • 346
  • 3
  • 5