Search code examples
pythonclassdecorator

Decorate main with try-except referencing to main instance


I have a template script as follows.

from shared import C

def do(ins):
    ins.x()

def main():
    try:
        ins = C(1,'a')
        do(ins)
    except:
        ins.handle()
    else:
        ins.finalize()

if __name__ == '__main__':
    main()

How to move try-except-else in decorator to avoid code duplication in every script based on template?

I create a decorator dec like below in shared.py which also contains class C

from functools import wraps

def dec(ins):
    def a(f):
        @wraps(f)
        def b(*a, **k):
            try:
                f(*a, **k)
            except:
                ins.handle()
            else:
                ins.finalize()
        return b
    return a

Failed attempt 1: Create instance in main and pass to main2

from shared import C, dec

def do(ins):
    ins.x()

@dec(ins)
def main():
    ins = C(1, 'a')
    do(ins)

if __name__ == '__main__':
    main()

Failed attempt 2: Set ins within decorator

from shared import C, dec

def do(ins):
    ins.x()

@ins = dec(C(1, 'a'))
def main():
    do(ins)

if __name__ == '__main__':
    main()

Failed attempt 3: Create instance in main idiom

from shared import C, dec

def do(ins):
    ins.x()

@dec(ins)
def main():
    do(ins)

if __name__ == '__main__':
    ins = C(1, 'a')

Successful attempt: Global variable

from shared import C, dec

ins = C(1, 'a')

def do(ins):
    ins.x()

@dec(ins)
def main():
    do(ins)

if __name__ == '__main__':
    main()

How to avoid creating global?


Solution

  • EDIT:

    You could modify your class do do the handling, which removed the need of a decorator.

    class C:
    
       def execute(self, func):
           try:
                func(self)
           except:
                self.handle()
           else:
                self.finalize()
    
    
    def main():
        ins = C(1, 'a')
        C.execute(do)
    
    

    You can achieve this by wrapping the do functions which has all necessary infos.

    def do_wrapper(do_func):
        @wraps(do_func)
        def wrapped_do(ins):
            try:
                do_func(ins)
            except:
                ins.handle()
            else:
                ins.finalize()
        return wrapped_do
    
    @do_wrapper
    def do(ins):
        ins.x()
    
    def main():
        ins = C(1, 'a')
        do(ins)
    

    Note you can also wrap functions 'on-the-fly' which previously were not wrapped.

    def main2():
        do_wrapper(do)(ins) # here the do function is wrapped on the fly