Belle II Software development
_notebooksupport.py
1
8from lxml import etree
9from lxml.builder import E
10from IPython import get_ipython
11from . import core as b2core
12import multiprocessing
13
14import ROOT
15from ROOT import Belle2
16
17
18_process_warning = False
19
20
21def _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
52def 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
57def 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
78def 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
98def _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
113def 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