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 if '2.7' in sys.version.split()[0]:
00057 logging.StreamHandler.__init__(self, stream = strm)
00058 else:
00059 logging.StreamHandler.__init__(self, strm = strm)
00060 if prefix is None:
00061 prefix = "# "
00062 self._filter = LogFilter(_log.name)
00063 self._formatter = LogFormatter(prefix = prefix)
00064 self.setFormatter(self._formatter)
00065 self.addFilter(self._filter)
00066 def setPrefix(self, prefix):
00067 self._formatter.prefix = prefix
00068 def printOn(self, step = 1, force = False):
00069 """
00070 Decrease the printing_level of 'step' units. ( >0 means no print)
00071 The level cannot go below 0, unless the force flag is set to True.
00072 A negative value of the treshold disables subsequent "PrintOff"s.
00073 """
00074 self._filter.printOn(step, force)
00075 def printOff(self, step = 1):
00076 """
00077 Increase the printing_level of 'step' units. ( >0 means no print)
00078 """
00079 self._filter.printOff(step)
00080 def disable(self, allowed = logging.WARNING):
00081 self._filter.disable(allowed)
00082 def enable(self, allowed = logging.WARNING):
00083 self._filter.enable(allowed)
00084
00085 _consoleHandler = None
00086 def GetConsoleHandler(prefix = None):
00087 global _consoleHandler
00088 if _consoleHandler is None:
00089 _consoleHandler = ConsoleHandler(prefix = prefix)
00090 elif prefix is not None:
00091 _consoleHandler.setPrefix(prefix)
00092 return _consoleHandler
00093
00094 def InstallRootLoggingHandler(prefix = None, level = None):
00095 root_logger = logging.getLogger()
00096 if not root_logger.handlers:
00097 root_logger.addHandler(GetConsoleHandler(prefix))
00098 root_logger.setLevel(logging.WARNING)
00099 if level is not None:
00100 root_logger.setLevel(level)
00101
00102 def PrintOn(step = 1, force = False):
00103 GetConsoleHandler().printOn(step, force)
00104 def PrintOff(step = 1):
00105 GetConsoleHandler().printOff(step)
00106
00107 class ParserError(RuntimeError):
00108 pass
00109
00110 def _find_file(f):
00111
00112 f = os.path.expandvars(f)
00113 if os.path.isfile(f):
00114 return os.path.realpath(f)
00115
00116 path = os.environ.get('JOBOPTSEARCHPATH','').split(os.pathsep)
00117
00118 candidates = [d for d in path if os.path.isfile(os.path.join(d,f))]
00119 if not candidates:
00120 raise ParserError("Cannot find '%s' in %s" % (f,path))
00121 return os.path.realpath(os.path.join(candidates[0],f))
00122
00123 _included_files = set()
00124 def _to_be_included(f):
00125 if f in _included_files:
00126 _log.warning("file '%s' already included, ignored.", f)
00127 return False
00128 _included_files.add(f)
00129 return True
00130
00131 class JobOptsParser:
00132 comment = re.compile(r'(//.*)$')
00133
00134
00135 comment_in_string = re.compile(r'(["\']).*//.*\1')
00136 directive = re.compile(r'^\s*#\s*([\w!]+)\s*(.*)\s*$')
00137 comment_ml = ( re.compile(r'/\*'), re.compile(r'\*/') )
00138 statement_sep = ";"
00139 reference = re.compile(r'^@([\w.]*)$')
00140
00141 def __init__(self):
00142
00143 self.units = {}
00144 self.defines = {}
00145 if sys.platform != 'win32':
00146 self.defines[ "WIN32" ] = True
00147
00148 def _include(self,file,function):
00149 file = _find_file(file)
00150 if _to_be_included(file):
00151 _log.info("--> Including file '%s'", file)
00152 function(file)
00153 _log.info("<-- End of file '%s'", file)
00154
00155 def parse(self,file):
00156
00157 statement = ""
00158
00159 ifdef_level = 0
00160 ifdef_skipping = False
00161 ifdef_skipping_level = 0
00162
00163 f = open(_find_file(file))
00164 l = f.readline()
00165 if l.startswith("#!"):
00166
00167
00168 l = f.readline()
00169
00170 while l:
00171 l = l.rstrip()+'\n'
00172
00173
00174 m = self.comment.search(l)
00175 if m:
00176
00177 m2 = self.comment_in_string.search(l)
00178
00179
00180 if not ( m2 and m2.start() < m.start() ):
00181
00182
00183 l = l[:m.start()]+l[m.end():]
00184
00185 m = self.directive.search(l)
00186 if m:
00187 directive_name = m.group(1)
00188 directive_arg = m.group(2).strip()
00189 if directive_name == "include":
00190 included_file = directive_arg.strip("'\"")
00191 importOptions(included_file)
00192 elif directive_name == "units":
00193 units_file = directive_arg.strip("'\"")
00194 self._include(units_file,self._parse_units)
00195 elif directive_name in [ "ifdef", "ifndef"]:
00196 ifdef_skipping_level = ifdef_level
00197 ifdef_level += 1
00198 if directive_arg in self.defines:
00199 ifdef_skipping = directive_name == "ifndef"
00200 else:
00201 ifdef_skipping = directive_name == "ifdef"
00202 elif directive_name == "else":
00203 ifdef_skipping = not ifdef_skipping
00204 elif directive_name == "endif":
00205 ifdef_level -= 1
00206 if ifdef_skipping and ifdef_skipping_level == ifdef_level:
00207 ifdef_skipping = False
00208 elif directive_name == "pragma":
00209 if not directive_arg:
00210 l = f.readline()
00211 continue
00212 pragma = directive_arg.split()
00213 if pragma[0] == "print":
00214 if len(pragma) > 1:
00215 if pragma[1].upper() in [ "ON", "TRUE", "1" ]:
00216 PrintOn()
00217 else:
00218 PrintOff()
00219 else:
00220 _log.warning("unknown directive '%s'", directive_name)
00221 l = f.readline()
00222 continue
00223
00224 if ifdef_skipping:
00225 l = f.readline()
00226 continue
00227
00228
00229 m = self.comment_ml[0].search(l)
00230 if m:
00231 l,l1 = l[:m.start()],l[m.end():]
00232 m = self.comment_ml[1].search(l1)
00233 while not m:
00234 l1 = f.readline()
00235 if not l1:
00236 break
00237 m = self.comment_ml[1].search(l1)
00238 if not l1 and not m:
00239 raise ParserError("End Of File reached before end of multi-line comment")
00240 l += l1[m.end():]
00241
00242 if self.statement_sep in l:
00243 i = l.index(self.statement_sep)
00244 statement += l[:i]
00245 self._eval_statement(statement.replace("\n","").strip())
00246 statement = l[i+1:]
00247
00248
00249 if statement.lstrip().startswith("//"):
00250 statement = ""
00251 else:
00252 statement += l
00253
00254 l = f.readline()
00255
00256 def _parse_units(self,file):
00257 for line in open(file):
00258 if '//' in line:
00259 line = line[:line.index('//')]
00260 line = line.strip()
00261 if not line:
00262 continue
00263 nunit, value = line.split('=')
00264 factor, unit = nunit.split()
00265 value = eval(value)/eval(factor)
00266 self.units[unit] = value
00267
00268 def _eval_statement(self,statement):
00269 from GaudiKernel.Proxy.Configurable import (ConfigurableGeneric,
00270 Configurable,
00271 PropertyReference)
00272
00273 _log.info("%s%s", statement, self.statement_sep)
00274
00275 property,value = statement.split("=",1)
00276
00277 inc = None
00278 if property[-1] in [ "+", "-" ]:
00279 inc = property[-1]
00280 property = property[:-1]
00281
00282 property = property.strip()
00283 value = value.strip()
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300 property = '.'.join([w.strip() for w in property.split('.')])
00301 component, property = property.rsplit('.',1)
00302 if component in Configurable.allConfigurables:
00303 cfg = Configurable.allConfigurables[component]
00304 else:
00305 cfg = ConfigurableGeneric(component)
00306
00307
00308 value = value.replace('true','True').replace('false','False')
00309 if value[0] == '{' :
00310
00311 if ':' in value and not ( value[:value.index(':')].count('"')%2 or value[:value.index(':')].count("'")%2 ) :
00312
00313 value = '{'+value[1:-1].replace('{','[').replace('}',']')+'}'
00314 else :
00315 value = value.replace('{','[').replace('}',']')
00316
00317
00318 value = value.replace('\\','\\\\')
00319
00320
00321 m = self.reference.match(value)
00322 if m:
00323
00324 value = PropertyReference(m.group(1))
00325 else:
00326 value = eval(value,self.units)
00327
00328
00329
00330
00331 if property not in cfg.__slots__ and not hasattr(cfg,property):
00332
00333 lprop = property.lower()
00334 for p in cfg.__slots__:
00335 if lprop == p.lower():
00336 _log.warning("property '%s' was requested for %s, but the correct spelling is '%s'", property, cfg.name(), p)
00337 property = p
00338 break
00339
00340
00341 if inc == "+":
00342 if hasattr(cfg,property):
00343 prop = getattr(cfg,property)
00344 if type(prop) == dict:
00345 for k in value:
00346 prop[k] = value[k]
00347 else:
00348 prop += value
00349 else:
00350 setattr(cfg,property,value)
00351 elif inc == "-":
00352 if hasattr(cfg,property):
00353 prop = getattr(cfg,property)
00354 if type(prop) is dict:
00355 for k in value:
00356 if k in prop:
00357 del prop[k]
00358 else:
00359 _log.warning("key '%s' not in %s.%s", k, cfg.name(), property)
00360 else:
00361 for k in value:
00362 if k in prop:
00363 prop.remove(k)
00364 else:
00365 _log.warning("value '%s' not in %s.%s", k, cfg.name(), property)
00366 else:
00367 setattr(cfg,property,value)
00368
00369 class _TempSysPath:
00370 def __init__(self, new_path):
00371 self.old_path = sys.path
00372 sys.path = new_path
00373 def __del__(self):
00374 sys.path = self.old_path
00375
00376 _parser = JobOptsParser()
00377
00378 def _import_python(file):
00379 execfile(file, {})
00380
00381 def _import_pickle(file):
00382 import pickle
00383 input = open(file, 'rb')
00384 catalog = pickle.load(input)
00385 _log.info('Unpickled %d configurables', len(catalog))
00386
00387 def _import_opts(file):
00388 _parser.parse(file)
00389
00390 _import_function_mapping = {
00391 ".py" : _import_python,
00392 ".pkl" : _import_pickle,
00393 ".opts" : _import_opts,
00394 }
00395
00396 def importOptions( optsfile ) :
00397
00398 optsfile = os.path.expandvars(optsfile)
00399
00400 dummy, ext = os.path.splitext(optsfile)
00401 if ext in _import_function_mapping:
00402
00403 optsfile = _find_file(optsfile)
00404 if _to_be_included(optsfile):
00405 _log.info("--> Including file '%s'", optsfile)
00406
00407 _import_function_mapping[ext](optsfile)
00408 _log.info("<-- End of file '%s'", optsfile)
00409 else:
00410 raise ParserError("Unknown file type '%s' ('%s')" % (ext,optsfile))
00411