osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Storing the state of script between steps


On 21Feb2014 12:59, Denis Usanov <usanovdd at gmail.com> wrote:
> I mostly develop on Python some automation scripts such as deployment (it's not about fabric and may be not ssh at all), testing something, etc. In this terms I have such abstraction as "step".
> 
> Some code:
> 
> class IStep(object):
>     def run():
>         raise NotImplementedError()
> 
> And the certain steps:
> 
> class DeployStep: ...
> class ValidateUSBFlash: ...
> class SwitchVersionS: ...
> 
> Where I implement run method. 
> Then I use some "builder" class which can add steps to internal list and has a method "start" running all step one by one.
> 
> And I like this. It's loosely coupled system. It works fine in simple cases. But sometimes some steps have to use the results from previous steps. And now I have problems. Before now I had internal dict in "builder" and named it as "world" and passed it to each run() methods of steps. It worked but I disliked this. 

Can you qualify exactly what you dislike about it?

I have a similar system I'm working on which chains operational
steps, and each step can queue multiple following steps.
It is still somewhat alpha.

I think it has pretty much the same state issue that you describe:
you need to keep state around, but passing it to each step feels
clunky: you have this state parameter that you need to maintain and
pass around all the time.

My wishlist for state is twofold; I'd like it to be more implicit,
for example have the state be implicit, such as in the program
scope, and wouldn't it be better to be able to ignore it when you
don't care about the state?

My solution is threefold at present:

First up, the core algorithm/framework always passes the state
variable around. So every "step" function looks somewhat like this:

  def step(self, argument, state):

where "state" is an object instead of a dict; otherwise esssentially
that same as your dict based approach. "argument" it the item to
be worked on in this step; my framework looks like a UNIX shell
pipeline, where arguments are passed down the pipeline from step
to step.

Second, steps which do not care about the state are written like this:

  def step(self, argument)

and installed via a wrapper:

  def step_full(self, argument, state):
    return step(self, argument)

to make it easier to write the simple case.

In your setup, I'd be writing each Step class as a subclass of a
generic step class that incorporates the wrapper:

  class GenericStep:

    def step(self, argument, state):
      return self.stateless_step(argument)

and then classes which do not care about the state would look like
this:

  class SimpleOperation(GenericStep):

    def stateless_step(self, argument):
      ... do stuff with argument ...

and classes which do operate on the state look like this:

  class StepWith SideEffects(GenericStep):

    def step(self, argument, state):
      ... do stuff with argument and modify state ...