Reraising an exception so it's handled in the same block - python

I have code that's a bit like this:
try:
# do stuff
except SomeSpecificException as sse:
if sse.some_property == some_special_value:
# handle the exception in a special way
else:
handle_exception_normally()
except:
handle_exception_normally()
I want to catch the specific exception and handle it in a special way, but only if it has a particular property. If it doesn't have that property, I want it to be handled just like any other exception (logging, screaming, etc.)
The code above works, but if possible, I want to avoid repeating handle_exception_normally() (DRY and all that).
Just putting raise in the else clause of the first except block does not work. A parent try block would catch that, but the catch-all clause in the same block will not.
I could nest two try blocks, but it's not very elegant; I'd rather just use
the code I have above.
Is there a better way?
Note that I'm using Python 3.

I would opt for:
try:
# do stuff
except Exception as e:
if e.args[0] == 'Discriminate Exception Here' and sse.some_property == some_special_value:
# handle the exception in a special way
else:
handle_exception_normally()
Moses Koledoye proposed:
try:
# do stuff
except Exception as e:
if getattr(e, 'some_property', None) == some_special_value:
# handle the exception in a special way
else:
handle_exception_normally()
Which is shorter but requires some_special_value to always be != None and attribute to be unique to your exception.
Examples of exception discrimination, with e.args[0]:
try:
5 / 0
except Exception as e:
print(e.args[0])
division by zero
With __class__.__name__:
try:
5 / 0
except Exception as e:
print(e.__class__.__name__)
ZeroDivisionError
With isinstance() (bit more CPU intensive) :
try:
5 / 0
except Exception as e:
isinstance(e, ZeroDivisionError)
True

I understand OP said they do not want to do this, but I'm throwing my lot in with the nested try block. I think it's the most readable way to go about this:
try:
try:
# do stuff
except SomeSpecificException as sse:
if sse.some_property == some_special_value:
# handle the exception in a special way
else:
raise
except:
handle_exception_normally()

Related

Catch exception in loop head but not in loop body

What's the best way to catch exceptions which occur in the *loop header instead of the whole loop or body.
Take the following example
for value in complex_generator(): # throws exceptions I might want to catch
... # do work here - but don't catch any exception
What I don't consider helpful is wrapping the whole loop in a try and except block like so:
try:
for value in complex_generator(): # throws exceptions I might want to catch
... # exceptions raised here will also be caught :(
except Exception:
... # handle exception here
Inspired from golang one might encapsulate the try-except-block and always return two elements:
def wrapper(iterable):
try:
for value in iterable:
yield value, None
except Exception as e:
yield None, e
for value, err in wrapper(complex_generator()):
if err != None:
... # handle error
else:
... # do work but don't catch any exception here
This however doesn't feel pythonic and a type checker would also require a additional check. Any ideas?
There are two levels of errors: Those that the generator throws, and those that your worker code throws. I would use a nested try:
try:
for value in complex_generator():
try:
# do work here
except ValueError:
# catch ValueError and keep going
except OtherError:
# catch OtherError and keep going
# any other error breaks the loop
except ExpectedGeneratorError:
# handle generator exception here
except:
# handle more errors
You could extract the inner part into a worker function to keep things tidy.
Get the generator first?
try:
cg = complex_generator()
except Exception as e:
cg = [] # could alternatively have a success boolean here, and wrap the `for` loop below in an `if`
for value in cg:
...
I still think #Tomalak's answer is the most pythonic way to solve this problem. However I want to share the solution I ended up using, because both the complex_generator as well as the loop body might raise the same exception.
from typing import Iterable, Iterator, TypeVar
T = TypeVar("T")
def wrapper(iterable: Iterable[T]) -> Iterator[T | Exception]:
try:
for value in iterable:
yield value
except Exception as e:
yield e
for value in wrapper(complex_generator()):
if isinstance(value, Exception):
handle_generator_error(value)
else:
try:
process(value)
except Exception as e:
handle_loop_error(e)
Note: For more complex scenarios one might use match (structural pattern matching) and move the body error handling inside the process function.

Python catching exception only if message matches

tl;dr
How to catch different exceptions of the same type but with different messages?
Situation
In ideal world I would handle exceptions like this:
try:
do_something()
except ExceptionOne:
handle_exception_one()
except ExceptionTwo:
handle_exception_two()
except Exception as e:
print("Other exception: {}".format(e))
But external code that I'm using can can throw, in my usage, two exceptions. Both are ValueErrors but have different message. I'd like to differentiate handling them. This is the approach that I tried to take (for easier presentation of my idea I raise AssertionError):
try:
assert 1 == 2, 'test'
except AssertionError('test'):
print("one")
except AssertionError('AssertionError: test'):
print("two")
except Exception as e:
print("Other exception: {}".format(e))
but this code always goes to the last print() and gives me
Other exception: test
Is there a way to catch exceptions this way? I'm assuming this is possible because Python lets me specify MESSAGE when catching exception ExceptionType('MESSAGE') but in practice I didn't manage to make it work. I also didn't find a definitive answer in the docs.
I would go for something like this:
try:
do_the_thing()
except AssertionError as ae:
if "message A" in ae.value:
process_exception_for_message_A()
elif "message B" in ae.value:
process_exception_for_message_B()
else:
default_exception_precessing()

exceptional handling is not working in python program

# phone num
try:
phone=soup.find("div", "phone-content")
for a in phone:
phone_result= str(a).get_text().strip().encode("utf-8")
print "Phone information:", phone_result
except ValueError:
phone_result="Error"
My program stops when there is a uni-code error but i want to use try except to avoid terminating the program. how can i do it?
I get different types of errors. i want something which just doesnt terminate the loop no matter whatever the error. just i want to skip the part of the loop which has error
By using
try:
...
except:
...
You could always catch every error in your try block attempt. The problem with THAT is that you'll catch even KeyboardInterrupt and SystemExit
However, it's better to catch Errors you're prepared to handle so that you don't hide bugs.
try:
...
except UnicodeError as e:
handle(e)
At a minimum, you should at least be as specific as catching the StandardError.
Demonstrated using your code:
try:
phone=soup.find("div", "phone-content")
for a in phone:
phone_result= str(a).get_text().strip().encode("utf-8")
print "Phone information:", phone_result
except StandardError as e:
phone_result="Error was {0}".format(e)
See the Exception Hierarchy to help you gauge the specificity of your error handling.
http://docs.python.org/2/library/exceptions.html#exception-hierarchy
I think you can try something like this first:
try:
whatever()
except Exception as e:
print e
Then you will get your desired(!) exception [if it already exists], and then change it to except somethingelse
Or you can go for custom exception which will look something like following, but you have raise it where necessary:
class ErrorWithCode(Exception):
def __init__(self, code):
self.code = code
def __str__(self):
return repr(self.code)

django - catch multiple exceptions

I have this view function:
def forum(request):
qs = Forum.objects.all()
try:
f = Forum.objects.filter().order_by('-id')[0] <------------problem
return render_to_response("forum.html",{'qs':qs,'f':f},context_instance=RequestContext(request))
except Forum.DoesNotExist or IndexError:
return render_to_response("forum.html",{'qs':qs},context_instance=RequestContext(request))
but it is still giving following error for the problem line above:
IndexError: list index out of range
is my code fine? can i catch multiple exceptions in this way?
When you have this in your code:
except Forum.DoesNotExist or IndexError:
It's actually evaluated as this:
except (Forum.DoesNotExist or IndexError):
where the bit in parentheses is an evaluated expression. Since or returns the first of its arguments if it's truthy (which a class is), that's actually equivalent to merely:
except Forum.DoesNotExist:
If you want to actually catch multiple different types of exceptions, you'd instead use a tuple:
except (Forum.DoesNotExist, IndexError):
You can catch multiple exceptions in this manner
try:
...
except (Forum.DoesNotExist, IndexError) as e:
...
If you want to log/handle each exception, then you can do it like this.
from django.core.exceptions import ObjectDoesNotExist
try:
your code here
except KeyError:
logger.error('You have key error')
except ObjectDoesNotExist:
logger.error('Object does not exist error')

except block does not catch the exception in python

my code is like below
class Something(models.Model)
def exception(self)
try:
Something.objects.all()
except Exception():
raise Exception()
called this method from testcases ,its working but i need to raise exception ,it does not catch the exception
and here is my test case
def test_exception(self):
instance = Something()
instance.exception()
its working fine but i need to raise exception from except block
This line:
except Exception():
should be:
except Exception:
def exception(self)
try:
Something.objects.all()
except Exception, err:
#print err.message (if you want)
raise err
This will catch the error and print the exact msg if required.
Why catch the Exception just to re-raise it?
If you are not doing anything in the except suite except re-raising the exception, then simply do not catch the exception in the first place:
#staticmethod
def exception():
Something.objects.all()
If you are doing something nontrivial inside the except suite, then:
def exception(self):
try:
Something.objects.all()
except Exception:
# do something (with self?)
raise
Then, to test that the exception method raises an Exception:
def test_exception(self):
instance = Something()
self.assertRaises(Exception, instance.exception)
This depends on Something.objects.all() raising Exception.
PS. If exception does not depend on self, then it is best to remove it from the argument list and make exception a staticmethod.
PPS. Exception is a very broad base exception class. A more specific exception would be more helpful for debugging, and allow other code to catch this specific exception instead of forcing it to handle any possible Exception.

Categories