Belle II Software development
Configuration.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9#include <framework/database/Configuration.h>
10#include <framework/logging/Logger.h>
11#include <framework/dataobjects/FileMetaData.h>
12#include <framework/database/Downloader.h>
13#include <framework/database/Database.h>
14#include <framework/utilities/Utils.h>
15#include <boost/python.hpp>
16#include <framework/core/PyObjConvUtils.h>
17#include <framework/core/PyObjROOTUtils.h>
18#include <boost/algorithm/string.hpp>
19
20#include <set>
21#include <regex>
22
23// Current default globaltag when generating events.
24#define CURRENT_DEFAULT_TAG "main_2024-12-20"
25
26namespace py = boost::python;
27
28namespace {
34 std::vector<std::string> extractStringList(const py::object& obj)
35 {
36 std::vector<std::string> result;
37 Belle2::PyObjConvUtils::iteratePythonObject(obj, [&result](const boost::python::object & item) {
38 py::object str(py::handle<>(PyObject_Str(item.ptr()))); // convert to string
39 py::extract<std::string> extract(str); // and extract
40 result.emplace_back(extract()); // and push back
41 return true;
42 });
43 // done, return
44 return result;
45 }
46}
47
48namespace Belle2::Conditions {
49 boost::python::list& CppOrPyList::ensurePy()
50 {
51 // convert to python list ...
52 if (m_value.index() == 0) {
53 boost::python::list tmp;
54 for (const auto& e : std::get<0>(m_value)) { tmp.append(e); }
55 m_value.emplace<boost::python::list>(std::move(tmp));
56 }
57 return std::get<1>(m_value);
58 }
59
60 std::vector<std::string>& CppOrPyList::ensureCpp()
61 {
62 // or convert to std::vector ...
63 if (m_value.index() == 1) {
64 std::vector<std::string> tmp = extractStringList(std::get<1>(m_value));
65 m_value.emplace<std::vector<std::string>>(std::move(tmp));
66 }
67 return std::get<0>(m_value);
68 }
69
70 void CppOrPyList::append(const std::string& element)
71 {
72 std::visit(Utils::VisitOverload{
73 [&element](std::vector<std::string>& list) {list.emplace_back(element);},
74 [&element](boost::python::list & list) {list.append(element);}
75 }, m_value);
76 }
77
78 void CppOrPyList::prepend(const std::string& element)
79 {
80 std::visit(Utils::VisitOverload{
81 [&element](std::vector<std::string>& list) {list.emplace(list.begin(), element);},
82 [&element](boost::python::list & list) {list.insert(0, element);}
83 }, m_value);
84 }
85
86 void CppOrPyList::shallowCopy(const boost::python::object& source)
87 {
88 ensurePy().slice(boost::python::_, boost::python::_) = source;
89 }
90
91 Configuration& Configuration::getInstance()
92 {
93 static Configuration instance;
94 return instance;
95 }
96
98 {
99 // Backwards compatibility with the existing BELLE2_CONDB_GLOBALTAG
100 // environment variable: If it is set disable replay
101 if (EnvironmentVariables::isSet("BELLE2_CONDB_GLOBALTAG")) {
102 fillFromEnv(m_globalTags, "BELLE2_CONDB_GLOBALTAG", "");
104 }
105 std::string serverList = EnvironmentVariables::get("BELLE2_CONDB_SERVERLIST", m_defaultMetadataProviderUrl);
106 fillFromEnv(m_metadataProviders, "BELLE2_CONDB_METADATA", serverList + " /cvmfs/belle.cern.ch/conditions/database.sqlite");
107 fillFromEnv(m_payloadLocations, "BELLE2_CONDB_PAYLOADS", "/cvmfs/belle.cern.ch/conditions");
108 }
109
111 {
114 }
115 *this = Configuration();
116 }
117
118 std::vector<std::string> Configuration::getDefaultGlobalTags() const
119 {
120 // currently the default globaltag can be overwritten by environment variable
121 // so keep that
122 return EnvironmentVariables::getOrCreateList("BELLE2_CONDB_GLOBALTAG", CURRENT_DEFAULT_TAG);
123 }
124
126 {
127 // same as above but as a python tuple ...
128 py::list list;
129 fillFromEnv(list, "BELLE2_CONDB_GLOBALTAG", CURRENT_DEFAULT_TAG);
130 return py::tuple(list);
131 }
132
133 void Configuration::setInputMetadata(const std::vector<FileMetaData>& inputMetadata)
134 {
136 m_inputMetadata = inputMetadata;
137 // make sure the list of globaltags to be used is created but empty
138 m_inputGlobaltags.emplace();
139 // now check for compatibility: make sure all metadata have the same globaltag
140 // setting. Unless we don't have metadata ...
141 if (inputMetadata.empty()) return;
142
143 std::optional<std::string> inputGlobaltags;
144 for (const auto& metadata : inputMetadata) {
145 if (!inputGlobaltags) {
146 inputGlobaltags = metadata.getDatabaseGlobalTag();
147 } else {
148 if (inputGlobaltags != metadata.getDatabaseGlobalTag()) {
149 B2WARNING("Input files metadata contain incompatible globaltag settings, globaltag replay not possible");
150 // no need to set anything
151 return;
152 }
153 }
154 }
155 // if it's still set and empty we have an empty input list ... warn specifically.
156 if (inputGlobaltags and inputGlobaltags->empty()) {
157 B2WARNING("Input files metadata all have empty globaltag setting, globaltag replay not possible");
158 return;
159 }
160 // set the list of globaltags from the string containing the globaltags
161 boost::split(*m_inputGlobaltags, *inputGlobaltags, boost::is_any_of(","));
162
163 // HACK: So, we successfully set the input globaltags from the input file,
164 // however we also decided that we want to add new payloads for
165 // boost, invariant mass, beam spot, collision axis in CMS.
166 // So if the release is older than when these features were introduced
167 // or if files were produced before specific date, extra GTs are appended.
168 // The appended GTs contain only the possible missing info and are added
169 // with lowest priority.
170 // If the files actually had all payloads these legacy payloads will never
171 // be used as they have lowest priority.
172 // Otherwise this should enable running over old files.
173 //
174 // TODO: Once we're sure all files being used contain all payloads remove this.
175
176 std::optional<std::string> relMin, dateMin;
177
178 for (const auto& metadata : inputMetadata) {
179 // get oldest release
180 std::string rel = metadata.getRelease().substr(0, 10);
181 if (std::regex_match(rel, std::regex("release-[0-9][0-9]"))) {
182 if (!relMin) relMin = rel;
183 relMin = min(*relMin, rel);
184 }
185
186 // get oldest production date
187 std::string date = metadata.getDate().substr(0, 10);
188 if (std::regex_match(date, std::regex("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]"))) {
189 if (!dateMin) dateMin = date;
190 dateMin = min(*dateMin, date);
191 }
192 }
193
194 // add IP GT if rel older than rel04 or for old files
195 if ((relMin && relMin < "release-04") ||
196 (!relMin && (!dateMin || dateMin < "2019-12-31"))) {
197 B2DEBUG(30, "Enabling legacy IP information globaltag in tag replay");
198 m_inputGlobaltags->emplace_back("Legacy_IP_Information");
199 }
200
201 // add CollisionAxisCMS GT if rel older than rel08 or for old files
202 if ((relMin && relMin < "release-08") ||
203 (!relMin && (!dateMin || dateMin < "2023-08-31"))) {
204 B2DEBUG(30, "Enabling legacy CollsionAxisCMS globaltag in tag replay");
205 m_inputGlobaltags->emplace_back("Legacy_CollisionAxisCMS");
206 }
207 // END TODO/HACK
208 }
209
210 std::vector<std::string> Configuration::getBaseTags() const
211 {
212 // return the list of base tags to be used: Either the default tag
213 // or the list of globaltags from the input files
214 if (not m_inputGlobaltags) return getDefaultGlobalTags();
215 return *m_inputGlobaltags;
216 }
217
218 std::vector<std::string> Configuration::getFinalListOfTags()
219 {
220 if (m_overrideEnabled) {
221 B2INFO("Global tag override is in effect: input globaltags and default globaltag will be ignored");
222 return m_globalTags.ensureCpp();
223 }
224
225 auto baseList = getBaseTags();
226 if (m_callback) {
227 // Create a dictionary of keyword arguments for the callback
228 py::dict arguments;
229 // we want a python list of the base tags
230 {
231 py::list baseListPy;
232 for (const auto& tag : baseList) baseListPy.append(tag);
233 arguments["base_tags"] = baseListPy;
234 }
235 // and set the user tags from our list.
236 arguments["user_tags"] = m_globalTags.ensurePy();
237 // and prepare list of metadata. It's None when no replay has been
238 // requested which should mean that we generate events
239 arguments["metadata"] = py::object();
240 // otherwise it's a list of file metadata instances
241 if (m_inputGlobaltags) {
242 py::list metaDataList;
243 for (const auto& m : m_inputMetadata) metaDataList.append(createROOTObjectPyCopy(m));
244 arguments["metadata"] = metaDataList;
245 }
246 // arguments ready, call callback function, python will handle the exceptions
247 py::object retval = (*m_callback)(*py::tuple(), **arguments);
248 // If the return value is not None it should be an iterable
249 // containing the final tag list
250 if (retval != py::object()) {
251 return extractStringList(retval);
252 }
253 // callback returned None so fall back to default
254 }
255 // Default tag replay ... bail if list of globaltags is empty
256 if (baseList.empty()) {
257 if (m_inputGlobaltags) {
258 B2FATAL(R"(No baseline globaltags available.
259 The input files you selected don't have compatible globaltags or an empty
260 globaltag setting. As such globaltag configuration cannot be determined
261 automatically.
262
263 If you really sure that it is a good idea to process these files together
264 you have to manually override the list of globaltags:
265
266 >>> basf2.conditions.override_globaltags()
267)");
268 }else{
269 B2FATAL(R"(No default globaltags available.
270 There is no default globaltag available for processing. This usually means
271 you set the environment variable BELLE2_CONDB_GLOBALTAG to an empty value.
272
273 As this is unlikely to work for even the most basic functionality this is not
274 directly supported anymore. If you really want to disable any access to the
275 conditions database please configure this explicitly
276
277 >>> basf2.conditions.metadata_providers = []
278 >>> basf2.conditions.override_globaltags([])
279)");
280 }
281 }
282 // We have base tags and possibly user tags, so return both
283 std::vector finalList = m_globalTags.ensureCpp();
284 for (const auto& tag : baseList) { finalList.emplace_back(tag); }
285 return finalList;
286 }
287
288 namespace {
290 boost::python::dict expertSettings(const boost::python::tuple& args, boost::python::dict kwargs)
291 {
292 if (py::len(args) != 1) {
293 // keyword only function: raise typerror on non-keyword arguments
294 PyErr_SetString(PyExc_TypeError, ("expert_settings() takes one positional argument but " +
295 std::to_string(len(args)) + " were given").c_str());
296 py::throw_error_already_set();
297 }
298 Configuration& self = py::extract<Configuration&>(args[0]);
299
300 py::dict result;
301 // We want to check for a list of names if they exist in the input keyword
302 // arguments. If so, set the new value. In any case, add to output
303 // dictionary. Simplest way: create a variadic lambda with references to the
304 // dictionary and arguments for name, getter and setter.
305 auto checkValue = [&kwargs, &result](const std::string & name, auto setter, auto getter) {
306 using value_type = decltype(getter());
307 if (kwargs.has_key(name)) {
308 value_type value{};
309 py::object object = kwargs[name];
310 try {
311 value = PyObjConvUtils::convertPythonObject(object, value);
312 } catch (std::runtime_error&) {
313 std::stringstream error;
314 error << "Cannot convert argument '" << name << "' to " << PyObjConvUtils::Type<value_type>::name();
315 PyErr_SetString(PyExc_TypeError, error.str().c_str());
316 py::throw_error_already_set();
317 }
318 setter(value);
319 // remove key from kwargs so we can easily check for ones we don't understand later
320 py::delitem(kwargs, py::object(name));
321 }
322 result[name] = PyObjConvUtils::convertToPythonObject(getter());
323 };
324 auto& downloader = Downloader::getDefaultInstance();
325 // That was all the heavy lifting, now just declare all known options :D
326 // I would love to indent this a bit better but astyle has different opinions
327 checkValue("save_payloads",
328 [&self](const std::string & path) { self.setNewPayloadLocation(path);},
329 [&self]() {return self.getNewPayloadLocation();});
330 checkValue("download_cache_location",
331 [&self](const std::string & path) { self.setDownloadCacheDirectory(path);},
332 [&self]() {return self.getDownloadCacheDirectory();});
333 checkValue("download_lock_timeout",
334 [&self](size_t timeout) { self.setDownloadLockTimeout(timeout);},
335 [&self]() { return self.getDownloadLockTimeout();});
336 checkValue("usable_globaltag_states",
337 [&self](const auto & states) { self.setUsableTagStates(states); },
338 [&self]() { return self.getUsableTagStates(); });
339 checkValue("connection_timeout",
340 [&downloader](unsigned int timeout) {downloader.setConnectionTimeout(timeout);},
341 [&downloader]() { return downloader.getConnectionTimeout();});
342 checkValue("stalled_timeout",
343 [&downloader](unsigned int timeout) {downloader.setStalledTimeout(timeout);},
344 [&downloader]() { return downloader.getStalledTimeout();});
345 checkValue("max_retries",
346 [&downloader](unsigned int retries) {downloader.setMaxRetries(retries);},
347 [&downloader]() { return downloader.getMaxRetries();});
348 checkValue("backoff_factor",
349 [&downloader](unsigned int factor) { downloader.setBackoffFactor(factor);},
350 [&downloader]() { return downloader.getBackoffFactor();});
351 // And lastly check if there is something in the kwargs we don't understand ...
352 if (py::len(kwargs) > 0) {
353 std::string message = "Unrecognized keyword arguments: ";
354 auto keys = kwargs.keys();
355 for (int i = 0; i < len(keys); ++i) {
356 if (i > 0) message += ", ";
357 message += py::extract<std::string>(keys[i]);
358 }
359 PyErr_SetString(PyExc_TypeError, message.c_str());
360 py::throw_error_already_set();
361 }
362 return result;
363 }
364 }
365
366 void Configuration::overrideGlobalTagsPy(const boost::python::list& globalTags)
367 {
368 setGlobalTagsPy(globalTags);
369 m_overrideEnabled = true;
370 }
371
373 {
374 //don't show c++ signature in python doc to keep it simple
375 py::docstring_options options(true, false, false);
376
377 void (Configuration::*overrideGTFlag)() = &Configuration::overrideGlobalTags;
378 void (Configuration::*overrideGTList)(const py::list&) = &Configuration::overrideGlobalTagsPy;
379 py::object expert = raw_function(expertSettings);
380 py::class_<Configuration>("ConditionsConfiguration", R"DOC(
381This class contains all configurations for the conditions database service
382
383* which globaltags to use
384* where to look for payload information
385* where to find the actual payload files
386* which temporary testing payloads to use
387
388But for most users the only thing they should need to care about is to set the
389list of additional `globaltags` to use.
390)DOC")
391 .add_property("override_enabled", &Configuration::overrideEnabled, R"DOC(
392Indicator whether or not the override of globaltags is enabled. If true then
393globaltags present in input files will be ignored and only the ones given in
394`globaltags` will be considered.
395)DOC")
396 .def("reset", &Configuration::reset, R"DOC(reset()
397
398Reset the conditions database configuration to its original state.
399)DOC")
400 .add_property("default_globaltags", &Configuration::getDefaultGlobalTagsPy, R"DOC(
401A tuple containing the default globaltags to be used if events are generated without an input file.
402)DOC")
403 .add_property("globaltags", &Configuration::getGlobalTagsPy, &Configuration::setGlobalTagsPy, R"DOC(
404List of globaltags to be used. These globaltags will be the ones with highest
405priority but by default the globaltags used to create the input files or the
406default globaltag will also be used.
407
408The priority of the globaltags in this list is highest first. So the first in
409the list will be checked first and all other globaltags will only be checked for
410payloads not found so far.
411
412Warning:
413 By default this list contains the globaltags to be used **in addition** to
414 the ones from the input file or the default one if no input file is present.
415 If this is not desirable you need to call `override_globaltags()` to disable
416 any addition or modification of this list.
417)DOC")
418 .def("append_globaltag", &Configuration::appendGlobalTag, py::args("name"), R"DOC(append_globaltag(name)
419
420Append a globaltag to the end of the `globaltags` list. That means it will be
421the lowest priority of all tags in the list.
422)DOC")
423 .def("prepend_globaltag", &Configuration::prependGlobalTag, py::args("name"), R"DOC(prepend_globaltag(name)
424
425Add a globaltag to the beginning of the `globaltags` list. That means it will be
426the highest priority of all tags in the list.
427)DOC")
428 .def("override_globaltags", overrideGTFlag)
429 .def("override_globaltags", overrideGTList, py::args("globaltags"), R"DOC(override_globaltags(list=None)
430
431Enable globaltag override. This disables all modification of the globaltag list at the beginning of processing:
432
433* the default globaltag or the input file globaltags will be ignored.
434* any callback set with `set_globaltag_callback` will be ignored.
435* the list of `globaltags` will be used exactly as it is.
436
437Parameters:
438 list (list(str) or None) if given this list will replace the current content of `globaltags`
439
440Warning:
441 it's still possible to modify `globaltags` after this call.
442)DOC")
443 .def("disable_globaltag_replay", &Configuration::disableGlobalTagReplay, R"DOC("disable_globaltag_replay()
444
445Disable global tag replay and revert to the old behavior that the default
446globaltag will be used if no other globaltags are specified.
447
448This is a shortcut to just calling
449
450 >>> conditions.override_globaltags()
451 >>> conditions.globaltags += list(conditions.default_globaltags)
452
453)DOC")
454 .def("append_testing_payloads", &Configuration::appendTestingPayloadLocation, py::args("filename"), R"DOC(append_testing_payloads(filename)
455
456Append a text file containing local test payloads to the end of the list of
457`testing_payloads`. This will mean they will have lower priority than payloads
458in previously defined text files but still higher priority than globaltags.
459
460Parameters:
461 filename (str): file containing a local definition of payloads and their
462 intervals of validity for testing
463
464Warning:
465 This functionality is strictly for testing purposes. Using local payloads
466 leads to results which cannot be reproduced by anyone else and thus cannot
467 be published.
468)DOC")
469 .def("prepend_testing_payloads", &Configuration::prependTestingPayloadLocation, py::args("filename"), R"DOC(prepend_testing_payloads(filename)
470
471Insert a text file containing local test payloads in the beginning of the list
472of `testing_payloads`. This will mean they will have higher priority than payloads in
473previously defined text files as well as higher priority than globaltags.
474
475Parameters:
476 filename (str): file containing a local definition of payloads and their
477 intervals of validity for testing
478
479Warning:
480 This functionality is strictly for testing purposes. Using local payloads
481 leads to results which cannot be reproduced by anyone else and thus cannot
482 be published.
483)DOC")
485List of text files to look for local testing payloads. Each entry should be a
486text file containing local payloads and their intervals of validity to be used
487for testing.
488
489Payloads found in these files and valid for the current run will have a higher
490priority than any of the `globaltags`. If a valid payload is present in multiple
491files the first one in the list will have higher priority.
492
493Warning:
494 This functionality is strictly for testing purposes. Using local payloads
495 leads to results which cannot be reproduced by anyone else and thus cannot
496 be published.
497)DOC")
498 .add_property("metadata_providers", &Configuration::getMetadataProvidersPy, &Configuration::setMetadataProvidersPy, R"DOC(
499List of metadata providers to use when looking for payload metadata. There are currently two supported providers:
500
5011. Central metadata provider to look for payloads in the central conditions database.
502 This provider is used for any entry in this list which starts with ``http(s)://``.
503 The URL should point to the top level of the REST api endpoints on the server
504
5052. Local metadata provider to look for payloads in a local SQLite snapshot taken
506 from the central server. This provider will be assumed for any entry in this
507 list not starting with a protocol specifier or if the protocol is given as ``file://``
508
509This list should rarely need to be changed. The only exception is for users who
510want to be able to use the software without internet connection after they
511downloaded a snapshot of the necessary globaltags with ``b2conditionsdb download``
512to point to this location.
513)DOC")
514 .add_property("default_metadata_provider_url", &Configuration::getDefaultMetadataProviderUrl, R"DOC(
515URL of the default central metadata provider to look for payloads in the
516conditions database.
517)DOC")
518 .add_property("payload_locations", &Configuration::getPayloadLocationsPy, &Configuration::setPayloadLocationsPy, R"DOC(
519List of payload locations to search for payloads which have been found by any of
520the configured `metadata_providers`. This can be a local directory or a
521``http(s)://`` url pointing to the payload directory on a server.
522
523For remote locations starting with ``http(s)://`` we assume that the layout of
524the payloads on the server is the same as on the main payload server:
525The combination of given location and the relative url in the payload metadata
526field ``payloadUrl`` should point to the correct payload on the server.
527
528For local directories, two layouts are supported and will be auto detected:
529
530flat
531 All payloads are in the same directory without any substructure with the name
532 ``dbstore_{name}_rev_{revision}.root``
533hashed
534 All payloads are stored in subdirectories in the form ``AB/{name}_r{revision}.root``
535 where ``A`` and ``B`` are the first two characters of the md5 checksum of the
536 payload file.
537
538Example:
539 Given ``payload_locations = ["payload_dir/", "http://server.com/payloads"]``
540 the framework would look for a payload with name ``BeamParameters`` in revision
541 ``45`` (and checksum ``a34ce5...``) in the following places
542
543
544 1. ``payload_dir/a3/BeamParameters_r45.root``
545 2. ``payload_dir/dbstore_BeamParameters_rev_45.root``
546 3. ``http://server.com/payloads/dbstore/BeamParameters/dbstore_BeamParameters_rev_45.root``
547 given the usual pattern of the ``payloadUrl`` metadata. But this could be
548 changed on the central servers so mirrors should not depend on this convention
549 but copy the actual structure of the central server.
550
551If the payload cannot be found in any of the given locations the framework will
552always attempt to download it directly from the central server and put it in a
553local cache directory.
554)DOC")
555 .def("expert_settings", expert, R"DOC(expert_settings(**kwargs)
556
557Set some additional settings for the conditions database.
558
559You can supply any combination of keyword-only arguments defined below. The
560function will return a dictionary containing all current settings.
561
562 >>> conditions.expert_settings(connection_timeout=5, max_retries=1)
563 {'save_payloads': 'localdb/database.txt',
564 'download_cache_location': '',
565 'download_lock_timeout': 120,
566 'usable_globaltag_states': {'PUBLISHED', 'RUNNING', 'TESTING', 'VALIDATED'},
567 'connection_timeout': 5,
568 'stalled_timeout': 60,
569 'max_retries': 1,
570 'backoff_factor': 5}
571
572Warning:
573 Modification of these parameters should not be needed, in rare
574 circumstances this could be used to optimize access for many jobs at once
575 but should only be set by experts.
576
577Parameters:
578 save_payloads (str): Where to store new payloads created during processing.
579 This should be a filename to contain the payload information and the payload
580 files will be placed in the same directory as the file.
581 download_cache_location (str): Where to store payloads which have been downloaded
582 from the central server. This could be a user defined directory, otherwise
583 empty string defaults to ``$TMPDIR/basf2-conditions`` where ``$TMPDIR`` is the
584 temporary directories defined in the system. Newly downloaded payloads will
585 be stored in this directory in a hashed structure, see `payload_locations`
586 download_lock_timeout (int): How many seconds to wait for a write lock when
587 concurrently downloading the same payload between different processes.
588 If locking fails the payload will be downloaded to a temporary file
589 separately for each process.
590 usable_globaltag_states (set(str)): Names of globaltag states accepted for
591 processing. This can be changed to make sure that only fully published
592 globaltags are used or to enable running on an open tag. It is not possible
593 to allow usage of 'INVALID' tags, those will always be rejected.
594 connection_timeout (int): timeout in seconds before connection should be
595 aborted. 0 sets the timeout to the default (300s)
596 stalled_timeout (int): timeout in seconds before a download should be
597 aborted if the speed stays below 10 KB/s, 0 disables this timeout
598 max_retries (int): maximum amount of retries if the server responded with
599 an HTTP response of 500 or more. 0 disables retrying
600 backoff_factor (int): backoff factor for retries in seconds. Retries are
601 performed using something similar to binary backoff: For retry :math:`n`
602 and a ``backoff_factor`` :math:`f` we wait for a random time chosen
603 uniformly from the interval :math:`[1, (2^{n} - 1) \times f]` in
604 seconds.
605)DOC")
606 .def("set_globaltag_callback", &Configuration::setGlobaltagCallbackPy, R"DOC(set_globaltag_callback(function)
607
608Set a callback function to be called just before processing.
609
610This callback can be used to further customize the globaltags to be used during
611processing. It will be called after the input files have been opened and checked
612with three keyword arguments:
613
614base_tags
615 The globaltags determined from either the input files or, if no input files
616 are present, the default globaltags
617
618user_tags
619 The globaltags provided by the user
620
621metadata
622 If there are not input files (e.g. generating events) this argument is None.
623 Otherwise it is a list of all the ``FileMetaData`` instances from all input files.
624 This list can be empty if there is no metadata associated with the input files.
625
626From this information the callback function should then compose the final list
627of globaltags to be used for processing and return this list. If ``None`` is
628returned the default behavior is applied as if there were no callback function.
629If anything else is returned the processing is aborted.
630
631If no callback function is specified the default behavior is equivalent to ::
632
633 def callback(base_tags, user_tags, metadata):
634 if not base_tags:
635 basf2.B2FATAL("No baseline globaltags available. Please use override")
636
637 return user_tags + base_tags
638
639If `override_enabled` is ``True`` then the callback function will not be called.
640
641Warning:
642 If a callback is set it is responsible to select the correct list of globaltags
643 and also make sure that all files are compatible. No further checks will be
644 done by the framework but any list of globaltags which is returned will be used
645 exactly as it is.
646
647 If the list of ``base_tags`` is empty that usually means that the input files
648 had different globaltag settings but it is the responsibility of the callback
649 to then verify if the list of globaltags is usable or not.
650
651 If the callback function determines that no working set of globaltags can be
652 determined then it should abort processing using a FATAL error or an exception
653)DOC")
654 ;
655
656 py::scope().attr("conditions") = py::ptr(&Configuration::getInstance());
657 }
658} // Belle2::Conditions namespace
bool m_overrideEnabled
is the globaltag override enabled?
CppOrPyList m_globalTags
the list with all user globaltags
void prependGlobalTag(const std::string &globalTag)
prepend a globaltag
Definition: Configuration.h:80
boost::python::tuple getDefaultGlobalTagsPy() const
Get the tuple of default globaltags as python version.
void appendGlobalTag(const std::string &globalTag)
Append a globaltag.
Definition: Configuration.h:78
Configuration()
Initialize default values.
void ensureEditable() const
Check whether the configuration object can be edited or if the database has been initialized already.
void setGlobaltagCallbackPy(const boost::python::object &obj)
Set a callback function from python which will be called when processing starts and should return the...
void disableGlobalTagReplay()
Disable global tag replay.
boost::python::list getGlobalTagsPy()
Get the list of user globaltags as python version.
Definition: Configuration.h:88
static Configuration & getInstance()
Get a reference to the instance which will be used when the Database is initialized.
void setMetadataProvidersPy(const boost::python::list &list)
Set the list of metadata providers in python.
boost::python::list getTestingPayloadLocationsPy()
Get the list of text files containing test payloads in python.
std::vector< std::string > getFinalListOfTags()
Get the final list of globaltags to be used for processing.
static void fillFromEnv(T &target, const std::string &envName, const std::string &defaultValue)
Fill a target object from a list of environment variables.
std::vector< FileMetaData > m_inputMetadata
the file metadata of all input files if globaltag replay is requested by input module
void setPayloadLocationsPy(const boost::python::list &list)
Set the list of payload locations in python.
CppOrPyList m_metadataProviders
the list with all the metadata providers
void setInputMetadata(const std::vector< FileMetaData > &inputMetadata)
To be called by input modules with the list of all input FileMetaData.
boost::python::list getMetadataProvidersPy()
Get the list of metadata providers in python.
void overrideGlobalTags()
Enable globaltag override: If this is called once than overrideEnabled() will return true and getFina...
std::vector< std::string > getBaseTags() const
Get the base globaltags to be used in addition to user globaltags.
void setTestingPayloadLocationsPy(const boost::python::list &list)
Set the list of text files containing test payloads in python.
std::optional< std::vector< std::string > > m_inputGlobaltags
the list of globaltags from all the input files to be used in addition to the user globaltags
void prependTestingPayloadLocation(const std::string &filename)
Prepend a local text file with testing payloads to the list.
boost::python::list getPayloadLocationsPy()
Get the list og payload locations in python.
static void exposePythonAPI()
expose this class to python
std::string m_defaultMetadataProviderUrl
default URL where to look for the metadata provider
std::string getDefaultMetadataProviderUrl()
Get the default URL where to look for the metadata provider.
CppOrPyList m_payloadLocations
the list with all the payload locations
std::vector< std::string > getDefaultGlobalTags() const
Get the std::vector of default globaltags.
void overrideGlobalTagsPy(const boost::python::list &globalTags)
Enable globaltag override and set the list of user globaltags in one go.
void reset()
Reset to default values.
void setGlobalTagsPy(const boost::python::list &globalTags)
Set the list of globaltags from python.
Definition: Configuration.h:84
bool overrideEnabled() const
Check if override is enabled by previous calls to overrideGlobalTags()
std::optional< boost::python::object > m_callback
the callback function to determine the final final list of globaltags
void appendTestingPayloadLocation(const std::string &filename)
Add a local text file with testing payloads.
bool m_databaseInitialized
bool indicating whether the database has been initialized, in which case any changes to the configura...
void prepend(const std::string &element)
Prepend an element to whatever representation we currently have.
void shallowCopy(const boost::python::object &source)
shallow copy all elements of the source object into the python representation.
void append(const std::string &element)
Append an element to whatever representation we currently have.
boost::python::list & ensurePy()
Return the python list version.
std::vector< std::string > & ensureCpp()
Return the C++ vector version.
std::variant< std::vector< std::string >, boost::python::list > m_value
Store either a std::vector or a python list of strings.
Definition: Configuration.h:45
static Downloader & getDefaultInstance()
Return the default instance.
Definition: Downloader.cc:132
static std::string get(const std::string &name, const std::string &fallback="")
Get the value of an environment variable or the given fallback value if the variable is not set.
static bool isSet(const std::string &name)
Check if a value is set in the database.
boost::python::object createROOTObjectPyCopy(const T &instance)
Create a python wrapped copy from a class instance which has a ROOT dictionary.
static Database & Instance()
Instance of a singleton Database.
Definition: Database.cc:41
static std::vector< std::string > getOrCreateList(const std::string &name, const std::string &fallback, const std::string &separators=" \t\n\r")
Get a list of values from an environment variable or the given fallback string if the variable is not...
static void reset(bool keepConfig=false)
Reset the database instance.
Definition: Database.cc:49
boost::python::object convertToPythonObject(const Scalar &value)
------------— From C++ TO Python Converter ---------------------—
bool iteratePythonObject(const boost::python::object &pyObject, Functor function)
Helper function to loop over a python object that implements the iterator concept and call a functor ...
Scalar convertPythonObject(const boost::python::object &pyObject, Scalar)
Convert from Python to given type.
static std::string name()
type name.