From fb6a69c40e71749edad36044488303664f602b06 Mon Sep 17 00:00:00 2001 From: Y Date: Sat, 24 Feb 2018 13:05:57 +0100 Subject: [PATCH] detect loops while building the workflow, not after: faster, better --- pyruse/workflow.py | 44 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/pyruse/workflow.py b/pyruse/workflow.py index b7b3902..98cf2d7 100644 --- a/pyruse/workflow.py +++ b/pyruse/workflow.py @@ -11,7 +11,7 @@ class Workflow: firstStep = None for label in actions: if not label in seen: - (entryPoint, seen, newDangling) = self._initChain(actions, label, seen) + (entryPoint, seen, newDangling) = self._initChain(actions, label, seen, (label,)) if firstStep is None: firstStep = entryPoint elif len(dangling) > 0: @@ -19,11 +19,8 @@ class Workflow: setter(entryPoint) dangling = newDangling self.firstStep = firstStep - loop = self._checkForLoops() - if loop: - raise RecursionError("Loop found in actions: %s\n" % loop) - def _initChain(self, actions, label, seen): + def _initChain(self, actions, label, seen, wholeChain): dangling = [] previousSetter = None firstStep = None @@ -38,12 +35,16 @@ class Workflow: obj.setStepName(label + '[' + str(stepNum) + ']') if mod.thenRun: (seen, dangling) = \ - self._branchToChain(obj.setNextStep, mod.thenRun, actions, seen, dangling) + self._branchToChain( + obj.setNextStep, mod.thenRun, wholeChain, + actions, seen, dangling) isThenCalled = True if mod.isFilter: if mod.elseRun: (seen, dangling) = \ - self._branchToChain(obj.setAltStep, mod.elseRun, actions, seen, dangling) + self._branchToChain( + obj.setAltStep, mod.elseRun, wholeChain, + actions, seen, dangling) else: dangling.append(obj.setAltStep) isPreviousDangling = mod.isFilter and not isThenCalled @@ -57,35 +58,16 @@ class Workflow: seen[label] = firstStep if len(dangling) == 0 else None return (firstStep, seen, dangling) - def _branchToChain(self, parentSetter, branchName, actions, seen, dangling): - if branchName in seen and seen[branchName]: + def _branchToChain(self, parentSetter, branchName, wholeChain, actions, seen, dangling): + if branchName in wholeChain: + raise RecursionError("Loop found in actions: %s\n" % str(wholeChain + (branchName,))) + elif branchName in seen and seen[branchName] is not None: parentSetter(seen[branchName]) elif branchName in actions: (entryPoint, seen, newDangling) = \ - self._initChain(actions, branchName, seen) + self._initChain(actions, branchName, seen, wholeChain + (branchName,)) parentSetter(entryPoint) dangling.extend(newDangling) else: raise ValueError("Action chain not found: %s\n" % branchName) return (seen, dangling) - - def _checkForLoops(self): - if self.firstStep is None: - return None - branches = [(self.firstStep, [], [])] - while True: - node, branchIds, branch = branches.pop() - idNode = id(node) - if idNode in branchIds: - return branch if self._withDebug else True - branchIds.append(idNode) - if self._withDebug: - branch.append(node.stepName) - if isinstance(node, base.Filter) and node.altStep: - altBranch = list(branch) - altBranchIds = list(branchIds) - branches.append((node.altStep, altBranchIds, altBranch)) - if node.nextStep: - branches.append((node.nextStep, branchIds, branch)) - if len(branches) == 0: - return None