"It's easier to ask for forgiveness than permission", or EAFP, is a common style in Python, where the coder writes code expecting an error and uses the error handling for expected flow control.
The problem I often find is that this can lead to confusing error messages. Consider this example.
some_dict = {} # oops a bug, this should have 'expected key'
try:
some_dict['optional key'] # expected error
except KeyError:
some_dict['expected key'] # unexpected error
Which yields:
Traceback (most recent call last):
File "eafp.py", line 4, in <module>
some_dict['optional key'] # expected error
KeyError: 'optional key'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "eafp.py", line 6, in <module>
some_dict['expected key'] # unexpected error
KeyError: 'expected key'
Specifically, the unexpected error message references the expected one. This is a trivial example, but in some situations the first error may be completely expected and un-noteworthy, but seems closely related to the real cause of the error and can cause some confusion.
My question is how best to handle this type of issue. Can the first error message be suppressed? Or modified to something less conspicuous?
For any wondering, I have ended up using the following (rather obvious) solution to remind myself when debugging.
import logging
logger = logging.getLogger(__name__)
some_dict = {} # oops a bug, this should have 'expected key'
try:
some_dict['optional key'] # expected error
except KeyError as err:
logger.info('Expected Error: %s', err)
some_dict['expected key'] # unexpected error
Related
A reproducible example:
import traceback
X = None
try:
X.text
except (TypeError, AttributeError) as e:
traceback.print_exc(e)
This will raise an error at traceback.print_exc(e):
TypeError: '>=' not supported between instances of 'AttributeError' and 'int'
Any suggestion why this happens?
print_exc doesn't take the exception object as an argument, it uses sys.exc_info() to obtain exception information. When you're passing it e, it's interpreting it as a positional argument for limit which expects a type int. I believe if you just remove the argument you'll get the result you're looking for.
traceback.print_exc documentation
Based on the documentation : Python Docs - traceback module
The first argument to traceback.print_exc isn't the exception, it is a depth limit of how many deep the traceback goes. You are hitting an exception within the traceback module itselse, since it expects the first argument to be a limit.
Your code needs to be :
import traceback
X = None
try:
X.text
except (TypeError, AttributeError) as e:
traceback.print_exc()
The exception data is kept as a thread global in sys.exc_info() which is what traceback.print_exc() uses.
I currently have the following code which throws an error on exception just how i want:
try:
something ....
except Exception as e:
print(
'You have encountered the following in the main function \n ERROR: {}'.format(e))
However in some cases if I get a specific exception such as:
invalid literal for int() with base 10: ''
I want to change the message of e in the exception to what i want.. how would i go about this?
If e == "invalid literal for int() with base 10: ''":
e = 'my new message'
print(e)
but it doesnt seem to be working
Try catching the type of error instead of parsing the text of the error.
More info can be found at Handling Exceptions section of Python help but to be fully thorough (because I feel dumb for initially answering a Python question in C#) you can sort out what exception type you're looking for with something like this:
>>> # Create the error
>>> int('3.6')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '3.6'
Where ValueError is the error type you need to catch.
More realistically, you can incorporate figuring it your uncaught error types into your program and (hopefully) identify them during testing:
>>> try:
... # something ....
... int('3.6') # for the example, we'll generate error on purpose
... # Assume we've already figured out what to do with these 3 errors
... except (RuntimeError, TypeError, NameError):
... print("We know what to do with these errors")
... # Our generic except to catch unhandled errors.
... except:
... print("Unhandled error: {0}".format(err))
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: '3.6'
Once you identify a new error type, add a specific handler for it:
>>> try:
... # something ....
... int('3.6')
... except (RuntimeError, TypeError, NameError):
... print("We know what to do with these errors")
... # The newly added handler for ValueError type
... except ValueError:
... print("And now we know what to do with a ValueError")
... print("My new message")
... except:
... print("Unhandled error: {0}".format(err))
And now we know what to do with a ValueError
My new message
Original (completely useless) answer kept here for posterity (and so the comments make sense)...
Try catching the type of error instead of parsing the text of the error.
e.g.
catch (FileNotFoundException e)
{
// FileNotFoundExceptions are handled here.
}
catch (IOException e)
{
// Extract some information from this exception, and then
// throw it to the parent method.
if (e.Source != null)
Console.WriteLine("IOException source: {0}", e.Source);
throw;
}
which is copied directly from here:
Microsoft try-catch (C# Reference)
I am using AWS and use AWS cloudwatch to view logs. While things should not break on AWS, they could. I just had such a case. Then I searched for Traceback and just got the lines
Traceback (most recent call last):
without the actual traceback. I have a working structured logging setup (see other question) and I would like to get tracebacks in a similar way.
So instead of:
Traceback (most recent call last):
File "/home/math/Desktop/test.py", line 32, in <module>
adf
NameError: name 'adf' is not defined
something like
{"message": "Traceback (most recent call last):\n File \"/home/math/Desktop/test.py\", line 32, in <module>\n adf\n NameError: name 'adf' is not defined", "lineno": 35, "pathname": "/home/math/Desktop/test.py"}
or even better also with the string in a JSON format.
The only way to achieve this I can think of is a giant try-except block. Pokemon-style. Is there a better solution?
You can use sys.excepthook. It is invoked whenever an exception occurs in your script.
import logging
import sys
import traceback
def exception_logging(exctype, value, tb):
"""
Log exception by using the root logger.
Parameters
----------
exctype : type
value : NameError
tb : traceback
"""
write_val = {'exception_type': str(exctype),
'message': str(traceback.format_tb(tb, 10))}
logging.exception(str(write_val))
Then in your script you have to override the value of sys.excepthook.
sys.excepthook = exception_logging
Now whenever an exception occurs it will be logged with your logger handler.
Note: Don't forget to setup logger before running this
In case somebody wants the exception logged in its default format, but in one line (for any reason), based on the accepted answer:
def exception_logging(exctype, value, tb):
"""
Log exception in one line by using the root logger.
Parameters
----------
exctype : exception type
value : seems to be the Exception object (with its message)
tb : traceback
"""
logging.error(''.join(traceback.format_exception(exctype, value, tb)))
Please also note, that it uses logging.error() instead of logging.exception() which also printed some extra "NoneType: None" line.
Also note that it only seems to work with uncaught exceptions.
For logging caught exceptions, visit How do I can format exception stacktraces in Python logging? and see also my answer.
A slight variation: If you run a Flask application, you can do this:
#app.errorhandler(Exception)
def exception_logger(error):
"""Log the exception."""
logger.exception(str(error))
return str(error)
Is there any way to raise two errors at the same time by using try and except?
For example, ValueError and KeyError.
How do I do that?
The question asks how to RAISE multiple errors not catch multiple errors.
Strictly speaking you can't raise multiple exceptions but you could raise an object that contains multiple exceptions.
raise Exception(
[
Exception("bad"),
Exception("really bad"),
Exception("really really bad"),
]
)
Question: Why would you ever want to do this?
Answer: In a loop when you want to raise an error but process the loop to completion.
For example when unit-testing with unittest2 you might want to raise an exception and keep processing then raise all of the errors at the end. This way you can see all of the errors at once.
def test_me(self):
errors = []
for modulation in self.modulations:
logging.info('Testing modulation = {modulation}'.format(**locals()))
self.digitalModulation().set('value', modulation)
reply = self.getReply()
try:
self._test_nodeValue(reply, self.digitalModulation())
except Exception as e:
errors.append(e)
if errors:
raise Exception(errors)
Python 3.11
Starting with 3.11 you can use ExceptionGroup to raise multiple exceptions.
raise ExceptionGroup("this was bad",
[
Exception("bad"),
Exception("really bad"),
Exception("really really bad"),
]
)
You could raise an error which inherits from both ValueError and KeyError. It would get caught by a catch block for either.
class MyError(ValueError, KeyError):
...
Yes, you can handle more than one error, either using
try:
# your code here
except (ValueError, KeyError) as e:
# catch it, the exception is accessable via the variable e
Or, directly add two "ways" of handling different errors:
try:
# your code here
except ValueError as e:
# catch it, the exception is accessable via the variable e
except KeyError as e:
# catch it, the exception is accessable via the variable e
You may also leave out the "e" variable.
Checkout the documentation: http://docs.python.org/tutorial/errors.html#handling-exceptions
The solution from #shrewmouse still requires to choose an exception class to wrap the caught exceptions.
Following solution uses Exception Chaining via finally to execute code after one exception occurs
We don't need to know beforehand, what exceptions occur
Note that only the first exception that occurs can be detected from the caller via except
if this is a problem, use #Collin's solution above to inherit from all collected exceptions
You'll see the exceptions separated by:
"During handling of the above exception, another exception occurred:"
def raise_multiple(errors):
if not errors: # list emptied, recursion ends
return
try:
raise errors.pop() # pop removes list entries
finally:
raise_multiple(errors) # recursion
If you have a task that needs to be done for each element of a list, you don't need to collect the Exceptions beforehand. Here's an example for multiple file deletion with multiple error reporting:
def delete_multiple(files):
if not files:
return
try:
os.remove(files.pop())
finally:
delete_multiple(files)
PS:
Tested with Python 3.8.5
To print full traceback per exception have a look at traceback.print_exc
The original question is answered since years. But as this page is the top search result for "python raise multiple" I share my approach to fill an (IMHO relevant) gap in the solution spectrum.
try :
pass
except (ValueError,KeyError):
pass
read more about Handling exceptions
You can Raise more than one exception like this:
try:
i = 0
j = 1 / i
except ZeroDivisionError:
try:
i = 'j'
j = 4 + i
except TypeError:
raise ValueError
NOTE: it may be that only the ValueError is raised but this error message seems right:
Traceback (most recent call last):
File "<pyshell#9>", line 3, in <module>
j = 1 / i
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#9>", line 7, in <module>
j = 4 + i
TypeError: unsupported operand type(s) for +: 'int' and 'str'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<pyshell#9>", line 9, in <module>
raise ValueError
ValueError
I want to catch an exception when user fails login due to wrong password .
So i make a function using imaplib .I enter a wrong password and get a traceback with error details.
Now my question is actually general.How do you identify the exception we have to mention in our "try and except" body from the error messages?
These is what I got->
>>> count("testarc31#gmail.com","Xbox#36")
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
count("testarc31#gmail.com","Xbox#36")
File "E:\Arindam\py_progs\Mail Notifier\0.0.19\Mail.py", line 24, in count
obj.login(m,p)
File "C:\Python27\lib\imaplib.py", line 500, in login
raise self.error(dat[-1])
error: [AUTHENTICATIONFAILED] Invalid credentials (Failure)
If i want to make a try and except,what will i mention in the exception part?
try:
login(mail,pass):
except ????:
something
Question :
1) What will be ???? here . Can it be deduced directly from the error report?
2) Is there a basic idea to identify what is the exception we have to use from each error we get ?
You want to use something like this:
try:
..code that might raise an exception...
except ExceptionType, e:
...do something...
In your case, that probably want this:
try:
login(mail,pass)
except imaplib.IMAP4.error, e:
print "Ouch -- an error from imaplib!"
To identify the type of an exception, you can look at its exception message. In this case it's just "error" -- unfortunately the module name is not included. You can get a better idea of exactly where it comes from by doing:
try:
login(mail,pass)
except Exception, e:
print type(e)