Reputation: 6730
I often find myself wanting to do something like this, I have something wrapped in try excepts like this
item= get_item()
try:
do_work(item)
except SomeError as err:
if err.code == 123:
do_something(item)
else:
# Actually I don't want to do something with this error code... I want to handle in 'except'
except:
put_back(item)
raise
Is there a way to raise into the except
block below from the else? (a continue
would be nice) I end up doing something like the following which isn't as clean
item= get_item()
try:
try:
do_work(item)
except SomeError as err:
if err.code == 123:
do_something(item)
else:
raise
except:
put_back(item)
raise
Is there anyway to do that?
Upvotes: 9
Views: 16900
Reputation: 29874
Context managers are excellent, but for some simple cases where you won't be reusing the logic anywhere else, they can be a bit heavy.
Instead of trying to have multiple except
blocks, you can just test the exception inside a single except
block:
item= get_item()
try:
do_work(item)
except Exception as err:
if isinstance(err, SomeError) and err.code == 123:
do_something(item)
else:
put_back(item)
raise
Note that this is pretty much what a context manager's __exit__
method ends up looking like, anyway.
Be wary that code that really belongs in finally
doesn't end up here.
Upvotes: 2
Reputation: 1121992
If you are using a recent enough python version (2.5 and up), you should switch to using a context manager instead:
class WorkItemContextManager(object):
def __enter__(self):
self.item = get_item()
return self.item
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
if exc_type is SomeError and exc_value.code == 123:
do_something(self.item)
return True # Exception handled
put_back(self.item)
Then:
with WorkItemContextManager() as item:
do_work(item)
The __exit__
method can return True if an exception has been handled; returning None will instead re-raise any exceptions raised in the with
block.
If not, you are looking for a finally
block instead:
item = get_item()
try:
do_work(item)
item = None
except SomeError as err:
if err.code == 123:
do_something(item)
item = None
finally:
if item is not None:
put_back(item)
The finally
suite is guaranteed to be executed when the try:
suite completes, or an exception has occurred. By setting item
to None
you basically tell the finally
suite everything completed just fine, no need to put it back.
The finally
handler takes over from your blanket except
handler. If there has been an exception in do_work
, item
will not be set to None. If the SomeError
handler doesn't catch the exception, or err.code
is not 123, item
will also not be set to None, and thus the put_back(item)
method is executed.
Upvotes: 9
Reputation: 43071
My suggestion would be to create a function (or series of functions) that wraps the method throwing errors which you'd like to control. Something like...
def wrapper(arg):
try:
do_work(arg)
except SomeError as e:
if e.code == 123:
do_something(item)
# Other possible cleanup code
else:
raise
...then, when you want to call it...
try:
wrapper(arg)
except SomeError as e:
put_back(arg)
Upvotes: 2
Reputation: 927
It's good to keep in mind what try-except flow is for, and one of their advantages is that they remove the need for status variables and status checks like
if not foo:
# do something
Also, an Exception class should represent a specific kind of error. If you need to make further decisions about the kind of error in an except block, it's a good sign that the class isn't specific enough to represent the program state. Your best bet is to subclass SomeError and only catch the subclass in the first except. Then other instances of SomeError will fall through to the second except block.
Upvotes: 1