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