I have been trying to capture an exception that returns the message "Error in file!" when reading from a file in Python.
I had managed to catch the error when the file isn't existing, but unfortunately I couldn't manage to capture the error when there is an error in a file.
Any suggestions?
try:
# ...
except IOError:
print("Error opening file!")
except Exception:
print("Error in file!")
raise
You can track the exception to find out what exactly went wrong in the try-block, so you can handle the new error.
In order to do this, just print details of the exception:
try:
....
except Exception as f:
print(f.__class__)
print(str(f))
This will give you the class of the error (e.g. NameError) and the specific error the program encountered. So, you can handle it with an additional except-clause or debug the code.
I'm looking for a tool that reports all the types of exceptions that could be raised from functions. i.e ones that is trying to explicitly handle.
If I had this example code, in a script or some function etc. I could run this tool and it would report OSError, ValueError, Exception as the output.
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
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()
I need to have a file deleted if not all the operations that must be done on it were successful (that is, if an exception is raised). It could have been as simple as using except:, deleting the file and then re-raising the exception, but in that case the original exception would be lost if the file cannot be deleted in the except clause for whatever arcane reason.
The best that I have been able to come up with is this:
try:
file_path = "whatever.jpg"
# do stuff with file
except:
exception_raised = True
raise
finally:
try:
if exception_raised:
os.unlink(file_path)
except:
pass
return file_path # everything OK
Does anybody know of a better, more Pythonic approach?
Another option is to simply store the exception if you don't want to lose it:
Python 3.x version:
try:
file_path = "whatever.jpg"
# do stuff with file
except BaseException as e:
try:
os.unlink(file_path)
except Exception:
traceback.print_exc()
raise e
The Python 2.x version is slightly more complex since you need to store the complete exception information manually (otherwise you'd lose the traceback):
try:
file_path = "whatever.jpg"
# do stuff with file
except:
e = sys.exc_info()
try:
os.unlink(file_path)
except Exception:
traceback.print_exc()
raise e[0], e[1], e[2]
Edit: Only catch subclasses of Exception in the inner try block, since you don't want do catch SystemExit or KeyboardInterrupt here. Also report any excpetion that occurred during unlinking instead of just dropping it.
import ftplib
import urllib2
import os
import logging
logger = logging.getLogger('ftpuploader')
hdlr = logging.FileHandler('ftplog.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
FTPADDR = "some ftp address"
def upload_to_ftp(con, filepath):
try:
f = open(filepath,'rb') # file to send
con.storbinary('STOR '+ filepath, f) # Send the file
f.close() # Close file and FTP
logger.info('File successfully uploaded to '+ FTPADDR)
except, e:
logger.error('Failed to upload to ftp: '+ str(e))
This doesn't seem to work, I get syntax error, what is the proper way of doing this for logging all kind of exceptions to a file
You have to define which type of exception you want to catch. So write except Exception, e: instead of except, e: for a general exception (that will be logged anyway).
Other possibility is to write your whole try/except code this way:
try:
with open(filepath,'rb') as f:
con.storbinary('STOR '+ filepath, f)
logger.info('File successfully uploaded to '+ FTPADDR)
except Exception, e: # work on python 2.x
logger.error('Failed to upload to ftp: '+ str(e))
in Python 3.x and modern versions of Python 2.x use except Exception as e instead of except Exception, e:
try:
with open(filepath,'rb') as f:
con.storbinary('STOR '+ filepath, f)
logger.info('File successfully uploaded to '+ FTPADDR)
except Exception as e: # work on python 3.x
logger.error('Failed to upload to ftp: '+ str(e))
The syntax is no longer supported in python 3. Use the following instead.
try:
do_something()
except BaseException as e:
logger.error('Failed to do something: ' + str(e))
If you want the error class, error message, and stack trace, use sys.exc_info().
Minimal working code with some formatting:
import sys
import traceback
try:
ans = 1/0
except BaseException as ex:
# Get current system exception
ex_type, ex_value, ex_traceback = sys.exc_info()
# Extract unformatter stack traces as tuples
trace_back = traceback.extract_tb(ex_traceback)
# Format stacktrace
stack_trace = list()
for trace in trace_back:
stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3]))
print("Exception type : %s " % ex_type.__name__)
print("Exception message : %s" %ex_value)
print("Stack trace : %s" %stack_trace)
Which gives the following output:
Exception type : ZeroDivisionError
Exception message : division by zero
Stack trace : ['File : .\\test.py , Line : 5, Func.Name : <module>, Message : ans = 1/0']
The function sys.exc_info() gives you details about the most recent exception. It returns a tuple of (type, value, traceback).
traceback is an instance of traceback object. You can format the trace with the methods provided. More can be found in the traceback documentation .
There are some cases where you can use the e.message or e.messages.. But it does not work in all cases. Anyway the more safe is to use the str(e)
try:
...
except Exception as e:
print(e.message)
Updating this to something simpler for logger (works for both python 2 and 3). You do not need traceback module.
import logging
logger = logging.Logger('catch_all')
def catchEverythingInLog():
try:
... do something ...
except Exception as e:
logger.error(e, exc_info=True)
... exception handling ...
This is now the old way (though still works):
import sys, traceback
def catchEverything():
try:
... some operation(s) ...
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
... exception handling ...
exc_value is the error message.
You can use logger.exception("msg") for logging exception with traceback:
try:
#your code
except Exception as e:
logger.exception('Failed: ' + str(e))
Using str(e) or repr(e) to represent the exception, you won't get the actual stack trace, so it is not helpful to find where the exception is.
After reading other answers and the logging package doc, the following two ways works great to print the actual stack trace for easier debugging:
use logger.debug() with parameter exc_info
try:
# my code
except SomeError as e:
logger.debug(e, exc_info=True)
use logger.exception()
or we can directly use logger.exception() to print the exception.
try:
# my code
except SomeError as e:
logger.exception(e)
After python 3.6, you can use formatted string literal. It's neat! (https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498)
try
...
except Exception as e:
logger.error(f"Failed to upload to ftp: {e}")
You can try specifying the BaseException type explicitly. However, this will only catch derivatives of BaseException. While this includes all implementation-provided exceptions, it is also possibly to raise arbitrary old-style classes.
try:
do_something()
except BaseException, e:
logger.error('Failed to do something: ' + str(e))
If you want to see the original error message, (file & line number)
import traceback
try:
print(3/0)
except Exception as e:
traceback.print_exc()
This will show you the same error message as if you didn't use try-except.
for the future strugglers,
in python 3.8.2(and maybe a few versions before that), the syntax is
except Attribute as e:
print(e)
Use str(ex) to print execption
try:
#your code
except ex:
print(str(ex))
In Python 3, str(ex) gives us the error message. You could use repr(ex) to get the full text, including the name of the exception raised.
arr = ["a", "b", "c"]
try:
print(arr[5])
except IndexError as ex:
print(repr(ex)) # IndexError: list index out of range
print(str(ex)) # list index out of range
There is also a way to get the raw values passed to the exception class without having to change the content type.
For e.g I raise type codes with error messages in one of my frameworks.
try:
# TODO: Your exceptional code here
raise Exception((1, "Your code wants the program to exit"))
except Exception as e:
print("Exception Type:", e.args[0][0], "Message:", e.args[0][1])
Output
Exception Type: 1 Message: 'Your code wants the program to exit'
The easiest way to do this is available through the Polog library. Import it:
$ pip install polog
And use:
from polog import log, config, file_writer
config.add_handlers(file_writer('file.log'))
with log('message').suppress():
do_something()
Note how much less space the code has taken up vertically: only 2 lines.