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