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