33 __doc__     = 
"""Evaluates and replaces mathematical expressions from a GDML file. 
   35 By default, each of the input file is renamed into a .bak file, and the output 
   36 replaces the old file. If no input file is specified, the file is read from standard 
   37 input and output to standard output, or to the value of the '--output' option. 
   38 The output option can also be specified to set the output file name, in which case 
   39 the input file is not renamed. If empty, output will be to standard output. 
   41 This scripts supports two modes: 
   42 - direct parser: a simple parser that looks for patterns '="xxx"' in a line 
   43   and replaces xxx with its value; it preserves the form of the input, 
   44   but it might silently fail to parse good GDML 
   45 - XML parser: a python XML parser evaluates the GDML file completely, then it 
   46   writes it anew; it will parse any XML, but it loses comments and can change 
   47   the order of the attributes 
   48 The XML parser is easy to extend to include "define" GDML lines, that are not 
   51 Expressions are evaluated by ROOT TFormula. 
   60    import xml.dom.minidom
 
   62 except ImportError: hasXML = 
False 
   67       RuntimeError.__init__(self, *args, **kargs);
 
   79       if not self.options.NoROOTformula:
 
   81          self.
formula = self.ROOT.TFormula(
"GDMLexpressionRemoverFormula", 
"0");
 
   89       return s.replace(
"^", 
"**")
 
   95          raise ConfigurationError \
 
   96            (
"Can't load ROOT module: I can't use ROOT to evaluate formulas.")
 
   97       ROOT.gErrorIgnoreLevel = ROOT.kFatal 
 
   98       f = ROOT.TFormula(
"FTest", 
"1/2*2");
 
  101            """This script won't work with ROOT version (%s);\n""" 
  102            """Please set upa recent version 5.\n""" 
  103            """(quick test: 'TFormula("F", "1/2*2").Eval(0.)' should return 1)""" 
  104            % ROOT.gROOT.GetVersion()
 
  116       except ValueError: 
pass 
  120          sanitized = self.
sanitize(expression)
 
  128       except ValueError: 
pass 
  131       if self.formula.Compile(expression) == 0:
 
  132          return str(self.formula.Eval(0.))
 
  146       GDMLexpressionRemover.__init__(self, *args, **kargs)
 
  150       """Returns a list of pairs: (prefix, double quoted string) 
  152       One of them may be None if no such element is present 
  172                tokens.append((prefix, word))
 
  184          if   mode == 
'p': prefix += c
 
  185          elif mode == 
'w': word += c
 
  187       if prefix 
or (word 
is not None):
 
  188          tokens.append((prefix, word))
 
  192    def apply(self, token, iLine = None):
 
  193       """Purifies the token""" 
  196          element = prefix 
if prefix 
is not None else "" 
  200                if iLine 
is not None:
 
  202                     "Evaluated '%s' into '%s' on line %d",
 
  203                     s, purified, iLine + 1
 
  205                   if self.options.WarnZero 
and (float(purified) == 0.):
 
  206                      logging.warn(
"On line %d: expression '%s' evaluated to 0",
 
  209                   logging.debug(
"Evaluated '%s' into '%s'", s, purified)
 
  210                   if self.options.WarnZero 
and (float(purified) == 0.):
 
  211                      logging.warn(
"Expression '%s' evaluated to 0", s)
 
  213             element += 
'"' + str(purified) + 
'"' 
  215          elements.append(element)
 
  217       return "".
join(elements)
 
  225    if not options.Fake 
and OutputFileName 
and (InputFileName == OutputFileName):
 
  226       raise ConfigurationError \
 
  227         (
"With the direct parser the input and output file must be different.")
 
  230    InputFile = 
open(InputFileName, 
'r') if InputFileName else sys.stdin 
  233       logging.info(
"Output will not be written in dry-run mode.")
 
  237       OutputFile = 
open(OutputFileName, 
'w') 
if OutputFileName 
else sys.stdout
 
  245       indent = line[:-len(beef)]
 
  249       purified = RemoveGDMLexpression.apply(beef, iLine)
 
  253          output = indent + purified
 
  254          print >>OutputFile, output
 
  258    if OutputFileName 
and OutputFile:
 
  259       logging.debug(
"GDML written to file '%s'", OutputFileName)
 
  269       GDMLexpressionRemover.__init__(self, *args, **kargs)
 
  273       """Purifies the attributes in the DOM node""" 
  274       attributes = node.attributes
 
  275       if not attributes: 
return 
  276       for name, value 
in attributes.items():
 
  277          purified = self.
purify(value)
 
  278          if value != purified:
 
  279             logging.debug(
"Evaluated '%s' into '%s'", value, purified)
 
  280             attributes[name] = str(purified)
 
  289    """Applies a function to the specified node and all its descendants.""" 
  291       for child 
in node.childNodes:
 
  293    func(node, level, *args, **kargs)
 
  305    InputFile = 
open(InputFileName, 
'r') if InputFileName else sys.stdin 
  308    DOMTree = xml.dom.minidom.parse(InputFile)
 
  309    GDML = DOMTree.documentElement
 
  317       logging.info(
"Output will not be written in dry-run mode.")
 
  320       OutputFile = 
open(OutputFileName, 
'w') 
if OutputFileName 
else sys.stdout
 
  322       OutputFile.write(GDML.toxml())
 
  323       OutputFile.write(
"\n")
 
  326          logging.debug(
"GDML written to file '%s'", OutputFileName)
 
  337      format=
"%(levelname)s: %(message)s",
 
  345    """Renames the input file into '.bak', then runs the parser""" 
  347    OldInputFileName = InputFileName
 
  348    OutputFileName = OldInputFileName
 
  351       InputFileName += 
".bak" 
  354       if os.path.exists(InputFileName):
 
  356            "Backup file '%s' is on the way. Please remove it first." 
  360       logging.debug(
"Renaming the input file into '%s'", InputFileName)
 
  361       os.rename(OldInputFileName, InputFileName)
 
  366       parser(InputFileName, OutputFileName, options=options)
 
  370          if not os.path.exists(OutputFileName):
 
  371             logging.debug(
"Restoring the input file name after a fatal error.")
 
  372             os.rename(InputFileName, OldInputFileName) 
 
  378       logging.info(
"File '%s' rewritten (old file in '%s')",
 
  379         OutputFileName, InputFileName
 
  396    SupportedParsers = [ 
'direct', 
'xml', 
'list' ]
 
  398       logging.warn(
"XML parser is not supported (cam't find python XML module)")
 
  399       SupportedParsers.remove(
'xml')
 
  402    parser = argparse.ArgumentParser(description=__doc__)
 
  403    parser.set_defaults(NoROOTformula=
False, Parser=SupportedParsers[0])
 
  405    parser.add_argument(
'--stdin', dest=
"FromSTDIN", action=
'store_true',
 
  406      help=
"read input from stdin")
 
  408    parser.add_argument(
"InputFiles", nargs=
"*", default=
None,
 
  409      help=
"input GDML files [default: stdin]")
 
  411    parser.add_argument(
"--parser", choices=SupportedParsers, dest=
"Parser",
 
  412      help=
"choose which parser to use ('list' for a list) [%(default)s]")
 
  414    parser.add_argument(
"--direct", action=
"store_const", const=
"direct",
 
  415      dest=
"Parser", help=
"use simple internal parser [%(default)s]")
 
  417    parser.add_argument(
"--xml", action=
"store_const", const=
"xml",
 
  418      dest=
"Parser", help=
"use complete XML parser [%(default)s]")
 
  428    parser.add_argument(
"--output", 
"-o", dest=
"OutputFile", default=
None,
 
  429      help=
"for a single input, use this as output file")
 
  431    parser.add_argument(
'--warnzero', 
'-z', dest=
"WarnZero", action=
'store_true',
 
  432      help=
"emit a warning each time an expression evaluates to 0 [%(default)s]")
 
  433    parser.add_argument(
'--dryrun', 
'--fake', 
'-n', dest=
"Fake", action=
'store_true',
 
  434      help=
"do not write output [%(default)s]")
 
  435    parser.add_argument(
'--verbose', 
'-v', dest=
"DoVerbose", action=
'store_true',
 
  436      help=
"shows all the changes on screen [%(default)s]")
 
  437    parser.add_argument(
'--debug', dest=
"DoDebug", action=
'store_true',
 
  438      help=
"enables debug messages on screen")
 
  440    parser.add_argument(
'--version', action=
'version',
 
  441      version=
'%(prog)s ' + __version__)
 
  443    arguments = parser.parse_args()
 
  449    logging.getLogger().setLevel \
 
  450      (logging.DEBUG 
if arguments.DoDebug 
else logging.INFO)
 
  452    arguments.LogMsg = logging.info 
if arguments.DoVerbose 
else logging.debug
 
  454    if arguments.Parser == 
'list':
 
  455       SupportedParsers.remove(
'list')
 
  456       logging.info(
"Supported parsers: '%s'.", 
"', '".
join(SupportedParsers)) 
 
  460    if arguments.Parser == 
'direct':
 
  461       Parser = RemoveMathFromGDMLfile
 
  462    elif arguments.Parser == 
'xml':
 
  463       Parser = RemoveMathFromXMLfile
 
  467    if bool(arguments.FromSTDIN) == 
bool(arguments.InputFiles):
 
  468       raise ConfigurationError \
 
  469         (
"Please either specify option --stdin OR some input files.")
 
  475    if arguments.FromSTDIN:
 
  476       Parser(
None, options=arguments)
 
  477    elif arguments.OutputFile 
is not None:
 
  478       if len(arguments.InputFiles) > 1:
 
  479          raise ConfigurationError \
 
  480            (
"Named output is supported only when a single input file is specified.")
 
  482       Parser(arguments.InputFiles[0], arguments.OutputFile, options=arguments)
 
  484       for InputFileName 
in arguments.InputFiles:
 
  485          RunParserOn(Parser, InputFileName, options=arguments)
 
  496 if __name__ == 
"__main__":
 
  499    except ConfigurationError, e:
 
  500       logging.error(
"%s" % str(e))
 
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration. 
S join(S const &sep, Coll const &s)
Returns a concatenation of strings in s separated by sep. 
def RemoveMathFromXMLfile
def RemoveMathFromGDMLfile
open(RACETRACK) or die("Could not open file $RACETRACK for writing")