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