CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
Mixins.py
Go to the documentation of this file.
1 import inspect
2 
4  """Denotes a class that can be used by the Processes class"""
5  pass
6 
8  def __init__(self):
9  self.indent_= 0
10  self.deltaIndent_ = 4
11  self.isCfg = True
12  def indentation(self):
13  return ' '*self.indent_
14  def indent(self):
15  self.indent_ += self.deltaIndent_
16  def unindent(self):
17  self.indent_ -= self.deltaIndent_
18 
20  """base class for classes which are used as the 'parameters' for a ParameterSet"""
21  def __init__(self):
22  self.__dict__["_isFrozen"] = False
23  self.__isTracked = True
24  self._isModified = False
25  def isModified(self):
26  return self._isModified
27  def resetModified(self):
28  self._isModified=False
29  def configTypeName(self):
30  if self.isTracked():
31  return type(self).__name__
32  return 'untracked '+type(self).__name__
33  def pythonTypeName(self):
34  if self.isTracked():
35  return 'cms.'+type(self).__name__
36  return 'cms.untracked.'+type(self).__name__
37  def dumpPython(self, options=PrintOptions()):
38  return self.pythonTypeName()+"("+self.pythonValue(options)+")"
39  def __repr__(self):
40  return self.dumpPython()
41  def isTracked(self):
42  return self.__isTracked
43  def setIsTracked(self,trackness):
44  self.__isTracked = trackness
45  def isFrozen(self):
46  return self._isFrozen
47  def setIsFrozen(self):
48  self._isFrozen = True
49 
51  """base class for parameter classes which only hold a single value"""
52  def __init__(self,value):
53  super(_SimpleParameterTypeBase,self).__init__()
54  self._value = value
55  if not self._isValid(value):
56  raise ValueError(str(value)+" is not a valid "+str(type(self)))
57  def value(self):
58  return self._value
59  def setValue(self,value):
60  if not self._isValid(value):
61  raise ValueError(str(value)+" is not a valid "+str(type(self)))
62  if value!=self._value:
63  self._isModified=True
64  self._value=value
65  def configValue(self, options=PrintOptions()):
66  return str(self._value)
67  def pythonValue(self, options=PrintOptions()):
68  return self.configValue(options)
69  def __eq__(self,other):
70  if isinstance(other,_SimpleParameterTypeBase):
71  return self._value == other._value
72  return self._value == other
73  def __ne__(self,other):
74  if isinstance(other,_SimpleParameterTypeBase):
75  return self._value != other._value
76  return self._value != other
77 
78 
80  """For injection purposes, pretend this is a new parameter type
81  then have a post process step which strips these out
82  """
83  def __init__(self,value, s='', loc=0, file=''):
84  super(UsingBlock,self).__init__(value)
85  self.s = s
86  self.loc = loc
87  self.file = file
88  self.isResolved = False
89  @staticmethod
90  def _isValid(value):
91  return isinstance(value,str)
92  def _valueFromString(value):
93  """only used for cfg-parsing"""
94  return string(value)
95  def insertInto(self, parameterSet, myname):
96  value = self.value()
97  # doesn't seem to handle \0 correctly
98  #if value == '\0':
99  # value = ''
100  parameterSet.addString(self.isTracked(), myname, value)
101  def dumpPython(self, options):
102  if options.isCfg:
103  return "process."+self.value()
104  else:
105  return self.value()
106 
107 
109  """Base class for classes which allow addition of _ParameterTypeBase data"""
110  def __init__(self,*arg,**kargs):
111  self.__dict__['_Parameterizable__parameterNames'] = []
112  self.__dict__["_isFrozen"] = False
113  """The named arguments are the 'parameters' which are added as 'python attributes' to the object"""
114  if len(arg) != 0:
115  #raise ValueError("unnamed arguments are not allowed. Please use the syntax 'name = value' when assigning arguments.")
116  for block in arg:
117  if type(block).__name__ != "PSet":
118  raise ValueError("Only PSets can be passed as unnamed argument blocks. This is a "+type(block).__name__)
119  self.__setParameters(block.parameters_())
120  self.__setParameters(kargs)
121  self._isModified = False
122 
123  def parameterNames_(self):
124  """Returns the name of the parameters"""
125  return self.__parameterNames[:]
126  def isModified(self):
127  if self._isModified:
128  return True
129  for name in self.parameterNames_():
130  param = self.__dict__[name]
131  if isinstance(param, _Parameterizable) and param.isModified():
132  self._isModified = True
133  return True
134  return False
135 
136  def hasParameter(self, params):
137  """
138  _hasParameter_
139 
140  check that pset provided has the attribute chain
141  specified.
142 
143  Eg, if params is [ 'attr1', 'attr2', 'attr3' ]
144  check for pset.attr1.attr2.attr3
145 
146  returns True if parameter exists, False if not
147  """
148  return (self.getParameter(params) != None)
149 
150  def getParameter(self, params):
151  """
152  _getParameter_
153 
154  Retrieve the specified parameter from the PSet Provided
155  given the attribute chain
156 
157  returns None if not found
158  """
159  lastParam = self
160  # Don't accidentally iterate over letters in a string
161  if type(params).__name__ == 'str':
162  return getattr(self, params, None)
163  for param in params:
164  lastParam = getattr(lastParam, param, None)
165  print str(lastParam)
166  if lastParam == None:
167  return None
168  return lastParam
169 
170  def parameters_(self):
171  """Returns a dictionary of copies of the user-set parameters"""
172  import copy
173  result = dict()
174  for name in self.parameterNames_():
175  result[name]=copy.deepcopy(self.__dict__[name])
176  return result
177 
178  def __addParameter(self, name, value):
179  if not isinstance(value,_ParameterTypeBase):
180  self.__raiseBadSetAttr(name)
181  if name in self.__dict__:
182  message = "Duplicate insert of member " + name
183  message += "\nThe original parameters are:\n"
184  message += self.dumpPython() + '\n'
185  raise ValueError(message)
186  self.__dict__[name]=value
187  self.__parameterNames.append(name)
188  self._isModified = True
189 
190  def __setParameters(self,parameters):
191  for name,value in parameters.iteritems():
192  self.__addParameter(name, value)
193 
194  def __setattr__(self,name,value):
195  #since labels are not supposed to have underscores at the beginning
196  # I will assume that if we have such then we are setting an internal variable
197  if self.isFrozen() and not (name in ["_Labelable__label","_isFrozen"] or name.startswith('_')):
198  message = "Object already added to a process. It is read only now\n"
199  message += " %s = %s" %(name, value)
200  message += "\nThe original parameters are:\n"
201  message += self.dumpPython() + '\n'
202  raise ValueError(message)
203  # underscored names bypass checking for _ParameterTypeBase
204  if name[0]=='_':
205  super(_Parameterizable,self).__setattr__(name,value)
206  elif not name in self.__dict__:
207  self.__addParameter(name, value)
208  self._isModified = True
209  else:
210  # handle the case where users just replace with a value, a = 12, rather than a = cms.int32(12)
211  if isinstance(value,_ParameterTypeBase):
212  self.__dict__[name] = value
213  else:
214  self.__dict__[name].setValue(value)
215  self._isModified = True
216 
217  def isFrozen(self):
218  return self._isFrozen
219  def setIsFrozen(self):
220  self._isFrozen = True
221  for name in self.parameterNames_():
222  self.__dict__[name].setIsFrozen()
223  def __delattr__(self,name):
224  if self.isFrozen():
225  raise ValueError("Object already added to a process. It is read only now")
226  super(_Parameterizable,self).__delattr__(name)
227  self.__parameterNames.remove(name)
228  @staticmethod
229  def __raiseBadSetAttr(name):
230  raise TypeError(name+" does not already exist, so it can only be set to a CMS python configuration type")
231  def dumpPython(self, options=PrintOptions()):
232  others = []
233  usings = []
234  for name in self.parameterNames_():
235  param = self.__dict__[name]
236  # we don't want minuses in names
237  name2 = name.replace('-','_')
238  options.indent()
239  #_UsingNodes don't get assigned variables
240  if name.startswith("using_"):
241  usings.append(options.indentation()+param.dumpPython(options))
242  else:
243  others.append(options.indentation()+name2+' = '+param.dumpPython(options))
244  options.unindent()
245  # usings need to go first
246  resultList = usings
247  resultList.extend(others)
248  return ',\n'.join(resultList)+'\n'
249  def __repr__(self):
250  return self.dumpPython()
251  def insertContentsInto(self, parameterSet):
252  for name in self.parameterNames_():
253  param = getattr(self,name)
254  param.insertInto(parameterSet, name)
255 
256 
258  """Base class for classes which are Parameterizable and have a 'type' assigned"""
259  def __init__(self,type_,*arg,**kargs):
260  self.__dict__['_TypedParameterizable__type'] = type_
261  #the 'type' is also placed in the 'arg' list and we need to remove it
262  #if 'type_' not in kargs:
263  # arg = arg[1:]
264  #else:
265  # del args['type_']
266  arg = tuple([x for x in arg if x != None])
267  super(_TypedParameterizable,self).__init__(*arg,**kargs)
268  saveOrigin(self, 1)
269  def _place(self,name,proc):
270  self._placeImpl(name,proc)
271  def type_(self):
272  """returns the type of the object, e.g. 'FooProducer'"""
273  return self.__type
274  def copy(self):
275  returnValue =_TypedParameterizable.__new__(type(self))
276  params = self.parameters_()
277  args = list()
278  if len(params) == 0:
279  args.append(None)
280  returnValue.__init__(self.__type,*args,
281  **params)
282  returnValue._isModified = self._isModified
283  return returnValue
284  def clone(self, *args, **params):
285  """Copies the object and allows one to modify the parameters of the clone.
286  New parameters may be added by specify the exact type
287  Modifying existing parameters can be done by just specifying the new
288  value without having to specify the type.
289  """
290  returnValue =_TypedParameterizable.__new__(type(self))
291  myparams = self.parameters_()
292  if len(myparams) == 0 and len(params) and len(args):
293  args.append(None)
294  if len(params):
295  #need to treat items both in params and myparams specially
296  for key,value in params.iteritems():
297  if key in myparams:
298  if isinstance(value,_ParameterTypeBase):
299  myparams[key] =value
300  else:
301  myparams[key].setValue(value)
302  else:
303  if isinstance(value,_ParameterTypeBase):
304  myparams[key]=value
305  else:
306  self._Parameterizable__raiseBadSetAttr(key)
307 
308  returnValue.__init__(self.__type,*args,
309  **myparams)
310  returnValue._isModified = False
311  returnValue._isFrozen = False
312  saveOrigin(returnValue, 1)
313  return returnValue
314 
315  @staticmethod
316  def __findDefaultsFor(label,type):
317  #This routine is no longer used, but I might revive it in the future
318  import sys
319  import glob
320  choices = list()
321  for d in sys.path:
322  choices.extend(glob.glob(d+'/*/*/'+label+'.py'))
323  if not choices:
324  return None
325  #now see if any of them have what we want
326  #the use of __import__ is taken from an example
327  # from the www.python.org documentation on __import__
328  for c in choices:
329  #print " found file "+c
330  name='.'.join(c[:-3].split('/')[-3:])
331  #name = c[:-3].replace('/','.')
332  mod = __import__(name)
333  components = name.split('.')
334  for comp in components[1:]:
335  mod = getattr(mod,comp)
336  if hasattr(mod,label):
337  default = getattr(mod,label)
338  if isinstance(default,_TypedParameterizable):
339  if(default.type_() == type):
340  params = dict()
341  for name in default.parameterNames_():
342  params[name] = getattr(default,name)
343  return params
344  return None
345 
346  def dumpConfig(self, options=PrintOptions()):
347  config = self.__type +' { \n'
348  for name in self.parameterNames_():
349  param = self.__dict__[name]
350  options.indent()
351  config+=options.indentation()+param.configTypeName()+' '+name+' = '+param.configValue(options)+'\n'
352  options.unindent()
353  config += options.indentation()+'}\n'
354  return config
355 
356  def dumpPython(self, options=PrintOptions()):
357  result = "cms."+str(type(self).__name__)+'("'+self.type_()+'"'
358  nparam = len(self.parameterNames_())
359  if nparam == 0:
360  result += ")\n"
361  elif nparam < 256:
362  result += ",\n"+_Parameterizable.dumpPython(self,options)+options.indentation() + ")\n"
363  else:
364  # too big. Need to dump externally
365  #NOTE: in future should explore just creating a dict
366  # {'foo':cms.uint32(1), .... }
367  # and pass it to the constructor using the **{...} notation
368  label = ""
369  try:
370  label = "process."+self.label_()
371  except:
372  label = "FIX-THIS"
373  result += ")\n" + self.dumpPythonAttributes(label, options)
374  return result
375 
376  def dumpPythonAttributes(self, myname, options):
377  """ dumps the object with all attributes declared after the constructor"""
378  result = ""
379  for name in self.parameterNames_():
380  param = self.__dict__[name]
381  result += options.indentation() + myname + "." + name + " = " + param.dumpPython(options) + "\n"
382  return result
383 
384  def nameInProcessDesc_(self, myname):
385  return myname;
386  def moduleLabel_(self, myname):
387  return myname
388  def insertInto(self, parameterSet, myname):
389  newpset = parameterSet.newPSet()
390  newpset.addString(True, "@module_label", self.moduleLabel_(myname))
391  newpset.addString(True, "@module_type", self.type_())
392  newpset.addString(True, "@module_edm_type", type(self).__name__)
393  self.insertContentsInto(newpset)
394  parameterSet.addPSet(True, self.nameInProcessDesc_(myname), newpset)
395 
396 
397 
399  """A 'mixin' used to denote that the class can be paired with a label (e.g. an EDProducer)"""
400  def label_(self):
401  if not hasattr(self, "_Labelable__label"):
402  raise RuntimeError("module has no label. Perhaps it wasn't inserted into the process?")
403  return self.__label
404  def hasLabel_(self):
405  return hasattr(self, "_Labelable__label") and self.__label is not None
406  def setLabel(self,label):
407  if self.hasLabel_() :
408  if self.label_() != label and label is not None :
409  msg100 = "Attempting to change the label of a Labelable object, possibly an attribute of the Process\n"
410  msg101 = "Old label = "+self.label_()+" New label = "+label+"\n"
411  msg102 = "Type = "+str(type(self))+"\n"
412  msg103 = "Some possible solutions:\n"
413  msg104 = " 1. Clone modules instead of using simple assignment. Cloning is\n"
414  msg105 = " also preferred for other types when possible.\n"
415  msg106 = " 2. Declare new names starting with an underscore if they are\n"
416  msg107 = " for temporaries you do not want propagated into the Process. The\n"
417  msg108 = " underscore tells \"from x import *\" and process.load not to import\n"
418  msg109 = " the name.\n"
419  msg110 = " 3. Reorganize so the assigment is not necessary. Giving a second\n"
420  msg111 = " name to the same object usually causes confusion and problems.\n"
421  msg112 = " 4. Compose Sequences: newName = cms.Sequence(oldName)\n"
422  raise ValueError(msg100+msg101+msg102+msg103+msg104+msg105+msg106+msg107+msg108+msg109+msg110+msg111+msg112)
423  self.__label = label
424  def label(self):
425  #print "WARNING: _Labelable::label() needs to be changed to label_()"
426  return self.__label
427  def __str__(self):
428  #this is probably a bad idea
429  # I added this so that when we ask a path to print
430  # we will see the label that has been assigned
431  return str(self.__label)
433  return str(self.__label)
435  return 'process.'+str(self.__label)
436  def _findDependencies(self,knownDeps,presentDeps):
437  #print 'in labelled'
438  myDeps=knownDeps.get(self.label_(),None)
439  if myDeps!=None:
440  if presentDeps != myDeps:
441  raise RuntimeError("the module "+self.label_()+" has two dependencies \n"
442  +str(presentDeps)+"\n"
443  +str(myDeps)+"\n"
444  +"Please modify sequences to rectify this inconsistency")
445  else:
446  myDeps=set(presentDeps)
447  knownDeps[self.label_()]=myDeps
448  presentDeps.add(self.label_())
449 
450 
452  """A 'mixin' used to denote that the class can be used without a label (e.g. a Service)"""
453  pass
454 
455 class _ValidatingListBase(list):
456  """Base class for a list which enforces that its entries pass a 'validity' test"""
457  def __init__(self,*arg,**args):
458  super(_ValidatingListBase,self).__init__(arg)
459  if 0 != len(args):
460  raise SyntaxError("named arguments ("+','.join([x for x in args])+") passsed to "+str(type(self)))
461  if not self._isValid(iter(self)):
462  raise TypeError("wrong types ("+','.join([str(type(value)) for value in iter(self)])+
463  ") added to "+str(type(self)))
464  def __setitem__(self,key,value):
465  if isinstance(key,slice):
466  if not self._isValid(value):
467  raise TypeError("wrong type being inserted into this container "+self._labelIfAny())
468  else:
469  if not self._itemIsValid(value):
470  raise TypeError("can not insert the type "+str(type(value))+" in container "+self._labelIfAny())
471  super(_ValidatingListBase,self).__setitem__(key,value)
472  def _isValid(self,seq):
473  # see if strings get reinterpreted as lists
474  if isinstance(seq, str):
475  return False
476  for item in seq:
477  if not self._itemIsValid(item):
478  return False
479  return True
480  def append(self,x):
481  if not self._itemIsValid(x):
482  raise TypeError("wrong type being appended to container "+self._labelIfAny())
483  super(_ValidatingListBase,self).append(x)
484  def extend(self,x):
485  if not self._isValid(x):
486  raise TypeError("wrong type being extended to container "+self._labelIfAny())
487  super(_ValidatingListBase,self).extend(x)
488  def __add__(self,rhs):
489  if not self._isValid(rhs):
490  raise TypeError("wrong type being added to container "+self._labelIfAny())
491  import copy
492  value = copy.copy(self)
493  value.extend(rhs)
494  return value
495  def insert(self,i,x):
496  if not self._itemIsValid(x):
497  raise TypeError("wrong type being inserted to container "+self._labelIfAny())
498  super(_ValidatingListBase,self).insert(i,x)
499  def _labelIfAny(self):
500  result = type(self).__name__
501  if hasattr(self, '__label'):
502  result += ' ' + self.__label
503  return result
504 
506  def __init__(self,*arg,**args):
507  _ParameterTypeBase.__init__(self)
508  if len (arg) == 1 and not isinstance(arg[0],str):
509  try:
510  arg = iter(arg[0])
511  except TypeError:
512  pass
513  super(_ValidatingParameterListBase,self).__init__(*arg,**args)
514  def value(self):
515  return list(self)
516  def setValue(self,v):
517  self[:] = []
518  self.extend(v)
519  self._isModified=True
520  def configValue(self, options=PrintOptions()):
521  config = '{\n'
522  first = True
523  for value in iter(self):
524  options.indent()
525  config += options.indentation()
526  if not first:
527  config+=', '
528  config+= self.configValueForItem(value, options)+'\n'
529  first = False
530  options.unindent()
531  config += options.indentation()+'}\n'
532  return config
533  def configValueForItem(self,item, options):
534  return str(item)
535  def pythonValueForItem(self,item, options):
536  return self.configValueForItem(item, options)
537  def __repr__(self):
538  return self.dumpPython()
539  def dumpPython(self, options=PrintOptions()):
540  result = self.pythonTypeName()+"("
541  n = len(self)
542  if n>=256:
543  #wrap in a tuple since they don't have a size constraint
544  result+=" ("
545  indented = False
546  for i, v in enumerate(self):
547  if i == 0:
548  if hasattr(self, "_nPerLine"):
549  nPerLine = self._nPerLine
550  else:
551  nPerLine = 5
552  else:
553  if not indented:
554  indented = True
555  options.indent()
556  result += ', '
557  if i % nPerLine == 0:
558  result += '\n'+options.indentation()
559  result += self.pythonValueForItem(v,options)
560  if indented:
561  options.unindent()
562  #result+=', '.join((self.pythonValueForItem(v,options) for v in iter(self)))
563  if n>=256:
564  result +=' ) '
565  result += ')'
566  return result
567  @staticmethod
568  def _itemsFromStrings(strings,converter):
569  return (converter(x).value() for x in strings)
570 
571 def saveOrigin(obj, level):
572  #frame = inspect.stack()[level+1]
573  frame = inspect.getframeinfo(inspect.currentframe(level+1))
574  # not safe under old python versions
575  #obj._filename = frame.filename
576  #obj._lineNumber = frame.lineno
577  obj._filename = frame[0]
578  obj._lineNumber = frame[1]
579 
580 if __name__ == "__main__":
581 
582  import unittest
584  def _itemIsValid(self,item):
585  return True
586  class testMixins(unittest.TestCase):
588  t = TestList(1)
589  self.assertEqual(t,[1])
590  t = TestList((1,))
591  self.assertEqual(t,[1])
592  t = TestList("one")
593  self.assertEqual(t,["one"])
594  t = TestList( [1,])
595  self.assertEqual(t,[1])
596  t = TestList( (x for x in [1]) )
597  self.assertEqual(t,[1])
598 
599  t = TestList(1,2)
600  self.assertEqual(t,[1,2])
601  t = TestList((1,2))
602  self.assertEqual(t,[1,2])
603  t = TestList("one","two")
604  self.assertEqual(t,["one","two"])
605  t = TestList(("one","two"))
606  self.assertEqual(t,["one","two"])
607  t = TestList( [1,2])
608  self.assertEqual(t,[1,2])
609  t = TestList( (x for x in [1,2]) )
610  self.assertEqual(t,[1,2])
611  t = TestList( iter((1,2)) )
612  self.assertEqual(t,[1,2])
613 
614 
615  def testLargeList(self):
616  #lists larger than 255 entries can not be initialized
617  #using the constructor
618  args = [i for i in xrange(0,300)]
619 
620  t = TestList(*args)
621  pdump= t.dumpPython()
622  class cms(object):
623  def __init__(self):
624  self.TestList = TestList
625  pythonized = eval( pdump, globals(),{'cms':cms()} )
626  self.assertEqual(t,pythonized)
627  def testUsingBlock(self):
628  a = UsingBlock("a")
629  self.assert_(isinstance(a, _ParameterTypeBase))
630  def testCopy(self):
631  class __Test(_TypedParameterizable):
632  pass
633  class __TestType(_SimpleParameterTypeBase):
634  def _isValid(self,value):
635  return True
636  a = __Test("MyType",t=__TestType(1), u=__TestType(2))
637  b = a.copy()
638  self.assertEqual(b.t.value(),1)
639  self.assertEqual(b.u.value(),2)
640  def testClone(self):
641  class __Test(_TypedParameterizable):
642  pass
643  class __TestType(_SimpleParameterTypeBase):
644  def _isValid(self,value):
645  return True
646  a = __Test("MyType",t=__TestType(1), u=__TestType(2))
647  b = a.clone(t=3, v=__TestType(4))
648  self.assertEqual(a.t.value(),1)
649  self.assertEqual(a.u.value(),2)
650  self.assertEqual(b.t.value(),3)
651  self.assertEqual(b.u.value(),2)
652  self.assertEqual(b.v.value(),4)
653  self.assertRaises(TypeError,a.clone,None,**{"v":1})
654  def testModified(self):
655  class __TestType(_SimpleParameterTypeBase):
656  def _isValid(self,value):
657  return True
658  a = __TestType(1)
659  self.assertEqual(a.isModified(),False)
660  a.setValue(1)
661  self.assertEqual(a.isModified(),False)
662  a.setValue(2)
663  self.assertEqual(a.isModified(),True)
664  a.resetModified()
665  self.assertEqual(a.isModified(),False)
666  unittest.main()
def _valueFromString
Definition: Mixins.py:92
def testUsingBlock
Definition: Mixins.py:627
bool setValue(Container &, const reco::JetBaseRef &, const JetExtendedData &)
associate jet with value. Returns false and associate nothing if jet is already associated ...
def dumpSequenceConfig
Definition: Mixins.py:432
def saveOrigin
Definition: Mixins.py:571
def testListConstruction
Definition: Mixins.py:587
def dumpSequencePython
Definition: Mixins.py:434
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def _itemIsValid
Definition: Mixins.py:584
list object
Definition: dbtoconf.py:77
def _findDependencies
Definition: Mixins.py:436
if(conf.exists("allCellsPositionCalc"))
double split
Definition: MVATrainer.cc:139
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run