Thomas Fellner
Thomas Fellner

Reputation: 113

Adding parameter to decorator removes cls parameter

I want to do logging of MongoEngine Documents in my flask application as described here. But I also want to be able to exclude certain attributes that this document class has.

If I give the apply function of a decorator an additional parameter excludes the cls parameter is no longer given.

Traceback (most recent call last):
  File ".../test.py", line 26, in <module>
    @__log_update.apply(excludes=[])
TypeError: apply() missing 1 required positional argument: 'cls'

In the simplified code

def handler(*events):
    """
    Signal decorator to allow use of callback functions as class decorators.
    """
    def decorator(fn):
        def apply(cls, excludes=[]):
            for exclude in excludes:
                print(exclude)

            for event in events:
                event.connect(fn, sender=cls)
            return cls

        fn.apply = apply
        return fn

    return decorator

@handler()
def __log_update(*args, **kwargs):
    pass


@__log_update.apply(excludes=['testString'])
class Test(object):
    testString = ''
    testInt = 0

But when just using @__log_update.apply without any parameter, the cls parameter is given and is <class '__main__.Test'> as it should be.

I need both to do the logging.

Upvotes: 1

Views: 63

Answers (1)

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

That about how decorator works.

When you apply it like so (no matter with or without parameters):

@__log_update.apply()

the result of invocation should be a decorator itself which then applied to the underlying class.
You need an additional layer that will make .apply() function to return a class decorator:

def handler(*events):
    """
    Signal decorator to allow use of callback functions as class decorators.
    """
    def decorator(fn):
        def apply(excludes=[]):
            def class_decor(cls):
                for exclude in excludes:
                    print(exclude)

                for event in events:
                    event.connect(fn, sender=cls)
                return cls
            return class_decor

        fn.apply = apply
        return fn

    return decorator

@handler()
def __log_update(*args, **kwargs):
    pass


@__log_update.apply(excludes=['testString'])
class Test(object):
    testString = ''
    testInt = 0

Upvotes: 1

Related Questions