Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python3 re-raising an exception with custom attribute?

here's my code from python2 which needs to be ported:

try:
  do_something_with_file(filename)

except:
  exc_type, exc_inst, tb = sys.exc_info()
  exc_inst.filename = filename
  raise exc_type, exc_inst, tb

with above code, I can get the whole exception with the problematic input file by checking whether an exception has 'filename' attribute.

however python3's raise has been changed. this is what 2to3 gave me for above code:

except Exception as e:
  et, ei, tb = sys.exc_info()
  e.filename = filename
  raise et(e).with_traceback(tb)

which gives me another error and I don't think filename attribute is preserved:

in __call__
    raise et(e).with_traceback(tb)
TypeError: function takes exactly 5 arguments (1 given)

What I just want is passing exceptions transparently with some information to track the input file. I miss python2's raise [exception_type[,exception_instance[,traceback]]] - How can I do this in python3?

like image 626
thkang Avatar asked Apr 05 '13 15:04

thkang


2 Answers

You can set the __traceback__ attribute:

except Exception as e:
    et, ei, tb = sys.exc_info()
    ei.filename = filename
    ei.__traceback__ = tb
    raise ei

or call .with_traceback() directly on the old instance:

except Exception as e:
    et, ei, tb = sys.exc_info()
    ei.filename = filename
    raise ei.with_traceback(tb)

However, the traceback is already automatically attached, there is no need to re-attach it, really.

See the raise statement documentation:

A traceback object is normally created automatically when an exception is raised and attached to it as the __traceback__ attribute, which is writable.

In this specific case, perhaps you wanted a different exception instead, with context?

class FilenameException(Exception):
    filename = None
    def __init__(self, filename):
        super().__init__(filename)
        self.filename = filename

try:
    something(filename)
except Exception as e:
    raise FilenameException(filename) from e

This would create a chained exception, where both exceptions would be printed if uncaught, and the original exception is available as newexception.__context__.

like image 99
Martijn Pieters Avatar answered Sep 28 '22 07:09

Martijn Pieters


You don't need to do anything; in Python 3 re-raised exceptions automatically have a full traceback from where they were originally raised.

try:
  do_something_with_file(filename)
except Exception as exc_inst:
  exc_inst.filename = filename
  raise exc_inst

This works because PyErr_NormalizeException sets the __traceback__ attribute appropriately on catching an exception in an except statement; see http://www.python.org/dev/peps/pep-3134/.

like image 28
ecatmur Avatar answered Sep 28 '22 05:09

ecatmur