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