The Gaudi Framework  master (cc9a61f4)
Loading...
Searching...
No Matches
_db.py
Go to the documentation of this file.
12import logging
13import os
14import sys
15
16
17class ConfDB2(object):
18 def __init__(self):
19 import shelve
20 from itertools import chain
21 from pathlib import Path
22
23 from GaudiPluginService.cpluginsvc import GAUDI_DEFAULT_PLUGIN_PATH
24
25 self._dbs = {}
26 pathvars = (
27 ["GAUDI_PLUGIN_PATH", "DYLD_LIBRARY_PATH"]
28 if sys.platform == "darwin"
29 else ["GAUDI_PLUGIN_PATH", "LD_LIBRARY_PATH"]
30 )
31 ignored_files = set(os.environ.get("CONFIGURABLE_DB_IGNORE", "").split(","))
32 for path in chain(
33 [GAUDI_DEFAULT_PLUGIN_PATH],
34 *[os.getenv(pv, "").split(os.pathsep) for pv in pathvars],
35 ):
36 if not path or not os.path.isdir(path):
37 continue
38 dbfiles = [
39 f.absolute().as_posix()
40 for f in Path(path).glob("*.confdb2")
41 if f.absolute().as_posix() not in ignored_files
42 ]
43 dbfiles.sort()
44 for db in [shelve.open(f, "r") for f in dbfiles]:
45 for key in db:
46 self._dbs.setdefault(key, db)
47
48 def __getitem__(self, key):
49 return self._dbs[key][key]
50
51 def __contains__(self, key):
52 return key in self._dbs
53
54 def __iter__(self):
55 return iter(self._dbs)
56
57
58# allow overriding the low level DB access for testing
59if "GAUDICONFIG2_DB" in os.environ:
60 exec(
61 "from {} import {} as _DB".format(*os.environ["GAUDICONFIG2_DB"].rsplit(".", 1))
62 )
63else: # pragma no cover
64 _DB = ConfDB2()
65
66
67_TRANS_TABLE = str.maketrans("<>&*,: ().", "__rp__s___")
68
69
71 """
72 Translate a C++ type name (with templates etc.) to Python identifier.
73 """
74 return name.replace(", ", ",").translate(_TRANS_TABLE)
75
76
77def split_namespace(typename):
78 """
79 Split a C++ qualified namespace in the tuple (top_namespace, rest) starting
80 searching the separator from pos.
81
82 >>> split_namespace('std::chrono::time_point')
83 ('std', 'chrono::time_point')
84 """
85 # find next namespace separator skipping template arguments
86 tpos = typename.find("<")
87 pos = typename.find("::")
88 # '<' can appear only in a class name, if we find a '::'
89 # earlier than a '<', then we got a namespace, else a class
90 if pos > 0 and (tpos < 0 or pos < tpos):
91 head = typename[:pos]
92 tail = typename[pos + 2 :]
93 else:
94 head = None
95 tail = typename
96 return (head, tail)
97
98
99class ConfigurablesDB(object):
100 """
101 Helper to expose Configurables classes (from Configurables database) as
102 a tree of subpackages, each mapped to a namespace.
103 """
104
105 def __init__(self, modulename, root=None):
106 """
107 @param modulename: name of the module
108 @param root: name of the root modules (that pointing to the root C++
109 namespace), None is ewuivalent to pass modulename as root
110 """
111 self._log = logging.getLogger(modulename)
112 self._log.debug("initializing module %r (with root %r)", modulename, root)
113
114 self.__name__ = modulename
115 self.__loader__ = None
116 self._root = root or modulename
117 assert (not root) or modulename.startswith(
118 root + "."
119 ), "modulename should be (indirect submodule of root)"
120
121 self._namespace = modulename[len(root) + 1 :].replace(".", "::") if root else ""
122 self._log.debug("%r mapping namespace %r", modulename, self._namespace or "::")
123
124 self._namespaces, self._classes = self._getEntries()
125 self._alt_names = {}
126 for cname in self._classes:
127 alt_name = _normalize_cpp_type_name(cname)
128 if alt_name != cname:
129 self._alt_names[alt_name] = cname
130 if " " in cname: # allow matching of 'T<A, B>' a well as 'T<A,B>'
131 self._alt_names[cname.replace(" ", "")] = cname
132 self.__all__ = list(
133 self._namespaces.union(self._classes).union(self._alt_names)
134 )
135
136 sys.modules[modulename] = self
137
138 for submodule in self._namespaces:
139 setattr(
140 self,
141 submodule,
142 ConfigurablesDB(".".join([self.__name__, submodule]), self._root),
143 )
144
145 def _getEntries(self):
146 """
147 Extract from the Configurables DB the namespaces and classes names in
148 the namespace this instance represents.
149 """
150 self._log.debug("getting list of entries under %r", self._namespace)
151 prefix = self._namespace + "::" if self._namespace else ""
152 prefix_len = len(prefix)
153
154 namespaces = set()
155 classes = set()
156 for name in _DB:
157 if name.startswith(prefix):
158 head, tail = split_namespace(name[prefix_len:])
159 if head:
160 namespaces.add(head)
161 else:
162 classes.add(tail)
163
164 self._log.debug(
165 "found %d namespaces and %d classes", len(namespaces), len(classes)
166 )
167 return (namespaces, classes)
168
169 def __getattr__(self, name):
170 """
171 Helper to instantiate on demand Configurable classes.
172 """
173 if name in self._classes:
174 fullname = "::".join([self._namespace, name]) if self._namespace else name
175 from ._configurables import makeConfigurableClass
176
177 self._log.debug("generating %r (%s)", name, fullname)
178 entry = makeConfigurableClass(
179 name,
180 __module__=self.__name__,
181 __qualname__=name,
182 __cpp_type__=fullname,
183 **_DB[fullname],
184 )
185 elif name.replace(" ", "") in self._alt_names:
186 entry = getattr(self, self._alt_names[name.replace(" ", "")])
187 elif name == "__spec__": # pragma no cover
188 import importlib
189
190 entry = importlib.machinery.ModuleSpec(
191 name=self.__package__,
192 loader=self.__loader__,
193 )
194 elif name == "__package__": # pragma no cover
195 entry = self.__name__
196 else:
197 raise AttributeError(
198 "module {!r} has no attribute {!r}".format(self.__name__, name)
199 )
200 setattr(self, name, entry)
201 return entry
202
203 def getByType(self, typename):
204 """
205 Return a configurable from the fully qualified type name (relative to
206 the current namespace).any
207 """
208 head, tail = split_namespace(typename)
209 if head:
210 return getattr(self, head).getByType(tail)
211 else:
212 return getattr(self, tail)
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition MsgStream.cpp:93
__contains__(self, key)
Definition _db.py:51
__getitem__(self, key)
Definition _db.py:48
getByType(self, typename)
Definition _db.py:203
__init__(self, modulename, root=None)
Definition _db.py:105
_normalize_cpp_type_name(name)
Definition _db.py:70
split_namespace(typename)
Definition _db.py:77