from abc import ABCMeta, abstractmethod
import inspect
-from iocontrol import PrevControl, NextControl
import nmoperator
def _spec(fn, name=None):
+ """ useful function that determines if "fn" has an argument "name".
+ if so, fn(name) is called otherwise fn() is called.
+
+ means that ispec and ospec can be declared with *or without*
+ a name argument. normally it would be necessary to have
+ "ispec(name=None)" to achieve the same effect.
+ """
if name is None:
return fn()
varnames = dict(inspect.getmembers(fn.__code__))['co_varnames']
#def process(i): pass
-class StageChain(StageCls):
+class StageHelper(Stage):
+ """ a convenience wrapper around something that is Stage-API-compliant.
+ (that "something" may be a static class, for example).
+
+ StageHelper happens to also be compliant with the Stage API,
+ it differs from the stage that it wraps in that all the "optional"
+ functions are provided (hence the designation "convenience wrapper")
+ """
+ def __init__(self, stage):
+ self.stage = stage
+ self._ispecfn = None
+ self._ospecfn = None
+ if stage is not None:
+ self.set_specs(self, self)
+
+ def ospec(self, name):
+ assert self._ospecfn is not None
+ return _spec(self._ospecfn, name)
+
+ def ispec(self, name):
+ assert self._ispecfn is not None
+ return _spec(self._ispecfn, name)
+
+ def set_specs(self, p, n):
+ """ sets up the ispecfn and ospecfn for getting input and output data
+ """
+ if hasattr(p, "stage"):
+ p = p.stage
+ if hasattr(n, "stage"):
+ n = n.stage
+ self._ispecfn = p.ispec
+ self._ospecfn = n.ospec
+
+ def new_specs(self, name):
+ """ allocates new ispec and ospec pair
+ """
+ return self.ispec("%s_i" % name), self.ospec("%s_o" % name)
+
+ def process(self, i):
+ if self.stage and hasattr(self.stage, "process"):
+ return self.stage.process(i)
+ return i
+
+ def setup(self, m, i):
+ if self.stage is not None and hasattr(self.stage, "setup"):
+ self.stage.setup(m, i)
+
+ def _postprocess(self, i): # XXX DISABLED
+ return i # RETURNS INPUT
+ if hasattr(self.stage, "postprocess"):
+ return self.stage.postprocess(i)
+ return i
+
+
+class StageChain(StageHelper):
""" pass in a list of stages, and they will automatically be
chained together via their input and output specs into a
combinatorial chain, to create one giant combinatorial block.
def __init__(self, chain, specallocate=False):
assert len(chain) > 0, "stage chain must be non-zero length"
self.chain = chain
+ StageHelper.__init__(self, None)
self.setup = self._sa_setup if specallocate else self._na_setup
-
- def ispec(self):
- """ returns the ispec of the first of the chain
- """
- return _spec(self.chain[0].ispec, "chainin")
-
- def ospec(self):
- """ returns the ospec of the last of the chain
- """
- return _spec(self.chain[-1].ospec, "chainout")
+ self.set_specs(self.chain[0], self.chain[-1])
def _sa_setup(self, m, i):
for (idx, c) in enumerate(self.chain):
return self.o # conform to Stage API: return last-loop output
-class StageHelper(Stage):
- """ a convenience wrapper around something that is Stage-API-compliant.
- (that "something" may be a static class, for example).
-
- StageHelper happens to also be compliant with the Stage API,
- it differs from the stage that it wraps in that all the "optional"
- functions are provided (hence the designation "convenience wrapper")
- """
- def __init__(self, stage):
- self.stage = stage
-
- def ospec(self, name):
- assert self.stage is not None
- return _spec(self.stage.ospec, name)
-
- def ispec(self, name):
- assert self.stage is not None
- return _spec(self.stage.ispec, name)
-
- def process(self, i):
- if self.stage and hasattr(self.stage, "process"):
- return self.stage.process(i)
- return i
-
- def setup(self, m, i):
- if self.stage is not None and hasattr(self.stage, "setup"):
- self.stage.setup(m, i)
-
- def _postprocess(self, i): # XXX DISABLED
- return i # RETURNS INPUT
- if hasattr(self.stage, "postprocess"):
- return self.stage.postprocess(i)
- return i
-