Search code examples
pythontimeoutsignalsdecoratorpython-decorators

How to have a decorator access variables from the decorated function?


I'm using a generic Python timeout decorater. I want the decorater to access variables from the function it is decorating. In this example, I want message to pass from the prepare function into the timeout function. I don't want to use a global variable as that sounds like bad practice.

def timeout(seconds):
    def decorator(func):
        def _handle_timeout(signum, frame):
            # Do something with variable message
            print("Sending the message didn't work!")
            print(message)
        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result

        return wraps(func)(wrapper)

    return decorator

@timeout(5)
def prepare(message):
    """Deploy app using Heroku to the MTurk Sandbox."""
    print("Preparing to send message!")
    send(message)

Solution

  • Push the handler into the wrapper so that it has access to that variable.

    from functools import wraps
    import signal
    import time
    
    def timeout(seconds):
        def decorator(func):
            def wrapper(message, *args, **kwargs):
                def _handle_timeout(signum, frame):
                    # Do something with variable message
                    print("Sending the message didn't work!")
                    print(message)
                signal.signal(signal.SIGALRM, _handle_timeout)
                signal.alarm(seconds)
                try:
                    result = func(message, *args, **kwargs)
                finally:
                    signal.alarm(0)
                return result
    
            return wraps(func)(wrapper)
    
        return decorator
    
    @timeout(1)
    def prepare(message):
        # Uncomment to force error
        # time.sleep(3)
        """Deploy app using Heroku to the MTurk Sandbox."""
        print("Preparing to send message!")
        print(message)
    
    
    if __name__ == "__main__":
        prepare("hi")