Exception thrown from Python exception
I've used a decorator class to decorate Exception, but it seems the exception doesn't seem to be thrown with the exact exception class. Using functools.update_wrapper to update the decorator class also doesn't work.
class ClsDecor(object):
def __init__(self,cls):
self.cls=cls
self.counter=0
def __call__(self,*args):
self.counter+=1
return self.cls(*args)
@ClsDecor
class Err(Exception):
def __init__(self):
Exception.__init__(self)
try:
raise Err()
except Err as e:
print 'catched'
except Exception as e:
print 'Not catched'
source to share
First of all, the method __call__
should be:
def __call__(self, *args):
return self.cls(*args)
Your code will throw an error internally __call__
and take you directly to print 'Not catched'
, this is a first level error. After fixing this error, you will reach a second level type mismatch error, which is well explained in dhke's comment. The basic idea is what Err
is actually the type ClsDecor
after @ClsDecor
, but you are returning an instance of the raw type Err
in __call__
which clearly does not match except Err
. You can easily use a decorator to archive your target, for example:
def err_dec(Cls):
class NewErr(Exception):
def __init__(self, *args, **kwargs):
self.err = Cls(*args, **kwargs)
return NewErr
@err_dec
class Err(Exception):
def __init__(self):
Exception.__init__(self)
Updated for requirement in comment:
def err_dec(Cls):
class NewErr(Exception):
c = 0
def __init__(self, *args, **kwargs):
NewErr.c = NewErr.c + 1
self.err = Cls(*args, **kwargs)
def counter(self):
return NewErr.c
return NewErr
@err_dec
class Err(Exception):
def __init__(self):
Exception.__init__(self)
try:
Err()
Err()
raise Err()
except Err as e:
print e.counter()
print 'catched'
except Exception as e:
print 'Not catched'
source to share
The problem is not with the design. The decoration may confuse you, but that's not a problem.
You need to understand what it is Err
. The decorator works like a normal definition, but then you apply the decorator to the object before assigning a name. This is equivalent to:
class Err(Exception):
def __init__(self):
Exception.__init__(self)
Err = ClsDecor(Err)
Err
Is now an instance ClsDecor
, and when you promote it, you are calling the instance (not the class), you are calling ClsDecor.__call__
(not ClsDecor.__init__
). There's an error when you try to call self.cls
with an argument ( args
), but self.cls
there is Err
one that takes no additional arguments (except self
).
You would see this if you typed an error in the default declaration except
. It's a good idea to print an error if you hit the default suggestion, as this is often because you encounter an unexpected exception:
except Exception as e:
print 'Not catched', repr(e)
raise
Or maybe you shouldn't be catching it - if you don't catch it, the exception and trace will be printed anyway.
source to share
With all the guys guys, I finally got the solution like this:
class ErrMetaClass(type):
def __new__(mcls,cls_name,cls_parents,cls_attr):
return super(ErrMetaClass,mcls).__new__(mcls,
cls_attr['_cls'].__name__, cls_parents,cls_attr)
def err_dec(cls):
class NewErr(Exception):
__metaclass__ = ErrMetaClass
_cls=cls
c=0
def __init__(self):
NewErr.c+=1
return NewErr
@err_dec
class Err(Exception):
def __init__(self):
Exception.__init__(self)
try:
raise Err()
except Err as e:
print Err.c
except Exception as e:
print type(e)
source to share