Belle II Software  release-08-01-10
root_utils.py
1 
8 
9 """Various functions to interact with ROOT objects and the runtime environment"""
10 
11 import ROOT
12 import contextlib
13 from functools import singledispatch
14 
15 
16 def root_walk(tdirectory):
17  """Walks the content of a TDirectory similar to os.walk.
18 
19  Yields 3-tuples of current TDirectories, contained TObjects and contained TDirectories
20  for each of the directories nested inside the given TDirectory in a depth first manner.
21 
22  Yields
23  ------
24  (TDirectory, list(TObject), list(TDirectory))
25  """
26  tkeys = tdirectory.GetListOfKeys()
27 
28  tobjects = []
29  tdirectories = []
30  for tkey in tkeys:
31  tobject = tdirectory.Get(tkey.GetName())
32  if isinstance(tobject, ROOT.TDirectory):
33  tdirectories.append(tobject)
34  else:
35  tobjects.append(tobject)
36 
37  yield tdirectory, tobjects, tdirectories
38 
39  # Continue depth first
40  for sub_tdirectory in tdirectories:
41  for tdirectory, tobjects, tdirectories in root_walk(sub_tdirectory):
42  yield tdirectory, tobjects, tdirectories
43 
44 
45 @contextlib.contextmanager
46 def root_open(tfile_or_file_path, tfile_options=None):
47  """Context manager to open a TFile.
48 
49  If a file path is given open the TFile and close it after the context is left.
50  If an already opened TFile is received simply return it and do not close on exit.
51 
52  Parameters
53  ----------
54  tfile_or_file_path : str or ROOT.TFile
55  Path to the file or the TFile that should be activated.
56  tfile_options : str
57  Option string forwarded to the ROOT.TFile constructor
58  Typical options as "RECREATE", "READ" or "UPDATE".
59  """
60  if isinstance(tfile_or_file_path, ROOT.TFile):
61  tfile = tfile_or_file_path
62  with root_cd(tfile) as tfile:
63  yield tfile
64  else:
65  save_tdirectory = ROOT.gROOT.CurrentDirectory().load()
66 
67  if tfile_options is None:
68  tfile = ROOT.TFile(tfile_or_file_path)
69  else:
70  tfile = ROOT.TFile(tfile_or_file_path, tfile_options)
71 
72  try:
73  yield tfile
74  finally:
75  tfile.Close()
76  save_tdirectory.cd()
77 
78 
79 @contextlib.contextmanager
80 def root_cd(tdirectory):
81  """Context manager that temporarily switches the current global ROOT directory while in the context.
82 
83  If a string as the name of a directory is given as the argument
84  try to switch to the directory with that name in the current ROOT folder.
85 
86  If it is not present create it.
87 
88  Parameters
89  ----------
90  tdirectory : ROOT.TDirectory or str
91  ROOT directory to switch to or name of a folder to switch.
92 
93  Returns
94  -------
95  ROOT.TDirectory
96  The new current ROOT directory.
97  """
98 
99  # Do not use ROOT.gDirectory here.
100  # Since ROOT.gDirectory gets transported as a reference it changes on a call to cd() as well,
101  # and can therefore not serve to save the former directory.
102  save_tdirectory = ROOT.gROOT.CurrentDirectory().load()
103 
104  if not tdirectory or "." == tdirectory:
105  tdirectory = save_tdirectory
106 
107  elif isinstance(tdirectory, str):
108  tdirectory_name = tdirectory
109 
110  # Look for the tdirectory with the name
111  # before trying to create it
112  tdirectory = save_tdirectory.GetDirectory(tdirectory_name)
113  if not tdirectory:
114  tdirectory = save_tdirectory.mkdir(tdirectory_name, tdirectory_name)
115  if not tdirectory:
116  raise RuntimeError("Could not create or find folder %s" % tdirectory_name)
117 
118  # If tdirectory_name is as hierachy like a/b/c make sure we select the most nested folder
119  # (and not a as the documentation of TDirectory.mkdir suggests).
120  tdirectory = save_tdirectory.GetDirectory(tdirectory_name)
121 
122  try:
123  if tdirectory is not None:
124  tdirectory.cd()
125  yield tdirectory
126 
127  finally:
128  save_tdirectory.cd()
129 
130 
131 def root_save_name(name):
132  """Strips all meta characters that might be unsafe to use as a ROOT name.
133 
134  Parameters
135  ----------
136  name : str
137  A name that should be transformed
138 
139  Returns
140  -------
141  str
142  Name with potentially harmful characters deleted / replaced.
143  """
144  deletechars = str.maketrans("", "", r"/$\#{}()[]=")
145  name = name.replace(' ', '_').replace('-', '_').replace(',', '_').translate(deletechars)
146  return name
147 
148 
149 def root_browse(tobject):
150  """Open a browser and show the given object.
151 
152  Parameters
153  ----------
154  tobject : ROOT.TObject
155  The object to be shown
156 
157  Returns
158  -------
159  ROOT.TBrowser
160  The new TBrowser used to show the object.
161  """
162  # Set the style to the style desired by the validation plots.
165 
166  tbrowser = ROOT.TBrowser()
167  if isinstance(tobject, ROOT.TObject):
168  if hasattr(tobject, "Browse"):
169  tobject.Browse(tbrowser)
170  else:
171  tbrowser.BrowseObject(tobject)
172  else:
173  raise ValueError("Can only browse ROOT objects inheriting from TObject.")
174  return tbrowser
175 
176 
177 @singledispatch
178 def root_ls(obj):
179  """Returns a list of names that are contained in the given obj.
180 
181  This is a convinience function to invesitigate the content of ROOT objects,
182  that dispatches on to object type and retieves different things depending on the type.
183  If the obj is a string it is interpreted as a filename.
184  """
185  return list(obj)
186 
187 
188 @root_ls.register(str)
189 def __(filename):
190  """Overloaded function for root_ls for filenames (e.g. opens the file and ls its content)."""
191  rootFile = ROOT.TFile(filename)
192  result = root_ls(rootFile)
193 
194  rootFile.Close()
195  del rootFile
196  return result
197 
198 
199 @root_ls.register(ROOT.TDirectory)
200 def __(tDirectory): # noqa
201  """Overloaded function for root_ls for ROOT directories (e.g. list the keys in the directory)."""
202  tKeys = list(tDirectory.GetListOfKeys())
203  result = sorted([tKey.GetName() for tKey in tKeys])
204  return result
205 
206 
207 @root_ls.register(ROOT.TTree)
208 @root_ls.register(ROOT.TNtuple)
209 def __(tTree): # noqa
210  """Overloaded function for root_ls for trees and ntuples (e.g. list the keys in the tuple/tree)."""
211  tBranches = list(tTree.GetListOfBranches())
212  result = sorted([tBranch.GetName() for tBranch in tBranches])
213  return result