Handling exceptions in python - python

What the best way of handling exceptions.
class SRException(Exception):
default_exception = True
def somefun1(email):
try:
user = somefun2(email)
except Exception, e:
raise SRException, ("could not find the email", e)
def somefun2(email):
try:
user = User.objects.get(email = email)
except Exception, e:
raise SRException, ("could not find the user objecets",e )
So when exception happens I get a long list of Exception
UserProfileException('could not find
the user or service objects',
UserProfileException('could not find
the user', ServicesException('could
not find the service',
DoesNotExist('Services matching query
does not exist.',))))))))
Error and above code examples are not the same. But I guess I made my point clear.
So what the best way of handling exceptions.
Should I not raise it in every exceptions. And I am sending mail to technical team every time exceptions occurs.

Your exception handler is too broad, catch only the specific exceptions you know you can handle. There is no sense in catching an Exception only to wrap it in another exception and re-raising it; an exception object carries a Traceback that will show you what path the code is going. Just let the Exception bubble up, and catch it at a level where you can recover from the exception.

It is usually not necessary to wrap exceptions at every level of the call stack. You are better off catching exceptions somewhere high up in the call stack and dumping a stack trace into the tech-support email. This will indicate quite clearly where the problem occurred and where it was called from.
With a bit of digging around in sys.exc_info()[2], you could even dump a complete list of parameters and locals in each stack frame, which would give support staff the offending email address.

First of all, never just check for Exception! Always use the correct subtype you are actually expecting.
Also you shouldn't encapsulate already useful exceptions into other ones; and in general use the type of the exception as an identification of what happens. Then you can simply keep throwing (informative) exceptions at low level, and catch them all separately at a higher level to determine the correct error message for the end user.

Related

Pytest - Altering the outcome based on the exception error raised

I was wondering how I could alter a pytest test outcome (from a fail to a skip) in the case that my error message includes a specific string.
Occasionally we get test failures using appium where the response from the appium server is a 500 error with the failure message: "An unknown server-side error occurred while processing the command." Its an issue that we need to solve, but for the meantime we want to basically say, if the test failed because of an error message similar to that, skip the test instead of failing it.
Ive considered and tried something like this:
def pytest_runtest_setup(item):
excinfo = None
try:
item.obj()
except Exception as e:
excinfo = sys.exc_info()
if excinfo and "An unknown server-side error occurred while processing the command." in str(excinfo[1]):
pytest.skip("Skipping test due to error message")
And this obviously won't work.
But I was hoping for a similar approach.
The successful answer:
In order for me to get this working #Teejay pointed out below I needed to use the runtest_call hook and assess the message there. Currently working well in my test suite!
#pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
output = yield
if output.excinfo:
for potential_error_message in expected_failure_messages.keys():
if output._excinfo[1].stacktrace and potential_error_message in output._excinfo[1].stacktrace:
pytest.skip(reason=expected_failure_messages[potential_error_message])
I recommend utilizing a hook wrapper to inspect the exception raised and act accordingly
#pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(item):
output = yield
if output.excinfo:
# Additional logic to target specific error
pytest.skip()
Your sketched idea is close to workable. I'd skip using sys.exc_info() and just inspect the str() value of the exception, and I'd restrict the classes of exception caught to the smallest set that covers the failures you're trying to ignore.
Something like:
try:
item.obj()
except OSError as e: # ideally a narrower subclass or tuple of classes
if 'An unknown server-side error occurred while processing the command.' in str(e):
pytest.skip(f'Skipping test due to error message {e}')
else:
raise e
The only additional logical change I've made is moving the if/skip into the exception handler and re-raising if it doesn't match the message you're expecting.
Restricting the classes of errors matched is a best practice to avoid catching situations you didn't intend to catch. It's probably harmless to over-catch here where you're inspecting the message, just a good habit to cultivate. But it might also let you identify a specific field of the exception class to check rather than just the string representation - eg OSError has the strerror attribute containing the error message from the OS, so if you've limited your except block to catching just those you know you'll have that attribute available.
I chose to include the exception in the skip message, you might decide differently if they're uninformative.

Exceptions: logging the traceback only once

I am scratching my head about what is the best-practice to get the traceback in the logfile only once. Please note that in general I know how to get the traceback into the log.
Let's assume I have a big program consisting of various modules and functions that are imported, so that it can have quite some depth and the logger is set up properly.
Whenever an exception may occur I do the following:
try:
do_something()
except MyError as err:
log.error("The error MyError occurred", exc_info=err)
raise
Note that the traceback is written to the log via the option exc_info=err.
My Problem is now that when everything gets a bit more complex and nested I loose control about how often this traceback is written to the log and it gets quite messy.
An example of the situation with my current solution for this problem is as follows:
from other_module import other_f
def main():
try:
# do something
val = other_f()
except (AlreadyLoggedError1, AlreadyLoggedError2, AlreadyLoggedError3):
# The error was caught within other_f() or deeper and
# already logged with traceback info where it occurred
# After logging it was raised like in the above example
# I do not want to log it again, so it is just raised
raise
except BroaderException as err:
# I cannot expect to have thought of all exceptions
# So in case something unexpected happened
# I want to have the traceback logged here
# since the error is not logged yet
log.error("An unecpected error occured", exc_info=err)
raise
The problem with this solution is, that I need to to keep track of all Exceptions that are already logged by myself and the line except (AlreadyLoggedError1, AlreadyLoggedError2, ...) gets arbitrary long and has to be put at any level between main() and the position the error actually occured.
So my question is: Is there some better (pythonic) way handling this? To be more specific: I want to raise the information that the exception was already logged together with the exception so that I do not have to account for that via an extra except block like in my above example.
The solution normally used for larger applications is for the low-level code to not actually do error handling itself if it's just going to be logged, but to put exception logging/handling at the highest level in the code possible, since exceptions will bubble up as far as needed. For example, libraries that send errors to a service like New Relic and Sentry don't need you to instrument each small part of your code that might throw an error, they are set up to just catch any exception and send it to a remote service for aggregation and tracking.

Python exceptions - catching all exceptions but the one expected

I am working on a simple automation script in Python, which could throw exceptions in various spots. In each of them I would like to log a specific message and exit the program. In order to do that, I raise SystemExit after catching the exception and handling it (performing specific logging operations and such).
In the top-level calling of main, I do the following:
if __name__ == "__main__":
try:
main()
except SystemExit: # handled exception
sys.exit(1)
except: # any unhandled exception
logging.error('Unexpected error: ', exc_info=True)
sys.exit(2)
However, using a bare except is something frowned upon. Is using an "exception tree" where I use a bare except to specify "anything but the exception that I've handled" a nonstandard way? Is there a better way to achieve this? I would still like to log these unhandled exceptions, even if they were not handled.
Edit: SystemExit is raised to note that an exception has been handled - no matter what the exception is in my case, I always want to stop running the scripts as any failure should result in an absolute failure.
The main reason I'm asking this is that PEP8 seems to consider using a bare except as an error, and even though I could use except BaseException, it should be just a syntactic difference. Is one way more standard than the other or is there another standard route of achieving this?
Bare exceptions trap things you do not want to trap, such as GeneratorExit. Do it this way:
except Exception as details:
logging.error('Unexpected error: {0}'.format(details))
The main issue with a bare except is that it can catch things like SystemExit and KeyboardInterrupt which are not standard 'code' errors and shouldn't usually be handled in the same way as an exception generated by your code. Using the Exception class doesn't cover those cases as they do not inherit from it, so it is more than a syntax difference.
https://docs.python.org/2/howto/doanddont.html#except
https://docs.python.org/3.1/howto/doanddont.html#except
If you want to handle those specific cases, then it is better to do so explicitly as you have done for SystemExit.
This worked for me:
try:
<code>
raise Exception("my error")
except Exception as e:
raise e
If my error occurs then the error message "my errror" is seen. If an unknown exception occurs then it displays the default exception handler's text. In either case an exception is raised and the script is halted.

Get Exit Status of a Nested Function?

I have a function called within a different function. In the nested function, various errors (e.g. improper arguments, missing parameters, etc.) should result in exit status 1. Something like:
if not os.path.isdir(filepath):
print('Error: could not find source directory...')
sys.exit(1)
Is this the correct way to use exit statuses within python? Should I have, instead,
return sys.exit(1)
??? Importantly, how would I reference the exit status of this nested function in the other function once the nested function had finished?
sys.exit() raises a SystemExit exception. Normally, you should not use it unless you really mean to exit your program.
You could catch this exception:
try:
function_that_uses_sys.exit()
except SystemExit as exc:
print exc.code
The .code attribute of the SystemExit exception is set to the proposed exit code.
However, you should really use a more specific exception, or create a custom exception for the job. A ValueError might be appropriate here, for example:
if not os.path.isdir(filepath):
raise ValueError('Error: could not find source directory {!r}'.format(filepath))
then catch that exception:
try:
function_that_may_raise_valueerror()
except ValueError as exc:
print "Oops, something went wrong: {}".format(exc.message)
By using sys.exit, you typically signal that you want the entire program to end. If you want to handle the error in a calling function, you should probably have the inner function raise a more specific exception instead. (You could catch the exception raised by SystemExit, but it would be a rather awkward way to pass error information out.)
I guess that the right thing to do is this:
if not os.path.isdir(filepath):
raise ValueError('the given filepath is not a directory')
However, the code as it stands still could be improved. One point is that a path to a file should never be a directory, so that is not an exceptional state. Maybe what you want is to just name it path without adding an unintended connotations.
Further, and that has actual functional implications, you are still not guaranteed to be able to access a directory there even if isdir() returns true! The reason is that something could have switched the thing under your feet, typically a malicious attacker, or, more simple, you could simply not have the rights to access it. If you care, you should rather just open the directory and handle the according errors instead of trying in advance to determine if something in the future will fail. This is in general a better approach, as the "normal" code doesn't get cluttered by such checks and you also don't pay any albeit small performance penalty except when an error occurs.

Do we use try,except in every single function?

Should we always enclose every function we write with a try...except block?
I ask this because sometimes in one function we raise Exception, and the caller that calls this function doesn't have exception
def caller():
stdout, stderr = callee(....)
def callee():
....
if stderr:
raise StandardError(....)
then our application crash. In this obvious case, I am tempted to enclose callee and caller with try..except.
But I've read so many Python code and they don't do these try..block all the time.
def cmd(cmdl):
try:
pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
except Exception, e:
raise e
stdout, stderr = pid.communicate()
if pid.returncode != 0:
raise StandardError(stderr)
return (stdout, stderr)
def addandremove(*args,**kwargs):
target = kwargs.get('local', os.getcwd())
f = kwargs.get('file', None)
vcs = kwargs.get('vcs', 'hg')
if vcs is "hg":
try:
stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
except StandardError, e:
// do some recovery
except Exception, e:
// do something meaningful
return True
The real thing that bothers me is this:
If there is a 3rd function that calls addandremove() in one of the statements, do we also surround the call with a try..except block? What if this 3rd function has 3 lines, and each function calls itself has a try-except? I am sorry for building this up. But this is the sort of problem I don't get.
Exceptions are, as the name implies, for exceptional circumstances - things that shouldn't really happen
..and because they probably shouldn't happen, for the most part, you can ignore them. This is a good thing.
There are times where you do except an specific exception, for example if I do:
urllib2.urlopen("http://example.com")
In this case, it's reasonable to expect the "cannot contact server" error, so you might do:
try:
urllib2.urlopen("http://example.com")
except urllib2.URLError:
# code to handle the error, maybe retry the server,
# report the error in a helpful way to the user etc
However it would be futile to try and catch every possible error - there's an inenumerable amount of things that could potentially go wrong.. As a strange example, what if a module modifies urllib2 and removes the urlopen attribute - there's no sane reason to expect that NameError, and no sane way you could handle such an error, therefore you just let the exception propagate up
Having your code exit with a traceback is a good thing - it allows you to easily see where the problem originate, and what caused it (based on the exception and it's message), and fix the cause of the problem, or handle the exception in the correct location...
In short, handle exceptions only if you can do something useful with them. If not, trying to handle all the countless possible errors will only make your code buggier and harder to fix
In the example you provide, the try/except blocks do nothing - they just reraise the exception, so it's identical to the much tidier:
def cmd(cmdl):
pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
stdout, stderr = pid.communicate()
if pid.returncode != 0:
raise StandardError(stderr)
return (stdout, stderr)
# Note: Better to use actual args instead of * and **,
# gives better error handling and docs from help()
def addandremove(fname, local = None, vcs = 'hg'):
if target is None:
target = os.getcwd()
if vcs is "hg":
stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
return True
About the only exception-handling related thing I might expect is to handle if the 'hg' command isn't found, the resulting exception isn't particularly descriptive. So for a library, I'd do something like:
class CommandNotFound(Exception): pass
def cmd(cmdl):
try:
pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
except OSError, e:
if e.errno == 2:
raise CommandNotFound("The command %r could not be found" % cmdl)
else:
# Unexpected error-number in OSError,
# so a bare "raise" statement will reraise the error
raise
stdout, stderr = pid.communicate()
if pid.returncode != 0:
raise StandardError(stderr)
return (stdout, stderr)
This just wraps the potentially confusing "OSError" exception in the clearer "CommandNotFound".
Rereading the question, I suspect you might be misunderstanding something about how Python exceptions work (the "and the caller that calls this function doesn't have exception" bit, so to be hopefully clarify:
The caller function does not need any knowledge of the exceptions that might be raised from the children function. You can just call the cmd() function and hope it works fine.
Say your code is in a mystuff module, and someone else wants to use it, they might do:
import mystuff
mystuff.addandremove("myfile.txt")
Or, maybe they want to give a nice error message and exit if the user doesn't have hg installed:
import mystuff
try:
mystuff.addandremove("myfile.txt")
except mystuff.CommandNotFound:
print "You don't appear to have the 'hg' command installed"
print "You can install it with by... etc..."
myprogram.quit("blahblahblah")
You should use a try catch block so that you can specifically locate the source of the exception. You can put these blocks around anything you want, but unless they produce some sort of useful information, there is no need to add them.
try/except clauses are really only useful if you know how to handle the error that gets raised. Take the following program:
while True:
n=raw_input("Input a number>")
try:
n=float(n)
break
except ValueError:
print ("That wasn't a number!") #Try again.
However, you may have a function like:
def mult_2_numbers(x,y):
return x*y
and the user may try to use it as:
my_new_list=mult_2_numbers([7,3],[8,7])
The user could put this in a try/except block, but then my_new_list wouldn't be defined and would probably just raise an exception later (likely a NameError). In that case, you'd make it harder to debug because the line number/information in the traceback is pointing to a piece of code which isn't the real problem.
There are a couple of programming decisions for your coding team to make regarding introspection type tools and "exception" handling.
One good place to use exception handling is with Operating System calls such as file operations. Reasoning is, for example, a file may have it's access restricted from being accessed by the client appliction. That access restriction is typically an OS-admin task, not the Python application function. So exceptions would be a good use where you application does NOT have control.
You can mutate the previous paragraph's application of exceptions to be broader, in the sense of using Exceptions for things outside the control of what your team codes, to such things as all "devices" or OS resources like OS timers, symbolic links, network connections, etc.
Another common use case is when you use a library or package that is -designed- to through lots of exceptions, and that package expects you to catch or code for that. Some packages are designed to throw as few exceptions as possible and expect you to code based on return values. Then your exceptions should be rare.
Some coding teams use exceptions as a way to log fringe cases of "events" within your application.
I find when desiding whether to use exceptions I am either programing to minimize failure by not having a lot of try/except and have calling routines expect either valid return values or expect an invalid return values. OR I program for failure. That is to say, I program for expected failure by functions, which I use alot of try/except blocks. Think of programming for "expected" failure like working with TCP; the packets aren't garenteed to get there or even in order, but there are exception handling with TCP by use of send/read retrys and such.
Personally I use try-except blocks around the smallest possible block sizes, usually one line of code.
It's up to you; the main exceptions' roles involve (a quote from this splendid book):
Error handling
Event notification
Special-case handling
Termination actions
Unusual control flows
When you know what the error will be, use try/except for debugging purpose. Otherwise, you don't have to use try/except for every function.

Categories