asemahle
asemahle

Reputation: 20805

Apply decorator all function in a class without using Metaclass

I have been using the following (Jython 2.7) code to decorate functions in some classes:

import sys
import inspect
from decorator import decorator

def useless_decorator(method, *args, **kwargs):
    #Does nothing yet :D
    return method(*args, **kwargs)

class UselessMetaClass(type):
    def __new__(cls, clsname, bases, dict):
        for name, method in dict.items():
            if not name.startswith('_') and inspect.isroutine(method):
                dict[name] = decorator(useless_decorator, method)
        return type.__new__(cls, clsname, bases, dict)

class Useless(object):
    __metaclass__ = UselessMetaClass

The goal is to decorate all public functions (i.e. ones with names that don't start with an underscore) with the useless_decorator. Of course, this behaviour is only expected in classes that inherit from Useless.

Unfortunately I've been running into metaclass conflict errors. I've had great difficulty debugging them and I think they're occurring for reasons beyond my control (due to a third party library I'm using: Sikuli).

But, maybe I don't need to use a metaclass at all! Does anyone know a way to simulate my above code without using a metaclass?

I.E., Is there any other way to apply a decorator to all functions in a class?

(P.S. I know I could manually decorate each function, but that's not the solution I'm looking for)

Upvotes: 2

Views: 727

Answers (1)

mata
mata

Reputation: 69052

Converting your metaclass to a class decorator should be straight forward. A class decorator simly receives the class as argument and returns the (modified) class:

def useless_class_decorator(cls):
    for name, method in cls.__dict__.items():
        if not name.startswith('_') and inspect.isroutine(method):
            setattr(cls, name, decorator(useless_decorator, method))
    return cls

The main difference here is that you can't direcly change cls.__dict__ here, as for new style classes that will be a dictproxy which does not support assignment, so you have to use setattr on the class instead. Then you simply create your class:

@useless_class_decorator
class Useless(object):
    def method_to_decorate(self, *args, *kwargs):
        ...

However this won't affect subclasses of Useless, those would also have to be decorated using the class decorator. If that's not acceptable, then a metaclass may be the better option...

Upvotes: 4

Related Questions