from functools import wraps

handlers = {
    # 'function_name': [
    #     ['pre handlers list'],
    #     ['post handlers list']
    # ]
}


def _register(function_name, handler, is_post_handler):
    handlers.setdefault(function_name, [[], []])[is_post_handler].append(handler)
    print(
        'Register "{}" {} "{}" execution'.format(
            handler.__name__, "after" if is_post_handler else "before", function_name
        )
    )


def register_pre_handler(function_name, handler):
    """
    Register pre handler for function, handler must accept same number of arguments as function.
    Handler must not modify any arguments.
    """
    _register(function_name, handler, False)


def register_post_handler(function_name, handler):
    """
    Register post handler for function,
        handler must accept function result as first argument and all other function arguments after that.
    Handler must not modify any arguments.
    """
    _register(function_name, handler, True)


def listener(funct):
    @wraps(funct)
    def wrapper(*args, **kwargs):
        pre, post = handlers.get(funct.__name__, [[], []])
        [x(*args, **kwargs) for x in pre]
        res = funct(*args)
        [x(res, *args, **kwargs) for x in post]
        return res

    return wrapper
