The Gaudi Framework  master (42b00024)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
_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  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
53 if "GAUDICONFIG2_DB" in os.environ:
54  exec(
55  "from {} import {} as _DB".format(*os.environ["GAUDICONFIG2_DB"].rsplit(".", 1))
56  )
57 else: # 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 
71 def 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 
93 class 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)
GaudiConfig2._db.ConfigurablesDB.getByType
def getByType(self, typename)
Definition: _db.py:197
GaudiConfig2._db._normalize_cpp_type_name
def _normalize_cpp_type_name(name)
Definition: _db.py:64
GaudiConfig2._db.ConfDB2.__contains__
def __contains__(self, key)
Definition: _db.py:45
GaudiConfig2._db.ConfDB2.__iter__
def __iter__(self)
Definition: _db.py:48
GaudiConfig2._db.ConfigurablesDB._alt_names
_alt_names
Definition: _db.py:119
GaudiConfig2._db.ConfigurablesDB.__all__
__all__
Definition: _db.py:126
GaudiConfig2._db.ConfDB2
Definition: _db.py:17
GaudiConfig2._db.ConfDB2.__getitem__
def __getitem__(self, key)
Definition: _db.py:42
GaudiConfig2._db.ConfigurablesDB.__name__
__name__
Definition: _db.py:108
GaudiConfig2._db.ConfigurablesDB._classes
_classes
Definition: _db.py:118
GaudiConfig2._db.split_namespace
def split_namespace(typename)
Definition: _db.py:71
GaudiConfig2._db.ConfigurablesDB._namespace
_namespace
Definition: _db.py:115
GaudiConfig2._db.ConfDB2.__init__
def __init__(self)
Definition: _db.py:18
GaudiConfig2._db.ConfigurablesDB.__loader__
__loader__
Definition: _db.py:109
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:105
GaudiConfig2._db.ConfigurablesDB.__getattr__
def __getattr__(self, name)
Definition: _db.py:163
GaudiConfig2._db.ConfDB2._dbs
_dbs
Definition: _db.py:21
GaudiConfig2._db.ConfigurablesDB
Definition: _db.py:93
GaudiConfig2._db.ConfigurablesDB._root
_root
Definition: _db.py:110
GaudiConfig2._db.ConfigurablesDB.__init__
def __init__(self, modulename, root=None)
Definition: _db.py:99
GaudiConfig2._db.ConfigurablesDB._getEntries
def _getEntries(self)
Definition: _db.py:139
GaudiConfig2._configurables.makeConfigurableClass
def makeConfigurableClass(name, **namespace)
Definition: _configurables.py:301