Forcing code flow to go to except block - python

I have:
try:
...
except Exception, e:
print "Problem. %s" % str(e)
However, somewhere in try, i will need it to behave as if it encountered an Exception. Is it un-pythonic to do:
try:
...
raise Exception, 'Type 1 error'
...
except Exception, e:
print "Problem. Type 2 error %s" % str(e)

I think this is a bad design. If you need to take some action if (and only if) an exception wasn't raised, that is what the else clause is there for. If you need to take some action unconditionally, that's what finally is for. here's a demonstration:
def myraise(arg):
try:
if arg:
raise ValueError('arg is True')
except ValueError as e:
print(e)
else:
print('arg is False')
finally:
print("see this no matter what")
myraise(1)
myraise(0)
You need to factor the unconditional code into finally and put the other stuff in except/else as appropriate.

I think what you are doing is "unPythonic". Trys should really only cover the small part (ideally one line) of the code which you expect might sometimes fail in a certain way. You should be able to use try/except/else/finally to get the required behaviour:
try:
#line which might fail
except ExceptionType: # the exception type which you are worried about
#what to do if it raises the exception
else:
#this gets done if try is successful
finally:
#this gets done last in both cases (try successful or not)

Related

Count exception thrown in Python [duplicate]

Is it possible to tell if there was an exception once you're in the finally clause? Something like:
try:
funky code
finally:
if ???:
print('the funky code raised')
I'm looking to make something like this more DRY:
try:
funky code
except HandleThis:
# handle it
raised = True
except DontHandleThis:
raised = True
raise
else:
raised = False
finally:
logger.info('funky code raised %s', raised)
I don't like that it requires to catch an exception, which you don't intend to handle, just to set a flag.
Since some comments are asking for less "M" in the MCVE, here is some more background on the use-case. The actual problem is about escalation of logging levels.
The funky code is third party and can't be changed.
The failure exception and stack trace does not contain any useful diagnostic information, so using logger.exception in an except block is not helpful here.
If the funky code raised then some information which I need to see has already been logged, at level DEBUG. We do not and can not handle the error, but want to escalate the DEBUG logging because the information needed is in there.
The funky code does not raise, most of the time. I don't want to escalate logging levels for the general case, because it is too verbose.
Hence, the code runs under a log capture context (which sets up custom handlers to intercept log records) and some debug info gets re-logged retrospectively:
try:
with LogCapture() as log:
funky_code() # <-- third party badness
finally:
# log events are buffered in memory. if there was an exception,
# emit everything that was captured at a WARNING level
for record in log.captured:
if <there was an exception>:
log_fn = mylogger.warning
else:
log_fn = getattr(mylogger, record.levelname.lower())
log_fn(record.msg, record.args)
Using a contextmanager
You could use a custom contextmanager, for example:
class DidWeRaise:
__slots__ = ('exception_happened', ) # instances will take less memory
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# If no exception happened the `exc_type` is None
self.exception_happened = exc_type is not None
And then use that inside the try:
try:
with DidWeRaise() as error_state:
# funky code
finally:
if error_state.exception_happened:
print('the funky code raised')
It's still an additional variable but it's probably a lot easier to reuse if you want to use it in multiple places. And you don't need to toggle it yourself.
Using a variable
In case you don't want the contextmanager I would reverse the logic of the trigger and toggle it only in case no exception has happened. That way you don't need an except case for exceptions that you don't want to handle. The most appropriate place would be the else clause that is entered in case the try didn't threw an exception:
exception_happened = True
try:
# funky code
except HandleThis:
# handle this kind of exception
else:
exception_happened = False
finally:
if exception_happened:
print('the funky code raised')
And as already pointed out instead of having a "toggle" variable you could replace it (in this case) with the desired logging function:
mylog = mylogger.WARNING
try:
with LogCapture() as log:
funky_code()
except HandleThis:
# handle this kind of exception
else:
# In case absolutely no exception was thrown in the try we can log on debug level
mylog = mylogger.DEBUG
finally:
for record in log.captured:
mylog(record.msg, record.args)
Of course it would also work if you put it at the end of your try (as other answers here suggested) but I prefer the else clause because it has more meaning ("that code is meant to be executed only if there was no exception in the try block") and may be easier to maintain in the long run. Although it's still more to maintain than the context manager because the variable is set and toggled in different places.
Using sys.exc_info (works only for unhandled exceptions)
The last approach I want to mention is probably not useful for you but maybe useful for future readers who only want to know if there's an unhandled exception (an exception that was not caught in any except block or has been raised inside an except block). In that case you can use sys.exc_info:
import sys
try:
# funky code
except HandleThis:
pass
finally:
if sys.exc_info()[0] is not None:
# only entered if there's an *unhandled* exception, e.g. NOT a HandleThis exception
print('funky code raised')
raised = True
try:
funky code
raised = False
except HandleThis:
# handle it
finally:
logger.info('funky code raised %s', raised)
Given the additional background information added to the question about selecting a log level, this seems very easily adapted to the intended use-case:
mylog = WARNING
try:
funky code
mylog = DEBUG
except HandleThis:
# handle it
finally:
mylog(...)
You can easily assign your caught exception to a variable and use it in the finally block, eg:
>>> x = 1
>>> error = None
>>> try:
... x.foo()
... except Exception as e:
... error = e
... finally:
... if error is not None:
... print(error)
...
'int' object has no attribute 'foo'
Okay, so what it sounds like you actually just want to either modify your existing context manager, or use a similar approach: logbook actually has something called a FingersCrossedHandler that would do exactly what you want. But you could do it yourself, like:
#contextmanager
def LogCapture():
# your existing buffer code here
level = logging.WARN
try:
yield
except UselessException:
level = logging.DEBUG
raise # Or don't, if you just want it to go away
finally:
# emit logs here
Original Response
You're thinking about this a bit sideways.
You do intend to handle the exception - you're handling it by setting a flag. Maybe you don't care about anything else (which seems like a bad idea), but if you care about doing something when an exception is raised, then you want to be explicit about it.
The fact that you're setting a variable, but you want the exception to continue on means that what you really want is to raise your own specific exception, from the exception that was raised:
class MyPkgException(Exception): pass
class MyError(PyPkgException): pass # If there's another exception type, you can also inherit from that
def do_the_badness():
try:
raise FileNotFoundError('Or some other code that raises an error')
except FileNotFoundError as e:
raise MyError('File was not found, doh!') from e
finally:
do_some_cleanup()
try:
do_the_badness()
except MyError as e:
print('The error? Yeah, it happened')
This solves:
Explicitly handling the exception(s) that you're looking to handle
Making the stack traces and original exceptions available
Allowing your code that's going to handle the original exception somewhere else to handle your exception that's thrown
Allowing some top-level exception handling code to just catch MyPkgException to catch all of your exceptions so it can log something and exit with a nice status instead of an ugly stack trace
If it was me, I'd do a little re-ordering of your code.
raised = False
try:
# funky code
except HandleThis:
# handle it
raised = True
except Exception as ex:
# Don't Handle This
raise ex
finally:
if raised:
logger.info('funky code was raised')
I've placed the raised boolean assignment outside of the try statement to ensure scope and made the final except statement a general exception handler for exceptions that you don't want to handle.
This style determines if your code failed. Another approach might me to determine when your code succeeds.
success = False
try:
# funky code
success = True
except HandleThis:
# handle it
pass
except Exception as ex:
# Don't Handle This
raise ex
finally:
if success:
logger.info('funky code was successful')
else:
logger.info('funky code was raised')
If exception happened --> Put this logic in the exception block(s).
If exception did not happen --> Put this logic in the try block after the point in code where the exception can occur.
Finally blocks should be reserved for "cleanup actions," according to the Python language reference. When finally is specified the interpreter proceeds in the except case as follows: Exception is saved, then the finally block is executed first, then lastly the Exception is raised.

How many exceptions are possible when reading a json in Python/django?

I have:
MY_PATH_DIR = 'path/to/my/json/file.json'
try:
with open(MY_PATH_DIR, 'r') as f:
MY_PATH_DIR = json.load(f)
except IOError, RuntimeError, ValueError:
pass
except PermissionDenied:
pass
And I want to catch all possible errors. With
IOError - I am catching errors when the file doesn't exist or has a
syntax error (non valid JSON).
RuntimeError - couldn't test it but I think that makes sense from the
documentation in case of an unexpected error
ValueError - I got from here in case nothing got returned
PermissionDenied - is a specific Django error
Are there any other Exceptions that would make sense? I'm not sure if OSError makes sense here. I think that would be raised earlier, right?
The purpose of capturing exceptions is to control the program's behavior when something bad happened, but in an expected way. If you are not even sure what would cause that exception happen, capturing it would only swallow the underlying programming errors you might have.
I wouldn't add as many kinds of exception as possible to that single block of code, you should only add what you care about. To take it to extreme, each line of code would yield certain exceptions but for obvious reason you couldn't do try except for all of them.
Edit:
For the sake of correctness, since you mentioned I don't want my code to break in any case, you could simply do:
try:
# json.load
except Exception as e:
print "Let's just ignore all exceptions, like this one: %s" % str(e)
This is would give you what exception happens as output.
import random
import sys
def main():
"""Demonstrate the handling of various kinds of exceptions."""
# This is like what you are doing in your code.
exceptions = IOError, RuntimeError, ValueError
try:
raise random.choice(exceptions)()
except exceptions as error:
print('Currently handling:', repr(error))
# The following is not much different from Shang Wang's answer.
try:
raise random.choice(exceptions)()
except Exception as error:
print('Currently handling:', repr(error))
# However, the following code will sometimes not handle the exception.
exceptions += SystemExit, KeyboardInterrupt, GeneratorExit
try:
raise random.choice(exceptions)()
except Exception as error:
print('Currently handling:', repr(error))
# The code can be slightly altered to take the new errors into account.
try:
raise random.choice(exceptions)()
except BaseException as error:
print('Currently handling:', repr(error))
# This does not take into account classes not in the exception hierarchy.
class Death:
pass
try:
raise Death()
except BaseException as error:
print('Currently handling:', repr(error))
# If your version of Python does not consider raising an exception from an
# instance of a class not derived from the BaseException class, the way to
# get around this problem would be with the following code instead.
try:
raise Death()
except:
error = sys.exc_info()[1]
print('Currently handling:', repr(error))
if __name__ == '__main__':
main()

Python - Try/Except, what is ok and what is wrong?

I have to deal with a large quantity of try/except. I'm in doubt about the right way of doing it.
Option 1:
inst = Some(param1, param2)
try:
is_valid = retry_func(partial(inst.some_other), max_retry=1)
except RetryException, e:
SendMail.is_valid_problem(e)
if is_valid:
print "continue to write your code"
...
*** more code with try/except ***
...
Option 2:
inst = Some(param1, param2)
try:
is_valid = retry_func(partial(inst.some_other), max_retry=1)
if is_valid:
print "continue to write your code"
...
*** more code with try/except ***
...
except RetryException, e:
SendMail.is_valid_problem(e)
In the Option 1, even is the exception is raised, "is_valid" will be tested and I don't need that.
In the Option 2, is what I think is correct but the code will look like a "callback hell".
What option should I choose or what option is the correct one?
Keep your exception handling as close as possible to the code that raises the exception. You don't want to accidentally mask a different problem in code you thought would not raise the same exception.
There is a third option here, use the else: suite of the try statement:
inst = Some(param1, param2)
try:
is_valid = retry_func(partial(inst.some_other), max_retry=1)
except RetryException, e:
SendMail.is_valid_problem(e)
else:
if is_valid:
print "continue to write your code"
...
*** more code with try/except ***
...
The else: suite is only executed if there was no exception raised in the try suite.
From your conditions, condition 1 is better and you can use else instead of if is_valid
Here are some of Try Except:
Here is simple syntax of try....except...else blocks:
try:
You do your operations here;
......................
except ExceptionI:
If there is ExceptionI, then execute this block.
except ExceptionII:
If there is ExceptionII, then execute this block.
......................
else:
If there is no exception then execute this block.
The except clause with multiple exceptions:
try:
You do your operations here;
......................
except(Exception1[, Exception2[,...ExceptionN]]]):
If there is any exception from the given exception list,
then execute this block.
......................
else:
If there is no exception then execute this block.
The try-finally clause:
try:
You do your operations here;
......................
Due to any exception, this may be skipped.
finally:
This would always be executed.
I think option 1 is better. The reason is that you should always put inside a try except only the code which you expect to throw the exception. Putting more code increase the risk of catching an unwanted exception.
Make sure that if an Error will occur, keep the cause inside the try statement. That way, it will catch the error and sort it out in the except section. There is also finally. If the try doesn't work, but the "except" error doesn't work, it will act out the "finally" statement. If there is no finally, the program will get stuck. This is a sample code with try and except:
import sys
import math
while True:
x=sys.stdin.readline()
x=float(x)
try:
x=math.sqrt(x)
y=int(x)
if x!=y:
print("Your number is not a square number.")
elif x==y:
print("Your number is a square number.")
except(ValueError):
print("Your number is negative.")
ValueError is the error you get from sqrt-ing a negative number.

Python nested try/except - raise first exception?

I'm trying to do a nested try/catch block in Python to print some extra debugging information:
try:
assert( False )
except:
print "some debugging information"
try:
another_function()
except:
print "that didn't work either"
else:
print "ooh, that worked!"
raise
I'd like to always re-raise the first error, but this code appears to raise the second error (the one caught with "that didn't work either"). Is there a way to re-raise the first exception?
raise, with no arguments, raises the last exception. To get the behavior you want, put the error in a variable so that you can raise with that exception instead:
try:
assert( False )
# Right here
except Exception as e:
print "some debugging information"
try:
another_function()
except:
print "that didn't work either"
else:
print "ooh, that worked!"
raise e
Note however that you should capture for a more specific exception rather than just Exception.
You should capture the first Exception in a variable.
try:
assert(False)
except Exception as e:
print "some debugging information"
try:
another_function()
except:
print "that didn't work either"
else:
print "ooh, that worked!"
raise e
raise by default will raise the last Exception.
raise raises the last exception caught unless you specify otherwise. If you want to reraise an early exception, you have to bind it to a name for later reference.
In Python 2.x:
try:
assert False
except Exception, e:
...
raise e
In Python 3.x:
try:
assert False
except Exception as e:
...
raise e
Unless you are writing general purpose code, you want to catch only the exceptions you are prepared to deal with... so in the above example you would write:
except AssertionError ... :

Try/except for specific error of type Exception

I have a certain function which does the following in certain cases:
raise Exception, 'someError'
and may raise other exceptions in other cases.
I want to treat differently the cases when the function raises Exception, 'someError' and the cases where the function raises other exceptions.
For example, I tried the following, but it didn't work as I expected.
try:
raise Exception, 'someError'
except Exception('someError'):
print('first case')
except:
print ('second case')
This prints 'second case'...
You can look at the message property of the exception
>>> try:
... raise Exception, 'someError'
... except Exception as e:
... if e.message == 'someError':
... print 'first case'
... else:
... print 'second case'
...
first case
but it's pretty hacky. It'd be better to just create two separate exceptions and catch each one individually.
You have to define your own exception class:
class FooErr(Exception):
pass
try:
raise FooErr("bar occured")
except FooErr:
print("don't care about foo")
except:
print("don't care about anything.")
see http://docs.python.org/tutorial/errors.html#user-defined-exceptions for more details.
By forcibly printing out the attributes for a specific exception I was able to find, at least for a WindowsError, where the error number is located.
import os
try:
os.mkdir('name') # folder already created, will error
except WindowsError as e:
if e.winerror == 183:
print 'This is the "Cannot create a file when that file already exists" error'
else:
print "This is an error I don't know about"
raise
I would guess the other exceptions have similar attributes

Categories

Resources