How to catch custom exception in Python [duplicate] - python

This question already has answers here:
Python: Catching specific exception
(3 answers)
Closed 5 years ago.
I'm using a python library in which at one point an exception is defined as follows:
raise Exception("Key empty")
I now want to be able to catch that specific exception, but I'm not sure how to do that.
I tried the following
try:
raise Exception('Key empty')
except Exception('Key empty'):
print 'caught the specific exception'
except Exception:
print 'caught the general exception'
but that just prints out caught the general exception.
Does anybody know how I can catch that specific Key empty exception? All tips are welcome!

Define your exception:
class KeyEmptyException(Exception):
def __init__(self, message='Key Empty'):
# Call the base class constructor with the parameters it needs
super(KeyEmptyException, self).__init__(message)
Use it:
try:
raise KeyEmptyException()
except KeyEmptyException as e:
print e
Update: based on the discussion in comment OP posted:
But the lib is not under my control. It's open source, so I can edit it, but I would preferably try to catch it without editing the library. Is that not possible?
say library raises an exception as
# this try is just for demonstration
try:
try:
# call your library code that can raise `Key empty` Exception
raise Exception('Key empty')
except Exception as e:
# if exception occurs, we will check if its
# `Key empty` and raise our own exception
if str(e) == 'Key empty':
raise KeyEmptyException()
else:
# else raise the same exception
raise e
except Exception as e:
# we will finally check what exception we are getting
print('Caught Exception', e)

you need to subclass Exception:
class EmptyKeyError(Exception):
pass
try:
raise EmptyKeyError('Key empty')
except EmptyKeyError as exc:
print(exc)
except Exception:
print('caught the general exception')

Related

Catch exception by __cause__

Python 3 allows raising exceptions from other exceptions e.g.:
try:
raise CustomException()
except CustomException as e:
try:
raise TypeError() from e
except TypeError as e:
print(type(e.__cause__))
The CustomException instance is stored in the exception object's __cause__ attribute.
The code above should print CustomException.
Is there a way to catch the original exception instead of the newly raised one?
try:
raise CustomException()
except CustomException as e:
try:
raise TypeError() from e
except CustomException as e:
print(type(e)) # should reach here
Overriding __subclasscheck__ does not work since I don't have access to the instance and I it is impossible to specify that CustomException is a subclass of all classes or of the cause class.
Is there a way to trick Python into thinking that the exception we're catching is of the type of __cause__?
If you have control over the exception that is raised you can perhaps make it a subclass of the raised exception:
try:
raise TypeError()
except TypeError as e:
try:
class CustomException(TypeError.__class__):
pass
raise CustomException() from e
except TypeError as e:
print(type(e)) # Reaches here
That said, the mechanism of catch-and-reraise is meant to hide what the original exception was so that later code doesn't depend on implementation details.
Simple solution: catch all exceptions and filter required:
try:
raise ZeroDivisionError()
except ZeroDivisionError as e:
try:
raise TypeError() from e
except Exception as ex:
if type(ex.__cause__) is ZeroDivisionError:
print('ZeroDivisionError')
else:
raise # re-raise exception if it has different __cause__

Python: When catching a generic (any) exception, how do I store the exception name in a variable?

I know I can store an exception name in a variable with this syntax:
try:
code
except TypeError as e:
logger.error(e)
except NameError as e:
logger.error(e)
How do I do the same for the generic except: message? I assume that this (which is the general idea) won't work:
try:
code
except as e:
logger.error(e)
You can you use type(e).__name__ to capture the name of any error you encounter, and access the message as you a normal variable, with e.message. All the built in errors (indexError, TypeError, etc.) are children of the class Exception, so they will be picked up. to save it as a variable named 'err':
try:
code
except Exception as e:
err = type(e).__name__
message = e.message
This will save the error type of any exception of the base python type Exception that you run into, using the built in __name__ variable
BaseException is the broadest type you can catch:
try:
# some code
except BaseException as e:
logger.error(e)
You are able to catch Exception:
import logging
try:
code
except TypeError as e:
logger.error(e)
except NameError as e:
logger.error(e)
except Exception as e:
logging.error(e)
I ran into this with Python 2, where old-style classes aren't caught by except Exception or except BaseException. I solved it by using sys.exc_info to access the current exception:
import sys
try:
code
except:
e = sys.exc_info()[1]
logging.error(e)

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.

Python exception logging, correct syntax?

I'm adding logging to some python code that deals with exceptions, in the example below what's the correct syntax for wanting to log exception details (e.g. via logger.exception()) when a TypeError or AttributeError occurs?
try:
...
except (TypeError, AttributeError):
# want to do a logger.exception(x) here but not sure what to use for x
...
raise CustomError("Unable to parse column status)
exception(...) is just a convenience method which takes a message just like the other methods:
def exception(self, msg, *args):
"""
Convenience method for logging an ERROR with exception information.
"""
self.error(msg, exc_info=1, *args)
So you would just use it like
logger.exception("Some error message")
and the logging handler will automatically add the exception information from the current exception. Only use this in an exception handler (i.e. in a except: block)!
If you want the exception details, you need to bind the exception itself to a local variable, like this:
except (TypeError, AttributeError), e:
# e is the Exception object
logger.exception(e)
If you need to do different things based on the type of the exception, then you can catch them separately:
except TypeError, e:
logger.exception('There was a Type Error; details are %s' % e)
# Do something, or raise another exception
except AttributeError, e:
logger.exception('There was an Attribute Error; details are %s' % e)
# Do something, or raise another exception
And if you need more information about the context of the exception itself, look into the sys.exc_info() function; it can get you the traceback, and the details about exactly where the exception occurred.

Try/except for specific error of type Exception

I have a certain function which does the following in certain cases:
raise Exception, 'someError'
and may raise other exceptions in other cases.
I want to treat differently the cases when the function raises Exception, 'someError' and the cases where the function raises other exceptions.
For example, I tried the following, but it didn't work as I expected.
try:
raise Exception, 'someError'
except Exception('someError'):
print('first case')
except:
print ('second case')
This prints 'second case'...
You can look at the message property of the exception
>>> try:
... raise Exception, 'someError'
... except Exception as e:
... if e.message == 'someError':
... print 'first case'
... else:
... print 'second case'
...
first case
but it's pretty hacky. It'd be better to just create two separate exceptions and catch each one individually.
You have to define your own exception class:
class FooErr(Exception):
pass
try:
raise FooErr("bar occured")
except FooErr:
print("don't care about foo")
except:
print("don't care about anything.")
see http://docs.python.org/tutorial/errors.html#user-defined-exceptions for more details.
By forcibly printing out the attributes for a specific exception I was able to find, at least for a WindowsError, where the error number is located.
import os
try:
os.mkdir('name') # folder already created, will error
except WindowsError as e:
if e.winerror == 183:
print 'This is the "Cannot create a file when that file already exists" error'
else:
print "This is an error I don't know about"
raise
I would guess the other exceptions have similar attributes

Categories

Resources