##########################################################################
# basf2 (Belle II Analysis Software Framework) #
# Author: The Belle II Collaboration #
# #
# See git log for contributors and copyright holders. #
# This file is licensed under LGPL-3.0, see LICENSE.md. #
##########################################################################
import os
import tempfile
from hep_ipython_tools import calculation_queue, calculation, information, calculation_list
[docs]class IPythonHandler:
"""
Handler class to start processes in an IPython notebook in a convenient way.
From this whole framework you should not need to create any instances by yourself but rather use the
given ipython handler for this.
Create a handler object in the beginning of your NB and use the two methods `process()`
and `process_parameter_space()` to turn parameters or a parameter creator function into a Calculation.
Do not create calculations on you own::
from tracking.validation.ipython_handler import handler
calculation = handler.process(parameters)
"""
def __init__(self):
"""
Each created log file gets registered and deleted if there are more than 20 log files present
or if the get_log function of the process is called (the log is saved elsewhere).
As the log files are saved to /tmp you have probably not to care about deleting them.
"""
#: A list of open log files.
self.log_files = []
#: A shortcut for returning information on the environment.
self.information = information.EnvironmentInformation()
#: The calculation type to use
self._calculation_type = calculation.Calculation
[docs] def process(self, result_queue=None, **kwargs):
"""
Turn a parameter set into a Calculation that you can start, stop or whatever you want.
Arguments:
result_queue: The CalculationQueue you want to use. Without giving
this as a parameter the function creates one for you. Create
one on your own with the function create_queue.
"""
if result_queue is None:
result_queue = calculation_queue.CalculationQueue()
calculation = self._calculation_type()
calculation.append(result_queue=result_queue, log_file_name=self.next_log_file_name(), parameters=None, **kwargs)
return calculation
[docs] def process_parameter_space(self, kwargs_creator_function, **parameter_lists):
"""
Create a list of calculations by combining all parameters with all parameters you provide and
feeding the tuple into the parameter_creator_function.
If the kwargs_creator_function has a parameter named queue, the function feeds the corresponding
created queue into the parameter_creator_function.
The parameter_creator_function must return a dictionary for every combination of parameters it gets,
which will be used to construct a process out of it.
See ipython_handler_basf2/ipython_handler for an example.
Please note that a list of calculations acts the same as a single calculation you would get from
the process function. You can handle 10 calculations the same way you would handle a single one.
The kwargs_creator_function can transform the incoming parameters into different ones. To make this
more clear, the resulting dictionary created by the kwargs_creator_function is called kwargs.
These are the ones, that will be used to create a calculation process, so they must be compatible to the
calculation you chose (namely compatible with the append function of the _calculation_type).
Arguments:
kwargs_creator_function: A function with as many input parameters
as parameters you provide. If the function has an additional
queue parameter it is fed with the corresponding queue for this
calculation.
parameter_lists: As many lists as you want. Every list is one
parameter. If you do not want a specific parameter
constellation to occur, you can return None in your
parameter_creator_function for this combination.
Usage::
def kwargs_creator_function(par_1, par_2, par_3, queue):
kwargs = {... f(par_1) ... g(par_2) ... h(par_3)}
queue.put(..., ...)
return kwargs
calculations = handler.process_parameter_space(kwargs_creator_function,
par_1=[1, 2, 3], par_2=["x", "y", "z"], par_3=[3, 4, 5])
The calculations will be created with the kwargs arguments.
"""
all_kwargs, all_queues, all_parameters = calculation_list.create_all_calculations(kwargs_creator_function,
**parameter_lists)
calculations = self._calculation_type()
for kwargs, q, parameters in zip(all_kwargs, all_queues, all_parameters):
calculations.append(result_queue=q, log_file_name=self.next_log_file_name(),
parameters=parameters, **kwargs)
return calculations
[docs] def next_log_file_name(self):
"""
Return the name of the next log file.
If there are more than 20 log files present,
start deleting the oldest ones.
"""
next_log_file = tempfile.mkstemp()
self.log_files.append(next_log_file)
while len(self.log_files) > 20:
first_log_file = self.log_files.pop(0)
f = first_log_file[0]
log_file_name = first_log_file[1]
os.close(f)
try:
os.unlink(log_file_name)
except OSError:
pass
return next_log_file[1]
[docs] @staticmethod
def create_queue():
"""
Create a Calculation queue. You need to do this if you want to pass it to your modules
and write to it while processing the events.
"""
return calculation_queue.CalculationQueue()