The Gaudi Framework  v36r1 (3e2fb5a8)
genconfuser.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
13 """
14 Generate _confDb.py files for ConfigurableUser classes.
15 """
16 from __future__ import division
17 import os
18 import sys
19 import time
20 import logging
22 
23 from pprint import pformat
24 from glob import glob
25 from GaudiKernel.ConfigurableDb import cfgDb
26 
27 logging.VERBOSE = (logging.INFO + logging.DEBUG) // 2
28 logging.addLevelName(logging.VERBOSE, "VERBOSE")
29 logging.verbose = lambda msg, *args, **kwargs: \
30  logging.log(logging.VERBOSE, msg, *args, **kwargs)
31 
32 
33 def _inheritsfrom(derived, basenames):
34  """
35  Check if any of the class names in 'basenames' is anywhere in the base
36  classes of the class 'derived'.
37  If 'derived' _is_ one in 'basenames', returns False.
38 
39  'basenames' can be a string or an iterable (of strings).
40  """
41  if isinstance(basenames, str):
42  basenames = (basenames, )
43  for b in derived.__bases__:
44  if b.__name__ in basenames:
45  return True
46  else:
47  if _inheritsfrom(b, basenames):
48  return True
49  return False
50 
51 
52 def loadConfigurableDb(build_dir=None):
53  '''
54  Equivalent to GaudiKernel.ConfigurableDb.loadConfigurableDb(), but does a
55  deep search and executes the '*.confdb' files instead of importing them.
56  '''
57  log = GaudiKernel.ConfigurableDb.log
58  from os.path import join as path_join
59  # look for the confdb files in all the reasonable places
60  # - CMake builds
61  confDbFiles = []
62  for path in sys.path:
63  confDbFiles += [
64  f for f in glob(path_join(path, '*', '*.confdb'))
65  if os.path.isfile(f)
66  ]
67  # - new-style CMake builds (look for all .confdb in the build tree)
68  if build_dir:
69  for root, _, files in os.walk(build_dir):
70  confDbFiles += [
71  os.path.join(root, f) for f in files if f.endswith('.confdb')
72  ]
73  # - used projects and local merged file
74  pathlist = os.getenv("LD_LIBRARY_PATH", "").split(os.pathsep)
75  for path in filter(os.path.isdir, pathlist):
76  confDbFiles += [
77  f for f in [
78  path_join(path, f) for f in os.listdir(path)
79  if f.endswith('.confdb')
80  ]
81  ]
82  # - load the confdb files
83  for confDb in set(confDbFiles):
84  log.debug("\t-loading [%s]..." % confDb)
85  try:
86  cfgDb._loadModule(confDb)
87  except Exception as err:
88  # It may happen that the file is found but not completely
89  # written, usually during parallel builds, but we do not care.
90  log.warning("Could not load file [%s] !", confDb)
91  log.warning("Reason: %s", err)
92  # top up with the regular merged confDb (for the used projects)
94 
95 
96 def getConfigurableUsers(modulename, root, mayNotExist=False):
97  """
98  Find in the module 'modulename' all the classes that derive from ConfigurableUser.
99  Return the list of the names.
100  The flag mayNotExist is used to choose the level of the logging message in case
101  the requested module does not exist.
102  """
103  # remember the old system path
104  oldpath = list(sys.path)
105  # we need to hack the sys.path to add the first part of the module name after root
106  moduleelements = modulename.split('.')
107  if len(moduleelements) > 1:
108  moddir = os.sep.join([root] + moduleelements[:-1])
109  else:
110  moddir = root
111  # this is the name of the submodule to import
112  shortmodname = moduleelements[-1]
113  # check if the module file actually exists
114  if not os.path.exists(os.path.join(moddir, shortmodname) + ".py"):
115  msg = "Module %s does not exist" % modulename
116  if mayNotExist:
117  logging.verbose(msg)
118  else:
119  logging.error(msg)
120  # no file -> do not try to import
121  return []
122  # prepend moddir to the path
123  sys.path.insert(0, moddir)
124  logging.verbose("sys.path prepended with %r", sys.path[0])
125 
126  logging.info("Looking for ConfigurableUser in %r", modulename)
127  g, l = {}, {}
128  try:
129  logging.verbose("importing %s", shortmodname)
130  exec("import %s as mod" % shortmodname, g, l)
131  finally:
132  # restore old sys.path
133  logging.verbose("restoring old sys.path")
134  sys.path = oldpath
135  mod = l["mod"]
136  if "__all__" in dir(mod) and mod.__all__:
137  all = mod.__all__
138  else:
139  all = [n for n in dir(mod) if not n.startswith("_")]
140  result = []
141  for name in all:
142  cfg = cfgDb.get(name)
143  if cfg and cfg["module"] != modulename:
144  # This name comes from another module
145  logging.verbose("Object %r already found in module %r", name,
146  cfg["module"])
147  continue
148  t = getattr(mod, name)
149  if isinstance(t, type) and _inheritsfrom(
150  t, ('ConfigurableUser', 'SuperAlgorithm')):
151  result.append(name)
152  logging.verbose("Found %r", result)
153  return result
154 
155 
156 def main():
157  from optparse import OptionParser
158  parser = OptionParser(
159  prog=os.path.basename(sys.argv[0]),
160  usage="%prog [options] <PackageName> [<Module1> ...]")
161  parser.add_option(
162  "-o",
163  "--output",
164  action="store",
165  type="string",
166  help=
167  "output file for confDb data [default = '../genConfDir/<PackageName>_user_confDb.py']."
168  )
169  parser.add_option(
170  "-r",
171  "--root",
172  action="store",
173  type="string",
174  help="root directory of the python modules [default = '../python'].")
175  parser.add_option(
176  "-v",
177  "--verbose",
178  action="store_true",
179  help="print some debugging information")
180  parser.add_option(
181  "--debug",
182  action="store_true",
183  help="print more debugging information")
184  parser.add_option(
185  "--build-dir",
186  action="store",
187  help=
188  "build directory where to look for .confdb files (search all subdirectories)"
189  )
190  parser.set_defaults(root=os.path.join("..", "python"))
191 
192  opts, args = parser.parse_args()
193 
194  if opts.debug:
195  log_level = logging.DEBUG
196  elif opts.verbose:
197  log_level = logging.VERBOSE
198  else:
199  log_level = logging.INFO if os.environ.get(
200  'VERBOSE') else logging.WARNING
201  logging.basicConfig(
202  format="%(levelname)s: %(message)s",
203  stream=sys.stdout,
204  level=log_level)
205 
206  if len(args) < 1:
207  parser.error("PackageName is required")
208 
209  package_name = args.pop(0)
210 
211  usingConvention = False
212  if not args:
213  # use the conventional module name <package>.Configuration
214  args = [package_name + ".Configuration"]
215  usingConvention = True
216 
217  genConfDir = os.path.join("..", os.environ.get("CMTCONFIG", ""),
218  "genConfDir")
219  if not os.path.exists(genConfDir):
220  genConfDir = os.path.join("..", "genConfDir")
221 
222  if not opts.output:
223  outputfile = os.path.join(genConfDir, package_name + '_user.confdb')
224  else:
225  outputfile = opts.output
226 
227  # We can disable the error on missing configurables only if we can import Gaudi.Configurables
228  # It must be done at this point because it may conflict with logging.basicConfig
229  try:
230  import Gaudi.Configurables
231  Gaudi.Configurables.ignoreMissingConfigurables = True
232  except:
233  pass
234  # load configurables database to avoid fake duplicates
235  loadConfigurableDb(opts.build_dir)
236  # ensure that local configurables are in the database
237  try:
238  # Add the local python directories to the python path to be able to import the local
239  # configurables
240  sys.path.insert(0, genConfDir)
241  sys.path.insert(0, os.path.join("..", "python"))
242  localConfDb = os.path.join(genConfDir, package_name,
243  package_name + '.confdb')
244  if os.path.exists(localConfDb):
245  cfgDb._loadModule(localConfDb)
246  # Extend the search path of the package module to find the configurables
247  package_module = __import__(package_name)
248  package_module.__path__.insert(
249  0, os.path.join(genConfDir, package_name))
250  except:
251  pass # ignore failures (not important)
252 
253  # Collecting ConfigurableUser specializations
254  cus = {}
255  for mod in args:
256  lst = None
257  try:
258  lst = getConfigurableUsers(
259  mod, root=opts.root, mayNotExist=usingConvention)
260  except ImportError:
261  import traceback
262  logging.error(
263  "Cannot import module %r:\n%s", mod,
264  traceback.format_exc().rstrip()) # I remove the trailing '\n'
265  return 2
266  if lst:
267  cus[mod] = lst
268  # Add the configurables to the database as fake entries to avoid duplicates
269  for m in lst:
270  cfgDb.add(
271  configurable=m, package='None', module='None', lib='None')
272  elif not usingConvention:
273  logging.warning(
274  "Specified module %r does not contain ConfigurableUser specializations",
275  mod)
276 
277  if cus:
278  logging.info("ConfigurableUser found:\n%s", pformat(cus))
279  # header
280  output = """## -*- ascii -*-
281 # db file automatically generated by %s on: %s
282 """ % (parser.prog, time.asctime())
283 
284  for mod in cus:
285  for cu in cus[mod]:
286  output += "%s %s %s\n" % (mod, 'None', cu)
287 
288  # trailer
289  output += "## %s\n" % package_name
290  elif usingConvention:
291  logging.info("No ConfigurableUser found")
292  output = ("# db file automatically generated by %s on: %s\n"
293  "# No ConfigurableUser specialization in %s\n") % (
294  parser.prog, time.asctime(), package_name)
295  else:
296  logging.error("No ConfigurableUser specialization found")
297  return 1
298 
299  # create the destination directory if not there
300  output_dir = os.path.dirname(outputfile)
301  try:
302  logging.info("Creating directory %r", output_dir)
303  os.makedirs(output_dir, 0o755)
304  except OSError as err:
305  import errno
306  if err.errno == errno.EEXIST:
307  # somebody already - perhaps concurrently - created that dir.
308  pass
309  else:
310  raise
311 
312  # write output to file
313  logging.verbose("Writing confDb data to %r", outputfile)
314  open(outputfile, "w").write(output)
315  return 0
316 
317 
318 if __name__ == '__main__':
319  retcode = main()
320  sys.exit(retcode)
genconfuser.loadConfigurableDb
def loadConfigurableDb(build_dir=None)
Definition: genconfuser.py:52
GaudiKernel.ConfigurableDb.loadConfigurableDb
def loadConfigurableDb()
Definition: ConfigurableDb.py:119
genconfuser._inheritsfrom
def _inheritsfrom(derived, basenames)
Definition: genconfuser.py:33
genconfuser.main
def main()
Definition: genconfuser.py:156
genconfuser.getConfigurableUsers
def getConfigurableUsers(modulename, root, mayNotExist=False)
Definition: genconfuser.py:96
GaudiKernel.ConfigurableDb
Definition: ConfigurableDb.py:1