Belle II Software  release-08-01-10
metamodules.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 """This file contains python modules that generally decorate other modules and paths
13 to serve a slightly changed purpose or circumstance.
14 
15 Note:
16  A great deal of the these modules are quite general purpose helper constructs,
17  which might be helpful in other parts of the basf2, and might therefore be better
18  placed in the framework folder.
19 """
20 
21 import basf2
22 from ROOT import Belle2
23 
24 import cProfile
25 import pstats
26 
27 import logging
28 
29 
30 def get_logger():
31  return logging.getLogger(__name__)
32 
33 
34 class WrapperModule(basf2.Module):
35 
36  """Wrapping module base class that wraps a single module to slightly change its behaviour.
37 
38  The base implementation does nothing, but forward the relevant methods to the wrapped module.
39 
40  Note:
41  Implements the decorator/wrapper pattern.
42 
43  Attributes:
44  module (basf2.Module): The wrapped module instance.
45  """
46 
47  def __init__(self, module):
48  """Create a new wrapper module around the given module. This base implementation does not change anything,
49  so there is no reason to use this base implementation alone."""
50  super(WrapperModule, self).__init__()
51  name = self.compose_wrapped_module_namecompose_wrapped_module_name(module)
52 
53 
54  self.modulemodule = module
55 
56  # Forward the logging parameters
57  self.set_debug_level(self.modulemodule.logging.debug_level)
58  self.set_abort_level(self.modulemodule.logging.abort_level)
59 
60  if self.modulemodule.logging.log_level != basf2.LogLevel.default:
61  self.set_log_level(self.modulemodule.logging.log_level)
62  self.set_log_info(self.modulemodule.logging.log_level,
63  self.modulemodule.logging.get_info(self.modulemodule.logging.log_level))
64 
65  # Forward the name of this module to the C++ world
66  self.set_name(name)
67 
68  @property
69  def wrapper_name(self):
70  """Name of the wrapper class."""
71  return self.__class__.__name__
72 
73  @property
74  def param(self):
75  """Forwards the parameters"""
76  return self.modulemodule.param
77 
78  @property
79  def available_params(self):
80  """Forwards the avaiilable parameters"""
81  return self.modulemodule.available_params
82 
83  def compose_wrapped_module_name(self, module):
84  """Compose a name that indicates the wrapped module."""
85  return "{wrapper_name}({module_name})".format(module_name=module.name(),
86  wrapper_name=self.wrapper_namewrapper_name)
87 
88  def get_name(self):
89  """Forwards the name()."""
90  return self.name()
91 
92  def initialize(self):
93  """Initialize method of the module"""
94  self.modulemodule.initialize()
95 
96  def beginRun(self):
97  """Begin run method of the module"""
98  self.modulemodule.beginRun()
99 
100  def event(self):
101  """Event method of the module"""
102  self.modulemodule.event()
103 
104  def endRun(self):
105  """End run method of the module"""
106  self.modulemodule.endRun()
107 
108  def terminate(self):
109  """Terminate method of the module"""
110  self.modulemodule.terminate()
111 
112 
114 
115  """Wrapper module that evaluates the computational performance of python modules.
116 
117  Uses cProfile.
118 
119  Attributes:
120  module (basf2.Module): The wrapped module that should be profiled.
121  Should be a module written in Python, since the profile interacts
122  with the interpretor for the measurements, but cannot look into c++ implementations.
123 
124  output_file_name (str, optional): Path to the file where the profiling information
125  shall be stored. Defaults to profile.txt.
126 
127  profiler (cProfile.Profile): Profiler instance to manage and extract the profiling statistics.
128  """
129 
130 
131  default_output_file_name = "profile.txt"
132 
133  def __init__(self, module, output_file_name=None):
134  """Create a new PyProfilingModule wrapped around the given module
135  which outputs its results to the output_file_name of given (if not, to profile.txt)."""
136  super(PyProfilingModule, self).__init__(module)
137 
138 
139  self.output_file_nameoutput_file_name = self.default_output_file_namedefault_output_file_name
140 
141  if output_file_name is not None:
142  self.output_file_nameoutput_file_name = output_file_name
143 
144  def initialize(self):
145  """Initialize method of the module"""
146 
147  self.profilerprofiler = cProfile.Profile()
148  super(PyProfilingModule, self).initialize()
149 
150  def event(self):
151  """Event method of the module"""
152  profiler = self.profilerprofiler
153  profiler.enable()
154  super(PyProfilingModule, self).event()
155  profiler.disable()
156 
157  def terminate(self):
158  """Terminate method of the module"""
159  super(PyProfilingModule, self).terminate()
160  sortby = 'cumulative'
161  with open(self.output_file_nameoutput_file_name, 'w') as profile_output_file:
162  profile_stats = pstats.Stats(self.profilerprofiler, stream=profile_output_file)
163  profile_stats.sort_stats(sortby)
164  profile_stats.print_stats()
165 
166 
168 
169  """Wrapper module to conditionally execute module and continue with the normal path afterwards.
170 
171  There are two ways to specify the condition.
172  One way is to override the condtion(self) method in a subclass.
173  The second way is to give a function as the second argument to the module constructor,
174  which is called each event.
175 
176  Attributes:
177  module (basf2.Module): The module executed, if the condition is met.
178 
179  condition (function() -> bool, optional): Function executed at each event to determine,
180  if the given module shall be executed.
181  If None call the condition method instead, which can be overridden by subclasses.
182 
183  """
184 
185  def __init__(self, module, condition=None):
186  """Initialisation method taking the module instance to be wrapped.
187 
188  Args:
189  module (basf2.Module): The module executed, if the condition is met.
190  condition (function() -> bool, optional): Function executed at each event to determine,
191  if the given module shall be executed.
192  If None call the condition method instead, which can be overridden by subclasses.
193  """
194 
195  super(IfModule, self).__init__(module)
196  if condition is not None:
197 
199  self.conditionconditioncondition = condition
200 
201  def condition(self):
202  """Condition method called if not given a condition function during construction.
203 
204  Can be overridden by a concrete subclass to specify
205  under which condition the wrapped module should be executed.
206  It can also be shadowed by a condition function given to the constructor of this module.
207 
208  Returns:
209  bool: The indication if the wrapped module should be executed.
210  Always True in the base implementation
211  """
212  return True
213 
214  def event(self):
215  """Event method of the module
216 
217  Evaluates the condition and sets the return value of this module
218  to trigger the execution of the wrapped module.
219  """
220 
221  if self.conditionconditioncondition():
222  super(IfModule, self).event()
223 
224 
225 def is_storearray_present(storearray_name,
226  storearray_durability=0):
227  """Checks if a StoreArray with name and durability is present in the DataStore.
228 
229  Only works during the event processing phase, but not on initialisation,
230  due to limitation of the python interface to the framework
231  """
232  storearray_list = Belle2.PyStoreArray.list(storearray_durability)
233  return storearray_name in storearray_list
234 
235 
237 
238  """Conditional execution of the wrapped module if a StoreArray is present.
239 
240  Attributes:
241  storearray_name (str): The name of the StoreArray which presence has to be checked.
242  storearray_durability (int): The durability of the StoreArray
243  storearray_is_present (bool): The flag whether the StoreArray is present.
244  Set during initialisation.
245  """
246 
247  def __init__(self,
248  module,
249  storearray_name,
250  storearray_durability=0):
251  """
252  Args:
253  module (basf2.Module): The module executed, if the condition is met.
254  storearray_name (str): The name of the StoreArray which presence has to be checked.
255  storearray_durability (int, optional): The durability of the StoreArray. Default 0.
256  """
257 
258  super(IfStoreArrayPresentModule, self).__init__(module)
259 
260 
261  self.storearray_namestorearray_name = storearray_name
262 
263 
264  self.storearray_durabilitystorearray_durability = storearray_durability
265 
266 
267  self.storearray_is_presentstorearray_is_present = None
268 
269  def initialize(self):
270  """Initialize the contianed module (only of the condition is true)."""
271  if self.conditionconditionconditioncondition():
272  super().initialize()
273 
274  def condition(self):
275  """Returns true if the StoreArray is present.
276 
277  Checks presence of the StoreArray once and remembers the result for all following events.
278  """
279  if self.storearray_is_presentstorearray_is_present is None:
280  self.storearray_is_presentstorearray_is_present = is_storearray_present(self.storearray_namestorearray_name,
281  self.storearray_durabilitystorearray_durability)
282  return self.storearray_is_presentstorearray_is_present
283 
284 
286 
287  """Conditional execution of the wrapped module based if a StoreArray is not present."""
288 
289  def condition(self):
290  """Returns false if the StoreArray is present.
291 
292  Checks presence of the StoreArray once and remembers the result for all following events.
293  """
294  return not IfStoreArrayPresentModule.condition(self)
295 
296 
298 
299  """Conditional execution of the wrapped module based on the presence of Monte Carlo information.
300  """
301 
302  def __init__(self, module):
303  """
304  Args:
305  module (basf2.Module): The module executed, if the condition is met.
306  """
307  super(IfMCParticlesPresentModule, self).__init__(module, "MCParticles")
308 
309 
310 class PathModule(basf2.Module):
311 
312  """Wrapper for a basf2 path into a module such that
313  it can be passed around and added to a basf2 path as a basf2 module.
314 
315  The wrapper is implement in such a way that it unconditionally executes
316  the contained path by means of a positive return value.
317  The calling path is continued after the execution of the wrapped path.
318 
319  Attributes:
320  _path (basf2.Path): The wrapped execution path.
321  """
322 
323  def __init__(self, path=None, modules=None):
324  """Initialises the module with a path to be wrapped.
325 
326  May also give a list of modules that should be appended to the path.
327  If the path is omitted a new basf2.Path is constructed.
328 
329  Args:
330  path (basf2.Path): The execution path to be wrapped.
331  If no path is given create a new one.
332 
333  modules (iterable of basf2.Module): Module to be added to the path.
334  """
335 
336  super(PathModule, self).__init__()
337 
338  if path is None:
339  path = basf2.create_path()
340 
341 
342  self._path_path = path
343 
344  if modules:
345  for module in modules:
346  path.add_module(module)
347 
348  self.if_true(path, basf2.AfterConditionPath.CONTINUE)
349 
350  # Pass a telling name to the C++ world
351  if modules is None:
352  itemCount = 0
353  else:
354  itemCount = len(modules)
355  self.set_name("{path_module} ({items} modules):".format(path_module=self.__class__.__name__,
356  items=itemCount))
357 
358  def event(self):
359  """Event method of the module
360 
361  Sets the return value of this module to true and triggers the execution of the wrapped path.
362  """
363 
364  self.return_value(True)
static std::vector< std::string > list(DataStore::EDurability durability=DataStore::EDurability::c_Event)
Return list of available arrays for given durability.
Definition: PyStoreArray.cc:28
def __init__(self, module, condition=None)
Definition: metamodules.py:185
condition
Condition function called at each event to determine if wrapped module should be executed.
Definition: metamodules.py:199
storearray_name
Name of the StoreArray to be checked (str).
Definition: metamodules.py:261
storearray_is_present
Flag that the StoreArray is present (bool).
Definition: metamodules.py:267
def __init__(self, module, storearray_name, storearray_durability=0)
Definition: metamodules.py:250
storearray_durability
Durability of the StoreArray to be checked (int).
Definition: metamodules.py:264
def __init__(self, path=None, modules=None)
Definition: metamodules.py:323
string default_output_file_name
The default name for output if none is given.
Definition: metamodules.py:131
profiler
The used profiler instance.
Definition: metamodules.py:147
output_file_name
The output file name the results will be written into.
Definition: metamodules.py:139
def __init__(self, module, output_file_name=None)
Definition: metamodules.py:133
def compose_wrapped_module_name(self, module)
Definition: metamodules.py:83