Search code examples
pythonpython-decorators

Defining a class inside a function to interrupt decorator execution


I'm trying to configure a decorator at run time. This is somewhat related to my earlier question: How to configure a decorator in Python

The motivation for this is that I'm trying to use the Thespian troupe code "as-is".

Is it legal to have this code here, where I've defined the class (and therefore called the decorator) inside a class method? Again, the reason for this is that I could feed the max_count argument prior to the decorator being call.

The module is calculator.calculator (yes, bad choice perhaps)

class Scheduler:
    def __init__(self):
        self.actor_system = None

    def start(self):
        self.actor_system = ActorSystem('multiprocTCPBase')

    def stop(self):
        self.actor_system.shutdown()

    def launch(self, count, func_and_data, status_cb):
        class CalcPayload:
            def __init__(self, func_and_data, status_cb):
                self.func_and_data = func_and_data
                self.status_cb = status_cb

        @troupe(max_count=count)
        class Execute(ActorTypeDispatcher):
            def receiveMsg_CalcPayload(self, msg, sender):
                func = msg.func_and_data['func']
                data = msg.func_and_data['data']
                status_cb = msg.status_cb

                self.send(sender, func(data, status_cb))

        exec_actor = self.actor_system.createActor(Execute)

        for index in range(len(func_and_data)):
            calc_config = CalcPayload(func_and_data[index], status_cb)
            self.actor_system.tell(exec_actor, calc_config)

        for index in range(len(func_and_data)):
            result = self.actor_system.listen(timeout)

        self.actor_system.tell(exec_actor, ActorExitRequest())

For various reasons, I can't apply the decorator to the class when I use it. There is a brief discussion on this in the question I referenced.


Solution

  • While not invalid, it is generally inadvisable to define a class as a local variable inside a function, as it would make access to the class difficult outside the function.

    Instead, you can define the classes outside the function, and apply the decorator function to the class when it's actually needed by calling the decorator function with the class object:

    class CalcPayload:
        def __init__(self, func_and_data, status_cb):
            self.func_and_data = func_and_data
            self.status_cb = status_cb
    
    
    class Execute(ActorTypeDispatcher):
        def receiveMsg_CalcPayload(self, msg, sender):
            func = msg.func_and_data['func']
            data = msg.func_and_data['data']
            status_cb = msg.status_cb
    
            self.send(sender, func(data, status_cb))
    
    class Scheduler:
        def __init__(self):
            self.actor_system = None
    
        def start(self):
            self.actor_system = ActorSystem('multiprocTCPBase')
    
        def stop(self):
            self.actor_system.shutdown()
    
        def launch(self, count, func_and_data, status_cb):
            exec_actor = self.actor_system.createActor(troupe(max_count=count)(Execute))
    
            for index in range(len(func_and_data)):
                calc_config = CalcPayload(func_and_data[index], status_cb)
                self.actor_system.tell(exec_actor, calc_config)
    
            for index in range(len(func_and_data)):
                result = self.actor_system.listen(timeout)
    
            self.actor_system.tell(exec_actor, ActorExitRequest())