Belle II Software  release-08-01-10
_notebooksupport.py
1 
8 from lxml import etree
9 from lxml.builder import E
10 from IPython import get_ipython
11 from . import core as b2core
12 import multiprocessing
13 
14 import ROOT
15 from ROOT import Belle2
16 
17 
18 _process_warning = False
19 
20 
21 def _create_pathhtml(path, name="Path", details=None):
22  """Create an html fragment for a given Path object"""
23  size = len(path.modules())
24  fragment = E.div(f"{name} with {size} modules")
25  if details is not None:
26  fragment.append(details)
27 
28  module_list = E.ol(style="list-style-type:decimal; font-style:normal;")
29  for m in path.modules():
30  name = m.name()
31  tname = m.type()
32  tname = f" (type \"{tname}\")" if tname != name else ""
33  module = E.li("Module ", E.b(name), tname)
34  detail_list = E.ul(style="margin:0em 0em 0.5em 0em")
35  for p in m.available_params():
36  if p.setInSteering:
37  detail_list.append(E.li(E.code(f"{p.name} = {p.values}")))
38  for c in m.get_all_conditions():
39  after = str(c.get_after_path())
40  details = E.span(" (condition: ", E.code(f"{c.get_operator()} {c.get_value()}"), ", afterwards:", E.code(after), ')')
41  detail_list.append(E.li(_create_pathhtml(c.get_path(), "Conditional Path", details=details),
42  style="list-style-type: disclosure-closed"))
43  if len(detail_list) > 0:
44  module.append(detail_list)
45 
46  module_list.append(module)
47  if len(module_list) > 0:
48  fragment.append(module_list)
49  return fragment
50 
51 
52 def print_path_html(path):
53  """Print a path (and all sub paths) as html"""
54  return etree.tostring(_create_pathhtml(path, "basf2.Path")).decode()
55 
56 
57 def print_module_html(module):
58  """Print a module (and its parameters) as well as the description"""
59  name = module.name()
60  tname = module.type()
61  tname = f" (type {tname})" if tname != name else ""
62  module_html = E.div("basf2.Module ", E.b(name), tname, E.p(module.description),
63  E.style("table.b2module * { text-align: left !important; }"))
64  cols = ["parameter", "type", "default", "current", "changed", "is required"]
65  if len(module.available_params()) > 0:
66  params = E.tbody()
67  for p in module.available_params():
68  params.append(E.tr(E.td(p.name), E.td(E.code(str(p.type))), E.td(E.code(str(p.default))), E.td(E.code(str(p.values))),
69  E.td(E.b("yes") if p.setInSteering else "no"),
70  E.td(E.b("yes") if p.forceInSteering else "no")))
71  params.append(E.tr(E.td(), E.td(p.description, colspan="5")))
72 
73  module_html.append(E.table(E.thead(E.tr(*[E.th(e) for e in cols])), params, **{"class": "b2module"}))
74 
75  return etree.tostring(module_html).decode()
76 
77 
78 def enable_notebooksupport():
79  """
80  Small function to enable some notebook compatibility.
81  Mostly make sure that log messages are shown correctly (they are C++ so they need some special love to show up).
82  """
83  # we're in a notebook, reset log system to print to python sys.stdout
84  # and we know we want colored output even though stdout might not be a terminal
85  b2core.logging.reset()
86  b2core.logging.add_console(True)
87  b2core.logging.enable_python_logging = True
88 
89  # we need to import ROOT because otherwise it can lockup later
90  import ROOT # noqa
91 
92  # register html converters for common objects
93  html_formatter = get_ipython().display_formatter.formatters['text/html']
94  html_formatter.for_type(b2core.Path, print_path_html)
95  html_formatter.for_type(b2core.Module, print_module_html)
96 
97 
98 def _child_process(pipe, path, max_events):
99  """Execute process() in a child process but send the persistent datastore objects back to the parent"""
100  # do the processing
101  b2core.process(path, max_events)
102  # no send back all the objects we want. Which currently is only the stats
103  return_objects = {}
104  for name in ["ProcessStatistics"]:
105  name = str(name)
106  storeobj = Belle2.PyStoreObj(name, Belle2.DataStore.c_Persistent)
107  if storeobj.isValid():
108  return_objects[name] = str(ROOT.TBufferJSON.ToJSON(storeobj.obj()))
109  pipe.send(return_objects)
110  pipe.close()
111 
112 
113 def process(path, max_event=0):
114  """
115  Start processing events using the modules in the given `basf2.Path` object.
116 
117  Can be called multiple times in one steering file.
118 
119  This is a convenience wrapper which will automatically call the
120  `process()` function in a separate process to prevent FATAL errors from killing
121  the notebook.
122 
123  Caveats:
124  As processing is done in a separate process the global state of the notebook
125  cannot be modified. For example a python modules collecting information will appear to be
126  empty after execution and global variables will also not be modified.
127 
128  If you need to modify the state of the notebook during processing you need to call
129  basf2.core.process
130 
131  Parameters:
132  path: The path with which the processing starts
133  max_event: The maximal number of events which will be processed, 0 for no limit
134 
135  Returns:
136  None
137  """
138  global _process_warning
139  if not _process_warning:
140  _process_warning = True
141  b2core.B2INFO("process() called in a Jupyter Notebook. See help(basf2.process) for caveats")
142 
143  datastore = Belle2.DataStore.Instance()
144  ctx = multiprocessing.get_context("fork")
145  nanny, child = ctx.Pipe()
146  process = ctx.Process(target=_child_process, args=(child, path, max_event))
147  process.start()
148  process.join()
149  if process.exitcode != 0:
150  raise RuntimeError("Event processing was not successful")
151  return_objects = nanny.recv()
152  nanny.close()
153  datastore.setInitializeActive(True)
154  try:
155  for name, value in return_objects.items():
156  storeobj = Belle2.PyStoreObj(name, Belle2.DataStore.c_Persistent)
157  if value is not None:
158  storeobj.registerInDataStore()
159  storeobj.create()
160  obj = ROOT.TBufferJSON.ConvertFromJSON(value)
161  ROOT.SetOwnership(obj, False)
162  storeobj.assign(obj, True)
163  finally:
164  datastore.setInitializeActive(False)
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:67