![]() |
|
|
Generated: 8 Jan 2009 |
00001 import os, sys, re 00002 00003 import logging 00004 _log = logging.getLogger(__name__) 00005 00006 class LogFormatter(logging.Formatter): 00007 def __init__(self, fmt=None, datefmt=None, prefix = "# "): 00008 logging.Formatter.__init__(self, fmt, datefmt) 00009 self.prefix = prefix 00010 def format(self, record): 00011 fmsg = logging.Formatter.format(self, record) 00012 prefix = self.prefix 00013 if record.levelno >= logging.WARNING: 00014 prefix += record.levelname + ": " 00015 s = "\n".join([ prefix + line 00016 for line in fmsg.splitlines() ]) 00017 return s 00018 00019 class LogFilter(logging.Filter): 00020 def __init__(self, name = ""): 00021 logging.Filter.__init__(self, name) 00022 self.printing_level = 0 00023 self.enabled = True 00024 self.threshold = logging.WARNING 00025 def filter(self, record): 00026 return record.levelno >= self.threshold or (self.enabled and self.printing_level <= 0) 00027 def printOn(self, step = 1, force = False): 00028 """ 00029 Decrease the printing_level of 'step' units. ( >0 means no print) 00030 The level cannot go below 0, unless the force flag is set to True. 00031 A negative value of the treshold disables subsequent "PrintOff"s. 00032 """ 00033 if force: 00034 self.printing_level -= step 00035 else: 00036 if self.printing_level > step: 00037 self.printing_level -= step 00038 else: 00039 self.printing_level = 0 00040 def printOff(self, step = 1): 00041 """ 00042 Increase the printing_level of 'step' units. ( >0 means no print) 00043 """ 00044 self.printing_level += step 00045 def disable(self, allowed = logging.WARNING): 00046 self.enabled = False 00047 self.threshold = allowed 00048 def enable(self, allowed = logging.WARNING): 00049 self.enabled = True 00050 self.threshold = allowed 00051 00052 class ConsoleHandler(logging.StreamHandler): 00053 def __init__(self, strm = None, prefix = None): 00054 if strm is None: 00055 strm = sys.stdout 00056 logging.StreamHandler.__init__(self, strm = strm) 00057 if prefix is None: 00058 prefix = "# " 00059 self._filter = LogFilter(_log.name) 00060 self._formatter = LogFormatter(prefix = prefix) 00061 self.setFormatter(self._formatter) 00062 self.addFilter(self._filter) 00063 def setPrefix(self, prefix): 00064 self._formatter.prefix = prefix 00065 def printOn(self, step = 1, force = False): 00066 """ 00067 Decrease the printing_level of 'step' units. ( >0 means no print) 00068 The level cannot go below 0, unless the force flag is set to True. 00069 A negative value of the treshold disables subsequent "PrintOff"s. 00070 """ 00071 self._filter.printOn(step, force) 00072 def printOff(self, step = 1): 00073 """ 00074 Increase the printing_level of 'step' units. ( >0 means no print) 00075 """ 00076 self._filter.printOff(step) 00077 def disable(self, allowed = logging.WARNING): 00078 self._filter.disable(allowed) 00079 def enable(self, allowed = logging.WARNING): 00080 self._filter.enable(allowed) 00081 00082 _consoleHandler = None 00083 def GetConsoleHandler(prefix = None): 00084 global _consoleHandler 00085 if _consoleHandler is None: 00086 _consoleHandler = ConsoleHandler(prefix = prefix) 00087 elif prefix is not None: 00088 _consoleHandler.setPrefix(prefix) 00089 return _consoleHandler 00090 00091 def InstallRootLoggingHandler(prefix = None, level = None): 00092 root_logger = logging.getLogger() 00093 if not root_logger.handlers: 00094 root_logger.addHandler(GetConsoleHandler(prefix)) 00095 root_logger.setLevel(logging.WARNING) 00096 if level is not None: 00097 root_logger.setLevel(level) 00098 00099 def PrintOn(step = 1, force = False): 00100 GetConsoleHandler().printOn(step, force) 00101 def PrintOff(step = 1): 00102 GetConsoleHandler().printOff(step) 00103 00104 class ParserError(RuntimeError): 00105 pass 00106 00107 def _find_file(f): 00108 # expand environment variables in the filename 00109 f = os.path.expandvars(f) 00110 if os.path.isfile(f): 00111 return os.path.realpath(f) 00112 00113 path = os.environ.get('JOBOPTSEARCHPATH','').split(os.pathsep) 00114 # find the full path to the option file 00115 candidates = [d for d in path if os.path.isfile(os.path.join(d,f))] 00116 if not candidates: 00117 raise ParserError("Cannot find '%s' in %s" % (f,path)) 00118 return os.path.realpath(os.path.join(candidates[0],f)) 00119 00120 _included_files = set() 00121 def _to_be_included(f): 00122 if f in _included_files: 00123 _log.warning("file '%s' already included, ignored.", f) 00124 return False 00125 _included_files.add(f) 00126 return True 00127 00128 class JobOptsParser: 00129 comment = re.compile(r'(//.*)$') 00130 # non-perfect R-E to check if '//' is inside a string 00131 # (a tokenizer would be better) 00132 comment_in_string = re.compile(r'(["\']).*//.*\1') 00133 directive = re.compile(r'^\s*#\s*([\w!]+)\s*(.*)\s*$') 00134 comment_ml = ( re.compile(r'/\*'), re.compile(r'\*/') ) 00135 statement_sep = ";" 00136 reference = re.compile(r'^@([\w.]*)$') 00137 00138 def __init__(self): 00139 # parser level states 00140 self.units = {} 00141 self.defines = {} 00142 if sys.platform != 'win32': 00143 self.defines[ "WIN32" ] = True 00144 00145 def _include(self,file,function): 00146 file = _find_file(file) 00147 if _to_be_included(file): 00148 _log.info("--> Including file '%s'", file) 00149 function(file) 00150 _log.info("<-- End of file '%s'", file) 00151 00152 def parse(self,file): 00153 # states for the "translation unit" 00154 statement = "" 00155 00156 ifdef_level = 0 00157 ifdef_skipping = False 00158 ifdef_skipping_level = 0 00159 00160 f = open(_find_file(file)) 00161 l = f.readline() 00162 if l.startswith("#!"): 00163 # Skip the first line if it starts with "#!". 00164 # It allows to use options files as scripts. 00165 l = f.readline() 00166 00167 while l: 00168 l = l.rstrip()+'\n' # normalize EOL chars (to avoid problems with DOS new-line on Unix) 00169 00170 # single line comment 00171 m = self.comment.search(l) 00172 if m: 00173 # check if the '//' is part of a string 00174 m2 = self.comment_in_string.search(l) 00175 # the '//' is part of a string if we find the quotes around it 00176 # and they are not part of the comment itself 00177 if not ( m2 and m2.start() < m.start() ): 00178 # if it is not the case, we can remove the comment from the 00179 # statement 00180 l = l[:m.start()]+l[m.end():] 00181 # process directives 00182 m = self.directive.search(l) 00183 if m: 00184 directive_name = m.group(1) 00185 directive_arg = m.group(2).strip() 00186 if directive_name == "include": 00187 included_file = directive_arg.strip("'\"") 00188 importOptions(included_file) 00189 elif directive_name == "units": 00190 units_file = directive_arg.strip("'\"") 00191 self._include(units_file,self._parse_units) 00192 elif directive_name in [ "ifdef", "ifndef"]: 00193 ifdef_skipping_level = ifdef_level 00194 ifdef_level += 1 00195 if directive_arg in self.defines: 00196 ifdef_skipping = directive_name == "ifndef" 00197 else: 00198 ifdef_skipping = directive_name == "ifdef" 00199 elif directive_name == "else": 00200 ifdef_skipping = not ifdef_skipping 00201 elif directive_name == "endif": 00202 ifdef_level -= 1 00203 if ifdef_skipping and ifdef_skipping_level == ifdef_level: 00204 ifdef_skipping = False 00205 elif directive_name == "pragma": 00206 if not directive_arg: 00207 l = f.readline() 00208 continue 00209 pragma = directive_arg.split() 00210 if pragma[0] == "print": 00211 if len(pragma) > 1: 00212 if pragma[1].upper() in [ "ON", "TRUE", "1" ]: 00213 PrintOn() 00214 else: 00215 PrintOff() 00216 else: 00217 _log.warning("unknown directive '%s'", directive_name) 00218 l = f.readline() 00219 continue 00220 00221 if ifdef_skipping: 00222 l = f.readline() 00223 continue 00224 00225 # multi-line comment 00226 m = self.comment_ml[0].search(l) 00227 if m: 00228 l,l1 = l[:m.start()],l[m.end():] 00229 m = self.comment_ml[1].search(l1) 00230 while not m: 00231 l1 = f.readline() 00232 if not l1: 00233 break # EOF 00234 m = self.comment_ml[1].search(l1) 00235 if not l1 and not m: 00236 raise ParserError("End Of File reached before end of multi-line comment") 00237 l += l1[m.end():] 00238 00239 if self.statement_sep in l: 00240 i = l.index(self.statement_sep) 00241 statement += l[:i] 00242 self._eval_statement(statement.replace("\n","").strip()) 00243 statement = l[i+1:] 00244 # it may happen (bug #37479) that the rest of the statement 00245 # contains a comment. 00246 if statement.lstrip().startswith("//"): 00247 statement = "" 00248 else: 00249 statement += l 00250 00251 l = f.readline() 00252 00253 def _parse_units(self,file): 00254 for line in open(file): 00255 if '//' in line: 00256 line = line[:line.index('//')] 00257 line = line.strip() 00258 if not line: 00259 continue 00260 nunit, value = line.split('=') 00261 factor, unit = nunit.split() 00262 value = eval(value)/eval(factor) 00263 self.units[unit] = value 00264 00265 def _eval_statement(self,statement): 00266 from GaudiKernel.Proxy.Configurable import (ConfigurableGeneric, 00267 Configurable, 00268 PropertyReference) 00269 #statement = statement.replace("\n","").strip() 00270 _log.info("%s%s", statement, self.statement_sep) 00271 00272 property,value = statement.split("=",1) 00273 00274 inc = None 00275 if property[-1] in [ "+", "-" ]: 00276 inc = property[-1] 00277 property = property[:-1] 00278 00279 property = property.strip() 00280 value = value.strip() 00281 00282 ## find the configurable to apply the property to 00283 #parent_cfg = None 00284 #while '.' in property: 00285 # component, property = property.split('.',1) 00286 # if parent_cfg: 00287 # if hasattr(parent_cfg,component): 00288 # cfg = getattr(parent_cfg,component) 00289 # else: 00290 # cfg = ConfigurableGeneric(component) 00291 # setattr(parent_cfg,component,cfg) 00292 # else: 00293 # cfg = ConfigurableGeneric(component) 00294 # parent_cfg = cfg 00295 00296 # remove spaces around dots 00297 property = '.'.join([w.strip() for w in property.split('.')]) 00298 component, property = property.rsplit('.',1) 00299 if component in Configurable.allConfigurables: 00300 cfg = Configurable.allConfigurables[component] 00301 else: 00302 cfg = ConfigurableGeneric(component) 00303 00304 #value = os.path.expandvars(value) 00305 value = value.replace('true','True').replace('false','False') 00306 if value[0] == '{' : 00307 # Try to guess if the values looks like a dictionary 00308 if ':' in value and not ( value[:value.index(':')].count('"')%2 or value[:value.index(':')].count("'")%2 ) : 00309 # for dictionaries, keep the surrounding {} 00310 value = '{'+value[1:-1].replace('{','[').replace('}',']')+'}' 00311 else : # otherwise replace all {} with [] 00312 value = value.replace('{','[').replace('}',']') 00313 00314 # We must escape '\' because eval tends to interpret them 00315 value = value.replace('\\','\\\\') 00316 00317 # interprete the @ operator 00318 m = self.reference.match(value) 00319 if m: 00320 # this allows late binding of references 00321 value = PropertyReference(m.group(1)) 00322 else: 00323 value = eval(value,self.units) 00324 00325 #if type(value) is str : value = os.path.expandvars(value) 00326 #elif type(value) is list : value = [ type(item) is str and os.path.expandvars(item) or item for item in value ] 00327 00328 if property not in cfg.__slots__ and not hasattr(cfg,property): 00329 # check if the case of the property is wrong (old options are case insensitive) 00330 lprop = property.lower() 00331 for p in cfg.__slots__: 00332 if lprop == p.lower(): 00333 _log.warning("property '%s' was requested for %s, but the correct spelling is '%s'", property, cfg.name(), p) 00334 property = p 00335 break 00336 00337 # consider the += and -= 00338 if inc == "+": 00339 if hasattr(cfg,property): 00340 prop = getattr(cfg,property) 00341 if type(prop) == dict: 00342 for k in value: 00343 prop[k] = value[k] 00344 else: 00345 prop += value 00346 else: 00347 setattr(cfg,property,value) 00348 elif inc == "-": 00349 if hasattr(cfg,property): 00350 prop = getattr(cfg,property) 00351 if type(prop) is dict: 00352 for k in value: 00353 if k in prop: 00354 del prop[k] 00355 else: 00356 _log.warning("key '%s' not in %s.%s", k, cfg.name(), property) 00357 else: 00358 for k in value: 00359 if k in prop: 00360 prop.remove(k) 00361 else: 00362 _log.warning("value '%s' not in %s.%s", k, cfg.name(), property) 00363 else: 00364 setattr(cfg,property,value) 00365 00366 class _TempSysPath: 00367 def __init__(self, new_path): 00368 self.old_path = sys.path 00369 sys.path = new_path 00370 def __del__(self): 00371 sys.path = self.old_path 00372 00373 _parser = JobOptsParser() 00374 00375 def _import_python(file): 00376 execfile(file, {}) 00377 00378 def _import_pickle(file): 00379 import pickle 00380 input = open(file, 'rb') 00381 catalog = pickle.load(input) 00382 _log.info('Unpickled %d configurables', len(catalog)) 00383 00384 def _import_opts(file): 00385 _parser.parse(file) 00386 00387 _import_function_mapping = { 00388 ".py" : _import_python, 00389 ".pkl" : _import_pickle, 00390 ".opts" : _import_opts, 00391 } 00392 00393 def importOptions( optsfile ) : 00394 # expand environment variables before checking the extension 00395 optsfile = os.path.expandvars(optsfile) 00396 # check the file type (extension) 00397 dummy, ext = os.path.splitext(optsfile) 00398 if ext in _import_function_mapping: 00399 # check if the file has been already included 00400 optsfile = _find_file(optsfile) 00401 if _to_be_included(optsfile): 00402 _log.info("--> Including file '%s'", optsfile) 00403 # include the file 00404 _import_function_mapping[ext](optsfile) 00405 _log.info("<-- End of file '%s'", optsfile) 00406 else: 00407 raise ParserError("Unknown file type '%s' ('%s')" % (ext,optsfile)) 00408