GrahamDumpleton / wrapt

A Python module for decorators, wrappers and monkey patching.
BSD 2-Clause "Simplified" License
2.04k stars 230 forks source link

Inconsistency in classmethod args in Python 3.9 #182

Closed cristianmatache closed 3 years ago

cristianmatache commented 3 years ago
from wrapt import decorator

@decorator
def deco(wrapped, instance, args, kwargs):
    print(args)
    return wrapped(*args, **kwargs)

class X:
    @classmethod
    @deco
    def method(cls, a):
        pass

# Python 3.8 and lower
X.method(1)
# (<class '__main__.X'>, 1)

# Python 3.9
X.method(1)
# (1,)

In the example above, in Python 3.9 the injected args don't include the class while in python 3.8 and lower, args includes the class object.

GrahamDumpleton commented 3 years ago

The difference is because there was a long standing bug in Python which I first reported back in 2013. They finally fixed it in Python 3.9.

So the behaviour of wrapt is correct based on how the respective Python versions worked.

The docs for wrapt mention this issue:

The docs need to be updated to mention that the behaviour in Python was finally corrected in Python 3.9 though.

cristianmatache commented 3 years ago

Thank you @GrahamDumpleton. This is very interesting. It is nice that regular methods defined on a metaclass and methods decorated with @classmethod on a regular class behave the same finally.