How to Undecorate in Python

In this article, we will see how to undecorate a decorated function.

Before hopping into the undecorating part, we need to identify which kind of objects can be decorated by which kind of objects in Python.

Decorators can be implemented in a number of different ways. A decorator, it can be either a class or a function, and wraps a function, a method or a class and alter its running beaviour.

We need to sure that the object we want to undecorate is has a closure. We can use __closure__ magic method to check this.

closure = o.__closure__

After evaluation, if closure is not null then we can check its closure content.

We will check the cells of the closure and fetch the function that is decorated.

For undecorate operation, there’s already a library but I want to show you how it works simply.

def looks_like_a_decorator(a):
    return (
        isfunction(a) or ismethod(a) or isclass(a)
    )

def undecorate(o):
    closure = o.__closure__
    if closure:
        for cell in closure:
            if cell.cell_contents is o:
                continue
                
            if looks_like_a_decorator(cell.cell_contents):
                undecd = undecorated(cell.cell_contents)
                if undecd:
                    return undecd

If you don’t know what cell_contents is then you can read about closures and try the code after that.

To test, we can simply create a simple class, decorate it and undecorate:

class A(object):
    decorated = False


def decorate(cls):
    def dec():
        ins = cls()
        ins.decorated = True
        return ins

    return dec


decorated = decorate(A)

assert decorated().decorated is True
assert undecorated(decorated) is A
assert undecorated(decorated)().decorated is False

and we will get the undecorated object.

Sources:
Understanding Python’s closures
undecorated