The Gaudi Framework  master (9ef1c226)
Loading...
Searching...
No Matches
ConfigurableDb.py
Go to the documentation of this file.
13"""A singleton holding informations on the whereabouts of all the automatically
14generated Configurables.
15This repository of (informations on) Configurables is used by the PropertyProxy
16class to locate Configurables and feed the JobOptionsSvc. It could also be used
17to feed the AthenaEmacs module..."""
18
19__all__ = ["CfgDb", "cfgDb", "loadConfigurableDb", "getConfigurable"]
20
21import logging
22
23log = logging.getLogger("ConfigurableDb")
24
25
26_TRANS_TABLE = str.maketrans("<>&*,: ().", "__rp__s___")
27
28
30 """
31 Translate a C++ type name (with templates etc.) to Python identifier.
32 """
33 return name.replace(", ", ",").translate(_TRANS_TABLE)
34
35
36class _CfgDb(dict):
37 """
38 A singleton class holding informations about automatically generated
39 Configurables.
40 --> package holding that Configurable
41 --> library holding the components related to that Configurable
42 --> python module holding the Configurable
43 --> a dictionary of duplicates
44 """
45
46 __slots__ = {
47 "_duplicates": {},
48 }
49
50 def __init__(self):
51 object.__init__(self)
52 self._duplicates = {}
53
54 def add(self, configurable, package, module, lib):
55 """Method to populate the Db.
56 It is called from the auto-generated Xyz_confDb.py files (genconf.cpp)
57 @param configurable: the name of the configurable being added
58 @param package: the name of the package which holds this Configurable
59 @param module: the name of the python module holding the Configurable
60 @param lib: the name of the library holding the component(s) (ie: the
61 C++ Gaudi component which is mapped by the Configurable)
62 """
63 cfg = {"package": package, "module": module, "lib": lib}
64 if configurable in self:
65 # check if it comes from the same library...
66 if cfg["lib"] != self[configurable]["lib"]:
67 log.debug(
68 "dup!! [%s] p=%s m=%s lib=%s", configurable, package, module, lib
69 )
70 if configurable in self._duplicates:
71 self._duplicates[configurable] += [cfg]
72 else:
73 self._duplicates[configurable] = [cfg]
74 else:
75 log.debug("added [%s] p=%s m=%s lib=%s", configurable, package, module, lib)
76 self[configurable] = cfg
77
78 def duplicates(self):
79 return self._duplicates
80
81 def _loadModule(self, fname):
82 f = open(fname)
83 for i, ll in enumerate(f):
84 l = ll.strip()
85 if l.startswith("#") or len(l) <= 0:
86 continue
87 try:
88 line = l.split()
89 cname = line[2]
90 pkg = line[0].split(".")[0]
91 module = line[0]
92 lib = line[1]
93 self.add(cname, pkg, module, lib)
94 except IndexError:
95 f.close()
96 raise Exception("invalid line format: %s:%d: %r" % (fname, i + 1, ll))
97 f.close()
98
99
100class _Singleton(object):
101 # the object this singleton is holding
102 # No other object will be created...
103 __obj = _CfgDb()
104
105 def __call__(self):
106 return self.__obj
107
108
109CfgDb = _Singleton()
110
111# clean-up
112del _Singleton
113del _CfgDb
114
115# default name for CfgDb instance
116cfgDb = CfgDb()
117
118# Helper function to load all ConfigurableDb files holding informations
119
120
122 """Helper function to load all ConfigurableDb files (modules) holding
123 informations about Configurables
124 """
125 import os
126 import sys
127 from itertools import chain
128 from os.path import join as path_join
129
130 from GaudiPluginService.cpluginsvc import GAUDI_DEFAULT_PLUGIN_PATH
131
132 log.debug("loading confDb files...")
133 nFiles = 0 # counter of imported files
134 pathvars = (
135 ["GAUDI_PLUGIN_PATH", "DYLD_LIBRARY_PATH"]
136 if sys.platform == "darwin"
137 else ["GAUDI_PLUGIN_PATH", "LD_LIBRARY_PATH"]
138 )
139 ignored_files = set(os.environ.get("CONFIGURABLE_DB_IGNORE", "").split(","))
140 for path in chain(
141 [GAUDI_DEFAULT_PLUGIN_PATH],
142 *[os.getenv(pv, "").split(os.pathsep) for pv in pathvars],
143 ):
144 if not path or not os.path.isdir(path):
145 continue
146 log.debug("walking in [%s]...", path)
147 confDbFiles = [
148 f
149 for f in [
150 path_join(path, f) for f in os.listdir(path) if f.endswith(".confdb")
151 ]
152 if os.path.isfile(f) and f not in ignored_files
153 ]
154 # check if we use "*_merged.confdb"
155 mergedConfDbFiles = [f for f in confDbFiles if f.endswith("_merged.confdb")]
156 if mergedConfDbFiles:
157 # use only the merged ones
158 confDbFiles = mergedConfDbFiles
159
160 for confDb in confDbFiles:
161 log.debug("\t-loading [%s]...", confDb)
162 try:
163 cfgDb._loadModule(confDb)
164 except Exception as err:
165 log.warning("Could not load file [%s] !", confDb)
166 log.warning("Reason: %s", err)
167 nFiles += 1
168 log.debug("loading confDb files... [DONE]")
169 nPkgs = len(set([k["package"] for k in cfgDb.values()]))
170 log.debug("loaded %i confDb packages", nPkgs)
171 return nFiles
172
173
174def getConfigurable(className, requester="", assumeCxxClass=True):
175 confClass = className
176 if assumeCxxClass:
177 # assume className is C++: --> translate to python
178 confClass = _normalize_cpp_type_name(confClass)
179
180 # see if I have it in my dictionary
181 confClassInfo = cfgDb.get(confClass)
182 if not confClassInfo:
183 confClassInfo = cfgDb.get(confClass)
184 # get the python module
185 confMod = confClassInfo and confClassInfo.get("module")
186 if not confMod:
187 log.warning("%s: Class %s not in database", requester, className)
188 return None
189 # load the module
190 try:
191 mod = __import__(confMod, globals(), locals(), confClass)
192 except ImportError:
193 log.warning(
194 "%s: Module %s not found (needed for configurable %s)",
195 requester,
196 confMod,
197 className,
198 )
199 return None
200 # get the class
201 try:
202 confClass = getattr(mod, confClass)
203 except AttributeError:
204 log.warning(
205 "%s: Configurable %s not found in module %s", requester, confClass, confMod
206 )
207 return None
208 # Got it!
209 log.debug("%s: Found configurable %s in module %s", requester, confClass, confMod)
210
211 return confClass
add(self, configurable, package, module, lib)
getConfigurable(className, requester="", assumeCxxClass=True)