I have an exception class that I'm constantly raising with the same messages. This is how I currently have it structured...
class MyException(Exception):
UNAUTHORIZED = 'UNAUTHORIZED'
SOME_OTHER_EXCEPTION = 'SOME_OTHER_EXCEPTION'
SOME_OTHER_THING = 'SOME_OTHER_THING'
# ...
def func():
# ...
raise MyException(MyException.UNAUTHORIZED)
That is quite un-pythonic. I'm wondering if I could do something like this instead...
class MyException(Exception):
UNAUTHORIZED = MyException('UNAUTHORIZED')
SOME_OTHER_EXCEPTION = MyException('SOME_OTHER_EXCEPTION')
SOME_OTHER_THING = MyException('SOME_OTHER_THING')
# ...
def func():
# ...
raise MyException.UNAUTHORIZED
... which looks a lot cleaner.
I'm wondering if this is okay to do... is there any importance where an exception is instantiated?
Or am I approaching this whole thing incorrectly? Should I be doing something like this instead?
class MyException(Exception):
pass
class UnauthorizedException(MyException):
def __init__(self):
super(UnauthorizedException, self).__init__('UNAUTHORIZED')
def func():
# ...
raise UnauthorizedException()
(In this case, I'd have about 10 different custom exception classes, which I think is a bit much, which is why I'm leaning toward my earlier idea.)
In Python 3, exceptions have a __traceback__ attribute storing the corresponding traceback. If you reuse the same exception object everywhere, that's going to overwrite __traceback__ and mess with traceback inspection.
Additionally, reusing exceptions will also mess with exception chaining: __context__ and __cause__. You are likely to see the wrong exceptions reported as the context or cause of reused exceptions.
Don't reuse exception objects. Rather than raise MyException(MyException.UNAUTHORIZED) or raise MyException.UNAUTHORIZED, you should go with the subclasses. It's a lot easier to do
except UnauthorizedException:
...
than it is to do
except MyException as e:
if e.args != (MyException.UNAUTHORIZED,):
raise
...
Related
I wrote a library that sometimes raises exceptions. There is an exception that I want to deprecate, and I would like to advise people to stop catching them, and provide advises in the warning message. But how to make an exception emit a DeprecationWarning when catched?
library code
import warnings
class MyException(ValueError):
...
warnings.warn(
"MyException is deprecated and will soon be replaced by `ValueError`.",
DeprecationWarning,
stacklevel=2,
)
...
def something():
raise MyException()
user code
try:
mylib.something()
except MyException: # <-- raise a DeprecationWarning here
pass
How can I modify MyException to achieve this?
You can't. None of the logic that occurs in except MyException is customizable. Particularly, it completely ignores things like __instancecheck__ or __subclasscheck__, so you can't hook into the process of determining whether an exception matches an exception class.
The closest you can get is having the warning happen when a user tries to access your exception class with from yourmodule import MyException or yourmodule.MyException. You can do that with a module __getattr__:
class MyException(ValueError):
...
# access _MyException instead of MyException to avoid warning
# useful if other submodules of a package need to use this exception
# also use _MyException within this file - module __getattr__ won't apply.
_MyException = MyException
del MyException
def __getattr__(name):
if name == 'MyException':
# issue warning
return _MyException
raise AttributeError
Try using this:
import warnings
class MyOtherException(Exception):
pass
class MyException(MyOtherException):
def __init__(self):
warnings.warn(
"MyException is deprecated and will soon be replaced by `MyOtherException`.",
DeprecationWarning,
stacklevel=2,
)
if __name__ == "__main__":
try:
mylib.something()
except Exception:
raise MyException()
I have a situation when unittest method calls a helper method,
if there is a runtime error on the helper method,I wanted to raise sys.exit()
and exit the process without executing any further test case,As sys.exit() is being handled & silenced
by the unittest I had to to use os._exit()
But I don't want to do this for all exception ,only for specific Exceptions in other case I want to raise the Exception as it is.
I have pasted the dummy code of my approach
Please let me know if this a reasonable approach or is there a better way to do this?
class RunTimeError(Exception):
pass
class ExeHandlingDecorator:
def __init__(self,method):
self._method=method
self._clean_up=HelperClass.stop
self._forced_exit=os._exit
self._exit_code=255
def __call__(self,*args,**kwargs):
try:
return self._method(*args,**kwargs)
except RunTimeError as error:
print(error)
self._clean_up()
self._forced_exit(self._exit_code)
except Exception as error:
raise error
class TestDummy(unittest.TestCase):
#classmethod
def setUpClass(cls):
"""setup test"""
#ExeHandlingDecorator
def __helper(self):
try:
"""Testing Environment created here,
RunTimeError might be raised by the called methods in this block"""
finally:
"""Environment clean up done here"""
def test_1(self):
data_to_be_asserted=self.__helper()
I'm in a situation where some meager important parts of a classes __init__ method could raise an exception. In that case I want to display an error message but carry on using the instance.
A very basic example:
class something(object):
def __init__(self):
do_something_important()
raise IrrelevantException()
def do_something_useful(self):
pass
try:
that_thing = something()
except IrrelevantException:
print("Something less important failed.")
that_thing.do_something_useful()
However, the last line does not work, because that_thing is not defined. Strange thing is, I could swear I've done things like this before and it worked fine. I even thougt about ways to keep people from using such an unfinished instance, because I found out it gets created even in case of errors. Now I wanted to use that and it does not work. Hmmm...?!?
PS: something was written by myself, so I'm in control of everything.
You can accomplish this by calling object.__new__() to create the object. Then after that call __init__() to create the object.
This will execute all of the code possible.
class IrrelevantException(Exception):
"""This is not important, keep executing."""
pass
class something(object):
def __init__(self):
print("Doing important stuff.")
raise IrrelevantException()
def do_something_useful(self):
print("Now this is useful.")
that_thing = object.__new__(something) # Create the object, does not call __init__
try:
that_thing.__init__() # Now run __init__
except IrrelevantException:
print("Something less important failed.")
that_thing.do_something_useful() # And everything that __init__ could do is done.
EDIT, as #abarnert pointed out. This code does presume that __init__() is defined, but __new__() is not.
Now if it can be assumed that __new__() will not error, it can replace object.__new__() in the code.
However, if there is an error in object.__new__(), there is no way to both create the instance, and have the actions in __new__() applied to it.
This is because __new__() returns the instance, versus __init__() which manipulates the instance. (When you call something(), the default __new__() function actually calls __init__() and then quietly returns the instance.)
So the most robust version of this code would be:
class IrrelevantException(Exception):
"""This is not important, keep executing."""
pass
class something(object):
def __init__(self):
print("Doing important stuff.")
raise IrrelevantException()
def do_something_useful(self):
print("Now this is useful.")
try:
that_thing = something.__new__(something) # Create the object, does not call __init__
except IrrelevantException:
# Well, just create the object without calling cls.__new__()
that_thing = object.__new__(something)
try:
that_thing.__init__() # Now run __init__
except IrrelevantException:
print("Something less important failed.")
that_thing.do_something_useful()
So, meanwhile both of these answer the question, this latter one should also help in the (admittedly rare) case where __new__() has an error, but this does not stop do_something_useful() from working.
From a comment:
PS: something was written by myself, so I'm in control of everything.
Well, then the answer is obvious: just remove that raise IrrelevantException()
Of course your real code probably doesn't have raise IrrelevantException, but instead a call to some dangerous_function() that might raise. But that's fine; you can handle the exception the same way you do anywhere else; the fact that you're inside an __init__ method makes no difference:
class something(object):
def __init__(self):
do_something_important()
try:
do_something_dangerous()
except IrrelevantException as e:
print(f'do_something_dangerous raised {e!r}')
do_other_stuff_if_you_have_any()
That's all there is to it. There's no reason your __init__ should be raising an exception, and therefore the question of how to handle that exception never arises in the first place.
If you can't modify something, but can subclass it, then you don't need anything fancy:
class IrrelevantException(Exception):
pass
def do_something_important():
pass
class something(object):
def __init__(self):
do_something_important()
raise IrrelevantException()
def do_something_useful(self):
pass
class betterthing(something):
def __init__(self):
try:
super().__init__() # use 2.x style if you're on 2.x of course
except IrrelevantException:
pass # or log it, or whatever
# You can even do extra stuff after the exception
that_thing = betterthing()
that_thing.do_something_useful()
Now do_something_important got called, and a something instance got returns that I was able to save and call do_something_useful on, and so on. Exactly what you were looking for.
You could of course hide something behind betterthing with some clever renaming tricks:
_something = something
class something(_something):
# same code as above
… or just monkeypatch something.__init__ with a wrapper function instead of wrapping the class:
_init = something.__init__
def __init__(self):
try:
_init(self)
except IrrelevantException:
pass
something.__init__ = __init__
But, unless there's a good reason that you can't be explicit about the fact that you're adding a wrapper it, it's probably better to be explicit.
You can't have both an exception raised and a value returned (without getting hacky). If this is all code you control, then may I suggest this pattern:
class something(object):
Exception = None
def __init__(self):
...
if BadStuff:
self.Exception = IrrelevantException()
...
that_thing = something()
if that_thing.Exception:
print(that_thing.Exception)
# carry on
Note, if you are just looking for a message, then don't bother creating an Exception object, but rather just set an error code/message on self, and check for it later.
I assume that you don't have control over the "something" class, so in that case you can call the method directly, assuming that there are no elements in the class that are needed. You're passing self=None though, so it won't be able to have any access to the class's variables.
class IrrelevantException(Exception):
x = "i don't matter"
class something(object):
def __init__(self):
raise IrrelevantException()
def do_something_useful(self):
print('hi')
#this will fail
try:
that_thing = something()
except IrrelevantException:
print("Something less important failed.")
#this will run
something.do_something_useful(None)
Alternatively you can use inheritance:
class mega_something(something):
def __init__(self):
print("its alive!")
that_other_thing = mega_something()
that_other_thing.do_something_useful()
The mega_something class won't run its parent constructor unless called.
Question
Why do virtual subclasses of an abstract Exception created using the ABCMeta.register not match under the except clause?
Background
I'd like to ensure that exceptions that get thrown by a package that I'm using are converted to MyException, so that code which imports my module can catch any exception my module throws using except MyException: instead of except Exception so that they don't have to depend on an implementation detail (the fact that I'm using a third-party package).
Example
To do this, I've tried registering an OtherException as MyException using an abstract base class:
# Tested with python-3.6
from abc import ABC
class MyException(Exception, ABC):
pass
class OtherException(Exception):
"""Other exception I can't change"""
pass
MyException.register(OtherException)
assert issubclass(OtherException, MyException) # passes
try:
raise OtherException("Some OtherException")
except MyException:
print("Caught MyException")
except Exception as e:
print("Caught Exception: {}".format(e))
The assertion passes (as expected), but the exception falls to the second block:
Caught Exception: Some OtherException
Alright, I looked into this some more. The answer is that it's a long-outstanding open issue in Python3 (there since the very first release) and apparently was first reported in 2011. As Guido said in the comments, "I agree it's a bug and should be fixed." Unfortunately, this bug has lingered due to concerns about the performance of the fix and some corner cases that need to be handled.
The core issue is that the exception matching routine PyErr_GivenExceptionMatches in errors.c uses PyType_IsSubtype and not PyObject_IsSubclass. Since types and objects are supposed to be the same in python3, this amounts to a bug.
I've made a PR to python3 that seems to cover all of the issues discussed in the thread, but given the history I'm not super optimistic it's going to get merged soon. We'll see.
The why is easy:
from abc import ABC
class MyException(Exception, ABC):
pass
class OtherException(Exception):
"""Other exception I can't change"""
pass
MyException.register(OtherException)
assert issubclass(OtherException, MyException) # passes
assert OtherException in MyException.__subclasses__() # fails
Edit: This assert mimics the outcome of the except clause, but does not represent what actually happens. Look at the accept answer for an explanation.
The workaround also is easy:
class OtherException(Exception):
pass
class AnotherException(Exception):
pass
MyException = (OtherException, AnotherException)
It seems that CPython once again takes some shortcuts and doesn't bother calling the metaclass's __instancecheck__ method for the classes listed in except clauses.
We can test this by implementing a custom metaclass with __instancecheck__ and __subclasscheck__ methods:
class OtherException(Exception):
pass
class Meta(type):
def __instancecheck__(self, value):
print('instancecheck called')
return True
def __subclasscheck__(self, value):
print('subclasscheck called')
return True
class MyException(Exception, metaclass=Meta):
pass
try:
raise OtherException("Some OtherException")
except MyException:
print("Caught MyException")
except Exception as e:
print("Caught Exception: {}".format(e))
# output:
# Caught Exception: Some OtherException
We can see that the print statements in the metaclass aren't executed.
I don't know if this is intended/documented behavior or not. The closest thing to relevant information I could find was from the exception handling tutorial:
A class in an except clause is compatible with an exception if it is
the same class or a base class thereof
Does that mean that classes have to be real subclasses (i.e. the parent class must be part of the subclass's MRO)? I don't know.
As for a workaround: You can simply make MyException an alias of OtherException.
class OtherException(Exception):
pass
MyException = OtherException
try:
raise OtherException("Some OtherException")
except MyException:
print("Caught MyException")
except Exception as e:
print("Caught Exception: {}".format(e))
# output:
# Caught MyException
In the case that you have to catch multiple different exceptions that don't have a common base class, you can define MyException as a tuple:
MyException = (OtherException, AnotherException)
Well, this doesn't really answer your question directly, but if you're trying to ensure a block of code calls your exception, you could take a different strategy by intercepting with a context manager.
In [78]: class WithException:
...:
...: def __enter__(self):
...: pass
...: def __exit__(self, exc, msg, traceback):
...: if exc is OtherException:
...: raise MyException(msg)
...:
In [79]: with WithException():
...: raise OtherException('aaaaaaarrrrrrggggh')
...:
---------------------------------------------------------------------------
OtherException Traceback (most recent call last)
<ipython-input-79-a0a23168647e> in <module>()
1 with WithException():
----> 2 raise OtherException('aaaaaaarrrrrrggggh')
OtherException: aaaaaaarrrrrrggggh
During handling of the above exception, another exception occurred:
MyException Traceback (most recent call last)
<ipython-input-79-a0a23168647e> in <module>()
1 with WithException():
----> 2 raise OtherException('aaaaaaarrrrrrggggh')
<ipython-input-78-dba8b409a6fd> in __exit__(self, exc, msg, traceback)
5 def __exit__(self, exc, msg, traceback):
6 if exc is OtherException:
----> 7 raise MyException(msg)
8
MyException: aaaaaaarrrrrrggggh
I want to build my application with a redis cache. but maybe redis is not available all the time in our case,
so I hope, it redis works well, we use it. if it can't work, just logging and ignore it this time.
for example:
try:
conn.sadd('s', *array)
except :
...
since there are many place I will run some conn.{rediscommand}, I don't like to use try/except every place.
so the solution maybe :
class softcache(redis.StrictRedis):
def sadd(key, *p):
try:
super(redis.StrictRedis, self).sadd(key, p)
except:
..
but since redis have many commands, I have to warp them one by one.
is it possible to custom a exception handler for a class to handle all the exceptions which come from this class ?
Silencing per default all exceptions is probably the worst thing you can do.
Anyway, for your problem you can write a generic wrapper that just redirects to the connection object.
class ReddisWrapper(object):
conn = conn # Here your reddis object
def __getattr__(self, attr):
def wrapper(*args, **kwargs):
# Get the real reddis function
fn = getattr(self.conn, attr)
# Execute the function catching exceptions
try:
return fn(*args, **kwargs)
# Specify here the exceptions you expect
except:
log(...)
return wrapper
And then you would call like this:
reddis = ReddisWrapper()
reddis.do_something(4)
This has not been tested, and will only work with methods. For properties you should catch the non callable exception and react properly.
Is it always the same Exception?
If so you could write a custom, Exception catching and logging decorator.
Something like the following:
def exception_catcher(fn):
try:
fn()
except Exception as e:
log(e)
Then just use it around your code:
#exception_catcher
sadd('s', *array)
The comment and link to Exceptions for the whole class suggested by #idanshmu will offer more detailed handling of different Exceptions per method.