The Gaudi Framework  v33r0 (d5ea422b)
_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._root = root or modulename
119  assert ((not root) or modulename.startswith(root + '.')), \
120  'modulename should be (indirect submodule of root)'
121 
122  self._namespace = modulename[len(root) + 1:].replace(
123  '.', '::') if root else ''
124  self._log.debug('%r mapping namespace %r', modulename, self._namespace
125  or '::')
126 
127  self._namespaces, self._classes = self._getEntries()
128  self._alt_names = {}
129  for cname in self._classes:
130  alt_name = _normalize_cpp_type_name(cname)
131  if alt_name != cname:
132  self._alt_names[alt_name] = cname
133  self.__all__ = list(
134  self._namespaces.union(self._classes).union(self._alt_names))
135 
136  sys.modules[modulename] = self
137 
138  for submodule in self._namespaces:
139  setattr(
140  self, submodule,
141  ConfigurablesDB('.'.join([self.__name__, submodule]),
142  self._root))
143 
144  def _getEntries(self):
145  '''
146  Extract from the Configurables DB the namespaces and classes names in
147  the namespace this instance represents.
148  '''
149  self._log.debug('getting list of entries under %r', self._namespace)
150  prefix = self._namespace + '::' if self._namespace else ''
151  prefix_len = len(prefix)
152 
153  namespaces = set()
154  classes = set()
155  for name in _DB:
156  if name.startswith(prefix):
157  head, tail = split_namespace(name[prefix_len:])
158  if head:
159  namespaces.add(head)
160  else:
161  classes.add(tail)
162 
163  self._log.debug('found %d namespaces and %d classes', len(namespaces),
164  len(classes))
165  return (namespaces, classes)
166 
167  def __getattr__(self, name):
168  '''
169  Helper to instantiate on demand Configurable classes.
170  '''
171  if name in self._alt_names:
172  entry = getattr(self, self._alt_names[name])
173  else:
174  if name not in self._classes:
175  raise AttributeError(
176  'module {!r} has no attribute {!r}'.format(
177  self.__name__, name))
178  fullname = ('::'.join([self._namespace, name])
179  if self._namespace else name)
180  from ._configurables import makeConfigurableClass
181  self._log.debug('generating %r (%s)', name, fullname)
182  entry = makeConfigurableClass(
183  name,
184  __module__=self.__name__,
185  __qualname__=name,
186  __cpp_type__=fullname,
187  **_DB[fullname])
188  setattr(self, name, entry)
189  return entry
190 
191  def getByType(self, typename):
192  '''
193  Return a configurable from the fully qualified type name (relative to
194  the current namespace).any
195  '''
196  head, tail = split_namespace(typename)
197  if head:
198  return getattr(self, head).getByType(tail)
199  else:
200  return getattr(self, tail)
def __init__(self)
Definition: _db.py:19
def getByType(self, typename)
Definition: _db.py:191
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
def __getitem__(self, key)
Definition: _db.py:38
def __iter__(self)
Definition: _db.py:44
def _normalize_cpp_type_name(name)
Definition: _db.py:72
def __init__(self, modulename, root=None)
Definition: _db.py:107
def __contains__(self, key)
Definition: _db.py:41
def split_namespace(typename)
Definition: _db.py:79
def _fix_clean_up()
Definition: _db.py:58
def __getattr__(self, name)
Definition: _db.py:167
def makeConfigurableClass(name, **namespace)