Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-12-01 23:40:20

0001 #------------------------------------------------------------
0002 #
0003 #
0004 # cmsconfig: a class to provide convenient access to the Python form
0005 # of a parsed CMS configuration file.
0006 #
0007 # Note: we have not worried about security. Be careful about strings
0008 # you put into this; we use a naked 'eval'!
0009 #
0010 #------------------------------------------------------------
0011 #
0012 # Tests for this class need to be run 'by hand', until we figure out
0013 # how to make use of the SCRAMV1 tools for running tests on Python
0014 # code.
0015 #
0016 #------------------------------------------------------------
0017 # TODO: We need some refactoring to handle the writing of module-like
0018 # objects more gracefully. Right now, we have to pull the classname
0019 # out of the dictionary in more than one place. Try making a class
0020 # which represents the module, which contains the dictionary now used,
0021 # and knows about special features: the class name, now to print the
0022 # guts without repeating the classname, etc.
0023 #------------------------------------------------------------
0024 
0025 import io
0026 import types
0027 
0028 # TODO: Refactor pset_dict_to_string and class printable_parameter to
0029 # have a consistent view of the problem. Perhaps have a class
0030 # representing the configuration data for a PSet object, rather than
0031 # just using a dictionary instance. See also __write_module_guts,
0032 # which should be refactored at the same time.
0033 
0034 def pset_dict_to_string(psetDict):
0035     """Convert dictionary representing a PSet to a string consistent
0036     with the configuration grammar."""
0037     stream = io.StringIO()
0038     stream.write('\n{\n')
0039 
0040     for name, value in psetDict.items():
0041         stream.write('%s' % printable_parameter(name, value))
0042         stream.write('\n')        
0043 
0044     stream.write('}\n')
0045     return stream.getvalue()
0046 
0047 
0048 def secsource_dict_to_string(secSourceDict):
0049     """Make a string representing the secsource"""
0050     stream = io.StringIO()
0051     stream.write("%s\n{\n" %  secSourceDict["@classname"][2])
0052     for name, value in secSourceDict.items():
0053         if name[0] != '@':
0054             stream.write('%s' % printable_parameter(name, value))
0055             stream.write('\n')
0056 
0057     stream.write('}\n')
0058     return stream.getvalue()
0059 
0060 
0061 class printable_parameter:
0062     """A class to provide automatic unpacking of the tuple (triplet)
0063     representation of a single parameter, suitable for printing.
0064 
0065     Note that 'value' may in fact be a list."""
0066 
0067     def __init__(self, aName, aValueTuple):
0068         self.name = aName
0069         self.type, self.trackedCode, self.value = aValueTuple
0070         # Because the configuration grammar treats tracked status as
0071         # the default, we only have to write 'untracked' as the
0072         # tracking code if the parameter is untracked.        
0073         if self.trackedCode == "tracked":
0074             self.trackedCode = ""
0075         else:
0076             self.trackedCode = "untracked " # trailing space is needed
0077 
0078         # We need special handling of some of the parameter types.
0079         if self.type in ["vbool", "vint32", "vuint32", "vdouble", "vstring", "VInputTag", "VESInputTag"]:   
0080             # TODO: Consider using cStringIO, if this is observed
0081             # to be a bottleneck. This may happen if many large
0082             # vectors are used in parameter sets.
0083             temp = '{'
0084             # Write out values as a comma-separated list
0085             temp += ", ".join(self.value)
0086             temp += '}'
0087             self.value = temp
0088 
0089         if self.type == "PSet":
0090             self.value = pset_dict_to_string(self.value)
0091         if self.type == "secsource":
0092             self.value = secsource_dict_to_string(self.value)
0093         if self.type == "VPSet":
0094             temp = '{'
0095             tup = [ pset_dict_to_string(x) for x in self.value ]
0096             temp += ", ".join( tup )
0097             temp += '}'
0098             self.value = temp
0099 
0100     def __str__(self):
0101         """Print this parameter in the right format for a
0102         configuration file."""
0103         s = "%(trackedCode)s%(type)s %(name)s = %(value)s" % self.__dict__
0104         return s    
0105 
0106 # I'm not using new-style classes, because I'm not sure that we can
0107 # rely on a new enough version of Python to support their use.
0108 
0109 class cmsconfig:
0110     """A class to provide convenient access to the contents of a
0111     parsed CMS configuration file."""
0112 
0113     def __init__(self, stringrep):
0114         """Create a cmsconfig object from the contents of the (Python)
0115         exchange format for configuration files."""
0116         self.psdata = eval(stringrep)
0117 
0118     def numberOfModules(self):
0119         return len(self.psdata['modules'])
0120 
0121     def numberOfOutputModules(self):
0122         return len(self.outputModuleNames())
0123 
0124     def moduleNames(self):
0125         """Return the names of modules. Returns a list."""
0126         return self.psdata['modules'].keys()
0127 
0128     def module(self, name):
0129         """Get the module with this name. Exception raised if name is
0130         not known. Returns a dictionary."""
0131         return self.psdata['modules'][name]
0132 
0133     def psetNames(self):
0134         """Return the names of psets. Returns a list."""
0135         return self.psdata['psets'].keys()
0136 
0137     def pset(self, name):
0138         """Get the pset with this name. Exception raised if name is
0139         not known. Returns a dictionary."""
0140         return self.psdata['psets'][name]
0141 
0142     def outputModuleNames(self):
0143         return self.psdata['output_modules']
0144 
0145     def moduleNamesWithSecSources(self):
0146         return self.psdata['modules_with_secsources']
0147 
0148     def esSourceNames(self):
0149         """Return the names of all ESSources. Names are of the form '<C++ type>@<label>' where
0150         label can be empty. Returns a list."""
0151         return self.psdata['es_sources'].keys()
0152 
0153     def esSource(self, name):
0154         """Get the ESSource with this name. Exception raised if name is
0155         not known. Returns a dictionary."""
0156         return self.psdata['es_sources'][name]
0157 
0158     def esModuleNames(self):
0159         """Return the names of all ESModules. Names are of the form '<C++ type>@<label>' where
0160         label can be empty. Returns a list."""
0161         return self.psdata['es_modules'].keys()
0162 
0163     def esModule(self, name):
0164         """Get the ESModule with this name. Exception raised if name is
0165         not known. Returns a dictionary."""
0166         return self.psdata['es_modules'][name]
0167 
0168     def esPreferNames(self):
0169         """Return the names of all es_prefer statements. Names are of the form 'esprefer_<C++ type>@<label>' where
0170         label can be empty. Returns a list."""
0171         return self.psdata['es_prefers'].keys()
0172 
0173     def esPrefer(self, name):
0174         """Get the es_prefer statement with this name. Exception raised if name is
0175         not known. Returns a dictionary."""
0176         return self.psdata['es_prefers'][name]
0177 
0178     def serviceNames(self):
0179         """Return the names of all Services. Names are actually the C++ class names
0180         Returns a list."""
0181         return self.psdata['services'].keys()
0182 
0183     def service(self, name):
0184         """Get the Service with this name. Exception raised if name is
0185         not known. Returns a dictionary."""
0186         return self.psdata['services'][name]
0187 
0188     def pathNames(self):
0189         return self.psdata['paths'].keys()
0190 
0191     def path(self, name):
0192         """Get the path description for the path of the given
0193         name. Exception raised if name is not known. Returns a
0194         string."""
0195         return self.psdata['paths'][name]
0196 
0197     def schedule(self):
0198         return self.psdata['schedule']
0199 
0200     def sequenceNames(self):
0201         return self.psdata['sequences'].keys()
0202 
0203     def sequence(self, name):
0204         """Get the sequence description for the sequence of the given
0205         name. Exception raised if name is not known. Returns a
0206         string."""
0207         return self.psdata['sequences'][name]
0208 
0209     def endpathNames(self):
0210         return self.psdata['endpaths'].keys()
0211 
0212     def endpath(self, name):
0213         """Return the endpath description, as a string."""
0214         return self.psdata['endpaths'][name]
0215 
0216     def mainInputSource(self):
0217         """Return the description of the main input source, as a
0218         dictionary."""
0219         return self.psdata['main_input']
0220 
0221     def looper(self):
0222         """Return the description of the looper, as a
0223         dictionary."""
0224         return self.psdata['looper']
0225 
0226     def procName(self):
0227         """Return the process name, a string"""
0228         return self.psdata['procname']
0229 
0230     def asConfigurationString(self):
0231         """Return a string conforming to the configuration file
0232         grammar, encoding this configuration."""
0233 
0234         # Let's try to make sure we lose no resources if something
0235         # fails in formatting...
0236         result = ""
0237 
0238         try:
0239             stream = io.StringIO()
0240             self.__write_self_to_stream(stream)
0241             result = stream.getvalue()
0242 
0243         finally:
0244             stream.close()
0245 
0246         return result
0247 
0248     def asPythonString(self):
0249         """Return a string containing the python psdata source of
0250         this object to facilitate saving and loading of python format"""
0251         result = "#!/usr/bin/env python\n"
0252         result += str(self.psdata)
0253         return result 
0254 
0255     def __write_self_to_stream(self, fileobj):
0256         """Private method.
0257         Return None.
0258         Write the contents of self to the file-like object fileobj."""
0259 
0260         # Write out the process block
0261         fileobj.write('process %s = \n{\n' % self.procName())
0262         self.__write_process_block_guts(fileobj)
0263         fileobj.write('}\n')
0264 
0265     def __write_process_block_guts(self, fileobj):
0266         """Private method.
0267         Return None.
0268         Write the guts of the process block to the file-like object
0269         fileobj."""
0270 
0271         # TODO: introduce, and deal with, top-level PSet objects and
0272         # top-level block objects.        
0273         self.__write_main_source(fileobj)
0274         self.__write_looper(fileobj)
0275         self.__write_psets(fileobj)
0276         self.__write_es_sources(fileobj)        
0277         self.__write_es_modules(fileobj)
0278         self.__write_es_prefers(fileobj)
0279         self.__write_modules(fileobj)
0280         self.__write_services(fileobj)
0281         self.__write_sequences(fileobj)
0282         self.__write_paths(fileobj)
0283         self.__write_endpaths(fileobj)
0284         self.__write_schedule(fileobj)
0285 
0286     def __write_psets(self, fileobj):
0287         """Private method.
0288         Return None
0289         Write all the psets to the file-like object fileobj."""
0290         for name in self.psetNames():
0291             psettuple = self.pset(name)
0292             # 8/2006: Wasn't writing trackedness!  Just re-use code
0293             # for embedded PSets
0294             fileobj.write('%s' % printable_parameter(name, psettuple))
0295             #fileobj.write("PSet %s = \n{\n" % (name) )
0296             #psetdict = psettuple[2]
0297             #self.__write_module_guts(psetdict, fileobj)
0298             #fileobj.write('}\n')
0299 
0300     def __write_modules(self, fileobj):
0301         """Private method.
0302         Return None
0303         Write all the modules to the file-like object fileobj."""
0304         for name in self.moduleNames():
0305             moddict = self.module(name)
0306             fileobj.write("module %s = %s\n{\n" % (name, moddict['@classname'][2]))
0307             self.__write_module_guts(moddict, fileobj)
0308             fileobj.write('}\n')
0309 
0310     def __write_es_sources(self, fileobj):
0311         """Private method.
0312         Return None
0313         Write all ESSources to the file-like object
0314         fileobj."""
0315         for name in self.esSourceNames():
0316             es_source_dict = self.esSource(name)
0317             fileobj.write("es_source %s = %s\n{\n" % (es_source_dict['@label'][2], es_source_dict['@classname'][2]))
0318             self.__write_module_guts(es_source_dict, fileobj)
0319             fileobj.write('}\n')
0320 
0321     def __write_es_modules(self, fileobj):
0322         """Private method.
0323         Return None
0324         Write all ESModules to the file-like object
0325         fileobj."""
0326         for name in self.esModuleNames():
0327             es_mod_dict = self.esModule(name)
0328             fileobj.write("es_module %s = %s\n{\n" % (es_mod_dict['@label'][2], es_mod_dict['@classname'][2]))
0329             self.__write_module_guts(es_mod_dict, fileobj)
0330             fileobj.write('}\n')
0331 
0332     def __write_es_prefers(self, fileobj):
0333         """Private method.
0334         Return None
0335         Write all es_prefer statements to the file-like object
0336         fileobj."""
0337         for name in self.esPreferNames():
0338             es_mod_dict = self.esPrefer(name)
0339             fileobj.write("es_prefer %s = %s\n{\n" % (es_mod_dict['@label'][2], es_mod_dict['@classname'][2]))
0340             self.__write_module_guts(es_mod_dict, fileobj)
0341             fileobj.write('}\n')
0342 
0343     def __write_services(self, fileobj):
0344         """Private method.
0345         Return None
0346         Write all Services to the file-like object
0347         fileobj."""
0348         for name in self.serviceNames():
0349             es_mod_dict = self.service(name)
0350             fileobj.write("service = %s\n{\n" % (es_mod_dict['@classname'][2]))
0351             self.__write_module_guts(es_mod_dict, fileobj)
0352             fileobj.write('}\n')
0353 
0354     def __write_sequences(self, fileobj):
0355         """Private method.
0356         Return None
0357         Write all the sequences to the file-like object fileobj."""
0358         for name in self.sequenceNames():
0359             fileobj.write("sequence %s = {%s}\n"  % (name, self.sequence(name)))
0360 
0361 
0362     def __write_paths(self, fileobj):
0363         """Private method.
0364         Return None
0365         Write all the paths to the file-like object fileobj."""
0366         for name in self.pathNames():
0367             fileobj.write("path %s = {%s}\n" % (name, self.path(name)))
0368 
0369 
0370     def __write_endpaths(self, fileobj):
0371         """Private method.
0372         Return None
0373         Write all the endpaths to the file-like object
0374         fileobj."""
0375         for name in self.endpathNames():
0376             fileobj.write("endpath %s = {%s}\n" % (name, self.endpath(name)))
0377 
0378     def __write_schedule(self, fileobj):
0379         fileobj.write("schedule = {%s}\n" % self.schedule())
0380 
0381     def __write_main_source(self, fileobj):
0382         """Private method.
0383         Return None
0384         Write the (main) source block to the file-like object
0385         fileobj."""
0386         mis = self.mainInputSource()  # this is a dictionary
0387         if mis:
0388             fileobj.write('source = %s\n{\n' % mis['@classname'][2])
0389             self.__write_module_guts(mis, fileobj)
0390             fileobj.write('}\n')
0391 
0392     def __write_looper(self, fileobj):
0393         """Private method.
0394         Return None
0395         Write the looper block to the file-like object
0396         fileobj."""
0397         mis = self.looper()  # this is a dictionary
0398         if mis:
0399             fileobj.write('looper = %s\n{\n' % mis['@classname'][2])
0400             self.__write_module_guts(mis, fileobj)
0401             fileobj.write('}\n')
0402 
0403 
0404     def __write_module_guts(self, moddict, fileobj):
0405         """Private method.
0406         Return None
0407         Print the body of the block for this 'module'. This includes
0408         all the dictionary contents except for the classname (because
0409         the classname does not appear within the block).
0410 
0411         NOTE: This should probably be a static method, because it doesn't
0412         use any member data of the object, but I'm not sure we can
0413         rely on a new-enough version of Python to make use of static
0414         methods."""
0415         for name, value in moddict.items():
0416             if name[0] != '@':
0417                 fileobj.write('%s' % printable_parameter(name, value))
0418                 fileobj.write('\n')
0419 
0420 
0421 
0422 if __name__ == "__main__":
0423     from sys import argv
0424     filename = "complete.pycfg"
0425     if len(argv) > 1:
0426         filename = argv[1]
0427 
0428     txt = file(filename).read()
0429     cfg = cmsconfig(txt)
0430     print(cfg.asConfigurationString())
0431 
0432