Reputation: 372
Lets say I have a function:
def ReadFile():
with open("/etc/passwd") as file:
data = file.read()
This function might succeed, in which case it needs to return a result, or it might fail, in which case I want to return a traceback of the exception, which will get emailed to me so I know something failed in my program, and exactly what failed.
To do this, I can do something like:
import traceback
def ReadFile():
try:
with open("/etc/passwd") as file:
data = file.read()
except IOError:
return traceback.format_exc()
return data
If it is able to read the file successfully, it returns the contents of the file. Otherwise, it returns the traceback of the exception.
traceback.format_exc() returns a string. If ReadFile() is supposed to return a list, or tuple or integer if it succeeds, than things are easy - when you call ReadFile(), if the result returned is a string, you know it failed and you can run code that emails you the error, and if the result is the type you expected (int, tuple, list or w/e), than you know it worked.
If ReadFile() is supposed to return a string, as it does in my example, than figuring out if ReadFile() succeeded or failed becomes significantly more difficult, as you need to parse the string to figure out if it looks like a traceback or the result you expected.
Is there a better way to do this? Perhaps some way to have traceback return some sort of object with the same information as traceback.format_exc() contains, so that it's easier to figure out if ReadFile() succeeded or failed?
Upvotes: 0
Views: 8659
Reputation: 26570
I suggest, instead of catching the exception inside this method, let it raise an exception and catch it from wherever you are calling.
In other words, don't put your try catch in that method. Put the try catch around whatever is calling that method, and catch what it could raise instead.
So, have this instead:
import traceback
def ReadFile():
with open("/etc/passwd") as file:
data = file.read()
return data
try:
r = ReadFile()
except IOError:
traceback.format_exc()
# or log or whatever operation
Alternatively, if you really want to keep your exceptions handled in that method and want to return some kind of "fail" signal, then you should probably return the exception object instead and look to see if it exists.
So, you can do this as well. Return the Exception object when you raise and check for it when you call your method to see if there was an exception
def ReadFile():
try:
with open("/etc/passwd") as file:
data = file.read()
except IOError as exc:
return exc
return data
if isinstance(r, Exception):
# do exception things here
I would opt for the first option, though.
Upvotes: 7
Reputation: 1221
If you want to return the exception object you can just do:
def ReadFile():
try:
with open("/etc/passwd") as file:
data = file.read()
except IOError as ex:
return ex
return data
However as Ismail says, its better to let it throw the exception like
def ReadFile():
with open("/etc/passwd") as file:
return file.read()
... # Some code that eventually calls the function
...
try:
data = ReadFile()
except IOError:
print("File read failed")
... # Handle error
Upvotes: 3
Reputation: 213368
Exceptions already "return" to the enclosing scope, this is entire reason why exceptions exist in the first place.
# See how simple this is?
def read_file():
with open('/etc/passwd') as file:
return file.read()
You then handle the exception at an appropriate place in the call chain, but only at the appropriate place. For expected exceptions you want to handle in specific ways, handle them where you have enough information to handle them correctly. For general exceptions, handle them at the top level event loop, or simply propagate them up and have the interpreter print them out.
Like most other things, the exact way you want to handle exceptions will differ from program to program. But returning exceptions as a function result is counterproductive.
Upvotes: 2