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'

      

+3


source to share


3 answers


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'

      

+2


source


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.

0


source


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)

      

0


source







All Articles