Minura Punchihewa
Minura Punchihewa

Reputation: 2025

Inheriting Decorator from Overwritten Subclass Function

I have a decorator that performs a couple of logging operations,

def event_logger(function):
    def wrapper(*args, **kwargs):
        # get the logger
        logger = logging.getLogger()

        logger.info("Some log")

        # execute the function
        return_value = function(*args, **kwargs)

        logger.info("Some log")
        return return_value

    return wrapper

Now, I have implemented a bunch of processors that each inherit from an interface (Factory Pattern).

My interface looks like this,

class IProcessor(ABC):
    @property
    def _logger(self):
        return logging.getLogger(__name__)

    @abstractmethod
    def process(self):
        """
        Execute processing operation
        """
        raise NotImplementedError("Subclasses must implement process method")

An example of a processor (subclass) would look like this,

class TestProcessor(IProcessor):
    @event_logger
    def process(self):
        self._logger.info("TestPreProcessor")

As shown here, the @event_logger decorator has been added to the process method of my subclass. This works, but it would mean that each time I have a new processor, I would need to remember to add the decorator.

Is there anyway I can avoid this maybe by adding the decorator to the process method of the interface or something? I know this will probably not work, since the subclass will be overwriting this method though. Is there some way that I can achieve this?

Upvotes: 0

Views: 49

Answers (1)

jsbueno
jsbueno

Reputation: 110271

You can add the decorator in the __init_subclass__ method of your interface class.

If the methods to be decorated are always "final": that is, they don't call the super() version of themselves, it is easy to do. If they do call their super-versions, which could also be decorated - then you have to add some state-checking logic to avoid performing the decorator code more than once (that may be tricky)


class IProcessor(ABC):

    _methods_to_add_logging = ("process", )

    def __init_subclass__(cls, *args, **kw):
        super().__init_subclass__(*args, **kw)
        for method_name in __class__._methods_to_add_logging:
            method = getattr(cls, method_name, None)
            if not method:
                continue
            method = event_logger(method)
            setattr(cls, method_name, method)

    @property
    def _logger(self):
        return logging.getLogger(__name__)

    @abstractmethod
    def process(self):
        """
        Execute processing operation
        """
        raise NotImplementedError("Subclasses must implement process method")

Upvotes: 1

Related Questions