Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-03-13 02:31:58

0001 import sys
0002 
0003 from builtins import range
0004 from .Mixins import _ConfigureComponent, PrintOptions
0005 from .Mixins import _Labelable, _Unlabelable
0006 from .Mixins import _ValidatingParameterListBase
0007 from .ExceptionHandling import *
0008 from .OrderedSet import OrderedSet
0009 
0010 class _HardDependency(object):
0011     """Information relevant for when a hard dependency,
0012        which uses the * operator, is found"""
0013     def __init__(self, sequenceName, depSet):
0014         self.sequenceName = sequenceName
0015         self.depSet = depSet
0016 
0017 class _Sequenceable(object):
0018     """Denotes an object which can be placed in a sequence"""
0019     def __init__(self):
0020         pass
0021     def __mul__(self,rhs):
0022         return _SequenceCollection(self,rhs)
0023     def __add__(self,rhs):
0024         return _SequenceCollection(self,rhs)
0025     def __invert__(self):
0026         return _SequenceNegation(self)
0027     def _clonesequence(self, lookuptable):
0028         try:
0029             return lookuptable[id(self)]
0030         except:
0031             raise KeyError("no "+str(type(self))+" with id "+str(id(self))+" found")
0032     def resolve(self, processDict,keepIfCannotResolve:bool=False):
0033         return self
0034     def isOperation(self):
0035         """Returns True if the object is an operator (e.g. *,+ or !) type"""
0036         return False
0037     def isLeaf(self):
0038         return False
0039     def _visitSubNodes(self,visitor):
0040         pass
0041     def visitNode(self,visitor):
0042         visitor.enter(self)
0043         self._visitSubNodes(visitor)
0044         visitor.leave(self)
0045     def _appendToCollection(self,collection):
0046         collection.append(self)
0047     def _errorstr(self):
0048         return "A Sequenceable type"
0049 
0050 def _checkIfSequenceable(caller, v):
0051     if not isinstance(v,_Sequenceable):
0052         typename = format_typename(caller)
0053         msg = format_outerframe(2)
0054         msg += "%s only takes arguments of types which are allowed in a sequence, but was given:\n" %typename
0055         msg +=format_typename(v)
0056         msg +="\nPlease remove the problematic object from the argument list"
0057         raise TypeError(msg)
0058 
0059 def _checkIfBooleanLogicSequenceable(caller, v):
0060     if not isinstance(v,_BooleanLogicSequenceable):
0061         typename = format_typename(caller)
0062         msg = format_outerframe(2)
0063         msg += "%s only takes arguments of types which are allowed in a boolean logic sequence, but was given:\n" %typename
0064         msg +=format_typename(v)
0065         msg +="\nPlease remove the problematic object from the argument list"
0066         raise TypeError(msg)
0067 
0068 class _BooleanLogicSequenceable(_Sequenceable):
0069     """Denotes an object which can be used in a boolean logic sequence"""
0070     def __init__(self):
0071         super(_BooleanLogicSequenceable,self).__init__()
0072     def __or__(self,other):
0073         return _BooleanLogicExpression(_BooleanLogicExpression.OR,self,other)
0074     def __and__(self,other):
0075         return _BooleanLogicExpression(_BooleanLogicExpression.AND,self,other)
0076 
0077 
0078 class _BooleanLogicExpression(_BooleanLogicSequenceable):
0079     """Contains the operation of a boolean logic expression"""
0080     OR = 0
0081     AND = 1
0082     def __init__(self,op,left,right):
0083         _checkIfBooleanLogicSequenceable(self,left)
0084         _checkIfBooleanLogicSequenceable(self,right)
0085         self._op = op
0086         self._items = list()
0087         #if either the left or right side are the same kind of boolean expression
0088         # then we can just add their items to our own. This keeps the expression
0089         # tree more compact
0090         if isinstance(left,_BooleanLogicExpression) and left._op == self._op:
0091             self._items.extend(left._items)
0092         else:
0093             self._items.append(left)
0094         if isinstance(right,_BooleanLogicExpression) and right._op == self._op:
0095             self._items.extend(right._items)
0096         else:
0097             self._items.append(right)
0098     def isOperation(self):
0099         return True
0100     def _visitSubNodes(self,visitor):
0101         for i in self._items:
0102             i.visitNode(visitor)
0103     def dumpSequencePython(self, options:PrintOptions=PrintOptions()):
0104         returnValue = ''
0105         join = ''
0106         operatorJoin =self.operatorString()
0107         for m in self._items:
0108             returnValue +=join
0109             join = operatorJoin
0110             if not isinstance(m,_BooleanLogicSequenceLeaf):
0111                 returnValue += '('+m.dumpSequencePython(options)+')'
0112             else:
0113                 returnValue += m.dumpSequencePython(options)
0114         return returnValue
0115     def operatorString(self):
0116         returnValue ='|'
0117         if self._op == self.AND:
0118             returnValue = '&'
0119         return returnValue
0120 
0121 
0122 class _SequenceLeaf(_Sequenceable):
0123     def __init__(self):
0124         pass
0125     def isLeaf(self):
0126         return True
0127 
0128 
0129 class _BooleanLogicSequenceLeaf(_BooleanLogicSequenceable):
0130     def __init__(self):
0131         pass
0132     def isLeaf(self):
0133         return True
0134 
0135 class _SequenceCollection(_Sequenceable):
0136     """Holds representation of the operations without having to use recursion.
0137     Operations are added to the beginning of the list and their operands are
0138     added to the end of the list, with the left added before the right
0139     """
0140     def __init__(self,*seqList):
0141         self._collection = list()
0142         for s in seqList:
0143             _checkIfSequenceable(self,s)
0144             s._appendToCollection(self._collection)
0145     def __mul__(self,rhs):
0146         _checkIfSequenceable(self,rhs)
0147         rhs._appendToCollection(self._collection)
0148         return self
0149     def __add__(self,rhs):
0150         _checkIfSequenceable(self,rhs)
0151         rhs._appendToCollection(self._collection)
0152         return self
0153     def __str__(self):
0154         sep = ''
0155         returnValue = ''
0156         for m in self._collection:
0157             if m is not None:
0158                 returnValue += sep+str(m)
0159                 sep = '+'
0160         return returnValue
0161     def _appendToCollection(self,collection):
0162         collection.extend(self._collection)
0163 
0164     def dumpSequencePython(self, options:PrintOptions=PrintOptions()) -> str:
0165         returnValue = ''
0166         separator = ''
0167         for item in self._collection:
0168             itemDump = item.dumpSequencePython(options)
0169             if itemDump:
0170                 returnValue += (separator + itemDump)
0171                 separator = '+'
0172         return returnValue
0173 
0174     def dumpSequenceConfig(self) -> str:
0175         returnValue = self._collection[0].dumpSequenceConfig()
0176         for m in self._collection[1:]:
0177             returnValue += '&'+m.dumpSequenceConfig()
0178         return returnValue
0179 
0180     def directDependencies(self,sortByType:bool=True):
0181         return findDirectDependencies(self, self._collection,sortByType=sortByType)
0182 
0183     def visitNode(self,visitor):
0184         for m in self._collection:
0185             m.visitNode(visitor)
0186 
0187     def resolve(self, processDict,keepIfCannotResolve:bool=False):
0188         self._collection = [x.resolve(processDict,keepIfCannotResolve) for x in self._collection]
0189         return self
0190 
0191     def index(self,item):
0192         return self._collection.index(item)
0193 
0194     def insert(self,index:int,item):
0195         self._collection.insert(index,item)
0196     def _replaceIfHeldDirectly(self,original,replacement):
0197         didReplace = False
0198         for i in self._collection:
0199             if original == i:
0200                 self._collection[self._collection.index(original)] = replacement
0201                 didReplace = True
0202             elif isinstance(i,_UnarySequenceOperator) and i._has(original):
0203                 didReplace = True
0204                 if replacement is None:
0205                     self._collection[self._collection.index(i)] = None
0206                 else:
0207                     self._collection[self._collection.index(i)] = type(i)(replacement)
0208         if replacement is None:
0209             self._collection = [ i for i in self._collection if i is not None]
0210         return didReplace
0211 
0212 
0213 def findDirectDependencies(element, collection,sortByType:bool=True):
0214     dependencies = []
0215     for item in collection:
0216         # skip null items
0217         if item is None:
0218             continue
0219         # EDFilter, EDProducer, EDAnalyzer, OutputModule
0220         # should check for Modules._Module, but that doesn't seem to work
0221         elif isinstance(item, _SequenceLeaf):
0222             t = 'modules'
0223         # cms.ignore(module), ~(module)
0224         elif isinstance(item, (_SequenceIgnore, _SequenceNegation)):
0225             if isinstance(item._operand, _SequenceCollection):
0226                 dependencies += item.directDependencies(sortByType)
0227                 continue
0228             t = 'modules'
0229         # _SequenceCollection
0230         elif isinstance(item, _SequenceCollection):
0231             dependencies += item.directDependencies(sortByType)
0232             continue
0233         # cms.Sequence
0234         elif isinstance(item, Sequence):
0235             if not item.hasLabel_():
0236                 dependencies += item.directDependencies(sortByType)
0237                 continue
0238             t = 'sequences'
0239         # cms.Task
0240         elif isinstance(item, Task):
0241             if not item.hasLabel_():
0242                 dependencies += item.directDependencies(sortByType)
0243                 continue
0244             t = 'tasks'
0245         # cms.ConditionalTask
0246         elif isinstance(item, ConditionalTask):
0247             if not item.hasLabel_():
0248                 dependencies += item.directDependencies(sortByType)
0249                 continue
0250             t = 'conditionaltasks'
0251         # SequencePlaceholder and TaskPlaceholder do not add an explicit dependency
0252         elif isinstance(item, (SequencePlaceholder, TaskPlaceholder, ConditionalTaskPlaceholder)):
0253             continue
0254         # unsupported elements
0255         else:
0256             sys.stderr.write("Warning: unsupported element '%s' in %s '%s'\n" % (str(item), type(element).__name__, element.label_()))
0257             continue
0258         dependencies.append((t, item.label_()))
0259     if sortByType:
0260         return sorted(set(dependencies), key = lambda t_item: (t_item[0].lower(), t_item[1].lower().replace('_cfi', '')))
0261     else:
0262         return dependencies
0263 
0264 
0265 class _ModuleSequenceType(_ConfigureComponent, _Labelable):
0266     """Base class for classes which define a sequence of modules"""
0267     def __init__(self,*arg, **argv):
0268         self.__dict__["_isFrozen"] = False
0269         self._seq = None
0270         if (len(arg) > 1 and not isinstance(arg[1], _TaskBase)) or (len(arg) > 0 and not isinstance(arg[0],_Sequenceable) and not isinstance(arg[0],_TaskBase)):
0271             typename = format_typename(self)
0272             msg = format_outerframe(2)
0273             msg += "The %s constructor takes zero or one sequenceable argument followed by zero or more arguments of type Task. But the following types are given:\n" %typename
0274             for item,i in zip(arg, range(1,20)):
0275                 try:
0276                     msg += "    %i) %s \n"  %(i, item._errorstr())
0277                 except:
0278                     msg += "    %i) Not sequenceable and not a Task\n" %(i)
0279             if len(arg) > 1 and isinstance(arg[0],_Sequenceable) and isinstance(arg[1], _Sequenceable):
0280                 msg += "Maybe you forgot to combine the sequenceable arguments via '*' or '+'."
0281             raise TypeError(msg)
0282         tasks = arg
0283         if len(arg) > 0 and isinstance(arg[0], _Sequenceable):
0284             self._seq = _SequenceCollection()
0285             arg[0]._appendToCollection(self._seq._collection)
0286             tasks = arg[1:]
0287         self._isModified = False
0288 
0289         self._tasks = OrderedSet()
0290 
0291         if len(tasks) > 0:
0292             self.associate(*tasks)
0293     def associate(self,*tasks):
0294         for task in tasks:
0295             if not isinstance(task, _TaskBase):
0296                 raise TypeError("associate only works with objects of type Task")
0297             self._tasks.add(task)
0298     def isFrozen(self) -> bool:
0299         return self._isFrozen
0300     def setIsFrozen(self):
0301         self._isFrozen = True
0302     def _place(self,name:str,proc):
0303         self._placeImpl(name,proc)
0304     def __imul__(self,rhs):
0305         _checkIfSequenceable(self, rhs)
0306         if self._seq is None:
0307             self.__dict__["_seq"] = _SequenceCollection()
0308         self._seq+=rhs
0309         return self
0310     def __iadd__(self,rhs):
0311         _checkIfSequenceable(self, rhs)
0312         if self._seq is None:
0313             self.__dict__["_seq"] = _SequenceCollection()
0314         self._seq += rhs
0315         return self
0316     def __str__(self):
0317         v = ExpandVisitor(type(self))
0318         self.visit(v)
0319         return v.resultString()
0320 
0321     def dumpConfig(self, options:PrintOptions) -> str:
0322         s = ''
0323         if self._seq is not None:
0324             s = self._seq.dumpSequenceConfig()
0325         return '{'+s+'}\n'
0326 
0327     def dumpPython(self, options:PrintOptions=PrintOptions()) -> str:
0328         """Returns a string which is the python representation of the object"""
0329         s = self.dumpPythonNoNewline(options)
0330         return s + "\n"
0331 
0332     def dumpPythonNoNewline(self, options:PrintOptions=PrintOptions()) -> str:
0333         s=''
0334         if self._seq is not None:
0335             s =self._seq.dumpSequencePython(options)
0336         associationContents = set()
0337         for task in self._tasks:
0338             if task.hasLabel_():
0339                 associationContents.add(_Labelable.dumpSequencePython(task, options))
0340             else:
0341                 associationContents.add(task.dumpPythonNoNewline(options))
0342         for iString in sorted(associationContents):
0343             if s:
0344                 s += ", "
0345             s += iString
0346         if len(associationContents) > 254:
0347             return 'cms.'+type(self).__name__+'(*['+s+'])'
0348         return 'cms.'+type(self).__name__+'('+s+')'
0349 
0350     def dumpSequencePython(self, options:PrintOptions=PrintOptions()) -> str:
0351         """Returns a string which contains the python representation of just the internal sequence"""
0352         # only dump the label, if possible
0353         if self.hasLabel_():
0354             return _Labelable.dumpSequencePython(self, options)
0355         elif len(self._tasks) == 0:
0356             if self._seq is None:
0357                 return ''
0358             s = self._seq.dumpSequencePython(options)
0359             if s:
0360               return '('+s+')'
0361             return ''
0362         return self.dumpPythonNoNewline(options)
0363 
0364     def dumpSequenceConfig(self) -> str:
0365         """Returns a string which contains the old config language representation of just the internal sequence"""
0366         # only dump the label, if possible
0367         if self.hasLabel_():
0368             return _Labelable.dumpSequenceConfig(self)
0369         else:
0370             # dump it verbose
0371             if self._seq is None:
0372                 return ''
0373             return '('+self._seq.dumpSequenceConfig()+')'
0374 
0375     def __repr__(self):
0376         s = ''
0377         if self._seq is not None:
0378            s = str(self._seq)
0379         return "cms."+type(self).__name__+'('+s+')\n'
0380 
0381     def directDependencies(self,sortByType:bool=True):
0382         """Returns the list of modules and other entities that are directly used"""
0383         result = []
0384         if self._seq:
0385           result += self._seq.directDependencies(sortByType=sortByType)
0386         if self._tasks:
0387           result += findDirectDependencies(self, self._tasks,sortByType=sortByType)
0388         return result
0389 
0390     def moduleNames(self):
0391         """Returns a set containing the names of all modules being used"""
0392         result = set()
0393         visitor = NodeNameVisitor(result)
0394         self.visit(visitor)
0395         return result
0396 
0397     def contains(self, mod):
0398         visitor = ContainsModuleVisitor(mod)
0399         self.visit(visitor)
0400         return visitor.result()
0401 
0402     def copy(self):
0403         returnValue =_ModuleSequenceType.__new__(type(self))
0404         if self._seq is not None:
0405             returnValue.__init__(self._seq)
0406         else:
0407             returnValue.__init__()
0408         returnValue._tasks = OrderedSet(self._tasks)
0409         return returnValue
0410 
0411     def copyAndExclude(self,listOfModulesToExclude):
0412         """Returns a copy of the sequence which excludes those module in 'listOfModulesToExclude'"""
0413         # You can exclude instances of these types EDProducer, EDFilter, OutputModule,
0414         # EDAnalyzer, ESSource, ESProducer, Service, Sequence, SequencePlaceholder, Task,
0415         # _SequenceNegation, and _SequenceIgnore.
0416         # Mostly this is very intuitive, but there are some complications in cases
0417         # where objects that contain other objects are involved. See the comments
0418         # for the _MutatingSequenceVisitor.
0419         v = _CopyAndExcludeSequenceVisitor(listOfModulesToExclude)
0420         self.visit(v)
0421         result = self.__new__(type(self))
0422         result.__init__(v.result(self)[0], *v.result(self)[1])
0423         return result
0424     def expandAndClone(self):
0425         # Name of this function is not very good. It makes a shallow copy with all
0426         # the subTasks and subSequences flattened out (removed), but keeping all the
0427         # modules that were in those subSequences and subTasks as well as the top level
0428         # ones. Note this will also remove placeholders so one should probably
0429         # call resolve before using this if the sequence contains any placeholders.
0430         visitor = ExpandVisitor(type(self))
0431         self.visit(visitor)
0432         return visitor.result()
0433     def _postProcessFixup(self,lookuptable):
0434         self._seq = self._seq._clonesequence(lookuptable)
0435         return self
0436     def replace(self, original, replacement):
0437         """Finds all instances of 'original' and substitutes 'replacement' for them.
0438            Returns 'True' if a replacement occurs."""
0439         # This works for either argument being of type EDProducer, EDFilter, OutputModule,
0440         # EDAnalyzer, ESProducer, ESSource, Service, Sequence, SequencePlaceHolder,
0441         # Task, _SequenceNegation, _SequenceIgnore. Although it will fail with a
0442         # raised exception if the replacement actually hits a case where a
0443         # non-Sequenceable object is placed in the sequenced part of a Sequence
0444         # or a type not allowed on a Task is put on a Task.
0445         # There is one special case where we need an explicit check to prevent
0446         # the algorithm from getting confused, either both or neither can be Tasks
0447         #
0448         # Mostly this is very intuitive, but there are some complications in cases
0449         # where objects that contain other objects are involved. See the comments
0450         # for the _MutatingSequenceVisitor.
0451 
0452         if (isinstance(original,Task) != isinstance(replacement,Task)):
0453                raise TypeError("replace only works if both arguments are Tasks or neither")
0454         if (isinstance(original,ConditionalTask) != isinstance(replacement,ConditionalTask)):
0455                raise TypeError("replace only works if both arguments are ConditionalTasks or neither")
0456         v = _CopyAndReplaceSequenceVisitor(original,replacement)
0457         self.visit(v)
0458         if v.didReplace():
0459             self._seq = v.result(self)[0]
0460             if v.result(self)[1]:
0461               self._tasks.clear()
0462               self.associate(*v.result(self)[1])
0463         return v.didReplace()
0464     def _replaceIfHeldDirectly(self,original,replacement):
0465         """Only replaces an 'original' with 'replacement' if 'original' is directly held.
0466             If another Sequence or Task holds 'original' it will not be replaced."""
0467         didReplace = False
0468         if original in self._tasks:
0469             self._tasks.remove(original)
0470             if replacement is not None:
0471                 self._tasks.add(replacement)
0472             didReplace = True
0473         if self._seq is not None:
0474             didReplace |= self._seq._replaceIfHeldDirectly(original,replacement)
0475         return didReplace
0476 
0477 
0478     def index(self,item):
0479         """Returns the index at which the item is found or raises an exception"""
0480         if self._seq is not None:
0481             return self._seq.index(item)
0482         raise ValueError(str(item)+" is not in the sequence")
0483     def insert(self,index,item):
0484         """Inserts the item at the index specified"""
0485         _checkIfSequenceable(self, item)
0486         if self._seq is None:
0487             self.__dict__["_seq"] = _SequenceCollection()
0488         self._seq.insert(index,item)
0489     def remove(self, something):
0490         """Remove the first occurrence of 'something' (a sequence or a module)
0491            Returns 'True' if the module has been removed, False if it was not found"""
0492         # You can remove instances of these types EDProducer, EDFilter, OutputModule,
0493         # EDAnalyzer, ESSource, ESProducer, Service, Sequence, SequencePlaceholder, Task,
0494         # _SequenceNegation, and _SequenceIgnore.
0495         # Mostly this is very intuitive, but there are some complications in cases
0496         # where objects that contain other objects are involved. See the comments
0497         # for the _MutatingSequenceVisitor.
0498         #
0499         # Works very similar to copyAndExclude, there are 2 differences. This changes
0500         # the object itself instead of making a copy and second it only removes
0501         # the first instance of the argument instead of all of them.
0502         v = _CopyAndRemoveFirstSequenceVisitor(something)
0503         self.visit(v)
0504         if v.didRemove():
0505             self._seq = v.result(self)[0]
0506             if v.result(self)[1]:
0507               self._tasks.clear()
0508               self.associate(*v.result(self)[1])
0509         return v.didRemove()
0510     def resolve(self, processDict,keepIfCannotResolve:bool=False):
0511         if self._seq is not None:
0512             self._seq = self._seq.resolve(processDict,keepIfCannotResolve)
0513         for task in self._tasks:
0514             task.resolve(processDict,keepIfCannotResolve)
0515         return self
0516     def __setattr__(self,name:str,value):
0517         if not name.startswith("_"):
0518             raise AttributeError("You cannot set parameters for sequence like objects.")
0519         else:
0520             self.__dict__[name] = value
0521     #def replace(self,old,new):
0522     #"""Find all instances of old and replace with new"""
0523     #def insertAfter(self,which,new):
0524     #"""new will depend on which but nothing after which will depend on new"""
0525     #((a*b)*c)  >> insertAfter(b,N) >> ((a*b)*(N+c))
0526     #def insertBefore(self,which,new):
0527     #"""new will be independent of which"""
0528     #((a*b)*c) >> insertBefore(b,N) >> ((a*(N+b))*c)
0529     #def __contains__(self,item):
0530     #"""returns whether or not 'item' is in the sequence"""
0531     #def modules_(self):
0532     def nameInProcessDesc_(self, myname:str):
0533         return myname
0534     def insertInto(self, parameterSet, myname:str, decoratedList):
0535         parameterSet.addVString(True, myname, decoratedList)
0536     def visit(self,visitor):
0537         """Passes to visitor's 'enter' and 'leave' method each item describing the module sequence.
0538         If the item contains 'sub' items then visitor will see those 'sub' items between the
0539         item's 'enter' and 'leave' calls.
0540         """
0541         if self._seq is not None:
0542             self._seq.visitNode(visitor)
0543         for item in self._tasks:
0544             visitor.enter(item)
0545             item.visit(visitor)
0546             visitor.leave(item)
0547 
0548 class _UnarySequenceOperator(_BooleanLogicSequenceable):
0549     """For ~ and - operators"""
0550     def __init__(self, operand):
0551        self._operand = operand
0552        if isinstance(operand, _ModuleSequenceType):
0553            raise RuntimeError("This operator cannot accept a sequence")
0554        if not isinstance(operand, _Sequenceable):
0555            raise RuntimeError("This operator cannot accept a non sequenceable type")
0556     def __eq__(self, other) -> bool:
0557         # allows replace(~a, b)
0558         return type(self) is type(other) and self._operand==other._operand
0559     def __ne__(self, other) -> bool:
0560         return not self.__eq__(other)
0561     def __hash__(self):
0562         # this definition implies that self._operand MUST NOT be changed after the construction
0563         return hash((type(self), self._operand))
0564     def _findDependencies(self,knownDeps, presentDeps):
0565         self._operand._findDependencies(knownDeps, presentDeps)
0566     def _clonesequence(self, lookuptable):
0567         return type(self)(self._operand._clonesequence(lookuptable))
0568     def _has(self, op) -> bool:
0569         return self._operand == op
0570     def resolve(self, processDict,keepIfCannotResolve=False):
0571         return type(self)(self._operand.resolve(processDict,keepIfCannotResolve))
0572     def isOperation(self) -> bool:
0573         return True
0574     def _visitSubNodes(self,visitor):
0575         self._operand.visitNode(visitor)
0576     def decoration(self) -> str:
0577         self._operand.decoration()
0578     def directDependencies(self,sortByType:bool=True):
0579         return self._operand.directDependencies(sortByType=sortByType)
0580     def label_(self) -> str:
0581         return self._operand.label_()
0582 
0583 class _SequenceNegation(_UnarySequenceOperator):
0584     """Used in the expression tree for a sequence as a stand in for the '!' operator"""
0585     def __init__(self, operand):
0586         super(_SequenceNegation,self).__init__(operand)
0587     def __str__(self) -> str:
0588         return '~%s' %self._operand
0589     def dumpSequenceConfig(self) -> str:
0590         return '!%s' %self._operand.dumpSequenceConfig()
0591     def dumpSequencePython(self, options:PrintOptions=PrintOptions()) -> str:
0592         if self._operand.isOperation():
0593             return '~(%s)' %self._operand.dumpSequencePython(options)
0594         return '~%s' %self._operand.dumpSequencePython(options)
0595     def decoration(self) -> str:
0596         return '!'
0597 
0598 class _SequenceIgnore(_UnarySequenceOperator):
0599     """Used in the expression tree for a sequence as a stand in for the '-' operator"""
0600     def __init__(self, operand):
0601         super(_SequenceIgnore,self).__init__(operand)
0602     def __str__(self) -> str:
0603         return 'ignore(%s)' %self._operand
0604     def dumpSequenceConfig(self) ->str:
0605         return '-%s' %self._operand.dumpSequenceConfig()
0606     def dumpSequencePython(self, options:PrintOptions=PrintOptions()) -> str:
0607         return 'cms.ignore(%s)' %self._operand.dumpSequencePython(options)
0608     def decoration(self) -> str:
0609         return '-'
0610 
0611 class _SequenceWait(_UnarySequenceOperator):
0612     """Used in the expression tree for a sequence as a stand in for the '|' operator"""
0613     def __init__(self, operand):
0614         super(_SequenceWait,self).__init__(operand)
0615     def __str__(self) -> str:
0616         return 'wait(%s)' %self._operand
0617     def dumpSequenceConfig(self) -> str:
0618         return '|%s' %self._operand.dumpSequenceConfig()
0619     def dumpSequencePython(self, options:PrintOptions=PrintOptions()) -> str:
0620         return 'cms.wait(%s)' %self._operand.dumpSequencePython(options)
0621     def decoration(self) -> str:
0622         return '|'
0623 
0624 class _SequenceWaitAndIgnore(_UnarySequenceOperator):
0625     """Used in the expression tree for a sequence as a stand in for the '+' operator"""
0626     def __init__(self, operand):
0627         super(_SequenceWaitAndIgnore,self).__init__(operand)
0628     def __str__(self) -> str:
0629         return 'wait(ignore(%s))' %self._operand
0630     def dumpSequenceConfig(self) -> str:
0631         return '+%s' %self._operand.dumpSequenceConfig()
0632     def dumpSequencePython(self, options:PrintOptions=PrintOptions()) -> str:
0633         return 'cms.wait(cms.ignore(%s))' %self._operand.dumpSequencePython(options)
0634     def decoration(self) -> str:
0635         return '+'
0636 
0637 def ignore(seq):
0638     """The EDFilter passed as an argument will be run but its filter value will be ignored
0639     """
0640     if isinstance(seq,_SequenceWait):
0641         return _SequenceWaitAndIgnore(seq._operand)
0642     return _SequenceIgnore(seq)
0643 
0644 def wait(seq):
0645     """All modules after this module in the sequence will wait for this module to finish before being scheduled to run.
0646     """
0647     if isinstance(seq,_SequenceIgnore):
0648         return _SequenceWaitAndIgnore(seq._operand)
0649     return _SequenceWait(seq)
0650 
0651 class Path(_ModuleSequenceType):
0652     def __init__(self,*arg,**argv):
0653         super(Path,self).__init__(*arg,**argv)
0654     def _placeImpl(self,name:str,proc):
0655         proc._placePath(name,self)
0656 
0657 class EndPath(_ModuleSequenceType):
0658     def __init__(self,*arg,**argv):
0659         super(EndPath,self).__init__(*arg,**argv)
0660     def _placeImpl(self,name:str,proc):
0661         proc._placeEndPath(name,self)
0662 
0663 class Sequence(_ModuleSequenceType,_Sequenceable):
0664     def __init__(self,*arg,**argv):
0665         super(Sequence,self).__init__(*arg,**argv)
0666     def _placeImpl(self,name:str,proc):
0667         proc._placeSequence(name,self)
0668     def _clonesequence(self, lookuptable):
0669         if id(self) not in lookuptable:
0670             #for sequences held by sequences we need to clone
0671             # on the first reference
0672             if self._seq is not None:
0673                 clone = type(self)(self._seq._clonesequence(lookuptable))
0674             else:
0675                 clone = type(self)()
0676             lookuptable[id(self)]=clone
0677             lookuptable[id(clone)]=clone
0678         return lookuptable[id(self)]
0679     def _visitSubNodes(self,visitor):
0680         self.visit(visitor)
0681 class SequencePlaceholder(_Sequenceable):
0682     def __init__(self, name:str):
0683         self._name = name
0684     def _placeImpl(self,name:str,proc):
0685         pass
0686     def __str__(self):
0687         return self._name
0688     def insertInto(self, parameterSet, myname:str):
0689         raise RuntimeError("The SequencePlaceholder "+self._name
0690                            +" was never overridden")
0691     def resolve(self, processDict,keepIfCannotResolve:bool=False):
0692         if not self._name in processDict:
0693             #print str(processDict.keys())
0694             if keepIfCannotResolve:
0695                 return self
0696             raise RuntimeError("The SequencePlaceholder "+self._name+ " cannot be resolved.\n Known keys are:"+str(processDict.keys()))
0697         o = processDict[self._name]
0698         if not isinstance(o,_Sequenceable):
0699             raise RuntimeError("The SequencePlaceholder "+self._name+ " refers to an object type which is not allowed to be on a sequence: "+str(type(o)))
0700         return o.resolve(processDict)
0701 
0702     def _clonesequence(self, lookuptable):
0703         if id(self) not in lookuptable:
0704             #for sequences held by sequences we need to clone
0705             # on the first reference
0706             clone = type(self)(self._name)
0707             lookuptable[id(self)]=clone
0708             lookuptable[id(clone)]=clone
0709         return lookuptable[id(self)]
0710     def copy(self):
0711         returnValue =SequencePlaceholder.__new__(type(self))
0712         returnValue.__init__(self._name)
0713         return returnValue
0714     def dumpSequenceConfig(self) -> str:
0715         return 'cms.SequencePlaceholder("%s")' %self._name
0716     def dumpSequencePython(self, options=PrintOptions()) -> str:
0717         return 'cms.SequencePlaceholder("%s")'%self._name
0718     def dumpPython(self, options:PrintOptions=PrintOptions()) -> str:
0719         result = 'cms.SequencePlaceholder(\"'
0720         if options.isCfg:
0721            result += 'process.'
0722         result += self._name+'\")\n'
0723         return result
0724 
0725 
0726 class Schedule(_ValidatingParameterListBase,_ConfigureComponent,_Unlabelable):
0727 
0728     def __init__(self,*arg,**argv):
0729         super(Schedule,self).__init__(*arg)
0730         self._tasks = OrderedSet()
0731         theKeys = list(argv.keys())
0732         if theKeys:
0733             if len(theKeys) > 1 or theKeys[0] != "tasks":
0734                 raise RuntimeError("The Schedule constructor can only have one keyword argument after its Path and\nEndPath arguments and it must use the keyword 'tasks'")
0735             taskList = argv["tasks"]
0736             # Normally we want a list of tasks, but we let it also work if the value is one Task
0737             if isinstance(taskList,Task):
0738                 self.associate(taskList)
0739             else:
0740                 try:
0741                     # Call this just to check that taskList is a list or other iterable object
0742                     self.__dummy(*taskList)
0743                 except:
0744                     raise RuntimeError("The Schedule constructor argument with keyword 'tasks' must have a\nlist (or other iterable object) as its value")
0745                 if taskList:
0746                     self.associate(*taskList)
0747 
0748     def __dummy(self, *args):
0749         pass
0750 
0751     def associate(self,*tasks):
0752         for task in tasks:
0753             if not isinstance(task, Task):
0754                 raise TypeError("The associate function in the class Schedule only works with arguments of type Task")
0755             self._tasks.add(task)
0756     @staticmethod
0757     def _itemIsValid(item) -> bool:
0758         return isinstance(item,Path) or isinstance(item,EndPath)
0759     def copy(self):
0760         import copy
0761         aCopy = copy.copy(self)
0762         aCopy._tasks = OrderedSet(self._tasks)
0763         return aCopy
0764     def _place(self,label:str,process):
0765         process.setPartialSchedule_(self,label)
0766     def _replaceIfHeldDirectly(self,original,replacement) -> bool:
0767         """Only replaces an 'original' with 'replacement' if 'original' is directly held.
0768         If a contained Path or Task holds 'original' it will not be replaced."""
0769         didReplace = False
0770         if original in self._tasks:
0771             self._tasks.remove(original)
0772             if replacement is not None:
0773                 self._tasks.add(replacement)
0774             didReplace = True
0775         indices = []
0776         for i, e in enumerate(self):
0777             if original == e:
0778                 indices.append(i)
0779         for i in reversed(indices):
0780             self.pop(i)
0781             if replacement is not None:
0782                 self.insert(i, replacement)
0783             didReplace = True
0784         return didReplace
0785 
0786     def moduleNames(self):
0787         result = set()
0788         visitor = NodeNameVisitor(result)
0789         for seq in self:
0790             seq.visit(visitor)
0791         for t in self._tasks:
0792             t.visit(visitor)
0793         return result
0794     def contains(self, mod) -> bool:
0795         visitor = ContainsModuleVisitor(mod)
0796         for seq in self:
0797             seq.visit(visitor)
0798             if visitor.result():
0799                 return True
0800         for t in self._tasks:
0801             t.visit(visitor)
0802             if visitor.result():
0803                 return True
0804         return visitor.result()
0805     def tasks(self):
0806         """Returns the list of Tasks (that may contain other Tasks) that are associated directly to the Schedule."""
0807         return self._tasks
0808     def dumpPython(self, options:PrintOptions=PrintOptions()) -> str:
0809         pathNames = ['process.'+p.label_() for p in self]
0810         if pathNames:
0811             s=', '.join(pathNames)
0812         else:
0813             s = ''
0814         associationContents = set()
0815         for task in self._tasks:
0816             if task.hasLabel_():
0817                 associationContents.add(_Labelable.dumpSequencePython(task, options))
0818             else:
0819                 associationContents.add(task.dumpPythonNoNewline(options))
0820         taskStrings = list()
0821         for iString in sorted(associationContents):
0822             taskStrings.append(iString)
0823         if taskStrings and s:
0824             return 'cms.Schedule(*[ ' + s + ' ], tasks=[' + ', '.join(taskStrings) + '])\n'
0825         elif s:
0826             return 'cms.Schedule(*[ ' + s + ' ])\n'
0827         elif taskStrings:
0828             return 'cms.Schedule(tasks=[' + ', '.join(taskStrings) + '])\n'
0829         else:
0830             return 'cms.Schedule()\n'
0831 
0832     def __str__(self) -> str:
0833         return self.dumpPython()
0834 
0835 # Fills a list of all Sequences visited
0836 # Can visit a Sequence, Path, or EndPath
0837 class SequenceVisitor(object):
0838     def __init__(self,d):
0839         self.deps = d
0840     def enter(self,visitee):
0841         if isinstance(visitee,Sequence):
0842             self.deps.append(visitee)
0843         pass
0844     def leave(self,visitee):
0845         pass
0846 
0847 # Fills a list of all Tasks visited
0848 # Can visit a Task, Sequence, Path, or EndPath
0849 class TaskVisitor(object):
0850     def __init__(self,d):
0851         self.deps = d
0852     def enter(self,visitee):
0853         if isinstance(visitee,Task):
0854             self.deps.append(visitee)
0855         pass
0856     def leave(self,visitee):
0857         pass
0858 
0859 # Fills a list of all ConditionalTasks visited
0860 # Can visit a ConditionalTask, Sequence, Path, or EndPath
0861 class ConditionalTaskVisitor(object):
0862     def __init__(self,d):
0863         self.deps = d
0864     def enter(self,visitee):
0865         if isinstance(visitee,ConditionalTask):
0866             self.deps.append(visitee)
0867         pass
0868     def leave(self,visitee):
0869         pass
0870 
0871 # Fills a list of all modules visited.
0872 # Can visit a Sequence, Path, EndPath, or Task
0873 # For purposes of this visitor, a module is considered
0874 # to be an object that is one of these types: EDProducer,
0875 # EDFilter, EDAnalyzer, OutputModule, ESProducer, ESSource,
0876 # Service. The last three of these can only appear on a
0877 # Task, they are not sequenceable. An object of one
0878 # of these types is also called a leaf.
0879 class ModuleNodeVisitor(object):
0880     def __init__(self,l):
0881         self.l = l
0882     def enter(self,visitee):
0883         if visitee.isLeaf():
0884             self.l.append(visitee)
0885         pass
0886     def leave(self,visitee):
0887         pass
0888 
0889 # Should not be used on Tasks.
0890 # Similar to ModuleNodeVisitor with the following
0891 # differences. It only lists the modules that were
0892 # contained inside a Task.  It should only be used
0893 # on Sequences, Paths, and EndPaths.
0894 class ModuleNodeOnTaskVisitor(object):
0895     def __init__(self,l):
0896         self.l = l
0897         self._levelInTasks = 0
0898     def enter(self,visitee):
0899         if isinstance(visitee, Task):
0900             self._levelInTasks += 1
0901         if self._levelInTasks == 0:
0902             return
0903         if visitee.isLeaf():
0904             self.l.append(visitee)
0905         pass
0906     def leave(self,visitee):
0907         if self._levelInTasks > 0:
0908             if isinstance(visitee, Task):
0909                 self._levelInTasks -= 1
0910 
0911 class ModuleNodeOnConditionalTaskVisitor(object):
0912     def __init__(self,l):
0913         self.l = l
0914         self._levelInTasks = 0
0915     def enter(self,visitee):
0916         if isinstance(visitee, ConditionalTask):
0917             self._levelInTasks += 1
0918         # This block gets the modules contained by SwitchProducer. It
0919         # needs to be before the "levelInTasks == 0" check because the
0920         # contained modules need to be treated like in ConditionalTask
0921         # also when the SwitchProducer itself is in the Path.
0922         if hasattr(visitee, "modulesForConditionalTask_"):
0923             self.l.extend(visitee.modulesForConditionalTask_())
0924         if self._levelInTasks == 0:
0925             return
0926         if visitee.isLeaf():
0927             self.l.append(visitee)
0928         pass
0929     def leave(self,visitee):
0930         if self._levelInTasks > 0:
0931             if isinstance(visitee, ConditionalTask):
0932                 self._levelInTasks -= 1
0933 
0934 # Should not be used on Tasks.
0935 # Similar to ModuleNodeVisitor with the following
0936 # differences. It only lists the modules that were
0937 # outside a Task, in the sequenced part of the sequence.
0938 # It should only be used on Sequences, Paths, and
0939 # EndPaths.
0940 class ModuleNodeNotOnTaskVisitor(object):
0941     def __init__(self,l):
0942         self.l = l
0943         self._levelInTasks = 0
0944     def enter(self,visitee):
0945         if isinstance(visitee, Task):
0946             self._levelInTasks += 1
0947         if self._levelInTasks > 0:
0948             return
0949         if visitee.isLeaf():
0950             self.l.append(visitee)
0951         pass
0952     def leave(self,visitee):
0953         if self._levelInTasks > 0:
0954             if isinstance(visitee, Task):
0955                 self._levelInTasks -= 1
0956 
0957 # Can visit Tasks, Sequences, Paths, and EndPaths
0958 # result will be set to True if and only if
0959 # the module is in the object directly or
0960 # indirectly through contained Sequences or
0961 # associated Tasks.
0962 class ContainsModuleVisitor(object):
0963     def __init__(self,mod):
0964         self._mod = mod
0965         self._result = False
0966 
0967     def result(self):
0968         return self._result
0969 
0970     def enter(self,visitee):
0971         if self._mod is visitee:
0972             self._result = True
0973 
0974     def leave(self,visitee):
0975         pass
0976 
0977 # Can visit Tasks, Sequences, Paths, and EndPaths
0978 # Fills a set of the names of the visited leaves.
0979 # For the labelable ones the name is the label.
0980 # For a Service the name is the type.
0981 # It raises an exception if a labelable object
0982 # does not have a label at all. It will return
0983 # 'None' if the label attribute exists but was set
0984 # to None. If a Service is not attached to the process
0985 # it will also raise an exception.
0986 class NodeNameVisitor(object):
0987     """ takes a set as input"""
0988     def __init__(self,l):
0989         self.l = l
0990     def enter(self,visitee):
0991         if visitee.isLeaf():
0992             if isinstance(visitee, _Labelable):
0993                 self.l.add(visitee.label_())
0994             else:
0995                 if visitee._inProcess:
0996                     self.l.add(visitee.type_())
0997                 else:
0998                     raise RuntimeError("Service not attached to process: {}".format(visitee.dumpPython()))
0999     def leave(self,visitee):
1000         pass
1001 
1002 # This visitor works only with Sequences, Paths and EndPaths
1003 # It will not work on Tasks
1004 class ExpandVisitor(object):
1005     """ Expands the sequence into leafs and UnaryOperators """
1006     def __init__(self, type):
1007         self._type = type
1008         self.l = []
1009         self.taskLeaves = []
1010         self.taskLeavesInConditionalTasks = []
1011         self.presentTaskLeaves = self.taskLeaves
1012         self._levelInTasks = 0
1013         self.conditionaltaskLeaves = []
1014         self._levelInConditionalTasks = 0
1015 
1016     def enter(self,visitee):
1017         if isinstance(visitee, Task):
1018             self._levelInTasks += 1
1019             return
1020         if isinstance(visitee, ConditionalTask):
1021             self.presentTaskLeaves = self.taskLeavesInConditionalTasks
1022             self._levelInConditionalTasks += 1
1023             return
1024         if visitee.isLeaf():
1025             if self._levelInTasks > 0:
1026                 self.presentTaskLeaves.append(visitee)
1027             elif self._levelInConditionalTasks > 0:
1028                 self.conditionaltaskLeaves.append(visitee)
1029             else:
1030                 self.l.append(visitee)
1031     def leave(self, visitee):
1032         if self._levelInTasks > 0:
1033             if isinstance(visitee, Task):
1034                 self._levelInTasks -= 1
1035             return
1036         if self._levelInConditionalTasks > 0:
1037             if isinstance(visitee, ConditionalTask):
1038                 self._levelInConditionalTasks -= 1
1039                 if 0 == self._levelInConditionalTasks:
1040                   self.presentTaskLeaves = self.taskLeaves
1041             return
1042         if isinstance(visitee,_UnarySequenceOperator):
1043             self.l[-1] = visitee
1044     def result(self):
1045         tsks = []
1046         if self.taskLeaves:
1047           tsks.append(Task(*self.taskLeaves))
1048         if self.conditionaltaskLeaves:
1049           ct = ConditionalTask(*self.conditionaltaskLeaves)
1050           if self.taskLeavesInConditionalTasks:
1051             ct.append(*self.taskLeavesInConditionalTasks)
1052           tsks.append(ct)
1053         if len(self.l) > 0:
1054             # why doesn't (sum(self.l) work?
1055             seq = self.l[0]
1056             for el in self.l[1:]:
1057                 seq += el
1058             return self._type(seq, *tsks)
1059         else:
1060             return self._type(*tsks)
1061     def resultString(self):
1062         sep = ''
1063         returnValue = ''
1064         for m in self.l:
1065             if m is not None:
1066                 returnValue += sep+str(m)
1067                 sep = '+'
1068         if returnValue:
1069             sep = ','
1070         for n in self.taskLeaves:
1071             if n is not None:
1072                 returnValue += sep+str(n)
1073             sep = ','
1074         return returnValue
1075 
1076 
1077 # This visitor is only meant to run on Sequences, Paths, and EndPaths
1078 # It intentionally ignores nodes on Tasks when it does this.
1079 class DecoratedNodeNameVisitor(object):
1080     """ Adds any '!' or '-' needed.  Takes a list """
1081     def __init__(self,l):
1082         self.l = l
1083         self._decoration =''
1084         self._levelInTasks = 0
1085 
1086     def initialize(self):
1087         self.l[:] = []
1088         self._decoration =''
1089         self._levelInTasks = 0
1090 
1091     def enter(self,visitee):
1092         if isinstance(visitee, _TaskBase):
1093             self._levelInTasks += 1
1094         if self._levelInTasks > 0:
1095             return
1096         if visitee.isLeaf():
1097             if hasattr(visitee, "_Labelable__label"):
1098                 self.l.append(self._decoration+visitee.label_())
1099             else:
1100                 error = "An object in a sequence was not found in the process\n"
1101                 if hasattr(visitee, "_filename"):
1102                     error += "From file " + visitee._filename
1103                 else:
1104                     error += "Dump follows\n" + repr(visitee)
1105                 raise RuntimeError(error)
1106         if isinstance(visitee,_BooleanLogicExpression):
1107             self.l.append(self._decoration+visitee.operatorString())
1108         if isinstance(visitee,_UnarySequenceOperator):
1109             self._decoration=visitee.decoration()
1110         else:
1111             self._decoration=''
1112 
1113     def leave(self,visitee):
1114         # Ignore if this visitee is inside a Task
1115         if self._levelInTasks > 0:
1116             if isinstance(visitee, _TaskBase):
1117                 self._levelInTasks -= 1
1118             return
1119         if isinstance(visitee,_BooleanLogicExpression):
1120             #need to add the 'go back' command to keep track of where we are in the tree
1121             self.l.append('@')
1122 
1123 # This visitor is only meant to run on Sequences, Paths, and EndPaths
1124 # Similar to DecoratedNodeNameVistor. The only difference
1125 # is it also builds a separate list of leaves on Tasks.
1126 class DecoratedNodeNamePlusVisitor(object):
1127     """ Adds any '!' or '-' needed.  Takes a list """
1128     def __init__(self,l):
1129         self.l = l
1130         self._decoration =''
1131         self._levelInTasks = 0
1132         self._leavesOnTasks = []
1133 
1134     def initialize(self):
1135         self.l[:] = []
1136         self._decoration =''
1137         self._levelInTasks = 0
1138         self._leavesOnTasks[:] = []
1139 
1140     def enter(self,visitee):
1141         if isinstance(visitee, Task):
1142             self._levelInTasks += 1
1143         if self._levelInTasks > 0:
1144             if visitee.isLeaf():
1145                 self._leavesOnTasks.append(visitee)
1146             return
1147         if visitee.isLeaf():
1148             if hasattr(visitee, "_Labelable__label"):
1149                 self.l.append(self._decoration+visitee.label_())
1150             else:
1151                 error = "An object in a sequence was not found in the process\n"
1152                 if hasattr(visitee, "_filename"):
1153                     error += "From file " + visitee._filename
1154                 else:
1155                     error += "Dump follows\n" + repr(visitee)
1156                 raise RuntimeError(error)
1157         if isinstance(visitee,_BooleanLogicExpression):
1158             self.l.append(self._decoration+visitee.operatorString())
1159         if isinstance(visitee,_UnarySequenceOperator):
1160             self._decoration=visitee.decoration()
1161         else:
1162             self._decoration=''
1163 
1164     def leave(self,visitee):
1165         # Ignore if this visitee is inside a Task
1166         if self._levelInTasks > 0:
1167             if isinstance(visitee, Task):
1168                 self._levelInTasks -= 1
1169             return
1170         if isinstance(visitee,_BooleanLogicExpression):
1171             #need to add the 'go back' command to keep track of where we are in the tree
1172             self.l.append('@')
1173 
1174     def leavesOnTasks(self):
1175         return self._leavesOnTasks
1176 
1177 class _CopyAndExcludeSequenceVisitorOld(object):
1178    """Traverses a Sequence and constructs a new sequence which does not contain modules from the specified list"""
1179    def __init__(self,modulesToRemove):
1180        self.__modulesToIgnore = modulesToRemove
1181        self.__stack = list()
1182        self.__stack.append(list())
1183        self.__result = None
1184        self.__didExclude = False
1185    def enter(self,visitee):
1186        if len(self.__stack) > 0:
1187            #add visitee to its parent's stack entry
1188            self.__stack[-1].append([visitee,False])
1189        if visitee.isLeaf():
1190            if visitee in self.__modulesToIgnore:
1191                self.__didExclude = True
1192                self.__stack[-1][-1]=[None,True]
1193        elif isinstance(visitee, Sequence):
1194            if visitee in self.__modulesToIgnore:
1195                self.__didExclude = True
1196                self.__stack[-1][-1]=[None,True]
1197            self.__stack.append(list())
1198        else:
1199            #need to add a stack entry to keep track of children
1200            self.__stack.append(list())
1201    def leave(self,visitee):
1202        node = visitee
1203        if not visitee.isLeaf():
1204            #were any children changed?
1205            l = self.__stack[-1]
1206            changed = False
1207            countNulls = 0
1208            nonNulls = list()
1209            for c in l:
1210                if c[1] == True:
1211                    changed = True
1212                if c[0] is None:
1213                    countNulls +=1
1214                else:
1215                    nonNulls.append(c[0])
1216            if changed:
1217                self.__didExclude = True
1218                if countNulls != 0:
1219                    #this node must go away
1220                    if len(nonNulls) == 0:
1221                        #all subnodes went away
1222                        node = None
1223                    else:
1224                        node = nonNulls[0]
1225                        for n in nonNulls[1:]:
1226                            node = node+n
1227                else:
1228                    #some child was changed so we need to clone
1229                    # this node and replace it with one that holds
1230                    # the new child(ren)
1231                    children = [x[0] for x in l ]
1232                    if not isinstance(visitee,Sequence):
1233                        node = visitee.__new__(type(visitee))
1234                        node.__init__(*children)
1235                    else:
1236                        node = nonNulls[0]
1237        if node != visitee:
1238            #we had to replace this node so now we need to
1239            # change parent's stack entry as well
1240            if len(self.__stack) > 1:
1241                p = self.__stack[-2]
1242                #find visitee and replace
1243                for i,c in enumerate(p):
1244                    if c[0]==visitee:
1245                        c[0]=node
1246                        c[1]=True
1247                        break
1248        if not visitee.isLeaf():
1249            self.__stack = self.__stack[:-1]
1250    def result(self):
1251        result = None
1252        for n in (x[0] for x in self.__stack[0]):
1253            if n is None:
1254                continue
1255            if result is None:
1256                result = n
1257            else:
1258                result = result+n
1259        return result
1260    def didExclude(self):
1261        return self.__didExclude
1262 
1263 
1264 # This visitor can also be used on Tasks.
1265 class _MutatingSequenceVisitor(object):
1266     """Traverses a Sequence and constructs a new sequence by applying the operator to each element of the sequence"""
1267 
1268     # In many cases this operates in an intuitive manner that needs
1269     # no explanation, but there are some complex cases and I will try to
1270     # explain these in the following comments.
1271     #
1272     # First of all the top level Sequence or Task being visited may contain
1273     # many objects of different types. These contained objects are never
1274     # modified. If they are not left the same, they are instead replaced
1275     # by other instances, replaced by new instances or removed.
1276     # Contained objects are only replaced or removed when they were directly
1277     # modified or if they contain something that was modified.
1278     # If all the contents of a Sequence, Task, _SequenceNegation or _SequenceIgnore
1279     # object that is not at the top level are removed, then the containing
1280     # object is also removed.
1281     # If the contents of a Sequence other than the top level sequence are
1282     # modified, then the sequence elements and Task objects it contains get
1283     # passed up to be included in the top level sequence. If the contents of
1284     # a Task are modified, a new Task object is created and passed up to be
1285     # included in the top level Sequence or Task. If it is a _SequenceNegation
1286     # or _SequenceIgnore instance it will simply be removed completely if its
1287     # operand is removed. If the operand is replaced then a new object of the
1288     # same type will be constructed replacing the old.
1289     #
1290     # Note that if a Sequence contains a SequencePlaceholder, the future contents
1291     # of that placeholder are not affected by the changes. If that is an issue,
1292     # then you probably want to resolve the placeholders before using this
1293     # class.
1294     #
1295     # If this is used multiple times on the same sequence or task, the consequences
1296     # might interfere with one another in unusual cases.
1297     #
1298     # One example, the matching to find objects to modify is based on instances
1299     # (the python id) being the same. So if you modify the contents of a Task or
1300     # Sequence and then subsequently try to modify that Sequence or Task, then
1301     # it will either no longer exist or be a different instance and so nothing
1302     # would get modified.  Note that the one exception to this matching by instance
1303     # is _SequenceIgnore and _SequenceNegation. In that case, two objects are
1304     # recognized as matching if the contained module is the same instance instead
1305     # of requiring the _SequenceNegation or _SequenceIgnore object to be the same
1306     # instance.
1307     #
1308     # Another example. There is an input operator that removes the first instance
1309     # of an object. Applying this visitor with that operation might give unexpected
1310     # results if another operation previously changed the number of times the
1311     # that instance appears or the order it appears in the visitation. This
1312     # should only be an issue if the item is on a Task and even then only in
1313     # unusual circumstances.
1314 
1315     def __init__(self,operator):
1316         self.__operator = operator
1317         # You add a list to the __stack when entering any non-Leaf object
1318         # and pop the last element when leaving any non-Leaf object
1319         self.__stack = list()
1320         self.__stack.append(list())
1321         self.__didApply = False
1322         self.__levelInModifiedNonLeaf = 0
1323     def enter(self,visitee):
1324         # Ignore the content of replaced or removed Sequences,
1325         # Tasks, and operators.
1326         if self.__levelInModifiedNonLeaf > 0:
1327             if not visitee.isLeaf():
1328                 self.__levelInModifiedNonLeaf += 1
1329             return
1330 
1331         # Just a sanity check
1332         if not len(self.__stack) > 0:
1333             raise RuntimeError("LogicError Empty stack in MutatingSequenceVisitor.\n"
1334                                "This should never happen. Contact a Framework developer.")
1335 
1336         # The most important part.
1337         # Apply the operator that might change things, The rest
1338         # of the class is just dealing with side effects of these changes.
1339         v = self.__operator(visitee)
1340 
1341         if v is visitee:
1342             # the operator did not change the visitee
1343             # The 3 element list being appended has the following contents
1344             # element 0 - either the unmodified object, the modified object, or
1345             #   a sequence collection when it is a Sequence whose contents have
1346             #   been modified.
1347             # element 1 - Indicates whether the object was modified.
1348             # element 2 - None or a list of tasks for a Sequence
1349             #   whose contents have been modified.
1350             self.__stack[-1].append([visitee, False, None])
1351             if not visitee.isLeaf():
1352                 # need to add a list to keep track of the contents
1353                 # of the Sequence, Task, or operator we just entered.
1354                 self.__stack.append(list())
1355         else:
1356             # the operator changed the visitee
1357             self.__didApply = True
1358             self.__stack[-1].append([v, True, None])
1359             if not visitee.isLeaf():
1360                 # Set flag to indicate modified Sequence, Task, or operator
1361                 self.__levelInModifiedNonLeaf = 1
1362     def leave(self,visitee):
1363 
1364         # nothing to do for leaf types because they do not have contents
1365         if visitee.isLeaf():
1366             return
1367 
1368         # Ignore if this visitee is inside something that was already removed
1369         # or replaced.
1370         if self.__levelInModifiedNonLeaf > 0:
1371             self.__levelInModifiedNonLeaf -= 1
1372             return
1373 
1374         # Deal with visitees which have contents (Sequence, Task, _SequenceIgnore,
1375         # or _SequenceNegation) and although we know the visitee itself did not get
1376         # changed by the operator, the contents of the visitee might have been changed.
1377 
1378         # did any object inside the visitee change?
1379         contents = self.__stack[-1]
1380         changed = False
1381         allNull = True
1382         for c in contents:
1383             if c[1] == True:
1384                 changed = True
1385             if c[0] is not None:
1386                 allNull = False
1387         if changed:
1388             if allNull:
1389                 self.__stack[-2][-1] = [None, True, None]
1390 
1391             elif isinstance(visitee, _UnarySequenceOperator):
1392                 node = visitee.__new__(type(visitee))
1393                 node.__init__(contents[0][0])
1394                 self.__stack[-2][-1] = [node, True, None]
1395 
1396             elif isinstance(visitee, _TaskBase):
1397                 nonNull = []
1398                 for c in contents:
1399                     if c[0] is not None:
1400                         nonNull.append(c[0])
1401                 self.__stack[-2][-1] = [visitee._makeInstance(*nonNull), True, None]
1402             elif isinstance(visitee, Sequence):
1403                 seq = _SequenceCollection()
1404                 tasks = list()
1405                 for c in contents:
1406                     if c[0] is None:
1407                         continue
1408                     if isinstance(c[0], _TaskBase):
1409                         tasks.append(c[0])
1410                     else:
1411                         seq = seq + c[0]
1412                         if c[2] is not None:
1413                             tasks.extend(c[2])
1414 
1415                 self.__stack[-2][-1] = [seq, True, tasks]
1416 
1417         # When you exit the Sequence, Task, or operator,
1418         # drop the list which holds information about
1419         # its contents.
1420         if not visitee.isLeaf():
1421             self.__stack = self.__stack[:-1]
1422 
1423     def result(self, visitedContainer):
1424 
1425         if isinstance(visitedContainer, _TaskBase):
1426             result = list()
1427             for n in (x[0] for x in self.__stack[0]):
1428                 if n is not None:
1429                     result.append(n)
1430             return result
1431 
1432         seq = _SequenceCollection()
1433         tasks = list()
1434         for c in self.__stack[0]:
1435             if c[0] is None:
1436                 continue
1437             if isinstance(c[0], _TaskBase):
1438                 tasks.append(c[0])
1439             else:
1440                 seq = seq + c[0]
1441                 if c[2] is not None:
1442                     tasks.extend(c[2])
1443         return [seq, tasks]
1444 
1445     def _didApply(self) -> bool:
1446         return self.__didApply
1447 
1448 # This visitor can also be used on Tasks.
1449 class _CopyAndRemoveFirstSequenceVisitor(_MutatingSequenceVisitor):
1450     """Traverses a Sequence and constructs a new sequence which does not contain modules from the specified list"""
1451     def __init__(self,moduleToRemove):
1452         class _RemoveFirstOperator(object):
1453             def __init__(self,moduleToRemove):
1454                 self.__moduleToRemove = moduleToRemove
1455                 self.__found = False
1456             def __call__(self,test):
1457                 if not self.__found and test is self.__moduleToRemove:
1458                     self.__found = True
1459                     return None
1460                 return test
1461         super(type(self),self).__init__(_RemoveFirstOperator(moduleToRemove))
1462     def didRemove(self) -> bool:
1463         return self._didApply()
1464 
1465 # This visitor can also be used on Tasks.
1466 class _CopyAndExcludeSequenceVisitor(_MutatingSequenceVisitor):
1467     """Traverses a Sequence and constructs a new sequence which does not contain the module specified"""
1468     def __init__(self,modulesToRemove):
1469         class _ExcludeOperator(object):
1470             def __init__(self,modulesToRemove):
1471                 self.__modulesToIgnore = modulesToRemove
1472             def __call__(self,test):
1473                 if test in modulesToRemove:
1474                     return None
1475                 return test
1476         super(type(self),self).__init__(_ExcludeOperator(modulesToRemove))
1477     def didExclude(self) -> bool:
1478         return self._didApply()
1479 
1480 # This visitor can also be used on Tasks.
1481 class _CopyAndReplaceSequenceVisitor(_MutatingSequenceVisitor):
1482     """Traverses a Sequence and constructs a new sequence which  replaces a specified module with a different module"""
1483     def __init__(self,target,replace):
1484         class _ReplaceOperator(object):
1485             def __init__(self,target,replace):
1486                 self.__target = target
1487                 self.__replace = replace
1488             def __call__(self,test):
1489                 if test == self.__target:
1490                     return self.__replace
1491                 return test
1492         super(type(self),self).__init__(_ReplaceOperator(target,replace))
1493     def didReplace(self) -> bool:
1494         return self._didApply()
1495 
1496 class _TaskBase(_ConfigureComponent, _Labelable) :
1497     def __init__(self, *items):
1498         self._collection = OrderedSet()
1499         self.add(*items)
1500 
1501     def __setattr__(self,name:str,value):
1502         if not name.startswith("_"):
1503             raise AttributeError("You cannot set parameters for {} objects.".format(self._taskType()))
1504         else:
1505             self.__dict__[name] = value
1506 
1507     def add(self, *items):
1508         for item in items:
1509             if not self._allowedInTask(item):
1510                 raise RuntimeError("Adding an entry of type '{0}' to a {1}.\n"
1511                                    "It is illegal to add this type to a {1}.".format(type(item).__name__, self._taskType()))
1512             self._collection.add(item)
1513 
1514     def fillContents(self, taskContents, options:PrintOptions=PrintOptions()):
1515         # only dump the label, if possible
1516         if self.hasLabel_():
1517             taskContents.add(_Labelable.dumpSequencePython(self, options))
1518         else:
1519             for i in self._collection:
1520                 if isinstance(i, _TaskBase):
1521                     i.fillContents(taskContents, options)
1522                 else:
1523                     taskContents.add(i.dumpSequencePython(options))
1524 
1525     def dumpPython(self, options:PrintOptions=PrintOptions()) -> str:
1526         s = self.dumpPythonNoNewline(options)
1527         return s + "\n"
1528 
1529     def dumpPythonNoNewline(self, options:PrintOptions=PrintOptions()) -> str:
1530         """Returns a string which is the python representation of the object"""
1531         taskContents = set()
1532         for i in self._collection:
1533             if isinstance(i, _TaskBase):
1534                 i.fillContents(taskContents, options)
1535             else:
1536                 taskContents.add(i.dumpSequencePython(options))
1537         s=''
1538         iFirst = True
1539         for item in sorted(taskContents):
1540             if not iFirst:
1541                 s += ", "
1542             iFirst = False
1543             s += item
1544         if len(taskContents) > 255:
1545             s = "*[" + s + "]"
1546         return "cms.{}({})".format(self._taskType(),s)
1547 
1548     def directDependencies(self,sortByType:bool=True):
1549         return findDirectDependencies(self, self._collection,sortByType=sortByType)
1550 
1551     def _isTaskComponent(self) -> bool:
1552         return False
1553 
1554     def isLeaf(self) -> bool:
1555         return False
1556 
1557     def visit(self,visitor):
1558         for i in self._collection:
1559             visitor.enter(i)
1560             if not i.isLeaf():
1561                 i.visit(visitor)
1562             visitor.leave(i)
1563 
1564     def _errorstr(self) -> str:
1565         return "{}(...)".format(self.taskType_())
1566 
1567     def __iter__(self):
1568         for key in self._collection:
1569             yield key
1570 
1571     def __str__(self) -> str:
1572         l = []
1573         v = ModuleNodeVisitor(l)
1574         self.visit(v)
1575         s = ''
1576         for i in l:
1577             if s:
1578                 s += ', '
1579             s += str (i)
1580         return s
1581 
1582     def __repr__(self) -> str:
1583         s = str(self)
1584         return "cms."+type(self).__name__+'('+s+')\n'
1585 
1586     def moduleNames(self):
1587         """Returns a set containing the names of all modules being used"""
1588         result = set()
1589         visitor = NodeNameVisitor(result)
1590         self.visit(visitor)
1591         return result
1592     def contains(self, mod):
1593         visitor = ContainsModuleVisitor(mod)
1594         self.visit(visitor)
1595         return visitor.result()
1596     def copy(self):
1597         return self._makeInstance(*self._collection)
1598     def copyAndExclude(self,listOfModulesToExclude):
1599         """Returns a copy of the sequence which excludes those module in 'listOfModulesToExclude'"""
1600         # You can exclude instances of these types EDProducer, EDFilter, ESSource, ESProducer,
1601         # Service, or Task.
1602         # Mostly this is very intuitive, but there are some complications in cases
1603         # where objects that contain other objects are involved. See the comments
1604         # for the _MutatingSequenceVisitor.
1605         for i in listOfModulesToExclude:
1606             if not i._isTaskComponent():
1607                 raise TypeError("copyAndExclude can only exclude objects that can be placed on a Task")
1608         v = _CopyAndExcludeSequenceVisitor(listOfModulesToExclude)
1609         self.visit(v)
1610         return self._makeInstance(*v.result(self))
1611     def copyAndAdd(self, *modulesToAdd):
1612         """Returns a copy of the Task adding modules/tasks"""
1613         t = self.copy()
1614         t.add(*modulesToAdd)
1615         return t
1616     def expandAndClone(self):
1617         # Name of this function is not very good. It makes a shallow copy with all
1618         # the subTasks flattened out (removed), but keeping all the
1619         # modules that were in those subTasks as well as the top level
1620         # ones.
1621         l = []
1622         v = ModuleNodeVisitor(l)
1623         self.visit(v)
1624         return self._makeInstance(*l)
1625     def replace(self, original, replacement):
1626         """Finds all instances of 'original' and substitutes 'replacement' for them.
1627            Returns 'True' if a replacement occurs."""
1628         # This works for either argument being of type EDProducer, EDFilter, ESProducer,
1629         # ESSource, Service, or Task.
1630         #
1631         # Mostly this is very intuitive, but there are some complications in cases
1632         # where objects that contain other objects are involved. See the comments
1633         # for the _MutatingSequenceVisitor.
1634 
1635         if not self._allowedInTask(original) or (not replacement is None and not self._allowedInTask(replacement)):
1636            raise TypeError("The {0} replace function only works with objects that can be placed on a {0}\n".format(self._taskType()) + \
1637                            "           replace was called with original type = {}\n".format(str(type(original))) + \
1638                            "           and replacement type = {}\n".format(str(type(replacement))))
1639         else:
1640             v = _CopyAndReplaceSequenceVisitor(original,replacement)
1641             self.visit(v)
1642             if v.didReplace():
1643                 self._collection.clear()
1644                 self.add(*v.result(self))
1645             return v.didReplace()
1646 
1647     def remove(self, something):
1648         """Remove the first occurrence of a module
1649            Returns 'True' if the module has been removed, False if it was not found"""
1650         # You can remove instances of these types EDProducer, EDFilter, ESSource,
1651         # ESProducer, Service, or Task,
1652         #
1653         # Mostly this is very intuitive, but there are some complications in cases
1654         # where objects that contain other objects are involved. See the comments
1655         # for the _MutatingSequenceVisitor.
1656         #
1657         # Works very similar to copyAndExclude, there are 2 differences. This changes
1658         # the object itself instead of making a copy and second it only removes
1659         # the first instance of the argument instead of all of them.
1660         if not self._allowedInTask(something):
1661            raise TypeError("remove only works with objects that can be placed on a Task")
1662         v = _CopyAndRemoveFirstSequenceVisitor(something)
1663         self.visit(v)
1664         if v.didRemove():
1665             self._collection.clear()
1666             self.add(*v.result(self))
1667         return v.didRemove()
1668 
1669     def resolve(self, processDict,keepIfCannotResolve:bool=False):
1670         temp = OrderedSet()
1671         for i in self._collection:
1672             if self._mustResolve(i):
1673                 temp.add(i.resolve(processDict,keepIfCannotResolve))
1674             else:
1675                 temp.add(i)
1676         self._collection = temp
1677         return self
1678         
1679 class _TaskBasePlaceholder(object):
1680     def __init__(self, name:str):
1681         self._name = name
1682     def _isTaskComponent(self) -> bool:
1683         return False
1684     def isLeaf(self) -> bool:
1685         return False
1686     def visit(self,visitor):
1687         pass
1688     def __str__(self) -> str:
1689         return self._name
1690     def insertInto(self, parameterSet, myname):
1691         raise RuntimeError("The {} {} was never overridden".format(self._typeName(), self._name))
1692     def resolve(self, processDict,keepIfCannotResolve:bool=False):
1693         if not self._name in processDict:
1694             if keepIfCannotResolve:
1695                 return self
1696             raise RuntimeError("The {} {} cannot be resolved.\n Known keys are: {}".format(self._typeName(), self._name,str(processDict.keys())))
1697         o = processDict[self._name]
1698         if not self._allowedInTask(o):
1699             raise RuntimeError("The {} {} refers to an object type which is not allowed to be on a task: {}".format(self._typeName(), self._name, str(type(o))))
1700         if isinstance(o, self._taskClass()):
1701             return o.resolve(processDict)
1702         return o
1703     def copy(self):
1704         return self._makeInstance(self._name)
1705     def dumpSequencePython(self, options:PrintOptions=PrintOptions()) -> str:
1706         return 'cms.{}("{}")'.format(self._typeName(), self._name)
1707     def dumpPython(self, options:PrintOptions=PrintOptions()) -> str:
1708         result = 'cms.{}(\"'.format(self._typeName())
1709         if options.isCfg:
1710            result += 'process.'
1711         result += self._name+'\")\n'
1712         return result
1713 
1714 class Task(_TaskBase) :
1715     """Holds EDProducers, EDFilters, ESProducers, ESSources, Services, and Tasks.
1716     A Task can be associated with Sequences, Paths, EndPaths, ConditionalTasks and the Schedule.
1717     An EDProducer or EDFilter will be enabled to run unscheduled if it is on
1718     a task associated with the Schedule or any scheduled Path or EndPath (directly
1719     or indirectly through Sequences) and not be on any scheduled Path or EndPath.
1720     ESSources, ESProducers, and Services will be enabled to run if they are on
1721     a Task associated with the Schedule or a scheduled Path or EndPath.  In other
1722     cases, they will be enabled to run if and only if they are not on a Task attached
1723     to the process.
1724     """
1725     @staticmethod
1726     def _taskType() -> str:
1727       return "Task"
1728     def _place(self, name:str, proc):
1729         proc._placeTask(name,self)
1730     def _isTaskComponent(self) -> bool:
1731         return True
1732     @staticmethod
1733     def _makeInstance(*items):
1734       return Task(*items)
1735     @staticmethod
1736     def _allowedInTask(item ) -> bool:
1737       return (isinstance(item, _ConfigureComponent) and item._isTaskComponent()) or isinstance(item, TaskPlaceholder)
1738     @staticmethod
1739     def _mustResolve(item) -> bool:
1740         return isinstance(item, Task) or isinstance(item, TaskPlaceholder)
1741       
1742 class TaskPlaceholder(_TaskBasePlaceholder):
1743     def _isTaskComponent(self) -> bool:
1744         return True
1745     @staticmethod
1746     def _typeName() -> str:
1747       return "TaskPlaceholder"
1748     @staticmethod
1749     def _makeInstance(name):
1750       return TaskPlaceholder(name)
1751     @staticmethod
1752     def _allowedInTask(obj) -> bool:
1753       return Task._allowedInTask(obj)
1754     @staticmethod
1755     def _taskClass():
1756       return Task
1757 
1758 
1759 class ConditionalTask(_TaskBase) :
1760     """Holds EDProducers, EDFilters, ESProducers, ESSources, Services, Tasks and ConditionalTasks.
1761     A ConditionalTask can be associated with Sequences, Paths, and EndPaths.
1762     An EDProducer or EDFilter will be added to a Path or EndPath based on which other
1763     modules on the Path consumes its data products. If that ConditionalTask assigned module
1764     is placed after an EDFilter, the module will only run if the EDFilter passes. If no module
1765     on the Path needs the module's data products, the module will be treated as if it were on a Task.
1766     """
1767     @staticmethod
1768     def _taskType():
1769       return "ConditionalTask"
1770     def _place(self, name:str, proc):
1771         proc._placeConditionalTask(name,self)
1772     def _isTaskComponent(self) -> bool:
1773         return False
1774     @staticmethod
1775     def _makeInstance(*items):
1776       return ConditionalTask(*items)
1777     @staticmethod
1778     def _allowedInTask(item) -> bool:
1779       return isinstance(item, ConditionalTask) or isinstance(item, ConditionalTaskPlaceholder) or Task._allowedInTask(item)
1780     @staticmethod
1781     def _mustResolve(item) -> bool:
1782         return Task._mustResolve(item) or isinstance(item, ConditionalTask) or isinstance(item, ConditionalTaskPlaceholder)
1783 
1784 
1785 class ConditionalTaskPlaceholder(_TaskBasePlaceholder):
1786     def _isTaskComponent(self) -> bool:
1787         return False
1788     @staticmethod
1789     def _typeName() -> str:
1790       return "ConditionalTaskPlaceholder"
1791     @staticmethod
1792     def _makeInstance(name):
1793       return ConditionalTaskPlaceholder(name)
1794     @staticmethod
1795     def _allowedInTask(obj) -> bool:
1796       return Task._allowedInTask(obj) or ConditionalTask._allowedInTask(obj)
1797     @staticmethod
1798     def _taskClass():
1799       return ConditionalTask
1800 
1801 
1802 if __name__=="__main__":
1803     import unittest
1804     class DummyModule(_Labelable, _SequenceLeaf, _ConfigureComponent):
1805         def __init__(self,name):
1806             self.setLabel(name)
1807         def _isTaskComponent(self):
1808             return True
1809         def __repr__(self):
1810             return self.label_()
1811     class DummyBooleanModule(_Labelable, _BooleanLogicSequenceLeaf):
1812         def __init__(self,name):
1813             self.setLabel(name)
1814     class TestModuleCommand(unittest.TestCase):
1815         def setUp(self):
1816             """Nothing to do """
1817             pass
1818         def testBoolean(self):
1819             a = DummyBooleanModule("a")
1820             b = DummyBooleanModule("b")
1821             p = Path( a & b)
1822             self.assertEqual(p.dumpPython(),"cms.Path(process.a&process.b)\n")
1823             l = list()
1824             namesVisitor = DecoratedNodeNameVisitor(l)
1825             p.visit(namesVisitor)
1826             self.assertEqual(l,['&','a','b','@'])
1827             p2 = Path( a | b)
1828             self.assertEqual(p2.dumpPython(),"cms.Path(process.a|process.b)\n")
1829             l[:]=[]
1830             p2.visit(namesVisitor)
1831             self.assertEqual(l,['|','a','b','@'])
1832             c = DummyBooleanModule("c")
1833             d = DummyBooleanModule("d")
1834             p3 = Path(a & b & c & d)
1835             self.assertEqual(p3.dumpPython(),"cms.Path(process.a&process.b&process.c&process.d)\n")
1836             l[:]=[]
1837             p3.visit(namesVisitor)
1838             self.assertEqual(l,['&','a','b','c','d','@'])
1839             p3 = Path(((a & b) & c) & d)
1840             self.assertEqual(p3.dumpPython(),"cms.Path(process.a&process.b&process.c&process.d)\n")
1841             p3 = Path(a & (b & (c & d)))
1842             self.assertEqual(p3.dumpPython(),"cms.Path(process.a&process.b&process.c&process.d)\n")
1843             p3 = Path((a & b) & (c & d))
1844             self.assertEqual(p3.dumpPython(),"cms.Path(process.a&process.b&process.c&process.d)\n")
1845             p3 = Path(a & (b & c) & d)
1846             self.assertEqual(p3.dumpPython(),"cms.Path(process.a&process.b&process.c&process.d)\n")
1847             p4 = Path(a | b | c | d)
1848             self.assertEqual(p4.dumpPython(),"cms.Path(process.a|process.b|process.c|process.d)\n")
1849             p5 = Path(a | b & c & d )
1850             self.assertEqual(p5.dumpPython(),"cms.Path(process.a|(process.b&process.c&process.d))\n")
1851             l[:]=[]
1852             p5.visit(namesVisitor)
1853             self.assertEqual(l,['|','a','&','b','c','d','@','@'])
1854             p5 = Path(a & b | c & d )
1855             self.assertEqual(p5.dumpPython(),"cms.Path((process.a&process.b)|(process.c&process.d))\n")
1856             l[:]=[]
1857             p5.visit(namesVisitor)
1858             self.assertEqual(l,['|','&','a','b','@','&','c','d','@','@'])
1859             p5 = Path(a & (b | c) & d )
1860             self.assertEqual(p5.dumpPython(),"cms.Path(process.a&(process.b|process.c)&process.d)\n")
1861             l[:]=[]
1862             p5.visit(namesVisitor)
1863             self.assertEqual(l,['&','a','|','b','c','@','d','@'])
1864             p5 = Path(a & b & c | d )
1865             self.assertEqual(p5.dumpPython(),"cms.Path((process.a&process.b&process.c)|process.d)\n")
1866             l[:]=[]
1867             p5.visit(namesVisitor)
1868             self.assertEqual(l,['|','&','a','b','c','@','d','@'])
1869             p6 = Path( a & ~b)
1870             self.assertEqual(p6.dumpPython(),"cms.Path(process.a&(~process.b))\n")
1871             l[:]=[]
1872             p6.visit(namesVisitor)
1873             self.assertEqual(l,['&','a','!b','@'])
1874             p6 = Path( a & ignore(b))
1875             self.assertEqual(p6.dumpPython(),"cms.Path(process.a&(cms.ignore(process.b)))\n")
1876             l[:]=[]
1877             p6.visit(namesVisitor)
1878             self.assertEqual(l,['&','a','-b','@'])
1879             p6 = Path( a & wait(b))
1880             self.assertEqual(p6.dumpPython(),"cms.Path(process.a&(cms.wait(process.b)))\n")
1881             l[:]=[]
1882             p6.visit(namesVisitor)
1883             self.assertEqual(l,['&','a','|b','@'])
1884             p6 = Path( a & wait(ignore(b)))
1885             self.assertEqual(p6.dumpPython(),"cms.Path(process.a&(cms.wait(cms.ignore(process.b))))\n")
1886             l[:]=[]
1887             p6.visit(namesVisitor)
1888             self.assertEqual(l,['&','a','+b','@'])
1889             p6 = Path( a & ignore(wait(b)))
1890             self.assertEqual(p6.dumpPython(),"cms.Path(process.a&(cms.wait(cms.ignore(process.b))))\n")
1891             l[:]=[]
1892             p6.visit(namesVisitor)
1893             self.assertEqual(l,['&','a','+b','@'])
1894             p6 = Path(~(a&b))
1895             self.assertEqual(p6.dumpPython(),"cms.Path(~(process.a&process.b))\n")
1896             l[:]=[]
1897             p6.visit(namesVisitor)
1898             self.assertEqual(l,['!&','a','b','@'])
1899 
1900         def testTaskConstructor(self):
1901             a = DummyModule("a")
1902             self.assertRaises(RuntimeError, lambda : Task(ConditionalTask(a)) )
1903 
1904         def testDumpPython(self):
1905             a = DummyModule("a")
1906             b = DummyModule('b')
1907             p = Path((a*b))
1908             #print p.dumpConfig('')
1909             self.assertEqual(p.dumpPython(),"cms.Path(process.a+process.b)\n")
1910             p2 = Path((b+a))
1911             #print p2.dumpConfig('')
1912             self.assertEqual(p2.dumpPython(),"cms.Path(process.b+process.a)\n")
1913             c = DummyModule('c')
1914             p3 = Path(c*(a+b))
1915             #print p3.dumpConfig('')
1916             self.assertEqual(p3.dumpPython(),"cms.Path(process.c+process.a+process.b)\n")
1917             p4 = Path(c*a+b)
1918             #print p4.dumpConfig('')
1919             self.assertEqual(p4.dumpPython(),"cms.Path(process.c+process.a+process.b)\n")
1920             p5 = Path(a+ignore(b))
1921             #print p5.dumpConfig('')
1922             self.assertEqual(p5.dumpPython(),"cms.Path(process.a+cms.ignore(process.b))\n")
1923             p5a = Path(a+wait(b))
1924             self.assertEqual(p5a.dumpPython(),"cms.Path(process.a+cms.wait(process.b))\n")
1925             p5b = Path(a+ignore(wait(b)))
1926             self.assertEqual(p5b.dumpPython(),"cms.Path(process.a+cms.wait(cms.ignore(process.b)))\n")
1927             p5c = Path(a+wait(ignore(b)))
1928             self.assertEqual(p5c.dumpPython(),"cms.Path(process.a+cms.wait(cms.ignore(process.b)))\n")
1929             p6 = Path(c+a*b)
1930             #print p6.dumpConfig('')
1931             self.assertEqual(p6.dumpPython(),"cms.Path(process.c+process.a+process.b)\n")
1932             p7 = Path(a+~b)
1933             self.assertEqual(p7.dumpPython(),"cms.Path(process.a+~process.b)\n")
1934             p8 = Path((a+b)*c)
1935             self.assertEqual(p8.dumpPython(),"cms.Path(process.a+process.b+process.c)\n")
1936             t1 = Task(a)
1937             t2 = Task(c, b)
1938             t3 = Task()
1939             p9 = Path((a+b)*c, t1)
1940             self.assertEqual(p9.dumpPython(),"cms.Path(process.a+process.b+process.c, cms.Task(process.a))\n")
1941             p10 = Path((a+b)*c, t2, t1)
1942             self.assertEqual(p10.dumpPython(),"cms.Path(process.a+process.b+process.c, cms.Task(process.a), cms.Task(process.b, process.c))\n")
1943             p11 = Path(t1, t2, t3)
1944             self.assertEqual(p11.dumpPython(),"cms.Path(cms.Task(), cms.Task(process.a), cms.Task(process.b, process.c))\n")
1945             d = DummyModule("d")
1946             e = DummyModule('e')
1947             f = DummyModule('f')
1948             t4 = Task(d, Task(f))
1949             s = Sequence(e, t4)
1950             p12 = Path(a+b+s+c,t1)
1951             self.assertEqual(p12.dumpPython(),"cms.Path(process.a+process.b+cms.Sequence(process.e, cms.Task(process.d, process.f))+process.c, cms.Task(process.a))\n")
1952             ct1 = ConditionalTask(a)
1953             ct2 = ConditionalTask(c, b)
1954             ct3 = ConditionalTask()
1955             p13 = Path((a+b)*c, ct1)
1956             self.assertEqual(p13.dumpPython(),"cms.Path(process.a+process.b+process.c, cms.ConditionalTask(process.a))\n")
1957             p14 = Path((a+b)*c, ct2, ct1)
1958             self.assertEqual(p14.dumpPython(),"cms.Path(process.a+process.b+process.c, cms.ConditionalTask(process.a), cms.ConditionalTask(process.b, process.c))\n")
1959             p15 = Path(ct1, ct2, ct3)
1960             self.assertEqual(p15.dumpPython(),"cms.Path(cms.ConditionalTask(), cms.ConditionalTask(process.a), cms.ConditionalTask(process.b, process.c))\n")
1961             ct4 = ConditionalTask(d, Task(f))
1962             s = Sequence(e, ct4)
1963             p16 = Path(a+b+s+c,ct1)
1964             self.assertEqual(p16.dumpPython(),"cms.Path(process.a+process.b+cms.Sequence(process.e, cms.ConditionalTask(process.d, process.f))+process.c, cms.ConditionalTask(process.a))\n")
1965 
1966             n = 260
1967             mods = []
1968             labels = []
1969             for i in range(0, n):
1970                 l = "a{}".format(i)
1971                 labels.append("process."+l)
1972                 mods.append(DummyModule(l))
1973             labels.sort()
1974             task = Task(*mods)
1975             self.assertEqual(task.dumpPython(), "cms.Task(*[" + ", ".join(labels) + "])\n")
1976             conditionalTask = ConditionalTask(*mods)
1977             self.assertEqual(conditionalTask.dumpPython(), "cms.ConditionalTask(*[" + ", ".join(labels) + "])\n")
1978 
1979             l = list()
1980             namesVisitor = DecoratedNodeNameVisitor(l)
1981             p.visit(namesVisitor)
1982             self.assertEqual(l, ['a', 'b'])
1983             l[:] = []
1984             p5.visit(namesVisitor)
1985             self.assertEqual(l, ['a', '-b'])
1986             l[:] = []
1987             p5a.visit(namesVisitor)
1988             self.assertEqual(l, ['a', '|b'])
1989             l[:] = []
1990             p5b.visit(namesVisitor)
1991             self.assertEqual(l, ['a', '+b'])
1992             l[:] = []
1993             p5c.visit(namesVisitor)
1994             self.assertEqual(l, ['a', '+b'])
1995             l[:] = []
1996             p7.visit(namesVisitor)
1997             self.assertEqual(l, ['a', '!b'])
1998             l[:] = []
1999             p10.visit(namesVisitor)
2000             self.assertEqual(l, ['a', 'b', 'c'])
2001             l[:] = []
2002             p12.visit(namesVisitor)
2003             self.assertEqual(l, ['a', 'b', 'e', 'c'])
2004             l[:] = []
2005             p16.visit(namesVisitor)
2006             self.assertEqual(l, ['a', 'b', 'e', 'c'])
2007             l[:] = []
2008             moduleVisitor = ModuleNodeVisitor(l)
2009             p8.visit(moduleVisitor)
2010             names = [m.label_() for m in l]
2011             self.assertEqual(names, ['a', 'b', 'c'])
2012             tph = TaskPlaceholder('a')
2013             self.assertEqual(tph.dumpPython(), 'cms.TaskPlaceholder("process.a")\n')
2014             sph = SequencePlaceholder('a')
2015             self.assertEqual(sph.dumpPython(), 'cms.SequencePlaceholder("process.a")\n')
2016             ctph = ConditionalTaskPlaceholder('a')
2017             self.assertEqual(ctph.dumpPython(), 'cms.ConditionalTaskPlaceholder("process.a")\n')
2018 
2019         def testDumpConfig(self):
2020             a = DummyModule("a")
2021             b = DummyModule('b')
2022             p = Path((a*b))
2023             #print p.dumpConfig('')
2024             self.assertEqual(p.dumpConfig(None),"{a&b}\n")
2025             p2 = Path((b+a))
2026             #print p2.dumpConfig('')
2027             self.assertEqual(p2.dumpConfig(None),"{b&a}\n")
2028             c = DummyModule('c')
2029             p3 = Path(c*(a+b))
2030             #print p3.dumpConfig('')
2031             self.assertEqual(p3.dumpConfig(None),"{c&a&b}\n")
2032             p4 = Path(c*a+b)
2033             #print p4.dumpConfig('')
2034             self.assertEqual(p4.dumpConfig(None),"{c&a&b}\n")
2035             p5 = Path(a+ignore(b))
2036             #print p5.dumpConfig('')
2037             self.assertEqual(p5.dumpConfig(None),"{a&-b}\n")
2038             p6 = Path(c+a*b)
2039             #print p6.dumpConfig('')
2040             self.assertEqual(p6.dumpConfig(None),"{c&a&b}\n")
2041             p7 = Path(a+~b)
2042             self.assertEqual(p7.dumpConfig(None),"{a&!b}\n")
2043             p8 = Path((a+b)*c)
2044             self.assertEqual(p8.dumpConfig(None),"{a&b&c}\n")
2045         def testVisitor(self):
2046             class TestVisitor(object):
2047                 def __init__(self, enters, leaves):
2048                     self._enters = enters
2049                     self._leaves = leaves
2050                 def enter(self,visitee):
2051                     #print visitee.dumpSequencePython()
2052                     if self._enters[0] != visitee:
2053                         raise RuntimeError("wrong node ("+str(visitee)+") on 'enter'")
2054                     else:
2055                         self._enters = self._enters[1:]
2056                 def leave(self,visitee):
2057                     if self._leaves[0] != visitee:
2058                         raise RuntimeError("wrong node ("+str(visitee)+") on 'leave'\n expected ("+str(self._leaves[0])+")")
2059                     else:
2060                         self._leaves = self._leaves[1:]
2061             a = DummyModule("a")
2062             b = DummyModule('b')
2063             multAB = a*b
2064             p = Path(multAB)
2065             t = TestVisitor(enters=[a,b],
2066                             leaves=[a,b])
2067             p.visit(t)
2068 
2069             plusAB = a+b
2070             p = Path(plusAB)
2071             t = TestVisitor(enters=[a,b],
2072                             leaves=[a,b])
2073             p.visit(t)
2074 
2075             c=DummyModule("c")
2076             d=DummyModule("d")
2077             e=DummyModule("e")
2078             f=DummyModule("f")
2079             g=DummyModule("g")
2080             ct1 = ConditionalTask(d)
2081             ct2 = ConditionalTask(e, ct1)
2082             ct3 = ConditionalTask(f, g, ct2)
2083             s=Sequence(plusAB, ct3, ct2)
2084             multSC = s*c
2085             p=Path(multSC, ct1, ct2)
2086             l = []
2087             v = ModuleNodeVisitor(l)
2088             p.visit(v)
2089             expected = [a,b,f,g,e,d,e,d,c,d,e,d]
2090             self.assertEqual(expected,l)
2091 
2092 
2093             t1 = Task(d)
2094             t2 = Task(e, t1)
2095             t3 = Task(f, g, t2)
2096             s=Sequence(plusAB, t3, t2)
2097             multSC = s*c
2098             p=Path(multSC, t1, t2)
2099 
2100             l = []
2101             v = ModuleNodeVisitor(l)
2102             p.visit(v)
2103             expected = [a,b,f,g,e,d,e,d,c,d,e,d]
2104             self.assertEqual(expected,l)
2105 
2106             l[:] = []
2107             v = ModuleNodeOnTaskVisitor(l)
2108             p.visit(v)
2109             expected = [f,g,e,d,e,d,d,e,d]
2110             self.assertEqual(expected,l)
2111 
2112             l[:] = []
2113             v = ModuleNodeNotOnTaskVisitor(l)
2114             p.visit(v)
2115             expected = [a,b,c]
2116             self.assertEqual(expected,l)
2117 
2118 
2119             t=TestVisitor(enters=[s,a,b,t3,f,g,t2,e,t1,d,t2,e,t1,d,c,t1,d,t2,e,t1,d],
2120                           leaves=[a,b,f,g,e,d,t1,t2,t3,e,d,t1,t2,s,c,d,t1,e,d,t1,t2])
2121             p.visit(t)
2122 
2123             notA= ~a
2124             p=Path(notA)
2125             t=TestVisitor(enters=[notA,a],leaves=[a,notA])
2126             p.visit(t)
2127         def testResolve(self):
2128             m1 = DummyModule("m1")
2129             m2 = DummyModule("m2")
2130             s1 = Sequence(m1)
2131             s2 = SequencePlaceholder("s3")
2132             s3 = Sequence(m2)
2133             p = Path(s1*s2)
2134             l = list()
2135             #resolver = ResolveVisitor(d)
2136             #p.visit(resolver)
2137             namesVisitor = DecoratedNodeNameVisitor(l)
2138             p.visit(namesVisitor)
2139             self.assertEqual(l, ['m1'])
2140             p.resolve(dict(s1=s1, s2=s2, s3=s3))
2141             l[:] = []
2142             p.visit(namesVisitor)
2143             self.assertEqual(l, ['m1', 'm2'])
2144             l[:]=[]
2145             s1 = Sequence(m1)
2146             s2 = SequencePlaceholder("s3")
2147             s3 = Sequence(m2)
2148             s4 = SequencePlaceholder("s2")
2149             p=Path(s1+s4)
2150             p.resolve(dict(s1=s1, s2=s2, s3=s3, s4=s4))
2151             p.visit(namesVisitor)
2152             self.assertEqual(l, ['m1', 'm2'])
2153             l[:]=[]
2154             m3 = DummyModule("m3")
2155             m4 = DummyModule("m4")
2156             s1 = Sequence(~m1)
2157             s2 = SequencePlaceholder("s3")
2158             s3 = Sequence(ignore(m2))
2159             s4 = Sequence(wait(m3) + ignore(wait(m4)))
2160             d = dict(s1=s1, s2=s2, s3=s3, s4=s4)
2161             p = Path(s1*s2*s4)
2162             p.resolve(dict(s1=s1, s2=s2, s3=s3, s4=s4))
2163             p.visit(namesVisitor)
2164             self.assertEqual(l, ['!m1', '-m2', '|m3', '+m4'])
2165         def testReplace(self):
2166             m1 = DummyModule("m1")
2167             m2 = DummyModule("m2")
2168             m3 = DummyModule("m3")
2169             m4 = DummyModule("m4")
2170             m5 = DummyModule("m5")
2171 
2172             s1 = Sequence(m1*~m2*m1*m2*ignore(m2))
2173             s2 = Sequence(m1*m2)
2174             l = []
2175             namesVisitor = DecoratedNodeNameVisitor(l)
2176             s1.visit(namesVisitor)
2177             self.assertEqual(l,['m1', '!m2', 'm1', 'm2', '-m2'])
2178 
2179             s3 = Sequence(~m1*s2)
2180             s3.replace(~m1, m2)
2181             l[:] = []
2182             s3.visit(namesVisitor)
2183             self.assertEqual(l, ['m2', 'm1', 'm2'])
2184             s3.replace(m2, ~m1)
2185             l[:] = []
2186             s3.visit(namesVisitor)
2187             self.assertEqual(l, ['!m1', 'm1', '!m1'])
2188 
2189             s3 = Sequence(ignore(m1)*s2)
2190             s3.replace(ignore(m1), m2)
2191             l[:] = []
2192             s3.visit(namesVisitor)
2193             self.assertEqual(l, ['m2', 'm1', 'm2'])
2194             s3.replace(m2, ignore(m1))
2195             l[:] = []
2196             s3.visit(namesVisitor)
2197             self.assertEqual(l, ['-m1', 'm1', '-m1'])
2198 
2199             ph = SequencePlaceholder('x')
2200             s4 = Sequence(Sequence(ph))
2201             s4.replace(ph,m2)
2202             self.assertEqual(s4.dumpPython(), "cms.Sequence(process.m2)\n")
2203 
2204             s1.replace(m2,m3)
2205             l[:] = []
2206             s1.visit(namesVisitor)
2207             self.assertEqual(l,['m1', '!m3', 'm1', 'm3', '-m3'])
2208             s2 = Sequence(m1*m2)
2209             s3 = Sequence(~m1*s2)
2210             l[:] = []
2211             s3.visit(namesVisitor)
2212             self.assertEqual(l,['!m1', 'm1', 'm2'])
2213             l[:] = []
2214             s3.replace(s2,m1)
2215             s3.visit(namesVisitor)
2216             self.assertEqual(l,['!m1', 'm1'])
2217 
2218             s1 = Sequence(m1+m2)
2219             s2 = Sequence(m3+m4)
2220             s3 = Sequence(s1+s2)
2221             s3.replace(m3,m5)
2222             l[:] = []
2223             s3.visit(namesVisitor)
2224             self.assertEqual(l,['m1','m2','m5','m4'])
2225 
2226             m6 = DummyModule("m6")
2227             m7 = DummyModule("m7")
2228             m8 = DummyModule("m8")
2229             m9 = DummyModule("m9")
2230 
2231             #Task
2232             t6 = Task(m6)
2233             t7 = Task(m7)
2234             t89 = Task(m8, m9)
2235 
2236             s1 = Sequence(m1+m2, t6)
2237             s2 = Sequence(m3+m4, t7)
2238             s3 = Sequence(s1+s2, t89)
2239             s3.replace(m3,m5)
2240             l[:] = []
2241             s3.visit(namesVisitor)
2242             self.assertEqual(l,['m1','m2','m5','m4'])
2243 
2244             s3.replace(m8,m1)
2245             self.assertTrue(s3.dumpPython() == "cms.Sequence(cms.Sequence(process.m1+process.m2, cms.Task(process.m6))+process.m5+process.m4, cms.Task(process.m1, process.m9), cms.Task(process.m7))\n")
2246 
2247             s3.replace(m1,m7)
2248             self.assertTrue(s3.dumpPython() == "cms.Sequence(process.m7+process.m2+process.m5+process.m4, cms.Task(process.m6), cms.Task(process.m7), cms.Task(process.m7, process.m9))\n")
2249             result = s3.replace(t7, t89)
2250             self.assertTrue(s3.dumpPython() == "cms.Sequence(process.m7+process.m2+process.m5+process.m4, cms.Task(process.m6), cms.Task(process.m7, process.m9), cms.Task(process.m8, process.m9))\n")
2251             self.assertTrue(result)
2252             result = s3.replace(t7, t89)
2253             self.assertFalse(result)
2254 
2255             t1 = Task()
2256             t1.replace(m1,m2)
2257             self.assertTrue(t1.dumpPython() == "cms.Task()\n")
2258 
2259             t1 = Task(m1)
2260             t1.replace(m1,m2)
2261             self.assertTrue(t1.dumpPython() == "cms.Task(process.m2)\n")
2262 
2263             t1 = Task(m1,m2, m2)
2264             t1.replace(m2,m3)
2265             self.assertTrue(t1.dumpPython() == "cms.Task(process.m1, process.m3)\n")
2266 
2267             t1 = Task(m1,m2)
2268             t2 = Task(m1,m3,t1)
2269             t2.replace(m1,m4)
2270             self.assertTrue(t2.dumpPython() == "cms.Task(process.m2, process.m3, process.m4)\n")
2271 
2272             t1 = Task(m2)
2273             t2 = Task(m1,m3,t1)
2274             t2.replace(m1,m4)
2275             self.assertTrue(t2.dumpPython() == "cms.Task(process.m2, process.m3, process.m4)\n")
2276 
2277             t1 = Task(m2)
2278             t2 = Task(m1,m3,t1)
2279             t2.replace(t1,m4)
2280             self.assertTrue(t2.dumpPython() == "cms.Task(process.m1, process.m3, process.m4)\n")
2281 
2282             t1 = Task(m2)
2283             t2 = Task(m1,m3,t1)
2284             t3 = Task(m5)
2285             t2.replace(m2,t3)
2286             self.assertTrue(t2.dumpPython() == "cms.Task(process.m1, process.m3, process.m5)\n")
2287 
2288             #ConditionalTask
2289             ct6 = ConditionalTask(m6)
2290             ct7 = ConditionalTask(m7)
2291             ct89 = ConditionalTask(m8, m9)
2292 
2293             cs1 = Sequence(m1+m2, ct6)
2294             cs2 = Sequence(m3+m4, ct7)
2295             cs3 = Sequence(cs1+cs2, ct89)
2296             cs3.replace(m3,m5)
2297             l[:] = []
2298             cs3.visit(namesVisitor)
2299             self.assertEqual(l,['m1','m2','m5','m4'])
2300 
2301             cs3.replace(m8,m1)
2302             self.assertEqual(cs3.dumpPython(), "cms.Sequence(cms.Sequence(process.m1+process.m2, cms.ConditionalTask(process.m6))+process.m5+process.m4, cms.ConditionalTask(process.m1, process.m9), cms.ConditionalTask(process.m7))\n")
2303 
2304             cs3.replace(m1,m7)
2305             self.assertEqual(cs3.dumpPython(), "cms.Sequence(process.m7+process.m2+process.m5+process.m4, cms.ConditionalTask(process.m6), cms.ConditionalTask(process.m7), cms.ConditionalTask(process.m7, process.m9))\n")
2306             result = cs3.replace(ct7, ct89)
2307             self.assertEqual(cs3.dumpPython(), "cms.Sequence(process.m7+process.m2+process.m5+process.m4, cms.ConditionalTask(process.m6), cms.ConditionalTask(process.m7, process.m9), cms.ConditionalTask(process.m8, process.m9))\n")
2308             self.assertTrue(result)
2309             result = cs3.replace(ct7, ct89)
2310             self.assertFalse(result)
2311 
2312             ct1 = ConditionalTask()
2313             ct1.replace(m1,m2)
2314             self.assertEqual(ct1.dumpPython(), "cms.ConditionalTask()\n")
2315 
2316             ct1 = ConditionalTask(m1)
2317             ct1.replace(m1,m2)
2318             self.assertEqual(ct1.dumpPython(), "cms.ConditionalTask(process.m2)\n")
2319 
2320             ct1 = ConditionalTask(m1,m2, m2)
2321             ct1.replace(m2,m3)
2322             self.assertEqual(ct1.dumpPython(), "cms.ConditionalTask(process.m1, process.m3)\n")
2323 
2324             ct1 = ConditionalTask(m1,m2)
2325             ct2 = ConditionalTask(m1,m3,ct1)
2326             ct2.replace(m1,m4)
2327             self.assertEqual(ct2.dumpPython(), "cms.ConditionalTask(process.m2, process.m3, process.m4)\n")
2328 
2329             ct1 = ConditionalTask(m2)
2330             ct2 = ConditionalTask(m1,m3,ct1)
2331             ct2.replace(m1,m4)
2332             self.assertEqual(ct2.dumpPython(), "cms.ConditionalTask(process.m2, process.m3, process.m4)\n")
2333 
2334             ct1 = ConditionalTask(m2)
2335             ct2 = ConditionalTask(m1,m3,ct1)
2336             ct2.replace(ct1,m4)
2337             self.assertEqual(ct2.dumpPython(), "cms.ConditionalTask(process.m1, process.m3, process.m4)\n")
2338 
2339             ct1 = ConditionalTask(m2)
2340             ct2 = ConditionalTask(m1,m3,ct1)
2341             ct3 = ConditionalTask(m5)
2342             ct2.replace(m2,ct3)
2343             self.assertEqual(ct2.dumpPython(), "cms.ConditionalTask(process.m1, process.m3, process.m5)\n")
2344 
2345         def testReplaceIfHeldDirectly(self):
2346             m1 = DummyModule("m1")
2347             m2 = DummyModule("m2")
2348             m3 = DummyModule("m3")
2349             m4 = DummyModule("m4")
2350             m5 = DummyModule("m5")
2351 
2352             s1 = Sequence(m1*~m2*m1*m2*ignore(m2))
2353             s1._replaceIfHeldDirectly(m2,m3)
2354             self.assertEqual(s1.dumpPython()[:-1],
2355                              "cms.Sequence(process.m1+~process.m3+process.m1+process.m3+cms.ignore(process.m3))")
2356 
2357             s2 = Sequence(m1*m2)
2358             l = []
2359             s3 = Sequence(~m1*s2)
2360             s3._replaceIfHeldDirectly(~m1, m2)
2361             self.assertEqual(s3.dumpPython()[:-1],
2362                              "cms.Sequence(process.m2+(process.m1+process.m2))")
2363             #Task
2364             m6 = DummyModule("m6")
2365             m7 = DummyModule("m7")
2366             m8 = DummyModule("m8")
2367             m9 = DummyModule("m9")
2368             t6 = Task(m6)
2369             t7 = Task(m7)
2370             t89 = Task(m8, m9)
2371 
2372             s1 = Sequence(m1+m2, t6)
2373             s2 = Sequence(m3+m4, t7)
2374             s3 = Sequence(s1+s2, t89)
2375             s3._replaceIfHeldDirectly(m3,m5)
2376             self.assertEqual(s3.dumpPython()[:-1], "cms.Sequence(cms.Sequence(process.m1+process.m2, cms.Task(process.m6))+cms.Sequence(process.m3+process.m4, cms.Task(process.m7)), cms.Task(process.m8, process.m9))")
2377             s2._replaceIfHeldDirectly(m3,m5)
2378             self.assertEqual(s2.dumpPython()[:-1],"cms.Sequence(process.m5+process.m4, cms.Task(process.m7))")
2379             self.assertEqual(s3.dumpPython()[:-1], "cms.Sequence(cms.Sequence(process.m1+process.m2, cms.Task(process.m6))+cms.Sequence(process.m5+process.m4, cms.Task(process.m7)), cms.Task(process.m8, process.m9))")
2380 
2381             s1 = Sequence(t6)
2382             s1._replaceIfHeldDirectly(t6,t7)
2383             self.assertEqual(s1.dumpPython()[:-1],"cms.Sequence(cms.Task(process.m7))")
2384 
2385             #ConditionalTask
2386             ct6 = ConditionalTask(m6)
2387             ct7 = ConditionalTask(m7)
2388             ct89 = ConditionalTask(m8, m9)
2389 
2390             s1 = Sequence(m1+m2, ct6)
2391             s2 = Sequence(m3+m4, ct7)
2392             s3 = Sequence(s1+s2, ct89)
2393             s3._replaceIfHeldDirectly(m3,m5)
2394             self.assertEqual(s3.dumpPython()[:-1], "cms.Sequence(cms.Sequence(process.m1+process.m2, cms.ConditionalTask(process.m6))+cms.Sequence(process.m3+process.m4, cms.ConditionalTask(process.m7)), cms.ConditionalTask(process.m8, process.m9))")
2395             s2._replaceIfHeldDirectly(m3,m5)
2396             self.assertEqual(s2.dumpPython()[:-1],"cms.Sequence(process.m5+process.m4, cms.ConditionalTask(process.m7))")
2397             self.assertEqual(s3.dumpPython()[:-1], "cms.Sequence(cms.Sequence(process.m1+process.m2, cms.ConditionalTask(process.m6))+cms.Sequence(process.m5+process.m4, cms.ConditionalTask(process.m7)), cms.ConditionalTask(process.m8, process.m9))")
2398 
2399             s1 = Sequence(ct6)
2400             s1._replaceIfHeldDirectly(ct6,ct7)
2401             self.assertEqual(s1.dumpPython()[:-1],"cms.Sequence(cms.ConditionalTask(process.m7))")
2402         def testIndex(self):
2403             m1 = DummyModule("a")
2404             m2 = DummyModule("b")
2405             m3 = DummyModule("c")
2406 
2407             s = Sequence(m1+m2+m3)
2408             self.assertEqual(s.index(m1),0)
2409             self.assertEqual(s.index(m2),1)
2410             self.assertEqual(s.index(m3),2)
2411 
2412         def testInsert(self):
2413             m1 = DummyModule("a")
2414             m2 = DummyModule("b")
2415             m3 = DummyModule("c")
2416             s = Sequence(m1+m3)
2417             s.insert(1,m2)
2418             self.assertEqual(s.index(m1),0)
2419             self.assertEqual(s.index(m2),1)
2420             self.assertEqual(s.index(m3),2)
2421 
2422             s = Sequence()
2423             s.insert(0, m1)
2424             self.assertEqual(s.index(m1),0)
2425 
2426             p = Path()
2427             p.insert(0, m1)
2428             self.assertEqual(s.index(m1),0)
2429 
2430         def testExpandAndClone(self):
2431             m1 = DummyModule("m1")
2432             m2 = DummyModule("m2")
2433             m3 = DummyModule("m3")
2434             m4 = DummyModule("m4")
2435             m5 = DummyModule("m5")
2436 
2437             s1 = Sequence(m1*~m2*m1*m2*ignore(m2))
2438             s2 = Sequence(m1*m2)
2439             s3 = Sequence(~m1*s2)
2440 
2441             p = Path(s1+s3)
2442             p2 = p.expandAndClone()
2443             l = []
2444             namesVisitor = DecoratedNodeNameVisitor(l)
2445             p2.visit(namesVisitor)
2446             self.assertEqual(l, ['m1', '!m2', 'm1', 'm2', '-m2', '!m1', 'm1', 'm2'])
2447 
2448             #Task
2449             m6 = DummyModule("m6")
2450             m7 = DummyModule("m7")
2451             m8 = DummyModule("m8")
2452             m9 = DummyModule("m9")
2453             p = Path(s1+s3, Task(m6))
2454             p2 = p.expandAndClone()
2455             l[:] = []
2456             p2.visit(namesVisitor)
2457             self.assertEqual(l, ['m1', '!m2', 'm1', 'm2', '-m2', '!m1', 'm1', 'm2'])
2458             self.assertEqual(p2.dumpPython(), "cms.Path(process.m1+~process.m2+process.m1+process.m2+cms.ignore(process.m2)+~process.m1+process.m1+process.m2, cms.Task(process.m6))\n")
2459 
2460             s2 = Sequence(m1*m2, Task(m9))
2461             s3 = Sequence(~m1*s2)
2462             t8 = Task(m8)
2463             t8.setLabel("t8")
2464             p = Path(s1+s3, Task(m6, Task(m7, t8)))
2465             p2 = p.expandAndClone()
2466             l[:] = []
2467             p2.visit(namesVisitor)
2468             self.assertEqual(l, ['m1', '!m2', 'm1', 'm2', '-m2', '!m1', 'm1', 'm2'])
2469             self.assertTrue(p2.dumpPython() == "cms.Path(process.m1+~process.m2+process.m1+process.m2+cms.ignore(process.m2)+~process.m1+process.m1+process.m2, cms.Task(process.m6, process.m7, process.m8, process.m9))\n")
2470 
2471             t1 = Task(m1,m2,m3)
2472             s1 = Sequence(t1)
2473             s2 = s1.expandAndClone()
2474             l[:] = []
2475             s2.visit(namesVisitor)
2476             self.assertEqual(l, [])
2477             self.assertTrue(s2.dumpPython() == "cms.Sequence(cms.Task(process.m1, process.m2, process.m3))\n")
2478 
2479             t1 = Task(m1,m2)
2480             t2 = Task(m1,m3,t1)
2481             t3 = t2.expandAndClone()
2482             self.assertTrue(t3.dumpPython() == "cms.Task(process.m1, process.m2, process.m3)\n")
2483             t4 = Task()
2484             t5 = t4.expandAndClone()
2485             self.assertTrue(t5.dumpPython() == "cms.Task()\n")
2486             #ConditionalTask
2487             s1 = Sequence(m1*~m2*m1*m2*ignore(m2))
2488             s2 = Sequence(m1*m2)
2489             s3 = Sequence(~m1*s2)
2490             p = Path(s1+s3, ConditionalTask(m6))
2491             p2 = p.expandAndClone()
2492             l[:] = []
2493             p2.visit(namesVisitor)
2494             self.assertEqual(l, ['m1', '!m2', 'm1', 'm2', '-m2', '!m1', 'm1', 'm2'])
2495             self.assertEqual(p2.dumpPython(), "cms.Path(process.m1+~process.m2+process.m1+process.m2+cms.ignore(process.m2)+~process.m1+process.m1+process.m2, cms.ConditionalTask(process.m6))\n")
2496 
2497             s2 = Sequence(m1*m2, ConditionalTask(m9))
2498             s3 = Sequence(~m1*s2)
2499             ct8 = ConditionalTask(m8)
2500             ct8.setLabel("ct8")
2501             p = Path(s1+s3, ConditionalTask(m6, ConditionalTask(m7, ct8)))
2502             p2 = p.expandAndClone()
2503             l[:] = []
2504             p2.visit(namesVisitor)
2505             self.assertEqual(l, ['m1', '!m2', 'm1', 'm2', '-m2', '!m1', 'm1', 'm2'])
2506             self.assertEqual(p2.dumpPython(), "cms.Path(process.m1+~process.m2+process.m1+process.m2+cms.ignore(process.m2)+~process.m1+process.m1+process.m2, cms.ConditionalTask(process.m6, process.m7, process.m8, process.m9))\n")
2507 
2508             t1 = ConditionalTask(m1,m2,m3)
2509             s1 = Sequence(t1)
2510             s2 = s1.expandAndClone()
2511             l[:] = []
2512             s2.visit(namesVisitor)
2513             self.assertEqual(l, [])
2514             self.assertEqual(s2.dumpPython(), "cms.Sequence(cms.ConditionalTask(process.m1, process.m2, process.m3))\n")
2515 
2516             t1 = ConditionalTask(m1,m2)
2517             t2 = ConditionalTask(m1,m3,t1)
2518             t3 = t2.expandAndClone()
2519             self.assertEqual(t3.dumpPython(), "cms.ConditionalTask(process.m1, process.m2, process.m3)\n")
2520             t4 = ConditionalTask()
2521             t5 = t4.expandAndClone()
2522             self.assertTrue(t5.dumpPython() == "cms.ConditionalTask()\n")
2523 
2524         def testAdd(self):
2525             m1 = DummyModule("m1")
2526             m2 = DummyModule("m2")
2527             m3 = DummyModule("m3")
2528             m4 = DummyModule("m4")
2529             s1 = Sequence(m1)
2530             s3 = Sequence(m3+ignore(m4))
2531             p = Path(s1)
2532             p += ~m2
2533             p *= s3
2534 
2535             l = []
2536             namesVisitor = DecoratedNodeNameVisitor(l)
2537             p.visit(namesVisitor)
2538             self.assertEqual(l, ['m1', '!m2', 'm3', '-m4'])
2539 
2540             s4 = Sequence()
2541             s4 +=m1
2542             l[:]=[]; s1.visit(namesVisitor); self.assertEqual(l,['m1'])
2543             self.assertEqual(s4.dumpPython(),"cms.Sequence(process.m1)\n")
2544             s4 = Sequence()
2545             s4 *=m1
2546             l[:]=[]; s1.visit(namesVisitor); self.assertEqual(l,['m1'])
2547             self.assertEqual(s4.dumpPython(),"cms.Sequence(process.m1)\n")
2548 
2549 
2550         def testRemove(self):
2551             m1 = DummyModule("m1")
2552             m2 = DummyModule("m2")
2553             m3 = DummyModule("m3")
2554             m4 = DummyModule("m4")
2555             s1 = Sequence(m1*m2+~m3)
2556             s2 = Sequence(m1*s1)
2557             l = []
2558             namesVisitor = DecoratedNodeNameVisitor(l)
2559             d = {'m1':m1 ,'m2':m2, 'm3':m3,'s1':s1, 's2':s2}
2560             l[:] = []; s1.visit(namesVisitor); self.assertEqual(l,['m1', 'm2', '!m3'])
2561             l[:] = []; s2.visit(namesVisitor); self.assertEqual(l,['m1', 'm1', 'm2', '!m3'])
2562             s1.remove(m2)
2563             l[:] = []; s1.visit(namesVisitor); self.assertEqual(l,['m1', '!m3'])
2564             l[:] = []; s2.visit(namesVisitor); self.assertEqual(l,['m1', 'm1', '!m3'])
2565             s2.remove(m3)
2566             l[:] = []; s1.visit(namesVisitor); self.assertEqual(l,['m1', '!m3'])
2567             l[:] = []; s2.visit(namesVisitor); self.assertEqual(l,['m1', 'm1'])
2568             s1 = Sequence( m1 + m2 + m1 + m2 )
2569             l[:] = []; s1.visit(namesVisitor); self.assertEqual(l,['m1', 'm2', 'm1', 'm2'])
2570             s1.remove(m2)
2571             l[:] = []; s1.visit(namesVisitor); self.assertEqual(l,['m1', 'm1', 'm2'])
2572             s1 = Sequence( m1 + m3 )
2573             s2 = Sequence( m2 + ignore(m3) + s1 + m3 )
2574             l[:] = []; s2.visit(namesVisitor); self.assertEqual(l,['m2', '-m3', 'm1', 'm3', 'm3'])
2575             s2.remove(s1)
2576             l[:] = []; s2.visit(namesVisitor); self.assertEqual(l,['m2', '-m3', 'm3'])
2577             s2.remove(m3)
2578             l[:] = []; s2.visit(namesVisitor); self.assertEqual(l,['m2','m3'])
2579             s1 = Sequence(m1*m2*m3)
2580             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2+process.m3)\n")
2581             s1.remove(m2)
2582             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m3)\n")
2583             s1 = Sequence(m1+m2+m3)
2584             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2+process.m3)\n")
2585             s1.remove(m2)
2586             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m3)\n")
2587             s1 = Sequence(m1*m2+m3)
2588             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2+process.m3)\n")
2589             s1.remove(m2)
2590             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m3)\n")
2591             s1 = Sequence(m1+m2*m3)
2592             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2+process.m3)\n")
2593             s1.remove(m2)
2594             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m3)\n")
2595             s1.remove(m1)
2596             s1.remove(m3)
2597             l[:]=[]; s1.visit(namesVisitor); self.assertEqual(l,[])
2598             self.assertEqual(s1.dumpPython(), "cms.Sequence()\n")
2599             s3 = Sequence(m1)
2600             s3.remove(m1)
2601             l[:]=[]; s3.visit(namesVisitor); self.assertEqual(l,[])
2602             self.assertEqual(s3.dumpPython(), "cms.Sequence()\n")
2603             s3 = Sequence(m1)
2604             s4 = Sequence(s3)
2605             s4.remove(m1)
2606             l[:]=[]; s4.visit(namesVisitor); self.assertEqual(l,[])
2607             self.assertEqual(s4.dumpPython(), "cms.Sequence()\n")
2608             #Task
2609             s1 = Sequence(m1+m2, Task(m3), Task(m4))
2610             s1.remove(m4)
2611             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2, cms.Task(process.m3))\n")
2612             s1 = Sequence(m1+m2+Sequence(Task(m3,m4), Task(m3), Task(m4)))
2613             s1.remove(m4)
2614             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2, cms.Task(process.m3), cms.Task(process.m4))\n")
2615             t1 = Task(m1)
2616             t1.setLabel("t1")
2617             t2 = Task(m2,t1)
2618             t2.setLabel("t2")
2619             t3 = Task(t1,t2,m1)
2620             t3.remove(m1)
2621             self.assertTrue(t3.dumpPython() == "cms.Task(process.m1, process.t2)\n")
2622             t3.remove(m1)
2623             self.assertTrue(t3.dumpPython() == "cms.Task(process.m1, process.m2)\n")
2624             t3.remove(m1)
2625             self.assertTrue(t3.dumpPython() == "cms.Task(process.m2)\n")
2626             t3.remove(m2)
2627             self.assertTrue(t3.dumpPython() == "cms.Task()\n")
2628             #ConditionalTask
2629             s1 = Sequence(m1+m2, ConditionalTask(m3), ConditionalTask(m4))
2630             s1.remove(m4)
2631             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2, cms.ConditionalTask(process.m3))\n")
2632             s1 = Sequence(m1+m2+Sequence(ConditionalTask(m3,m4), ConditionalTask(m3), ConditionalTask(m4)))
2633             s1.remove(m4)
2634             self.assertEqual(s1.dumpPython(), "cms.Sequence(process.m1+process.m2, cms.ConditionalTask(process.m3), cms.ConditionalTask(process.m4))\n")
2635             t1 = ConditionalTask(m1)
2636             t1.setLabel("t1")
2637             t2 = ConditionalTask(m2,t1)
2638             t2.setLabel("t2")
2639             t3 = ConditionalTask(t1,t2,m1)
2640             t3.remove(m1)
2641             self.assertEqual(t3.dumpPython(), "cms.ConditionalTask(process.m1, process.t2)\n")
2642             t3.remove(m1)
2643             self.assertEqual(t3.dumpPython(), "cms.ConditionalTask(process.m1, process.m2)\n")
2644             t3.remove(m1)
2645             self.assertEqual(t3.dumpPython(), "cms.ConditionalTask(process.m2)\n")
2646             t3.remove(m2)
2647             self.assertEqual(t3.dumpPython(), "cms.ConditionalTask()\n")
2648 
2649         def testCopyAndExclude(self):
2650             a = DummyModule("a")
2651             b = DummyModule("b")
2652             c = DummyModule("c")
2653             d = DummyModule("d")
2654             s = Sequence(a+b+c)
2655             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+process.b+process.c)\n")
2656             s = Sequence(a+b+c+d)
2657             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2658             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2659             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+process.b+process.d)\n")
2660             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+process.b+process.c)\n")
2661             s=Sequence(a*b+c+d)
2662             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2663             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2664             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+process.b+process.d)\n")
2665             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+process.b+process.c)\n")
2666             s = Sequence(a+b*c+d)
2667             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2668             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2669             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+process.b+process.d)\n")
2670             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+process.b+process.c)\n")
2671             s2 = Sequence(a+b)
2672             s = Sequence(c+s2+d)
2673             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.c+process.b+process.d)\n")
2674             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.c+process.a+process.d)\n")
2675             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence((process.a+process.b)+process.d)\n")
2676             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.c+(process.a+process.b))\n")
2677             self.assertEqual(s.copyAndExclude([a,b]).dumpPython(),"cms.Sequence(process.c+process.d)\n")
2678             s3 = s.copyAndExclude([c])
2679             s2.remove(a)
2680             self.assertEqual(s3.dumpPython(),"cms.Sequence((process.b)+process.d)\n")
2681             s4 = s.copyAndExclude([a,b])
2682             seqs = []
2683             sequenceVisitor = SequenceVisitor(seqs)
2684             s.visit(sequenceVisitor)
2685             self.assertEqual(len(seqs),1)
2686             seqs[:] = []
2687             s4.visit(sequenceVisitor)
2688             self.assertEqual(len(seqs),0)
2689             self.assertEqual(s4.dumpPython(),"cms.Sequence(process.c+process.d)\n")
2690             holder = SequencePlaceholder("x")
2691             s3 = Sequence(b+d,Task(a))
2692             s2 = Sequence(a+b+holder+s3)
2693             s = Sequence(c+s2+d)
2694             seqs[:] = []
2695             s.visit(sequenceVisitor)
2696             self.assertTrue(seqs == [s2,s3])
2697             s2 = Sequence(a+b+holder)
2698             s = Sequence(c+s2+d)
2699             self.assertEqual(s.copyAndExclude([holder]).dumpPython(),"cms.Sequence(process.c+process.a+process.b+process.d)\n")
2700             s2 = Sequence(a+b+c)
2701             s = Sequence(s2+d)
2702             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2703             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2704             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+process.b+process.d)\n")
2705             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence((process.a+process.b+process.c))\n")
2706             self.assertEqual(s.copyAndExclude([s2]).dumpPython(),"cms.Sequence(process.d)\n")
2707             s2 = Sequence(a+b+c)
2708             s = Sequence(s2*d)
2709             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2710             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2711             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+process.b+process.d)\n")
2712             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence((process.a+process.b+process.c))\n")
2713             self.assertEqual(s.copyAndExclude([a,b,c]).dumpPython(),"cms.Sequence(process.d)\n")
2714             s = Sequence(ignore(a)+b+c+d)
2715             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2716             self.assertEqual(s.copyAndExclude([ignore(a)]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2717             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(cms.ignore(process.a)+process.c+process.d)\n")
2718             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(cms.ignore(process.a)+process.b+process.d)\n")
2719             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(cms.ignore(process.a)+process.b+process.c)\n")
2720             s = Sequence(a+ignore(b)+c+d)
2721             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(cms.ignore(process.b)+process.c+process.d)\n")
2722             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2723             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+cms.ignore(process.b)+process.d)\n")
2724             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+cms.ignore(process.b)+process.c)\n")
2725             s = Sequence(a+b+c+ignore(d))
2726             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+cms.ignore(process.d))\n")
2727             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+cms.ignore(process.d))\n")
2728             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+process.b+cms.ignore(process.d))\n")
2729             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+process.b+process.c)\n")
2730             s = Sequence(~a+b+c+d)
2731             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+process.d)\n")
2732             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(~process.a+process.c+process.d)\n")
2733             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(~process.a+process.b+process.d)\n")
2734             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(~process.a+process.b+process.c)\n")
2735             s = Sequence(a+~b+c+d)
2736             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(~process.b+process.c+process.d)\n")
2737             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2738             self.assertEqual(s.copyAndExclude([~b]).dumpPython(),"cms.Sequence(process.a+process.c+process.d)\n")
2739             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+~process.b+process.d)\n")
2740             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+~process.b+process.c)\n")
2741             s = Sequence(a+b+c+~d)
2742             self.assertEqual(s.copyAndExclude([a]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d)\n")
2743             self.assertEqual(s.copyAndExclude([b]).dumpPython(),"cms.Sequence(process.a+process.c+~process.d)\n")
2744             self.assertEqual(s.copyAndExclude([c]).dumpPython(),"cms.Sequence(process.a+process.b+~process.d)\n")
2745             self.assertEqual(s.copyAndExclude([d]).dumpPython(),"cms.Sequence(process.a+process.b+process.c)\n")
2746             self.assertEqual(s.copyAndExclude([a,b,c,d]).dumpPython(),"cms.Sequence()\n")
2747 
2748             #Task
2749             e = DummyModule("e")
2750             f = DummyModule("f")
2751             g = DummyModule("g")
2752             h = DummyModule("h")
2753             t1 = Task(h)
2754             s = Sequence(a+b+c+~d, Task(e,f,Task(g,t1)))
2755             self.assertEqual(s.copyAndExclude([a,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.Task(process.e, process.f, process.g))\n")
2756             self.assertEqual(s.copyAndExclude([a,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.Task(process.e, process.f, process.g))\n")
2757             self.assertEqual(s.copyAndExclude([a,e,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.Task(process.f, process.g))\n")
2758             self.assertEqual(s.copyAndExclude([a,e,f,g,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d)\n")
2759             self.assertEqual(s.copyAndExclude([a,b,c,d]).dumpPython(),"cms.Sequence(cms.Task(process.e, process.f, process.g, process.h))\n")
2760             self.assertEqual(s.copyAndExclude([t1]).dumpPython(),"cms.Sequence(process.a+process.b+process.c+~process.d, cms.Task(process.e, process.f, process.g))\n")
2761             taskList = []
2762             taskVisitor = TaskVisitor(taskList)
2763             s.visit(taskVisitor)
2764             self.assertEqual(len(taskList),3)
2765             s2 = s.copyAndExclude([g,h])
2766             taskList[:] = []
2767             s2.visit(taskVisitor)
2768             self.assertEqual(len(taskList),1)
2769             t2 = Task(t1)
2770             taskList[:] = []
2771             t2.visit(taskVisitor)
2772             self.assertEqual(taskList[0],t1)
2773             s3 = Sequence(s)
2774             self.assertEqual(s3.copyAndExclude([a,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.Task(process.e, process.f, process.g))\n")
2775             s4 = Sequence(s)
2776             self.assertEqual(s4.copyAndExclude([a,b,c,d,e,f,g,h]).dumpPython(),"cms.Sequence()\n")
2777             t1 = Task(e,f)
2778             t11 = Task(a)
2779             t11.setLabel("t11")
2780             t2 = Task(g,t1,h,t11)
2781             t3 = t2.copyAndExclude([e,h])
2782             self.assertTrue(t3.dumpPython() == "cms.Task(process.f, process.g, process.t11)\n")
2783             t4 = t2.copyAndExclude([e,f,g,h,a])
2784             self.assertTrue(t4.dumpPython() == "cms.Task()\n")
2785             #ConditionalTask
2786             t1 = ConditionalTask(h)
2787             s = Sequence(a+b+c+~d, ConditionalTask(e,f,ConditionalTask(g,t1)))
2788             self.assertEqual(s.copyAndExclude([a,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.ConditionalTask(process.e, process.f, process.g))\n")
2789             self.assertEqual(s.copyAndExclude([a,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.ConditionalTask(process.e, process.f, process.g))\n")
2790             self.assertEqual(s.copyAndExclude([a,e,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.ConditionalTask(process.f, process.g))\n")
2791             self.assertEqual(s.copyAndExclude([a,e,f,g,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d)\n")
2792             self.assertEqual(s.copyAndExclude([a,b,c,d]).dumpPython(),"cms.Sequence(cms.ConditionalTask(process.e, process.f, process.g, process.h))\n")
2793             self.assertEqual(s.copyAndExclude([t1]).dumpPython(),"cms.Sequence(process.a+process.b+process.c+~process.d, cms.ConditionalTask(process.e, process.f, process.g))\n")
2794             taskList = []
2795             taskVisitor = ConditionalTaskVisitor(taskList)
2796             s.visit(taskVisitor)
2797             self.assertEqual(len(taskList),3)
2798             s2 = s.copyAndExclude([g,h])
2799             taskList[:] = []
2800             s2.visit(taskVisitor)
2801             self.assertEqual(len(taskList),1)
2802             t2 = ConditionalTask(t1)
2803             taskList[:] = []
2804             t2.visit(taskVisitor)
2805             self.assertEqual(taskList[0],t1)
2806             s3 = Sequence(s)
2807             self.assertEqual(s3.copyAndExclude([a,h]).dumpPython(),"cms.Sequence(process.b+process.c+~process.d, cms.ConditionalTask(process.e, process.f, process.g))\n")
2808             s4 = Sequence(s)
2809             self.assertEqual(s4.copyAndExclude([a,b,c,d,e,f,g,h]).dumpPython(),"cms.Sequence()\n")
2810             t1 = ConditionalTask(e,f)
2811             t11 = ConditionalTask(a)
2812             t11.setLabel("t11")
2813             t2 = ConditionalTask(g,t1,h,t11)
2814             t3 = t2.copyAndExclude([e,h])
2815             self.assertEqual(t3.dumpPython(), "cms.ConditionalTask(process.f, process.g, process.t11)\n")
2816             t4 = t2.copyAndExclude([e,f,g,h,a])
2817             self.assertEqual(t4.dumpPython(), "cms.ConditionalTask()\n")
2818 
2819         def testSequenceTypeChecks(self):
2820             m1 = DummyModule("m1")
2821             m2 = DummyModule("m2")
2822             s1 = Sequence(m1*m2)
2823             def testRaise():
2824                 s1.something = 1
2825             self.assertRaises(AttributeError,testRaise)
2826             def testRaise2():
2827                 s2 = Sequence(m1*None)
2828             self.assertRaises(TypeError,testRaise2)
2829         def testCopy(self):
2830             a = DummyModule("a")
2831             b = DummyModule("b")
2832             c = DummyModule("c")
2833             p1 = Path(a+b+c)
2834             p2 = p1.copy()
2835             e = DummyModule("e")
2836             p2.replace(b,e)
2837             self.assertEqual(p1.dumpPython(),"cms.Path(process.a+process.b+process.c)\n")
2838             self.assertEqual(p2.dumpPython(),"cms.Path(process.a+process.e+process.c)\n")
2839             p1 = Path(a+b+c)
2840             p2 = p1.copy()
2841             p1 += e
2842             self.assertEqual(p1.dumpPython(),"cms.Path(process.a+process.b+process.c+process.e)\n")
2843             self.assertEqual(p2.dumpPython(),"cms.Path(process.a+process.b+process.c)\n")
2844             #Task
2845             t1 = Task(a, b)
2846             t2 = t1.copy()
2847             self.assertTrue(t1.dumpPython() == t2.dumpPython())
2848             t1Contents = list(t1._collection)
2849             t2Contents = list(t2._collection)
2850             self.assertTrue(id(t1Contents[0]) == id(t2Contents[0]))
2851             self.assertTrue(id(t1Contents[1]) == id(t2Contents[1]))
2852             self.assertTrue(id(t1._collection) != id(t2._collection))
2853             #ConditionalTask
2854             t1 = ConditionalTask(a, b)
2855             t2 = t1.copy()
2856             self.assertTrue(t1.dumpPython() == t2.dumpPython())
2857             t1Contents = list(t1._collection)
2858             t2Contents = list(t2._collection)
2859             self.assertTrue(id(t1Contents[0]) == id(t2Contents[0]))
2860             self.assertTrue(id(t1Contents[1]) == id(t2Contents[1]))
2861             self.assertTrue(id(t1._collection) != id(t2._collection))
2862 
2863         def testCopyAndAdd(self):
2864             a = DummyModule("a")
2865             b = DummyModule("b")
2866             c = DummyModule("c")
2867             d = DummyModule("d")
2868             e = DummyModule("e")
2869             #Task
2870             t1 = Task(a, b, c)
2871             self.assertEqual(t1.dumpPython(), "cms.Task(process.a, process.b, process.c)\n")
2872             t2 = t1.copyAndAdd(d, e)
2873             self.assertEqual(t1.dumpPython(), "cms.Task(process.a, process.b, process.c)\n")
2874             self.assertEqual(t2.dumpPython(), "cms.Task(process.a, process.b, process.c, process.d, process.e)\n")
2875             t3 = t2.copyAndExclude([b])
2876             self.assertEqual(t1.dumpPython(), "cms.Task(process.a, process.b, process.c)\n")
2877             self.assertEqual(t2.dumpPython(), "cms.Task(process.a, process.b, process.c, process.d, process.e)\n")
2878             self.assertEqual(t3.dumpPython(), "cms.Task(process.a, process.c, process.d, process.e)\n")
2879             t4 = t1.copyAndExclude([b]).copyAndAdd(d)
2880             self.assertEqual(t4.dumpPython(), "cms.Task(process.a, process.c, process.d)\n")
2881             t5 = t2.copyAndExclude([b]).copyAndAdd(d)
2882             self.assertEqual(t5.dumpPython(), "cms.Task(process.a, process.c, process.d, process.e)\n")
2883             t6 = t4.copyAndAdd(Task(b))
2884             self.assertEqual(t6.dumpPython(), "cms.Task(process.a, process.b, process.c, process.d)\n")
2885             #ConditionalTask
2886             t1 = ConditionalTask(a, b, c)
2887             self.assertEqual(t1.dumpPython(), "cms.ConditionalTask(process.a, process.b, process.c)\n")
2888             t2 = t1.copyAndAdd(d, e)
2889             self.assertEqual(t1.dumpPython(), "cms.ConditionalTask(process.a, process.b, process.c)\n")
2890             self.assertEqual(t2.dumpPython(), "cms.ConditionalTask(process.a, process.b, process.c, process.d, process.e)\n")
2891             t3 = t2.copyAndExclude([b])
2892             self.assertEqual(t1.dumpPython(), "cms.ConditionalTask(process.a, process.b, process.c)\n")
2893             self.assertEqual(t2.dumpPython(), "cms.ConditionalTask(process.a, process.b, process.c, process.d, process.e)\n")
2894             self.assertEqual(t3.dumpPython(), "cms.ConditionalTask(process.a, process.c, process.d, process.e)\n")
2895             t4 = t1.copyAndExclude([b]).copyAndAdd(d)
2896             self.assertEqual(t4.dumpPython(), "cms.ConditionalTask(process.a, process.c, process.d)\n")
2897             t5 = t2.copyAndExclude([b]).copyAndAdd(d)
2898             self.assertEqual(t5.dumpPython(), "cms.ConditionalTask(process.a, process.c, process.d, process.e)\n")
2899             t6 = t4.copyAndAdd(Task(b))
2900             self.assertEqual(t6.dumpPython(), "cms.ConditionalTask(process.a, process.b, process.c, process.d)\n")
2901 
2902         def testInsertInto(self):
2903             from FWCore.ParameterSet.Types import vstring
2904             class TestPSet(object):
2905                 def __init__(self):
2906                     self._dict = dict()
2907                 def addVString(self,isTracked,label,value):
2908                     self._dict[label]=value
2909             a = DummyModule("a")
2910             b = DummyModule("b")
2911             c = DummyModule("c")
2912             d = DummyModule("d")
2913             p = Path(a+b+c+d)
2914             decoratedList = []
2915             lister = DecoratedNodeNameVisitor(decoratedList)
2916             p.visit(lister)
2917             ps = TestPSet()
2918             p.insertInto(ps,"p",decoratedList)
2919             self.assertEqual(ps._dict, {"p":vstring("a","b","c","d")})
2920             s = Sequence(b+c)
2921             p = Path(a+s+d)
2922             decoratedList[:] = []
2923             p.visit(lister)
2924             ps = TestPSet()
2925             p.insertInto(ps,"p",decoratedList)
2926             self.assertEqual(ps._dict, {"p":vstring("a","b","c","d")})
2927 
2928     unittest.main()