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