Catch exception in loop head but not in loop body - python

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.

Related

How to avoid duplicates when python multiple except executes the same method?

I have the code block like below:
try:
method()
except ErrorType1:
todo()
return
except ErrorType2 as e:
todo()
raise e
Basically for the two error types, I need to execute todo() first, then either return or raise e. Is it possible to just write todo() once? I was thinking using finally but don't think that actually works.
You could catch both exceptions in one except clause, execute todo and then decide to do based on the exception type:
try:
method()
except (ErrorType1, ErrorType2) as e:
todo()
if isinstance(e, ErrorType1):
return
raise
Note - as pointed out by #ShadowRanger in the comments to the question - you should just use raise to re-raise the existing exception, using raise e will raise a second copy of it, resulting in the traceback including the line with raise e on it as well as the line where the original error occurred.
If you have an common set of instructions (either encapsulated as a function or series of functions) that must be executed as part of an exception handling, consider using a context manager to encapsulate the common bits. The following two results in identical outcome, albeit with different construction (one using try..finally, the other using try..except).
from contextlib import contextmanager
#contextmanager
def context1(method):
print("starting context1")
completed = False
try:
yield method()
completed = True
finally:
if completed:
commit()
else:
rollback()
print("finished")
#contextmanager
def context2(method):
print("starting context2")
try:
yield method()
except Exception:
rollback()
raise
else:
commit()
print("finished")
The latter one will not be able to deal with KeyboardInterrupt or other exceptions that subclass off BaseException, so for certain use case this is not exactly ideal, though it is included to follow suite of the question. The first one is more of a response to the fact that you never appeared to have tried using finally, but rather simply thinking it does not actually works, and thus provided to show it can be used to achieve your goal (where only todo() in the question is executed if failure, through the use of a boolean variable).
In both cases, note how the common control flow is fully encapsulated inside the context manager, and usage is fairly straightforward like so such that all the unique extra cases can be done with another try..except block around the with context block.
try:
with context1(f) as result:
pass # or use the result to do something
except Exception: ...
# all the special unique cases be handled here.
To complete demo, more code is below; the commit and rollback functions I defined the following:
def commit():
print("commit")
def rollback():
print("rollback")
Now to test it, I defined the following helpers:
from functools import partial
class ErrorType1(Exception):
pass
class ErrorType2(Exception):
pass
def raise_e(e):
raise e
subject = [
object,
partial(raise_e, ErrorType1),
partial(raise_e, ErrorType2),
]
With the tests defined as such (replace context1 with context2 for the other demonstration):
for f in subject:
try:
with context1(f) as result:
print('done - got result %r' % result)
except ErrorType2:
print("ErrorType2 will be raised")
# raise # uncomment to actually re-raise the exception
except Exception as e:
print("Exception trapped: %r raised by %r" % (e, f))
Note the output of both the above should look about like so (aside from context1 vs context2):
starting context1
done - got result <object object at 0x7f20ccd3e180>
commit
finished
starting context1
rollback
Exception trapped: ErrorType1() raised by functools.partial(<function raise_e at 0x7f20ccb30af0>, <class '__main__.ErrorType1'>)
starting context1
rollback
ErrorType2 will be raised

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

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()

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')

python calling custom exceptions from if-statement and try-except

So, I've created a custom exception that I want to call in 2 different ways (a if/else statement, and a try/except statement). Here is the custom exception:
class CustomException(Exception):
def __init__(self, value=None, *args, **kwargs):
self.parameter = value
for key, value in kwargs.items():
setattr(self, key, value)
for key, value in self.__dict__.items():
print "%s => %s" % ( key, value )
def __str__(self):
return repr(self.parameter)
Here is how I am wanting to implement the custom exception:
try:
if something:
#make an error
;lsdfj
else:
raise CustomException('this is my custom message', file='somefile.txt', var2='something')
except Exception, e:
raise CustomException(e)
My issues, I believe, are two fold:
1: When the standard NameError that is thrown in the try/except block (due to ;lsdfj), I want to pass CustomExceptions some extra parameters like 'file', just like the if/else implementation; how would I do that?
2: When the custom exception is raised (from the if/else statement being false), the CustomExceptions class ends up being called twice, because I raise it in the if/else block then it gets raised again within the except: section. I don't know how to get around this.
So, in the above case, I want to call CustomException when the if-statement is not true, and I want to call it when there is a standard exception thrown inside the code block... but currently, if something: evaluates to false then the CustomException will be raised twice...
So I want the custom exception to be used unilaterally throughout my code for if/else conditions, and standard python exceptions...
I know this explanation was convoluted but I'm not sure how else to explain what I'm after... Any help would be much appreciated! Thanks in advance!
In order not to raise the exception twice, you should wrap the try/except block around the if statemnt only, like so:
if something:
try:
#make an error
;fdsfas
except Exception, e:
raise CustomException(e.message, file='somefile.txt', var2='something')
else:
raise CustomException('this is my custom message', file='somefile.txt', var2='something')
And in order to pass the custom exception some parameters you must provide that parameters to the constructor of the class just like you did in the if/else statement.
You could in the except block use:
if not isinstance(e, CustomException): raise CustomException(e)
Edit:
A sys.exc_info() before the raise inside the except will successfully remove the traceback to the source of the exception i.e. NameError.

What are some elegant ways to abstract out repetitive exception handling in python?

When handling exceptions in python, I find myself repeating code quite often. The basic pattern is something of the form:
try:
action_here()
except CommonException1:
Action_always_taken_for_CommonException1()
except CommonException2:
Action_always_taken_for_CommonException2()
except Exception:
Default_action_always_taken()
What I would like to do is to some how abstract this repetitive code out to a function or class. I know one way to do it is to call an exception handling function with the exception object, such as:
try:
action_here()
except Exception as e:
handle_exception(e)
Then in this function determine the exception based on class.
def handle_exception(e):
if type(e) == type(CommonException1()):
Action_always_taken_for_CommonException1()
elif type(e) == type(CommonException2()):
Action_always_taken_for_CommonException2())
else:
Default_action_always_taken()
This, however, feels clunky and inelegant. So my question is, what are some other alternatives to handling repetitive exception handling?
This situation is one of the main use cases for context managers and the with statement:
from __future__ import with_statement # Needed in 2.5, but not in 2.6 or later
from contextlib import contextmanager
#contextmanager
def handle_exceptions():
try:
yield # Body of the with statement effectively runs here
except CommonException1:
Action_always_taken_for_CommonException1()
except CommonException2:
Action_always_taken_for_CommonException2()
except Exception:
Default_action_always_taken()
# Used as follows
with handle_exceptions():
action_here()
If you dislike the repeated if / elseif blocks, you could put your handles in a dict, keyed by type:
handlers = { type(CommonException1()) : Action_always_taken_forCommonException1,
type(CommonException2()) : Action_always_taken_forCommonException2 }
def handle_exception(te):
if te in handlers:
handlers[te]()
else:
Default_action()
Which you could then run with:
try:
action_here()
except Exception as e:
handle_exception(type(e))
In addition: If you find yourself writing these try blocks frequently, then you could write your own context manager (see here). At the action_here() side, your code would then look like this:
with my_error_handling_context():
action_here1()
action_here2()
In this case, the handle_exception code would essentially be your context manager's __exit__ method (which will always get passed any exceptions raised during the with block).
Although a solution using a context manager (as proposed by others) is the most elegant, and would be what I would recommend too, I'd like to point out that your handle_exception function could be written more elegantly by re-raising the exception:
def handle_exception(e):
try:
raise e
except CommonException1:
Action_always_taken_for_CommonException1()
except CommonException2:
Action_always_taken_for_CommonException2()
except Exception:
Default_action_always_taken()

Categories