The Gaudi Framework  v36r1 (3e2fb5a8)
ProcessJobOptions.py
Go to the documentation of this file.
1 
11 import os
12 import sys
13 import re
14 import time
15 
16 import logging
17 _log = logging.getLogger(__name__)
18 
19 
20 class LogFormatter(logging.Formatter):
21  def __init__(self, fmt=None, datefmt=None, prefix="# ", with_time=False):
22  logging.Formatter.__init__(self, fmt, datefmt)
23  self.prefix = prefix
24  self.with_time = with_time
25 
26  def format(self, record):
27  fmsg = logging.Formatter.format(self, record)
28  prefix = self.prefix
29  if self.with_time:
30  prefix += '%f ' % time.time()
31  if record.levelno >= logging.WARNING:
32  prefix += record.levelname + ": "
33  s = "\n".join([prefix + line for line in fmsg.splitlines()])
34  return s
35 
36 
37 class LogFilter(logging.Filter):
38  def __init__(self, name=""):
39  logging.Filter.__init__(self, name)
40  self.printing_level = 0
41  self.enabled = True
42  self.threshold = logging.WARNING
43 
44  def filter(self, record):
45  return record.levelno >= self.threshold or (self.enabled and
46  self.printing_level <= 0)
47 
48  def printOn(self, step=1, force=False):
49  """
50  Decrease the printing_level of 'step' units. ( >0 means no print)
51  The level cannot go below 0, unless the force flag is set to True.
52  A negative value of the threshold disables subsequent "PrintOff"s.
53  """
54  if force:
55  self.printing_level -= step
56  else:
57  if self.printing_level > step:
58  self.printing_level -= step
59  else:
60  self.printing_level = 0
61 
62  def printOff(self, step=1):
63  """
64  Increase the printing_level of 'step' units. ( >0 means no print)
65  """
66  self.printing_level += step
67 
68  def disable(self, allowed=logging.WARNING):
69  self.enabled = False
70  self.threshold = allowed
71 
72  def enable(self, allowed=logging.WARNING):
73  self.enabled = True
74  self.threshold = allowed
75 
76 
77 class ConsoleHandler(logging.StreamHandler):
78  def __init__(self, stream=None, prefix=None, with_time=False):
79  if stream is None:
80  stream = sys.stdout
81  logging.StreamHandler.__init__(self, stream)
82  if prefix is None:
83  prefix = "# "
84  self._filter = LogFilter(_log.name)
85  self._formatter = LogFormatter(prefix=prefix, with_time=with_time)
86  self.setFormatter(self._formatter)
87  self.addFilter(self._filter)
88 
89  def setPrefix(self, prefix):
90  self._formatter.prefix = prefix
91 
92  def printOn(self, step=1, force=False):
93  """
94  Decrease the printing_level of 'step' units. ( >0 means no print)
95  The level cannot go below 0, unless the force flag is set to True.
96  A negative value of the threshold disables subsequent "PrintOff"s.
97  """
98  self._filter.printOn(step, force)
99 
100  def printOff(self, step=1):
101  """
102  Increase the printing_level of 'step' units. ( >0 means no print)
103  """
104  self._filter.printOff(step)
105 
106  def disable(self, allowed=logging.WARNING):
107  self._filter.disable(allowed)
108 
109  def enable(self, allowed=logging.WARNING):
110  self._filter.enable(allowed)
111 
112 
113 _consoleHandler = None
114 
115 
116 def GetConsoleHandler(prefix=None, stream=None, with_time=False):
117  global _consoleHandler
118  if _consoleHandler is None:
119  _consoleHandler = ConsoleHandler(
120  prefix=prefix, stream=stream, with_time=with_time)
121  elif prefix is not None:
122  _consoleHandler.setPrefix(prefix)
123  return _consoleHandler
124 
125 
127  level=None,
128  stream=None,
129  with_time=False):
130  root_logger = logging.getLogger()
131  if not root_logger.handlers:
132  root_logger.addHandler(GetConsoleHandler(prefix, stream, with_time))
133  root_logger.setLevel(logging.WARNING)
134  if level is not None:
135  root_logger.setLevel(level)
136 
137 
138 def PrintOn(step=1, force=False):
139  GetConsoleHandler().printOn(step, force)
140 
141 
142 def PrintOff(step=1):
143  GetConsoleHandler().printOff(step)
144 
145 
146 class ParserError(RuntimeError):
147  pass
148 
149 
150 def _find_file(f):
151  # expand environment variables in the filename
152  f = os.path.expandvars(f)
153  if os.path.isfile(f):
154  return os.path.realpath(f)
155 
156  path = os.environ.get('JOBOPTSEARCHPATH', '').split(os.pathsep)
157  # find the full path to the option file
158  candidates = [d for d in path if os.path.isfile(os.path.join(d, f))]
159  if not candidates:
160  raise ParserError("Cannot find '%s' in %s" % (f, path))
161  return os.path.realpath(os.path.join(candidates[0], f))
162 
163 
164 _included_files = set()
165 
166 
168  if f in _included_files:
169  _log.warning("file '%s' already included, ignored.", f)
170  return False
171  _included_files.add(f)
172  return True
173 
174 
176  comment = re.compile(r'(//.*)$')
177  # non-perfect R-E to check if '//' is inside a string
178  # (a tokenizer would be better)
179  comment_in_string = re.compile(r'(["\']).*//.*\1')
180  directive = re.compile(r'^\s*#\s*([\w!]+)\s*(.*)\s*$')
181  comment_ml = (re.compile(r'/\*'), re.compile(r'\*/'))
182  statement_sep = ";"
183  reference = re.compile(r'^@([\w.]*)$')
184 
185  def __init__(self):
186  # parser level states
187  self.units = {}
188  self.defines = {}
189  if sys.platform != 'win32':
190  self.defines["WIN32"] = True
191 
192  def _include(self, file, function):
193  file = _find_file(file)
194  if _to_be_included(file):
195  _log.info("--> Including file '%s'", file)
196  function(file)
197  _log.info("<-- End of file '%s'", file)
198 
199  def parse(self, file):
200  # states for the "translation unit"
201  statement = ""
202 
203  ifdef_level = 0
204  ifdef_skipping = False
205  ifdef_skipping_level = 0
206 
207  in_string = False
208 
209  f = open(_find_file(file))
210  l = f.readline()
211  if l.startswith("#!"):
212  # Skip the first line if it starts with "#!".
213  # It allows to use options files as scripts.
214  l = f.readline()
215 
216  while l:
217  l = l.rstrip(
218  ) + '\n' # normalize EOL chars (to avoid problems with DOS new-line on Unix)
219 
220  # single line comment
221  m = self.comment.search(l)
222  if m:
223  # check if the '//' is part of a string
224  m2 = self.comment_in_string.search(l)
225  # the '//' is part of a string if we find the quotes around it
226  # and they are not part of the comment itself
227  if not (m2 and m2.start() < m.start()):
228  # if it is not the case, we can remove the comment from the
229  # statement
230  l = l[:m.start()] + l[m.end():]
231  # process directives
232  m = self.directive.search(l)
233  if m:
234  directive_name = m.group(1)
235  directive_arg = m.group(2).strip()
236  if directive_name == "include":
237  included_file = directive_arg.strip("'\"")
238  importOptions(included_file)
239  elif directive_name == "units":
240  units_file = directive_arg.strip("'\"")
241  self._include(units_file, self._parse_units)
242  elif directive_name in ["ifdef", "ifndef"]:
243  ifdef_skipping_level = ifdef_level
244  ifdef_level += 1
245  if directive_arg in self.defines:
246  ifdef_skipping = directive_name == "ifndef"
247  else:
248  ifdef_skipping = directive_name == "ifdef"
249  elif directive_name == "else":
250  ifdef_skipping = not ifdef_skipping
251  elif directive_name == "endif":
252  ifdef_level -= 1
253  if ifdef_skipping and ifdef_skipping_level == ifdef_level:
254  ifdef_skipping = False
255  elif directive_name == "pragma":
256  if not directive_arg:
257  l = f.readline()
258  continue
259  pragma = directive_arg.split()
260  if pragma[0] == "print":
261  if len(pragma) > 1:
262  if pragma[1].upper() in ["ON", "TRUE", "1"]:
263  PrintOn()
264  else:
265  PrintOff()
266  else:
267  _log.warning("unknown directive '%s'", directive_name)
268  l = f.readline()
269  continue
270 
271  if ifdef_skipping:
272  l = f.readline()
273  continue
274 
275  # multi-line comment
276  m = self.comment_ml[0].search(l)
277  if m:
278  l, l1 = l[:m.start()], l[m.end():]
279  m = self.comment_ml[1].search(l1)
280  while not m:
281  l1 = f.readline()
282  if not l1:
283  break # EOF
284  m = self.comment_ml[1].search(l1)
285  if not l1 and not m:
286  raise ParserError(
287  "End Of File reached before end of multi-line comment")
288  l += l1[m.end():]
289 
290  # if we are in a multiline string, we add to the statement
291  # everything until the next '"'
292  if in_string:
293  string_end = l.find('"')
294  if string_end >= 0:
295  statement += l[:string_end + 1]
296  l = l[string_end + 1:]
297  in_string = False # the string ends here
298  else:
299  statement += l
300  l = ''
301  else: # check if we have a string
302  string_start = l.find('"')
303  if string_start >= 0:
304  string_end = l.find('"', string_start + 1)
305  if string_end >= 0:
306  # the string is opened and closed
307  statement += l[:string_end + 1]
308  l = l[string_end + 1:]
309  else:
310  # the string is only opened
311  statement += l
312  in_string = True
313  l = f.readline()
314  continue
315 
316  if self.statement_sep in l:
317  i = l.index(self.statement_sep)
318  statement += l[:i]
319  self._eval_statement(statement.strip().replace("\n", "\\n"))
320  statement = l[i + 1:]
321  # it may happen (bug #37479) that the rest of the statement
322  # contains a comment.
323  if statement.lstrip().startswith("//"):
324  statement = ""
325  else:
326  statement += l
327 
328  l = f.readline()
329 
330  def _parse_units(self, file):
331  for line in open(file):
332  if '//' in line:
333  line = line[:line.index('//')]
334  line = line.strip()
335  if not line:
336  continue
337  nunit, value = line.split('=')
338  factor, unit = nunit.split()
339  value = eval(value) / eval(factor)
340  self.units[unit] = value
341 
342  def _eval_statement(self, statement):
343  from GaudiKernel.Proxy.Configurable import (
344  ConfigurableGeneric, Configurable, PropertyReference)
345  #statement = statement.replace("\n","").strip()
346  _log.info("%s%s", statement, self.statement_sep)
347 
348  property, value = statement.split("=", 1)
349 
350  inc = None
351  if property[-1] in ["+", "-"]:
352  inc = property[-1]
353  property = property[:-1]
354 
355  property = property.strip()
356  value = value.strip()
357 
358  # find the configurable to apply the property to
359  #parent_cfg = None
360  # while '.' in property:
361  # component, property = property.split('.',1)
362  # if parent_cfg:
363  # if hasattr(parent_cfg,component):
364  # cfg = getattr(parent_cfg,component)
365  # else:
366  # cfg = ConfigurableGeneric(component)
367  # setattr(parent_cfg,component,cfg)
368  # else:
369  # cfg = ConfigurableGeneric(component)
370  # parent_cfg = cfg
371 
372  # remove spaces around dots
373  property = '.'.join([w.strip() for w in property.split('.')])
374  component, property = property.rsplit('.', 1)
375  if component in Configurable.allConfigurables:
376  cfg = Configurable.allConfigurables[component]
377  else:
378  cfg = ConfigurableGeneric(component)
379 
380  #value = os.path.expandvars(value)
381  value = value.replace('true', 'True').replace('false', 'False')
382  if value[0] == '{':
383  # Try to guess if the values looks like a dictionary
384  if ':' in value and not (value[:value.index(':')].count('"') % 2 or
385  value[:value.index(':')].count("'") % 2):
386  # for dictionaries, keep the surrounding {}
387  value = '{' + \
388  value[1:-1].replace('{', '[').replace('}', ']') + '}'
389  else: # otherwise replace all {} with []
390  value = value.replace('{', '[').replace('}', ']')
391 
392  # We must escape '\' because eval tends to interpret them
393  value = value.replace('\\', '\\\\')
394  # Restore special cases ('\n', '\t' and '\"') (see GAUDI-1001)
395  value = (value.replace(r"\\n", r"\n").replace(r"\\t", r"\t").replace(
396  r'\\"', r'\"'))
397  # replace r'\n' and r'\t' that are outside double quoted strings
398  value = '"'.join([(v if i % 2 else re.sub(r'\\[nt]', ' ', v))
399  for i, v in enumerate(value.split('"'))])
400 
401  # interprete the @ operator
402  m = self.reference.match(value)
403  if m:
404  # this allows late binding of references
405  value = PropertyReference(m.group(1))
406  else:
407  value = eval(value, self.units)
408 
409  #if type(value) is str : value = os.path.expandvars(value)
410  # elif type(value) is list : value = [ type(item) is str and os.path.expandvars(item) or item for item in value ]
411 
412  if property not in cfg.__slots__ and not hasattr(cfg, property):
413  # check if the case of the property is wrong (old options are case insensitive)
414  lprop = property.lower()
415  for p in cfg.__slots__:
416  if lprop == p.lower():
417  _log.warning(
418  "property '%s' was requested for %s, but the correct spelling is '%s'",
419  property, cfg.name(), p)
420  property = p
421  break
422 
423  # consider the += and -=
424  if inc == "+":
425  if hasattr(cfg, property):
426  prop = getattr(cfg, property)
427  if type(prop) == dict:
428  for k in value:
429  prop[k] = value[k]
430  else:
431  prop += value
432  else:
433  setattr(cfg, property, value)
434  elif inc == "-":
435  if hasattr(cfg, property):
436  prop = getattr(cfg, property)
437  if type(prop) is dict:
438  for k in value:
439  if k in prop:
440  del prop[k]
441  else:
442  _log.warning("key '%s' not in %s.%s", k,
443  cfg.name(), property)
444  else:
445  for k in value:
446  if k in prop:
447  prop.remove(k)
448  else:
449  _log.warning("value '%s' not in %s.%s", k,
450  cfg.name(), property)
451  else:
452  setattr(cfg, property, value)
453 
454 
456  def __init__(self, new_path):
457  self.old_path = sys.path
458  sys.path = new_path
459 
460  def __del__(self):
461  sys.path = self.old_path
462 
463 
464 _parser = JobOptsParser()
465 
466 
467 def _import_python(file):
468  with open(file) as f:
469  code = compile(f.read(), file, 'exec')
470  exec(code, {})
471 
472 
473 def _import_pickle(file):
474  import pickle
475  input = open(file, 'rb')
476  catalog = pickle.load(input)
477  _log.info('Unpickled %d configurables', len(catalog))
478 
479 
480 def _import_opts(file):
481  _parser.parse(file)
482 
483 
484 _import_function_mapping = {
485  ".py": _import_python,
486  ".pkl": _import_pickle,
487  ".opts": _import_opts,
488 }
489 
490 
491 def importOptions(optsfile):
492  # expand environment variables before checking the extension
493  optsfile = os.path.expandvars(optsfile)
494  # check the file type (extension)
495  dummy, ext = os.path.splitext(optsfile)
496  if ext in _import_function_mapping:
497  # check if the file has been already included
498  optsfile = _find_file(optsfile)
499  if _to_be_included(optsfile):
500  _log.info("--> Including file '%s'", optsfile)
501  # include the file
502  _import_function_mapping[ext](optsfile)
503  _log.info("<-- End of file '%s'", optsfile)
504  else:
505  raise ParserError("Unknown file type '%s' ('%s')" % (ext, optsfile))
506 
507 
508 # Import a file containing declaration of units.
509 # It is equivalent to:
510 #
511 # #units "unitsfile.opts"
512 #
513 
514 
515 def importUnits(unitsfile):
516  # expand environment variables
517  unitsfile = os.path.expandvars(unitsfile)
518  # we do not need to check the file type (extension) because it must be a
519  # units file
520  _parser._include(unitsfile, _parser._parse_units)
GaudiKernel.ProcessJobOptions.GetConsoleHandler
def GetConsoleHandler(prefix=None, stream=None, with_time=False)
Definition: ProcessJobOptions.py:116
GaudiKernel.ProcessJobOptions.JobOptsParser.parse
def parse(self, file)
Definition: ProcessJobOptions.py:199
GaudiKernel.ProcessJobOptions._import_opts
def _import_opts(file)
Definition: ProcessJobOptions.py:480
GaudiKernel.ProcessJobOptions.JobOptsParser._parse_units
def _parse_units(self, file)
Definition: ProcessJobOptions.py:330
GaudiKernel.ProcessJobOptions.importUnits
def importUnits(unitsfile)
Definition: ProcessJobOptions.py:515
GaudiKernel.ProcessJobOptions.LogFormatter
Definition: ProcessJobOptions.py:20
GaudiKernel.ProcessJobOptions.ConsoleHandler.disable
def disable(self, allowed=logging.WARNING)
Definition: ProcessJobOptions.py:106
GaudiKernel.ProcessJobOptions.JobOptsParser.comment
comment
Definition: ProcessJobOptions.py:176
GaudiKernel.ProcessJobOptions.JobOptsParser.comment_ml
tuple comment_ml
Definition: ProcessJobOptions.py:181
GaudiKernel.ProcessJobOptions._find_file
def _find_file(f)
Definition: ProcessJobOptions.py:150
GaudiKernel.ProcessJobOptions.LogFilter.disable
def disable(self, allowed=logging.WARNING)
Definition: ProcessJobOptions.py:68
GaudiKernel.ProcessJobOptions.LogFilter.printOff
def printOff(self, step=1)
Definition: ProcessJobOptions.py:62
GaudiKernel.ProcessJobOptions.ConsoleHandler._formatter
_formatter
Definition: ProcessJobOptions.py:85
GaudiKernel.ProcessJobOptions._TempSysPath.old_path
old_path
Definition: ProcessJobOptions.py:457
GaudiKernel.ProcessJobOptions.LogFilter.printing_level
printing_level
Definition: ProcessJobOptions.py:40
GaudiKernel.ProcessJobOptions.JobOptsParser.statement_sep
string statement_sep
Definition: ProcessJobOptions.py:182
GaudiKernel.ProcessJobOptions._to_be_included
def _to_be_included(f)
Definition: ProcessJobOptions.py:167
GaudiKernel.ProcessJobOptions.JobOptsParser._include
def _include(self, file, function)
Definition: ProcessJobOptions.py:192
GaudiKernel.ProcessJobOptions.LogFilter.enabled
enabled
Definition: ProcessJobOptions.py:41
GaudiKernel.ProcessJobOptions._TempSysPath.__init__
def __init__(self, new_path)
Definition: ProcessJobOptions.py:456
GaudiKernel.ProcessJobOptions.LogFilter
Definition: ProcessJobOptions.py:37
GaudiKernel.ProcessJobOptions.JobOptsParser.__init__
def __init__(self)
Definition: ProcessJobOptions.py:185
GaudiKernel.ProcessJobOptions.ConsoleHandler._filter
_filter
Definition: ProcessJobOptions.py:84
GaudiKernel.ProcessJobOptions.ConsoleHandler.printOff
def printOff(self, step=1)
Definition: ProcessJobOptions.py:100
GaudiKernel.ProcessJobOptions._TempSysPath.__del__
def __del__(self)
Definition: ProcessJobOptions.py:460
GaudiKernel.ProcessJobOptions.LogFormatter.with_time
with_time
Definition: ProcessJobOptions.py:24
GaudiKernel.ProcessJobOptions.JobOptsParser.defines
defines
Definition: ProcessJobOptions.py:188
GaudiKernel.ProcessJobOptions.ConsoleHandler.setPrefix
def setPrefix(self, prefix)
Definition: ProcessJobOptions.py:89
GaudiKernel.ProcessJobOptions.JobOptsParser.units
units
Definition: ProcessJobOptions.py:187
GaudiKernel.ProcessJobOptions.LogFormatter.format
def format(self, record)
Definition: ProcessJobOptions.py:26
GaudiKernel.ProcessJobOptions.JobOptsParser.directive
directive
Definition: ProcessJobOptions.py:180
GaudiKernel.ProcessJobOptions.JobOptsParser
Definition: ProcessJobOptions.py:175
GaudiKernel.ProcessJobOptions.ConsoleHandler.printOn
def printOn(self, step=1, force=False)
Definition: ProcessJobOptions.py:92
GaudiKernel.ProcessJobOptions.InstallRootLoggingHandler
def InstallRootLoggingHandler(prefix=None, level=None, stream=None, with_time=False)
Definition: ProcessJobOptions.py:126
GaudiKernel.ProcessJobOptions.ConsoleHandler.enable
def enable(self, allowed=logging.WARNING)
Definition: ProcessJobOptions.py:109
GaudiKernel.ProcessJobOptions.LogFormatter.__init__
def __init__(self, fmt=None, datefmt=None, prefix="# ", with_time=False)
Definition: ProcessJobOptions.py:21
GaudiKernel.ProcessJobOptions.importOptions
def importOptions(optsfile)
Definition: ProcessJobOptions.py:491
GaudiKernel.ProcessJobOptions.LogFilter.enable
def enable(self, allowed=logging.WARNING)
Definition: ProcessJobOptions.py:72
GaudiKernel.ProcessJobOptions.ConsoleHandler
Definition: ProcessJobOptions.py:77
GaudiKernel.ProcessJobOptions._import_pickle
def _import_pickle(file)
Definition: ProcessJobOptions.py:473
gaudirun.type
type
Definition: gaudirun.py:154
GaudiKernel.ProcessJobOptions.LogFormatter.prefix
prefix
Definition: ProcessJobOptions.py:23
GaudiKernel.ProcessJobOptions.JobOptsParser._eval_statement
def _eval_statement(self, statement)
Definition: ProcessJobOptions.py:342
GaudiKernel.ProcessJobOptions._import_python
def _import_python(file)
Definition: ProcessJobOptions.py:467
GaudiKernel.ProcessJobOptions.LogFilter.__init__
def __init__(self, name="")
Definition: ProcessJobOptions.py:38
GaudiKernel.ProcessJobOptions._TempSysPath
Definition: ProcessJobOptions.py:455
GaudiKernel.ProcessJobOptions.JobOptsParser.comment_in_string
comment_in_string
Definition: ProcessJobOptions.py:179
GaudiKernel.ProcessJobOptions.PrintOff
def PrintOff(step=1)
Definition: ProcessJobOptions.py:142
GaudiKernel.ProcessJobOptions.JobOptsParser.reference
reference
Definition: ProcessJobOptions.py:183
GaudiKernel.ProcessJobOptions.LogFilter.printOn
def printOn(self, step=1, force=False)
Definition: ProcessJobOptions.py:48
GaudiKernel.ProcessJobOptions.ParserError
Definition: ProcessJobOptions.py:146
GaudiKernel.ProcessJobOptions.LogFilter.filter
def filter(self, record)
Definition: ProcessJobOptions.py:44
GaudiKernel.ProcessJobOptions.PrintOn
def PrintOn(step=1, force=False)
Definition: ProcessJobOptions.py:138
GaudiKernel.ProcessJobOptions.LogFilter.threshold
threshold
Definition: ProcessJobOptions.py:42
GaudiKernel.ProcessJobOptions.ConsoleHandler.__init__
def __init__(self, stream=None, prefix=None, with_time=False)
Definition: ProcessJobOptions.py:78