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