All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sbndcode/sbndcode/gallery/python/ROOTutils.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 
4 __doc__ = """
5 Collection of utilities to ease interaction with ROOT.
6 
7 Unsurprisingly, this module requires ROOT.
8 """
9 
10 __all__ = [
11  "splitROOTpath", "createROOTpath", "getROOTclass",
12  "activateDirectory",
13  "ROOT",
14  ]
15 
16 ################################################################################
17 ###
18 ### Try to save the command line arguments from unconsiderate ROOT behaviour
19 ###
20 def ROOTloader():
21  """
22  The ROOT interpreter likes to peek at the command line arguments and interpret
23  them its own way.
24  For example, an option `-b` will be interpreted to set ROOT in batch mode.
25  Likewise, if a `--help` argument is present on the command line, the
26  interpreter will print the ROOT help message and, even worse, halt the script.
27  This is clearly not very friendly to the script.
28 
29  The initialization of the interpreter happens lazily the first time anything
30  is read out of `ROOT` module: `import ROOT` is not enough, but pretty much
31  everything after that involving ROOT is (exception: `ROOT.gROOT` will not
32  trigger the interpreter, but trying to get anything out of `ROOT.gROOT` will).
33  That makes it complicate to control when that happens.
34 
35  This function triggers the interpreter initialization after having removed all
36  command line options, and finally restores them (we use a context manager to
37  show that we know Python). The loaded module is returned.
38 
39  This function is called as soon as this module is imported. It is important
40  that this happens as early as possible, possibly as a replacement of ROOT
41  import, as:
42 
43  from ROOTutils import ROOT
44 
45  from ROOTutils import *
46 
47  import ROOTutils
48  import ROOT
49 
50  or equivalent.
51  """
52  import sys, logging
53  try: alreadyLoaded = 'gInterpreter' in dir(ROOT)
54  except NameError: alreadyLoaded = False
55  if alreadyLoaded:
56  logging.warning(
57  "ROOT module was loaded before ROOTutils.py: command line arguments may be garbled"
58  )
59  return sys.modules['ROOT']
60  # if already loaded
61 
62  class EmptyArgs:
63  def __enter__(self):
64  self.args = sys.argv
65  logging.debug("Saving command line: %s", self.args)
66  sys.argv = sys.argv[0:1]
67  logging.debug(
68  "Replaced command line %s with %s before loading ROOT module",
69  self.args, sys.argv)
70  def __exit__(self, exc_type, exc_value, traceback):
71  sys.argv = self.args
72  logging.debug("Restored command line %s", sys.argv)
73  # class EmptyArgs
74 
75  with EmptyArgs():
76  import ROOT
77  ROOT.gInterpreter # make sure that an interpreter is initialized
78  return ROOT
79  # with
80 
81 # ROOTloader()
82 
83 ROOT = ROOTloader() # import ROOT...
84 del ROOTloader
85 
86 ################################################################################
87 ### Print vectors easily
88 def TVector2ToString(v):
89  return "( %g, %g )" % (v.X(), v.Y())
90 def TVector3ToString(v):
91  return "( %g, %g, %g )" % (v.X(), v.Y(), v.Z())
93  return "( %g, %g, %g; %g )" % (v.X(), v.Y(), v.Z(), v.T())
94 
95 ROOT.TVector2.__str__ = TVector2ToString
96 ROOT.TVector3.__str__ = TVector3ToString
97 ROOT.TLorentzVector.__str__ = TLorentzVectorToString
98 
99 
100 ################################################################################
101 ### File management
102 ###
103 def splitROOTpath(path):
104  """
105  Returns the specified path split into file path and ROOT directory path.
106 
107  The `path` is in the form:
108  "/UNIX/path/to/file.root:internal/ROOT/directory/and/object".
109  The returned value is a pair `(filePath, dirPath)`: in the example, that
110  would be `("/UNIX/path/to/file.root", "internal/ROOT/directory/and/object")`.
111 
112  Note: for compatibility with some ROOT tradition, the separator ':' can be
113  replaced by '/'
114  """
115 
116  # this implementation is not robust, but I am in a hurry :-P
117  try:
118  filePath, ROOTpath = path.rsplit('.root')
119  except ValueError:
120  raise RuntimeError("Path '{}' does not include a ROOT file.".format(path))
121  filePath += '.root'
122  ROOTpath.lstrip(':')
123  ROOTpath.lstrip('/')
124  return filePath, ROOTpath
125 
126 # splitROOTpath()
127 
128 
129 def createROOTpath(path, fileMode = "UPDATE"):
130  """
131  Creates a complete ROOT directory path.
132 
133  The `path` is in the form:
134  "/UNIX/path/to/file.root:internal/ROOT/directory/structure".
135  The ROOT file `/UNIX/path/to/file.root` will be created with the specified
136  `fileMode` (path may be relative), then the `TDirectoryFile` hierarchy
137  `internal/ROOT/directory/structure` will be created under it.
138  The return value is a pair `(file, dir)`, where `file` is a open `TFile`
139  for `/UNIX/path/to/file.root` and `dir` is the `TDirectory` object of
140  `structure`.
141 
142  Remember to keep track of `file`, or else python may close it compromising
143  `dir` as well.
144  """
145 
146  filePath, ROOTpath = splitROOTpath(path)
147 
148  ROOTfile = ROOT.TFile(filePath, fileMode)
149  if not ROOTfile.IsOpen():
150  raise RuntimeError \
151  ("Can't open ROOT file '{}' in '{}' mode".format(filePath, fileMode))
152 
153  # instead of using `TDirectory.mkdir()`, we do that manually
154  ROOTpathElements = ROOTpath.split('/')
155  ROOTdir = ROOTfile
156  for ROOTdirName in ROOTpathElements:
157  if not ROOTdirName: continue # empty name does nothing
158  daughterDir = ROOTdir.GetDirectory(ROOTdirName)
159  if not daughterDir:
160  daughterDir = ROOTdir.CreateDirectory(ROOTdirName)
161  if not daughterDir:
162  raise RuntimeError("Can't access directory '{}' under '{}'".format
163  (ROOTdirName, ROOTdir.GetPath()))
164  ROOTdir = daughterDir
165  # for
166 
167  return ROOTfile, ROOTdir
168 
169 # createROOTpath()
170 
171 
172 class DirectoryChanger:
173  """
174  Object changing ROOT directory while on scope.
175 
176  The purpose is to make a ROOT directory current only as long as it is needed.
177  The most typical uses of this objects include the automatic restoration of
178  the previous directory as the object falls out of scope.
179  Two methods are supported:
180  1. function scope:
181 
182  def writeEverythingInto(dir, everything):
183  dirChanger = ROOTutils.DirectoryChanger(dir)
184  for item in everything: item.Write()
185  # writeEverythingInto()
186 
187  2. local scope (equivalent to using `activateDirectory()`):
188 
189  with DirectoryChanger(dir):
190  for item in everything: item.Write()
191  # with
192 
193 
194  """
195  def __init__(self, newDir = None, saveDir = None):
196  if saveDir: self.saveDir(saveDir)
197  else: self.saveCurrentDir()
198  self.newDir = newDir
199  self.changeDir()
200  # __init__()
201 
202  def saveCurrentDir(self): self.saveDir(ROOT.gDirectory)
203 
204  def saveDir(self, ROOTdir): self.oldDir = ROOTdir
205 
206  def changeDir(self):
207  if self.newDir: self.newDir.cd()
208 
209  def restoreDir(self):
210  if self.oldDir: self.oldDir.cd()
211 
212  def forget(self): self.oldDir = None
213 
214  def __del__(self):
215  self.restoreDir()
216 
217  def __enter__(self):
218  self.changeDir()
219 
220  def __exit__(self, exc_type, exc_value, traceback):
221  self.restoreDir()
222  self.forget()
223 
224 # DirectoryChanger()
225 
226 
227 def activateDirectory(ROOTdir):
228  """
229  Sets a directory with `DirectoryChanger`.
230 
231  Example:
232 
233  dir = outputFile.GetDirectory("plots")
234  with activateDirectory(dir):
235  for plot in plots: item.Write()
236  # with
237 
238  """
239  return DirectoryChanger(ROOTdir)
240 
241 
242 ################################################################################
243 def getROOTclass(classPath):
244  """Returns the object specified by `classPath` within ROOT module.
245 
246  Throws `AttributeError` if any object in the path is not available.
247 
248  Example: `getROOTclass('geo::GeometryCore')` returns `ROOT.geo.GeometryCore`.
249  """
250  classPath = classPath.replace('::', '.').lstrip('.')
251  base = ROOT
252  for objName in classPath.split('.'):
253  base = getattr(base, objName)
254  return base
255 # getROOTclass()
256 
257 
258 ################################################################################
static std::string format(PyObject *obj, unsigned int pos, unsigned int indent, unsigned int maxlen, unsigned int depth)
Definition: fclmodule.cxx:374
def ROOTloader
Try to save the command line arguments from unconsiderate ROOT behaviour.
tuple dir
Definition: dropbox.py:28