12 #include <framework/pybasf2/Framework.h>
14 #include <framework/core/PyObjConvUtils.h>
15 #include <framework/core/Environment.h>
16 #include <framework/core/RandomNumbers.h>
17 #include <framework/core/EventProcessor.h>
18 #include <framework/core/ModuleManager.h>
19 #include <framework/datastore/DataStore.h>
20 #include <framework/database/DBStore.h>
21 #include <framework/database/Database.h>
22 #include <framework/pcore/pEventProcessor.h>
23 #include <framework/pcore/ZMQEventProcessor.h>
24 #include <framework/pcore/zmq/utils/ZMQAddressUtils.h>
25 #include <framework/utilities/FileSystem.h>
26 #include <framework/database/Configuration.h>
28 #include <framework/logging/Logger.h>
29 #include <framework/logging/LogSystem.h>
31 #include <boost/python.hpp>
35 using namespace boost::python;
39 Framework::Framework()
41 DataStore::s_DoCleanup =
true;
42 LogSystem::Instance().enableErrorSummary(
true);
44 if (!RandomNumbers::isInitialized()) {
45 RandomNumbers::initialize();
47 Environment::Instance();
51 Framework::~Framework()
57 ModuleManager::Instance().reset();
58 DataStore::s_DoCleanup =
false;
60 Conditions::Configuration::getInstance().reset();
64 void Framework::addModuleSearchPath(
const std::string& path)
66 ModuleManager::Instance().addModuleSearchPath(path);
70 void Framework::setExternalsPath(
const std::string& path)
72 Environment::Instance().setExternalsPath(path);
76 ModulePtr Framework::registerModule(
const std::string& moduleName)
78 return ModuleManager::Instance().registerModule(moduleName);
82 ModulePtr Framework::registerModule(
const std::string& moduleName,
const std::string& sharedLibPath)
84 return ModuleManager::Instance().registerModule(moduleName, sharedLibPath);
88 void Framework::process(
PathPtr startPath,
long maxEvent)
90 if (Environment::Instance().getDryRun()) {
91 Environment::Instance().setJobInformation(startPath);
95 static bool already_executed =
false;
96 static std::set<const Module*> previously_run_modules;
97 static int errors_from_previous_run = 0;
98 const auto moduleListUnique = startPath->buildModulePathList(
true);
99 if (already_executed) {
100 B2WARNING(
"Calling process() more than once per steering file is still experimental, please check results carefully! Python modules especially should reinitialise their state in initialise() to avoid problems");
101 if (startPath->buildModulePathList(
true) != startPath->buildModulePathList(
false)) {
102 B2FATAL(
"Your path contains the same module instance in multiple places. Calling process() multiple times is not implemented for this case.");
106 for (
const auto& m : moduleListUnique) {
107 if (previously_run_modules.count(m.get()) > 0) {
109 startPath = std::static_pointer_cast<Path>(startPath->clone());
114 for (
const auto& m : moduleListUnique) {
115 previously_run_modules.insert(m.get());
118 int numLogError = LogSystem::Instance().getMessageCounter(LogConfig::c_Error);
119 if (numLogError != errors_from_previous_run) {
120 B2FATAL(numLogError <<
" ERROR(S) occurred! The processing of events will not be started.");
124 LogSystem::Instance().resetMessageCounter();
125 DataStore::Instance().reset();
126 DataStore::Instance().setInitializeActive(
true);
128 auto& environment = Environment::Instance();
130 already_executed =
true;
131 if (environment.getNumberProcesses() == 0) {
133 processor.setProfileModuleName(environment.getProfileModuleName());
134 processor.process(startPath, maxEvent);
136 if (environment.getUseZMQ()) {
138 if (environment.getZMQSocketAddress().empty()) {
139 environment.setZMQSocketAddress(ZMQAddressUtils::randomSocketName());
142 processor.process(startPath, maxEvent);
145 processor.process(startPath, maxEvent);
148 errors_from_previous_run = LogSystem::Instance().getMessageCounter(LogConfig::c_Error);
150 DBStore::Instance().reset();
154 Database::Instance().reset(
true);
155 }
catch (std::exception& e) {
156 B2ERROR(
"Uncaught exception encountered: " << e.what());
157 DataStore::Instance().reset();
160 B2ERROR(
"Uncaught exception encountered!");
161 DataStore::Instance().reset();
168 void Framework::setNumberProcesses(
int numProcesses)
170 Environment::Instance().setNumberProcesses(numProcesses);
174 int Framework::getNumberProcesses()
176 return Environment::Instance().getNumberProcesses();
180 void Framework::setPicklePath(
const std::string& path)
182 Environment::Instance().setPicklePath(path);
186 std::string Framework::getPicklePath()
188 return Environment::Instance().getPicklePath();
191 void Framework::setStreamingObjects(
const boost::python::list& streamingObjects)
193 auto vec = PyObjConvUtils::convertPythonObject(streamingObjects, std::vector<std::string>());
194 Environment::Instance().setStreamingObjects(vec);
197 std::string Framework::findFile(
const std::string& filename,
const std::string& type,
bool ignore_errors)
202 result = FileSystem::findFile(filename, ignore_errors);
204 result = FileSystem::findFile(filename, type, ignore_errors);
206 if (!ignore_errors and result.empty()) {
211 PyErr_SetFromErrnoWithFilename(PyExc_FileNotFoundError, filename.c_str());
212 boost::python::throw_error_already_set();
221 boost::python::list Framework::getModuleSearchPathsPython()
223 boost::python::list returnList;
225 for (
const std::string& path : ModuleManager::Instance().getModuleSearchPaths())
226 returnList.append(boost::python::object(path));
231 boost::python::dict Framework::getAvailableModulesPython()
233 boost::python::dict returnDict;
234 for (
const auto& modulePair : ModuleManager::Instance().getAvailableModules())
235 returnDict[boost::python::object(modulePair.first)] = boost::python::object(modulePair.second);
240 boost::python::list Framework::getRegisteredModulesPython()
242 boost::python::list returnList;
244 for (
const ModulePtr& mod : ModuleManager::Instance().getCreatedModules())
245 returnList.append(boost::python::object(mod));
250 #if !defined(__GNUG__) || defined(__ICC)
252 #pragma GCC diagnostic push
253 #pragma GCC diagnostic ignored "-Wunused-local-typedefs"
255 BOOST_PYTHON_FUNCTION_OVERLOADS(process_overloads, Framework::process, 1, 2)
256 #if !defined(__GNUG__) || defined(__ICC)
258 #pragma GCC diagnostic pop
262 PyObject* PyExc_ModuleNotCreatedError{
nullptr};
265 void moduleNotCreatedTranslator(
const ModuleManager::ModuleNotCreatedError& e)
267 PyErr_SetString(PyExc_ModuleNotCreatedError, e.what());
271 void Framework::exposePythonAPI()
273 PyExc_ModuleNotCreatedError = PyErr_NewExceptionWithDoc(
"basf2.ModuleNotCreatedError",
274 "This exception is raised when a basf2 module could not be created for any reason",
275 PyExc_RuntimeError,
nullptr);
276 scope().attr(
"ModuleNotCreatedError") = handle<>(borrowed(PyExc_ModuleNotCreatedError));
277 register_exception_translator<ModuleManager::ModuleNotCreatedError>(moduleNotCreatedTranslator);
279 ModulePtr(*registerModule1)(
const std::string&) = &Framework::registerModule;
280 ModulePtr(*registerModule2)(
const std::string&,
const std::string&) = &Framework::registerModule;
283 docstring_options options(
true,
true,
false);
286 class_<Framework, std::shared_ptr<Framework>, boost::noncopyable>(
"Framework",
"Initialize and Cleanup functions", no_init);
287 std::shared_ptr<Framework> initguard{
new Framework()};
288 scope().attr(
"__framework") = initguard;
290 def(
"add_module_search_path", &Framework::addModuleSearchPath, R
"DOCSTRING(
291 Add a directory in which to search for compiled basf2 C++ `Modules <Module>`.
293 This directory needs to contain the shared libraries containing the compiled
294 modules as well as companion files ending in ``.b2modmap`` which contain a list
295 of the module names contained in each library.
298 The newly added path will not override existing modules
301 path (str): directory containing the modules.
302 )DOCSTRING", args("path"));
303 def(
"set_externals_path", &Framework::setExternalsPath, R
"DOCSTRING(
304 Set the path to the externals to be used.
307 This will not change the library and executable paths but will just change
308 the directory where to look for certain data files like the Evtgen particle
309 definition file. Don't use this unless you really know what you are doing.
312 path (str): new top level directory for the externals
313 )DOCSTRING", args("path"));
314 def(
"list_module_search_paths", &Framework::getModuleSearchPathsPython, R
"DOCSTRING(
315 Return a python list containing all the directories included in the module
319 `add_module_search_path`
321 def("list_available_modules", &Framework::getAvailableModulesPython, R
"DOCSTRING(
322 Return a dictionary containing the names of all known modules
323 as keys and the name of the shared library containing these modules as values.
325 def("list_registered_modules", &Framework::getRegisteredModulesPython, R
"DOCSTRING(
326 Return a list with pointers to all previously created module instances by calling `register_module()`
328 def("get_pickle_path", &Framework::getPicklePath, R
"DOCSTRING(
329 Return the filename where the pickled path is or should be stored
331 def("set_pickle_path", &Framework::setPicklePath, R
"DOCSTRING(
332 Set the filename where the pickled path should be stored or retrieved from
333 )DOCSTRING", args("path"));
334 def(
"set_nprocesses", &Framework::setNumberProcesses, R
"DOCSTRING(
335 Sets number of worker processes for parallel processing.
337 Can be overridden using the ``-p`` argument to basf2.
340 Setting this to 1 will have one parallel worker job which is almost always
341 slower than just running without parallel processing but is still provided to
342 allow debugging of parallel execution.
345 nproc (int): number of worker processes. 0 to disable parallel processing.
347 def("get_nprocesses", &Framework::getNumberProcesses, R
"DOCSTRING(
348 Gets number of worker processes for parallel processing. 0 disables parallel processing
350 def("set_streamobjs", &Framework::setStreamingObjects, R
"DOCSTRING(
351 Set the names of all DataStore objects which should be sent between the
352 parallel processes. This can be used to improve parallel processing performance
353 by removing objects not required.
358 docstring_options param_options(
true,
false,
false);
359 def(
"_register_module", registerModule1);
360 def(
"_register_module", registerModule2, R
"DOCSTRING(register_module(name, library=None)
361 Register a new Module.
363 This function will try to create a new instance of a module with the given name. If no library is given it will try to find the module by itself from the module search path. Optionally one can specify the name of a shared library containing the module code then this library will be loaded
366 `list_module_search_paths()`, `add_module_search_path()`
369 name (str): Type of the module to create
370 library (str): Optional, name of a shared library containing the module
373 An instance of the module if successful.
376 will raise a `ModuleNotCreatedError` if there is any problem creating the module.
378 def("_process", &Framework::process, process_overloads(R
"DOCSTRING(process(path, num_events=0)
379 Processes up to max_events events by starting with the first module in the specified path.
381 This method starts processing events only if there is a module in the path
382 which is capable of specifying the end of the data flow.
385 path (Path): The processing starts with the first module of this path.
386 max_events (int): The maximum number of events that will be processed.
387 If the number is smaller than 1, all events will be processed (default).
392 def("find_file", &Framework::findFile, (arg(
"filename"), arg(
"data_type") =
"", arg(
"silent") =
false), R
"DOC(
393 Try to find a file and return its full path
395 If ``data_type`` is empty this function will try to find the file
397 1. in ``$BELLE2_LOCAL_DIR``,
398 2. in ``$BELLE2_RELEASE_DIR``
399 3. relative to the current working directory.
401 Other known ``data_type`` values are
404 Example data for examples and tutorials. Will try to find the file
406 1. in ``$BELLE2_EXAMPLES_DATA_DIR``
407 2. in ``$VO_BELLE2_SW_DIR/examples-data``
408 3. relative to the current working directory
411 Data for Validation purposes. Will try to find the file in
413 1. in ``$BELLE2_VALIDATION_DATA_DIR``
414 2. in ``$VO_BELLE2_SW_DIR/validation-data``
415 3. relative to the current working directory
417 .. versionadded:: release-03-00-00
420 filename (str): relative filename to look for, either in a central place or
421 in the current working directory
422 data_type (str): case insensitive data type to find. Either empty string or
423 one of ``"examples"`` or ``"validation"``
424 silent (bool): If True don't print any errors and just return an empty
425 string if the file cannot be found