I ran into a perplexing situation where the @validator(...) decorator factory in Pydantic 1.x expects you to name your first argument cls, but stops working if you add the @classmethod decorator. So they appeared to be mislabeled instance methods.

The trick is that only the unmodified method is an instance method. The decorator returns a class method, just like the @classmethod decorator does. The trick depends on duck typing: if you return a function whose first argument is self.__class__, you get a class method instead of an instance method.

Here’s an example of how that works:

def pass_class_decorator(func):
    def wrapper(cls, *args, **kwargs):
        print(f"Class is: {cls.__class__}")
        return func(cls.__class__, *args, **kwargs)
    return wrapper

class Example1:
    @pass_class_decorator
    def method(cls, x: int) -> int:
        print(f"x is: {x}")
        return x

obj1 = Example1()
obj1.method(5)
# Output:
# Class is: <class '__main__.Example1'>
# x is: 5

Compare that with the usual pattern:

def pass_instance_decorator(func):
    def wrapper(self, *args, **kwargs):
        print(f"Instance is: {self}")
        return func(self, *args, **kwargs)
    return wrapper

class Example2:
    @pass_instance_decorator
    def method(self, x: int) -> int:
        print(f"x is: {x}")
        return x

obj2 = Example2()
obj2.method(5)
# Output:
# Instance is: <__main__.Example2 object at 0x...>
# x is: 5

The key is the phrase cls.__class__ in return func(cls.__class__, *args, **kwargs).