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")