3 __author__ =
"clat@hep.ph.bham.ac.uk"
6 from PyJobTransformsCore.trferr
import TransformConfigError
7 from future.utils
import with_metaclass
11 """Base class for all fixed types, implemented as descriptors. Each instance of a specific fixed type
12 (a derived class) can be set either to None (to indicate no value has been set), or to a type specified
13 by the derived class. The type is checked by the derived class by implementing the member function
16 def __init__(self,doc,default,allowedValues=None):
20 if allowedValues
is not None:
22 allowedValues.__iter__
23 except AttributeError:
24 allowedValues = [ allowedValues ]
25 varName =
"%s.allowedValues" % self.
__name
28 if default
is not None:
30 varName =
"%s default" % self.
__name
42 varName =
"%s.%s" % (instance.name(), self.
__name)
50 instance._attributeDictionary()[self.
_attributeName(instance)] = value
54 return '_%s__%s' % (instance.__class__.__name__,self.
__name)
66 """The documentation string"""
71 """The help string: type, documentation and possible values"""
72 hlp =
'(%s) %s' % (self.__class__.__name__, self.
__doc)
74 hlp +=
'. Possible values: %s' % self.
__allowed
84 """Private helper functin to check the type of <value>. May convert to another type.
85 This implementation does nothing, it just returns value.
86 This function can be overridden in derived class to do type checking.
87 It should return the value (possible with new type), or raise a TransformConfigError
88 exception in case of problems. """
93 """Private helper function to check the value of <value>. This function is
94 called after calling _checkType. <value> can therefore be considered to be
96 This implementation checks that the value is one of the allowed values (if defined).
97 This function can be overridden in derived class to do type & additional value checking.
98 It has to return the value (adapted if needed) if all is OK. It has to raise
99 a TransformConfigError exception in case of problems.
100 <variableName> is the name of the variable that is being set and is typically
101 only used for error messages."""
103 raise TransformConfigError(
'%s value %r is not in %s' %
110 """Private helper function which is called when the value of the object is set.
111 It is called after _checkType() and _checkValue(), so the value can be
112 assumed to be correct.
113 This function can be overridden in a derived class, typically to trigger additional action
114 when the value is set.
115 In case of error, raise a TransformConfigError exception, otherwise just return.
116 This implementation does nothing.
117 <variableName> is the name of the variable that is being set and is typically
118 only used for error messages."""
124 for v
in allowedValues:
125 self._checkType(variableName,v)
131 def __init__(self,doc,default='',allowedValues=None):
132 Descriptor.__init__(self,doc,default,allowedValues)
136 valType =
type(value).__name__
138 raise TransformConfigError(
'%s should be a string. Got %s instead.' % (variableName, valType) )
141 return Descriptor._checkValue(self,variableName,value)
147 Descriptor.__init__(self,doc,default,[
True,
False])
153 Descriptor.__init__(self,doc,default,allowedValues)
160 raise TransformConfigError(
'%s value %s is not a float' % (variableName, value) )
163 return Descriptor._checkValue(self,variableName,value)
169 Descriptor.__init__(self,doc,default,allowedValues)
177 raise TransformConfigError(
'%s value %s is not an int' % (variableName, value) )
179 if value !=
float(inValue):
180 raise TransformConfigError(
'%s value %s is not an int' % (variableName, value) )
183 return Descriptor._checkValue(self,variableName,value)
188 """List with a unique set of entries (duplicates are removed).
189 List of allowed values are the entries that are allowed in the list."""
190 def __init__(self,doc,default=None,allowedValues=None):
191 if default
is None: default = []
192 Descriptor.__init__(self,doc,default,allowedValues)
196 """Check that <value> is of type list of tuple, and convert to a list if it is a tuple."""
210 raise TransformConfigError(
'%s should be a list or tuple. Got %s instead.' %
211 ( variableName,
type( value ).__name__ ) )
220 raise TransformConfigError(
'%s value %r is not one of %s' %
221 (variableName, value, allowed) )
226 return list( newValue )
232 for v
in allowedValues:
233 newAllowed.add( self.
_checkType(variableName,[v])[0] )
234 return list( newAllowed )
239 """List with a unique set of strings (duplicates are removed)"""
240 def __init__(self,doc,default=None,allowedValues=None):
241 UniqueList.__init__(self,doc,default,allowedValues)
245 """Add check that all entries are strings"""
247 value = UniqueList._checkType(self,variableName,value)
250 if not isinstance( v, str ):
251 raise TransformConfigError(
"Entry %r in %s is not a string (but an %s)" %
252 ( v, variableName,
type( v ).__name__ ) )
259 """Any python (string) boolean expression where 'value' stands for the value.
260 For example, for a number range check: \"0 < value < 100\" """
290 if '__slots__' not in dict:
291 raise TransformConfigError(
'Class %s does not have member __slots__. Please add __slots__ = ()'
292 ' to the class definition' % (name,) )
297 slots = self.__slots__
298 descrName =
'__properties'
299 if descrName
not in slots:
301 slots += ( descrName, )
303 dict[
'__slots__'] = slots
305 setattr(self,descrName,[])
306 descrList = getattr(self,descrName)
308 for n,attrib
in dict.items():
309 if isinstance(attrib,Descriptor):
310 setattr(attrib,
'_Descriptor__name',n)
311 descrList.append(attrib)
313 type.__init__(self,name,bases,dict)
318 __slots__ = (
'__name',
'__attributes' )
321 """name is used in printout. The default name is derived from the filename of the python file
322 where constructor is called"""
325 filename = os.path.basename(sys._getframe(1).f_code.co_filename)
326 name = os.path.splitext(filename)[0]
336 value = getattr(self,name)
337 me.append(
"%s.%s = %r \t# %s" % (prefix,name,value,descr.help()) )
338 prefix =
' '*len(prefix)
339 return os.linesep.join(me)
343 """Return the total number of properties (this class plus all base classes)"""
348 """Provide iteration over the full list of properties (the Descriptors, not the values)"""
353 """Dictionary to store the values. Used in the class Descriptor."""
358 """Return the full list of properties (the descriptors, not the values)"""
361 for cl
in self.__class__.__bases__ + (self.__class__,):
362 descr += getattr(cl,
'__properties')
367 """Get the property (the descriptor, not the value) named <name>.
368 It raises an TransformConfigError if the class does not have proporty <name>."""
369 for d
in self.properties():
370 if d.name() == name:
return d
372 raise TransformConfigError(
'class %s does not have property %s' % (self.__class__.__name__, name))
376 """Return bool indicating if this class has property <name>"""
378 self.getProperty(name)
380 except TransformConfigError:
385 """Dictionary with the attibutes (name:value). It is a copy to make it read-only."""
386 return self.__attributes.
copy()
390 """The name of the configuration instance (mainly for printout)"""
396 """Configuration class for JobTransforms.
397 It has a number of properties shared among all jobtransforms. Those properties are
398 processed by class JobTransform."""
399 __slots__ = (
'__metadata', )
402 auxfiles =
ListOfStrings(
"List of needed auxiliary files (to be found in DATAPATH)")
404 maxeventsfactor =
Float(
"maximum number of output events = minevents * maxeventsfactor",2.0,
406 maxeventsstrategy =
String(
"what to do if number of input events is less than maxEvents",
'INPUTEVENTS',
407 [
'IGNORE',
'INPUTEVENTS',
'ABORT'])
412 """name is used in printout. The default name is derived from the filename of the python file
413 where constructor is called"""
414 JobConfig.__init__(self,name)
416 if metaData
is not None:
417 propertyNames = [ d.name()
for d
in self.
properties() ]
419 if m
not in propertyNames:
420 raise TransformConfigError(
'Requested metadata %s is not an attribute of %s' % (m,self.name()) )
425 me = JobConfig.__str__(self,prefix)
429 prefix +=
' '*len(self.name())
430 me += os.linesep + prefix +
'MetaData attributes: '
439 """A dictionary of metadata to be added to the metadata of the output files.
440 Values set to None will not be added to the list. Values will be converted to strings.
441 Python lists will be transformed to a string with comma separated entries."""
444 value = getattr(self,name)
445 if value
is not None:
447 if type(value).__name__ ==
'list': value =
','.
join([
str(v)
for v
in value])
448 meta[name] =
str(value)