'Intercept specific function invocations using parent/child classes?

I am writing a framework that allows us to intercept certain function invocations so that we can manage them(enforce timeouts and retries, persist return values etc.). Right now I'm thinking of introducing an abstract class with a method that invokes an abstract method and forcing users/clients to inherit the abstract class and do their function invocations inside the abstract method.

Specifically, I am writing an abstract class Fruit that expects clients to:

  1. Extend Fruit and override the eat_fruit method
  2. Invoke the eat method when actually wanting to use the overriden eat_fruit method

The reason I'm thinking of doing this is that we want to record the number of eat_fruit invocations

class Fruit:
    def eat(self, *args, **kwargs):
        self.eat_fruit(*args, **kwargs)
        
    def eat_fruit(self, *args, **kwargs):
        pass

class Apple(Fruit):
    def eat_fruit(self, *args, **kwargs):
        print("Eat Apple")
        
apple = Apple()
apple.eat()

However, we're running into a problem with extensibility. Let's say the client needs to add input parameter color to Apple's eat_fruit method, they can add it like this:

class Fruit:
    def eat(self, *args, **kwargs):
        self.eat_fruit(*args, **kwargs)
        
    def eat_fruit(self, *args, **kwargs):
        pass

class Apple(Fruit):
    def eat_fruit(self, color, *args, **kwargs):
        print("Eat "+ color + " Apple")
        
apple = Apple()
apple.eat("Red")

This is workable but relies on them knowing that Fruit's eat method passes *args and **kwargs directly to the eat_fruit method, which seems to be an implementation detail in the eat method that may be subject to change.

I'm wondering if there are better ways to accomplish the same thing. In particular,

  1. Is it possible for them to invoke eat_fruit directly(rather than invoking the parent class's eat method and assume that the arguments will be forwarded to eat_fruit)? I can't think of a way for them to do that while still letting us keep track of eat_fruit invocations
  2. Is there a better way(rather than using parent-child classes) to intercept function invocations? The final goal of this framework is to intercept specific function invocations made by clients so that we can manage them(enforce timeouts and retries, persist return values etc.).

Thanks! Jessica



Solution 1:[1]

In my experience, frameworks and APIs solve this via an Event-driven architecture.

While other methods might solve your problems, like with decorators, if you want full control over what is happening, you can consider this architecture.

Basically, instead of executing functions directly, you "fire" or "invoke" an "event" that is associated with that function, but which also can be associated with multiple functions. The user can have their own functions, and the framework can have its own functions, without either party having to know too much about the other. The user is happy that his functions are executing, and the framework is happy to have control over any event that is fired or invoked.

However this is not a small feature that a framework can have, especially if you are implementing it from scratch. So when considering to provide this feature in your framework, it is worth checking if the language you are using already has support for it or if a 3rd party library is available that can provide this feature for you.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1