Flask API Routes not catching exceptions - python

May be a little confused on how flask handles errors. Working on adding error handling to a DB backend. I essentially have a worker script, a router, and error handling script.
The error handling is pretty basic. I have a separate module with the following classes. They inherit from exception so there is not much added to them
class EmptyQueryError(Exception):
pass
class BadURLError(Exception):
pass
My main route method is as follows, and calls a method which takes care of post process/etc.
def queryJobData(projectId, jobId):
try:
dbv.jobData(projectId, jobId)
except EmptyQueryError as e:
abort(400, "An unexpected Exception Occurred: {}".format(e))
except Exception as e:
abort(400, "An unexpected Exception Occurred: {}".format(e))
Lastly, the main driver function is contained in the dbv object from about. The flow of this works properly, as for when I pass in valued project and job ids, the query is successful and it returns a document as I aim to. However, when I purposely insert errors to get an exception to raise, that's where issues occur. Here is the handler function:
def jobData(self, projectId, jobId):
try:
print("[Querying the job data]")
if (request.method == "GET"):
qData = [x for x in self.collection.find({"projectId": projectId, "jobId": jobId})]
# input(qData)
if (len(qData) == 0):
raise EmptyQueryError("EmptyQueryError: The url you attempted to query did not return any data")
pp.data = qData
unrolled_data = pp.unrollData()
df = pd.DataFrame(unrolled_data)
pps = PostProcessSummary(df)
table_test = pps.pivot_summary()
return dumps(table_test)
except Exception as e:
print(e)
finally:
pass
I purposely did not import the "request" module so it raises an error. I can see it gets caught by "jobData":
However, it never enters one of the exception blocks in "queryJobData", where the handle "jobData" is called.
To say the least this has thrown me a for a loop. Almost all other pieces of software I've built would handle this exception accordingly. (ie it follows the pattern where if one exception is raised elsewhere, it should be handled by the parent calling the child generating the exception). First time using Flask so I imagine I'm missing something obvious I can't find in documentation.
edit:
The exception in jobData() gets caught and it exists back into queryJobData() as if nothing happens. For instance in this block it goes directly to the return and not to handle the raised exception
try:
dbv_ret = dbv.jobData(projectId, jobId)
return dbv_ret
except TypeError:
abort(400, "TypeError Occurred")
except EmptyQueryError:
print("[ARE WE HERE?!]")
abort(404, "Empty Query")
except BadURLError:
abort(404, "Bad URL")
except Exception as e:
abort(404, "An unexptec exception has occurred: {}".format(e))

You a catching all exceptions in you function, so anything that happens inside the try/except block will be caught inside your except block, and then your finally block will be executed.
If you want to pass the EmptyQueryError and BadURLError exceptions to the calling function, raise it outside the try/except block. Or, if you want, re-raise it inside your except block
class EmptyQueryError(Exception):
pass
class BadURLError(Exception):
pass
def jobData():
try:
print("[Querying the job data]")
# this will be caught by the except block
raise EmptyQueryError("EmptyQueryError: The url you attempted to query did not return any data")
except Exception as e:
# catching all exceptions. Including the ones you are raising.
# if you don't re-raise the exception here, no error will be passed
print("Wow, exception")
print(e)
finally:
print("I'm in finally block ;)")
# not returning anything
pass
if __name__ == "__main__":
try:
returned_value = jobData()
# will print None
print(returned_value)
except EmptyQueryError as query_err:
# will never be caught
print("Got a EmptyQueryError")
except BadURLError as url_err:
# will never be caught
print("Got a BadURLError")
The sequence of prints will be:
[Querying the job data]
Wow, exception
EmptyQueryError: The url you attempted to query did not return any data
I'm in finally block ;)
None
You can do something like:
def jobData(data):
if len(data) == 0:
raise EmptyQueryError("EmptyQueryError: The url you attempted to query did not return any data")
try:
# do your thing here
pass
except Exception as e:
print(e)
finally:
print("I'm in finally block ;)")
# not returning anything
pass
Now the EmptyQueryError will be caught by the calling function.

Related

class '_mysql_exceptions.DataError': How to check a variable type in an if condition

I need to return update/insert result, from the database class, back to the calling class to differentiate
between success and error.
An update/insert returns <type long'> while a database error returns
<class '_mysql_exceptions.DataError'>.
Since I am not sure about the return type during a success that it would always be a long type, I am checking for type class.
And, I couldn't do it. I tried these:
try:
x = cursor.execute(q, d)
conn.commit()
return x #Return this to the calling class
except MySQLdb.Error, e:
return e #Return this to the calling class
if isinstance(e, class): #Doesn't work
if issubclass(e, _mysql_exceptions): #Doesn't work
How do I check the type of e here?
If I am doing it all wrong, please suggest something nice, thanks.
The issue is that isinstance(obj, class) is not valid syntax, and _mysql_exceptions is a module, not an exception type, which raises a TypeError. To explicitly check an exception type, you can catch each individually:
from _mysql.exceptions import DataError, SomeOtherError, ...
from requests import HTTPError # as an example of a different error
try:
x = cursor.execute(q, d)
conn.commit()
except DataError as e:
# do something
except SomeOtherError as e:
# do something else
except HTTPError as e:
# your connection is broken
# maybe raise from e?
You need to catch that explicit error type, then you don't need to do if isinstance. Start with no exception handling at all, this will lead you to the exceptions that you do need to handle, and anything else should be considered unexpected and should cause the application to either crash or propagate some helpful error message to let you know something bad happened:
try:
some_function()
except ValueError as e:
# this is expected, and is handled accordingly
handle_expected_error()
# This is optional, normally a bare exception block is considered bad practice,
# but can allow your application to continue functioning while raising some
# helpful error so this isn't suppressed
except Exception as e:
# this is not expected, I'm going to propagate this error
# up to be obvious what happened
handle_unexpected_error()
#or
raise from e
Edit: What if I want a calling class to handle the exception?
Reasonable, and I would lean on catching the exception. Instead of handling the exception, I would allow the function to just raise the exception and handle it in the calling class. As a really simple example:
class MyClass:
def __init__(self, conn, cursor):
self.conn = conn
self.cursor = cursor
def some_function(self):
# This raises an error, note I'm not handling it here
x = self.cursor.execute()
self.conn.commit()
return x
def main_function(self):
try:
x = self.some_function()
except DataError as e:
handle_exception()
# unexpected, handle this here
except Exception as e:
raise from e
# or do something else

python: Exception flow: Continue to down catch block after catching?

I am curious if there is a way in python to continue on within try/catch block, after you catch an exception, look at its properties, and if not relevant, then continue down the stack.
try:
# Code
except AppleError as apple_ex:
# look at 'apple_ex.error_code' error body, and if not relevant,
# continue on to next down the catch block...
# In other words, proceed to except BananaError and so on down.
except BananaError as banana_ex:
# ...
except Exception as ex:
# ...
That is not how exceptions are handled in Python. When you raise an exception in a try block, if you handle catching it in the except, it will fall inside that block, but will not continue to the next except at that same level. Observe this functional example:
try:
raise AttributeError()
except AttributeError:
raise TypeError()
except TypeError:
print("it got caught") # will not catch the TypeError raised above
So, in your try, we raise an AttributeError, we catch it, and then raise a TypeError inside catching the AttributeError.
The except TypeError will not catch that TypeError.
Based on how you are explaining your problem, you need to rethink how you are handling your exceptions and see if you can determine the handling of errors somewhere else, and raise the error there.
For example:
def some_func():
try:
thing()
except SomeException:
# analyze the exception here and raise the error you *should* raise
if apple_error_thing:
raise AppleError
elif banana_error_thing:
raise BananaError
else:
raise UnknownException
def your_func():
try:
some_func()
except AppleError as e:
print('Apple')
except BananaError as e:
print('Banana')
except UnknownException as e:
print('Unknown')
An AppleError is still an AppleError and not a BananaError, even if error_code is not relevant, so it makes no sense to fall through to BananaError.
You could instead define specific errors for your different error codes:
GRANNY_SMITH_ERROR = 1
MACINTOSH_ERROR = 2
class AppleError(Exception):
def __init__(self, error_code, *args):
super(AppleError, self).__init__(*args)
self.error_code = error_code
class GrannySmithError(AppleError):
def __init__(self, *args):
super(GrannySmithError, self).__init__(GRANNY_SMITH_ERROR, *args)
class MacintoshError(AppleError):
def __init__(self, *args):
super(MacintoshError, self).__init__(MACINTOSH_ERROR, *args)
Then you can try to match the specific error:
try: raise MacintoshError()
except MacintoshError as exc: print("mac")
except GrannySmithError as exc: print("granny smith")
If you do not care to distinguish between different types of apple errors, you can still trap all apple errors:
try: raise MacintoshError()
except AppleError as exc: print("generic apple")
You can combine these, for example, only doing special processing for GrannySmith, not for Macintosh:
try: raise MacintoshError()
except GrannySmithError as exc: print("granny smith")
except AppleError as exc: print("generic apple")
The important thing is to list the errors from most specific to least specific. If you test for AppleError before GrannySmithError, then it will never enter the GrannySmith block.
No, that isn't possible. After the exception is handled by the inner except it doesn't have the ability to get handled by the outer except:
From the docs on the try statement:
When the end of this block is reached, execution continues normally after the entire try statement. (This means that if two nested handlers exist for the same exception, and the exception occurs in the try clause of the inner handler, the outer handler will not handle the exception.)
In short your only solution might be to have another handler at an outer level and re-raise the exception in the inner handler, that is:
try:
try:
raise ZeroDivisionError
except ZeroDivisionError as e:
print("caught")
raise ZeroDivisionError
except ZeroDivisionError as f:
print("caught")
Now the nested except raises an exception which is consequently caught by a similar handler.

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

Why isn't my user defined exception being handled properly?

I'm wondering a user defined exception I've raised in my python program from within a class isn't being handled by the correct exception handler within my main(). Say I have a class:
class Pdbalog:
# Constructor
def __init__(self, logtype):
if logtype == 1 or logtype == 2:
# These are valid
self.logtypeV = logtype
...<continue processing>
else:
# Invalid
raise Exception("Invalid Logtype")
My main looks like:
from pdbalog import *
def main():
try:
mylog = Pdbalog(10)
...<other code here>
except "Invalid Logtype":
print('Exiting...')
except:
print('Unhandled exception')
raise
I would expect the when main is run that the line where I instantiate the Pdbalog object would raise an exception (Exception("Invalid Logtype")) and the exception handler in main (except "Invalid Logtype") would print the output string "Exiting...". However, it does not. It is being handled by the unhandled exception handler. What ends up happening is the string "Unhandled exception" is being output. Why isn't the
except "Invalid Logtype":
handling the exception?
I am using an old version of python (2.4).
Exception("Invalid Logtype") is still just an Exception, just now with an error message. "Invalid Logtype" isn't an error, just a str, so you can't catch it.
Try:
class InvalidLogtype(Exception): pass
try:
raise InvalidLogType
except InvalidLogType:
pass
Note that you can catch based on error messages by doing
except Exception, e:
if e.args == ("Invalid Logtype",):
...
else:
raise
Try this instead:
class InvalidLogType(Exception):
pass
then
raise InvalidLogType()
then
except InvalidLogType:
etc

Handling all but one exception

How to handle all but one exception?
try:
something
except <any Exception except for a NoChildException>:
# handling
Something like this, except without destroying the original traceback:
try:
something
except NoChildException:
raise NoChildException
except Exception:
# handling
The answer is to simply do a bare raise:
try:
...
except NoChildException:
# optionally, do some stuff here and then ...
raise
except Exception:
# handling
This will re-raise the last thrown exception, with original stack trace intact (even if it's been handled!).
New to Python ... but is not this a viable answer?
I use it and apparently works.... and is linear.
try:
something
except NoChildException:
assert True
except Exception:
# handling
E.g., I use this to get rid of (in certain situation useless) return exception FileExistsError from os.mkdir.
That is my code is:
try:
os.mkdir(dbFileDir, mode=0o700)
except FileExistsError:
assert True
and I simply accept as an abort to execution the fact that the dir is not somehow accessible.
I'd offer this as an improvement on the accepted answer.
try:
dosomestuff()
except MySpecialException:
ttype, value, traceback = sys.exc_info()
raise ttype, value, traceback
except Exception as e:
mse = convert_to_myspecialexception_with_local_context(e, context)
raise mse
This approach improves on the accepted answer by maintaining the original stacktrace when MySpecialException is caught, so when your top-level exception handler logs the exception you'll get a traceback that points to where the original exception was thrown.
You can do type checking on exceptions! Simply write
try:
...
except Exception as e:
if type(e) == NoChildException:
raise
It still includes the original stack trace.
I found a context in which catching all errors but one is not a bad thing, namely unit testing.
If I have a method:
def my_method():
try:
something()
except IOError, e:
handle_it()
Then it could plausibly have a unit test that looks like:
def test_my_method():
try:
my_module.my_method()
except IOError, e:
print "shouldn't see this error message"
assert False
except Exception, e:
print "some other error message"
assert False
assert True
Because you have now detected that my_method just threw an unexpected exception.

Categories

Resources