Tuesday, 6 January 2026

Conditional Decorator

After my previous post about decorating decorators I was thinking about some more potential use of this technique, and the idea of applying a decorator conditionally came up. Python supports applying a decorator conditionally using an if-else expression like this:


def log_call(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"In function: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper 

@(log_call if debugging else lambda x: x)
def do_something(a, b):
    return a + b
    

That's pretty nice, but at the same time quite limited. We apply or not apply a decorator based on a condition at the time the function being decorated is defined. But what if we want to decide whether the decorator logic applies based on a dynamic value, each time the decorated function is invoked? We can have a (meta)decorator: conditional, that we apply to another decorator when this decorator is applied, not defined. conditional creates a new decorator that traps in its closure the original decorator and a boolean function (condition_fn) that decides whether the decorator has to be applied. This new decorator receives a function and returns a new function that in each invocation checks (based on condition_fn) if the original decorator has to be applied. Less talk, more code:


def conditional(decorator, condition_fn: Callable):
    """
    metadecorator: createa a new decorator that applies the original decorator only if `condition_fn` returns True.
    """
    def conditional_deco(fn: Callable):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            if condition_fn():
                return decorator(fn)(*args, **kwargs)
            else:
                return fn(*args, **kwargs)
        return wrapper
    return conditional_deco

@(conditional(log_call, lambda: debugging))
def do_something2(a, b):
    return a + b    

print(f"- debugging {debugging}")
print(do_something2(7, 3)) 
debugging = False
print(f"- debugging {debugging}")
print(do_something2(7, 3))
print("------------------------")

# - debugging True
# In function: do_something2
# 10
# - debugging False
# 10

No comments:

Post a Comment