pythondecoratordecorator-chaining

How to assure applying order of function decorators in Python?


Some decorators should only be used in the outermost layer.

A decorator that augments the original function and add a configure parameter is one example.

from functools import wraps

def special_case(f):
    @wraps(f)
    def _(a, b, config_x=False):
        if config_x:
           print "Special case here"
           return
        return f(a, b)

How can I avoid decorators like this getting decorated by another decorator?

EDIT

It is really disgusting to let everyone trying to apply a new decorator worry about the application order.

So, is it possible to avoid this kind of situation? Is it possible to add a config option without introducing a new parameter?


Solution

  • Normally, when one write a decorator to be used generically, one does not estrict the number or name of arguments for the function it is wrapping.

    Most decorators out there accept a list o positional arguments, and amapping of keyword arguments as parameters for their wrapper, and pass those, as received, to the decorated function:

    def deco(func):
       def wrapper(*args, **kwargs):
           ... decorator stuff here ...
           return func(*args, **kwargs)
    

    Threfore, if the decorator is to receive a parameter that it should "consume" - like the config_x you mention, all you have to do is to document it, have it as a keyword parameter, and pick it from kwargs. To avoid name clashes on parameters, one can, for example, prefix this parameter name with the decorator's own name or other distinct name:

    def deco(func):
       def wrapper(*args, **kwargs):
           if "deco_config_x" in kwargs):
               config_x = kwargs.pop(deco_config_x)
           ... decorator stuff here ...
           return func(*args, **kwargs)
    

    This way, the decorator may be put anywhere on a "decorator stack" - it will pick the parameter(s) addressed to it, and those bellow it won't get any stranger parameter. Theonly requirement is that your functions and decorators as a whole juts let keyword parametrs they don't know about to pass through.