Search code examples
pythondecoratorself

Change instance attributes inside decorator


I have searched for this problem, but I did not find an answer to what I'm exactly looking for.

Basically, I want to wrap a class constructor in a try/except clause so it ignores specific types of errors inside the constructor (but logs and prints them anyway). The best way I have found to do this is to wrap my method with a decorator, since there are other classes in which I want to do the same, but I don't want to keep repeating the same try/except clauses.

However, the object must remember whether an exception has happened in the constructor (save it in a boolean attribute from that object) so that I can use that information later when that object calls a specific method later. So, I tried doing something like in this snippet:

def detectAndIgnoreErrors(fn):
    def wrappedFunc(*args, **kwargs):
        try:
            fn(*args, **kwargs)
            self.HasAnExceptionOccurredInInit = False
        except KeyError as e:
            self.HasAnExceptionOccurredInInit = True
            # Log and print exception
        except RuntimeError as e:
            self.HasAnExceptionOccurredInInit = True
            # Log and print exception

    return wrappedFunc


class Foo(FooSuperclass):

    @detectAndIgnoreErrors
    def __init__(self):
        # Do stuff that may raise exceptions
        pass

    def doStuff(self):
        if self.HasAnExceptionOccurredInInit:
            # Do stuff
            pass
        else:
            # Do other stuff
            pass

fooInstance = Foo()
fooInstance.doStuff()

The idea here would be for the object to ignore errors in the constructor, and later when the doStuff() method is called, the object remembers whether an exception occurred or not with HasAnExceptionOccurredInInit and adjusts its behavior accordingly. However, the interpreter says that the self name is not defined (which makes sense, as I was trying to access it outside the class scope).

Then, I tried putting the decorator as a class member, and then later I tried putting it as a class member in Foo's parent class, but none of these alternatives worked.

After some research, I realized that decorators are resolved at definition time, not during execution time, so there's no way self can be used this way, so I don't know how I can solve this problem.

If someone knows how to solve this problem (or perhaps a better solution instead of decorators), it would be very appreciated.


Solution

  • The wrapped function doesn't have a parameter named self; you need to specify that specifically if you are going to assume fn is a method.

    def detectAndIgnoreErrors(fn):
        def wrappedFunc(self, *args, **kwargs):
            try:
                fn(self, *args, **kwargs)
                self.HasAnExceptionOccurredInInit = False
            except KeyError as e:
                self.HasAnExceptionOccurredInInit = True
                # Log and print exception
            except RuntimeError as e:
                self.HasAnExceptionOccurredInInit = True
                # Log and print exception
    
        return wrappedFunc