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