ATLAS Offline Software
ConfigurationShelve.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 # @file: AthenaCommon/python/ConfigurationShelve.py
4 # @author: Wim Lavrijsen (WLavrijsen@lbl.gov)
5 # @author: Sebastien Binet <binet@cern.ch>
6 
7 import pickle
8 import shelve
9 
10 """Shelves for writing jars of configuration. Example:
11 
12 WRITING:
13 
14  shelve = ConfigurationShelve( "myjob" )
15  jar = ConfigurationJar( 'myjar' )
16  shelve.store( jar )
17 
18 READING:
19  shelve = ConfigurationShelve( "myjob" )
20  jar = shelve.retrieve( 'myjar' )
21 """
22 
23 
24 
25 __version__ = '1.1.0'
26 __author__ = """\
27 Wim Lavrijsen (WLavrijsen@lbl.gov),
28 Sebastien Binet <binet@cern.ch>"""
29 
30 __all__ = [ 'ConfigurationShelve', 'ConfigurationJar',
31  'saveToAscii', 'loadFromAscii',
32  'saveToPickle', 'loadFromPickle',
33  'cmpConfigs',
34  ]
35 
36 
38  __openShelves = {}
39 
40  def __init__( self, name ):
41  try:
42  self._shelve = self.__openShelves[ name ]
43  except KeyError:
44  self._shelve = shelve.open( name, protocol = pickle.HIGHEST_PROTOCOL )
45 
46  def __getitem__( self, key, refresh = True ):
47  if refresh:
48  # remove current configuration first, as much as possible, to prevent
49  # hysteresis; note that if the user keeps configurables around in the
50  # main (or any other) space, a merge will occur
51  from AthenaCommon.AlgSequence import AlgSequence
52 
53  topSeq = AlgSequence()
54  topSeq.removeAll()
55 
56  from AthenaCommon.AppMgr import theApp, ServiceMgr, ToolSvc, theAuditorSvc
57 
58  theApp._streams.removeAll()
59  ServiceMgr.removeAll()
60  ToolSvc.removeAll()
61  theAuditorSvc.removeAll()
62 
63  del topSeq, theApp, ServiceMgr, ToolSvc, theAuditorSvc
64 
65  return self._shelve[ key ]
66 
67  def __setitem__( self, key, value ):
68  self._shelve[ key ] = value
69 
70  def store( self, jar ):
71  return self.__setitem__( jar.getName(), jar )
72 
73  def retrieve( self, jarname, refresh = True ):
74  return self.__getitem__( jarname, refresh )
75 
76 
77 
79  def __init__( self, name ):
80  self.name = name
81 
82  from AthenaCommon.AppMgr import theApp, ServiceMgr
83  from AthenaCommon.JobProperties import jobproperties
84 
85  from . import AlgSequence as _as
86  self.athMasterSeq = _as.AthSequencer ("AthMasterSeq")
87  self.athAlgSeq = _as.AthSequencer ("AthAlgSeq")
88  self.athOutSeq = _as.AthSequencer ("AthOutSeq")
89 
90  self.AppMgr = theApp
91  self.ServiceMgr = ServiceMgr # takes care of AuditorSvc & ToolSvc
92  self.JobProperties = jobproperties
93 
94  def getName( self ):
95  return self.name
96 
97  def content( self ):
98  return self.__getstate__()
99 
100  def __getstate__( self ):
101  d = dict()
102 
103  d[ 'name' ] = self.name
104 
105  d[ 'AthMasterSeq' ] = self.athMasterSeq
106  d[ 'AthAlgSeq' ] = self.athAlgSeq
107  d[ 'AthOutSeq' ] = self.athOutSeq
108  d[ 'AppMgr' ] = self.AppMgr
109  d[ 'Streams' ] = self.AppMgr._streams
110  d[ 'ServiceMgr' ] = self.ServiceMgr #takes care of {Auditor,Tool}Svc
111  d[ 'JobProperties' ] = self.JobProperties
112 
113  return d
114 
115  def __setstate__( self, d ):
116  # get handles to the global objects for convenience
117  self.__init__( d[ 'name' ] )
118 
119  # the mere existence of 'd' has done its work through the "magic"
120  # of instances shared on name for all configurables
121 
122  # now handle jobproperties
123  import AthenaCommon.JobProperties as JobProperties
124  JobProperties.jobproperties = d['JobProperties']
125 
126  def __str__(self):
127  import os
128  return os.linesep.join( [ "%s" % v for v in self.content().values() ] )
129 
130 
131 
132 
133 #----- storing to db
134 def storeJobOptionsCatalogue( cfg_fname ):
135  """Inspect the 'configured' JobOptionsSvc and existing services,
136  then dump the properties of each component into a pickle file.
137  """
138 
139  from collections import defaultdict
140  jocat = defaultdict( dict )
141  jocfg = defaultdict( dict )
142 
143  def _fillCfg( client, props ):
144  for p in props:
145  n, v = p
146  if hasattr( v, 'toStringProperty' ):
147  v = str( v.toStringProperty() )
148  elif hasattr( v, 'toString' ):
149  v = str( v.toString() )
150  elif type (v) is float:
151  # str(1.8e12) will give '1.8e+12' in py2
152  # and `1800000000000.0' in py3.
153  # Convert floats like this for consistency.
154  v = '%g'%v
155  else:
156  v = str( v )
157  jocfg[ client ][ n ] = v
158 
159  from AthenaCommon.AppMgr import theApp
160  from AthenaCommon.AppMgr import ServiceMgr as svcMgr
161  from AthenaCommon.Configurable import Configurable as C
162 
163  # tickle the Gaudi C++ side and configure the ApplicationMgr
164  theApp.setup( recursive = True )
165 
166  app_props = [ (k,v.value())
167  for k,v in theApp.getHandle().properties().items() ]
168  _fillCfg( 'ApplicationMgr', app_props )
169 
170  # get all services that now already exist, as they require special treatment;
171  # the appmgr handle talks directly to the ServiceManager and services() returns
172  # a copy of the services names
173  svcs = theApp.getHandle().services()
174 
175  # now assume that if these services carry configuration, then they should exist
176  # on the service manager configurable
177  for svcname in svcs:
178  svc = getattr( svcMgr, svcname, None )
179  if svc:
180  _fillCfg( svcname, svc.getValuedProperties().items() )
181 
182  # make sure to propagate the EventLoop properties through the josvc
183  try:
184  evLoopName = theApp.EventLoop.split('/')[-1]
185  evLoop = getattr( svcMgr, evLoopName )
186 
187  props = []
188  for k,v in evLoop.properties().items():
189  if v != C.propertyNoValue:
190  props.append( (k,v) )
191  _fillCfg( evLoopName, props )
192 
193  except AttributeError:
194  pass # no properties defined for EventLoop type
195 
196  # get the values for all other components (these may contain duplicates of
197  # the ones above in svcs, and there may even be conflicts)
198  import AthenaPython.PyAthena as PyAthena
199  josvc = PyAthena.py_svc( 'JobOptionsSvc',iface="Gaudi::Interfaces::IOptionsSvc")
200  allProps=josvc.items() #a std::tuple<string,string>
201  for prop in allProps:
202  cn=str(prop._0).split(".")
203  v=str(prop._1)
204  client=".".join(cn[:-1])
205  n=cn[-1]
206  jocat[ client ][ n ] = v
207 
208  # take care of some ancient history
209  for k in ( 'Go', 'Exit' ):
210  if k in jocfg[ 'ApplicationMgr' ]:
211  del jocfg[ 'ApplicationMgr' ][k]
212 
213  # workaround for pycomps
214  pycomps = []
215  for c in C.allConfigurables.values():
216  if not isinstance( c, (PyAthena.Alg,
217  PyAthena.AlgTool,
218  PyAthena.Svc,
219  PyAthena.Aud) ):
220  continue
221  # FIXME: should check if the component is still 'active'
222  # ie: is it in one of the TopAlg,SvcMgr,ToolSvc and children ?
223  try:
224  delattr( c, 'msg' )
225  except AttributeError:
226  pass
227 
228  pycomps.append( c )
229 
230  # write out all parts; start with the jocat, so that it can be loaded by
231  # itself only, if need be
232 
233  cfg = open( cfg_fname, 'wb' ) # output pickle file
234 
235  pickle.dump( jocat, cfg ) # jobopt catalogue contents
236  pickle.dump( jocfg, cfg ) # special services' properties
237  pickle.dump( pycomps, cfg ) # python components workaround
238 
239  cfg.close()
240 
241  return cfg_fname
242 
243 
244 #----- loading from DB
245 def loadJobOptionsCatalogue( cfg_fname ):
246  """Load properties from a pickle file, previously dumped by
247  storeConfiguration, back into the JobOptionsSvc.
248  """
249  # read jobopt catalogue dump and pycomps back in
250  cfg = open( cfg_fname, 'rb' )
251 
252  jocat = pickle.load( cfg )
253  jocfg = pickle.load( cfg )
254  pycomps = pickle.load( cfg )
255 
256  cfg.close()
257 
258  kw = jocfg[ 'ApplicationMgr' ]
259  from AthenaCommon.AppMgr import theApp
260  theApp.JobOptionsSvcType = kw[ 'JobOptionsSvcType' ]
261  theApp.MessageSvcType = kw[ 'MessageSvcType' ]
262  theApp.getHandle( kw )
263  del jocfg[ 'ApplicationMgr' ]
264 
265  # no longer want to call setup(), since there are no Configurables to
266  # setup; it would be a no-op, already, but __build_master_sequence is
267  # broken, so make sure this can't be run ...
268  def noop( self ):
269  pass
270  theApp.__class__.setup = noop
271 
272  import AthenaPython.PyAthena as PyAthena
273  josvc = PyAthena.py_svc( 'JobOptionsSvc', createIf = False, iface = 'Gaudi::Interfaces::IOptionsSvc')
274 
275  # restore job catalogue entries
276  import GaudiPython.Bindings as gaudi
277  for client in jocat:
278  if client == "ApplicationMgr":
279  # ApplicationMgr properties are already set
280  continue
281  for n,v in jocat[ client ].items():
282  josvc.set( client+'.'+n, v )
283 
284  # restore special services properties
285  for client in jocfg:
286  svc = PyAthena.py_svc( client, createIf = False, iface='IProperty' )
287  for n,v in jocfg[ client ].items():
288  # See comment above.
289  p = gaudi.StringProperty()
290  p.setName(n)
291  p.fromString(v).ignore()
292  svc.setProperty( p )
293 
294  # pycomps hack-around
295  if pycomps:
296  import AthenaPython.Configurables as _C
297  _C.PyComponents.instances = dict( (p.name, p) for p in pycomps )
298  for p in pycomps:
299  if hasattr( p, 'setup' ):
300  if callable( p.setup ):
301  p.setup()
302 
303 
304 
305 def saveToAscii(out, cfgName=None):
306  """
307  Helper function to store the current configuration to (ASCII) file
308  """
309  import os
310 
311  # temporarily set the Configurable log level to make sure that the printout
312  # does not get suppressed
313  from AthenaCommon import Configurable
314  from AthenaCommon import Constants
315  llevel = Configurable.log.level
316  Configurable.log.setLevel( Constants.INFO )
317 
318  if cfgName is None: cfgName = "Snapshot"
319  content = ConfigurationJar( cfgName ).content()
320 
321  if type(out) is str:
322  out = open( out, "w" )
323  for v in content.values(): out.write( str(v) + os.linesep )
324 
325  Configurable.log.setLevel( llevel )
326 
327  return
328 
329 def loadFromAscii(fileName, cfgName=None):
330  """
331  Helper function to retrieve a configuration from an ASCII file.
332  """
333  raise NotImplementedError("Sorry no restoration from ASCII file (yet?)")
334 
335 def saveToPickle(fileName, cfgName=None):
336  """
337  Helper function to store the current configuration to a pickle file
338  """
339  if cfgName is None: cfgName = "Snapshot"
340  shelve = ConfigurationShelve( fileName )
341  jar = ConfigurationJar( cfgName )
342  shelve.store( jar )
343 
344  return
345 
346 def loadFromPickle(fileName, cfgName=None):
347  """
348  Helper function to retrieve a configuration from a pickle file
349  """
350  if cfgName is None: cfgName = "Snapshot"
351  import os
352  fileName = os.path.expanduser(os.path.expandvars(fileName))
353  shelve = ConfigurationShelve( fileName )
354  # configuration is loaded by side-effects...
355  jar = shelve.retrieve( cfgName )
356  return jar
357 
358 def cmpConfigs (ref, chk, refName=None, chkName=None):
359  """
360  Helper function to compare 2 configurations.
361  @param ref the path to a pickle file where a configuration was stored
362  @param chk the path to a pickle file where a configuration was stored
363  @param refName the name of the configuration jar to load
364  @param chkName the name of the configuration jar to load
365  if `ref` and/or `chk` are joboptions (ie: their extension is '.py') a shelve
366  is automatically created on the fly
367  """
368  import os
369  _path = os.path
370  ref = _path.expanduser(_path.expandvars(ref))
371  chk = _path.expanduser(_path.expandvars(chk))
372 
373  from PyUtils.Decorators import forking
374  # if we are given joboptions, create ConfigurationShelves on the fly
375  @forking
376  def _create_shelve (joboname):
377  from tempfile import NamedTemporaryFile
378  fname = os.path.basename (joboname)
379  fname = os.path.splitext(fname)[0] + '.pkl'
380  fname = NamedTemporaryFile(prefix=fname, suffix='.pkl').name
381  job = [ "from AthenaCommon.Include import include",
382  "include ('%s')" % joboname,
383  "from AthenaCommon.ConfigurationShelve import saveToPickle",
384  "saveToPickle ('%s')" % fname,
385  # exit before actually running the job
386  "raise SystemExit(0)",
387  ]
388  jobofile = NamedTemporaryFile(suffix='.py')
389  map (jobofile.writelines, [l+os.linesep for l in job])
390  jobofile.flush()
391 
392  from subprocess import getstatusoutput
393  sc,out = getstatusoutput ('athena.py %s' % jobofile.name)
394 
395  jobofile.close()
396  if sc==0:
397  return fname
398  return (sc, out)
399 
400  if os.path.splitext(ref)[1]=='.py':
401  print ("::: creating a shelve on the fly for [%s]..."%ref)
402  ref = _create_shelve (ref)
403  if not isinstance(ref, str):
404  raise RuntimeError (
405  'could not create a shelve on the fly:\n%s'%ref[1])
406  import atexit
407  atexit.register (os.unlink, ref)
408 
409  if os.path.splitext(chk)[1]=='.py':
410  print ("::: creating a shelve on the fly for [%s]..."%chk)
411  chk = _create_shelve (chk)
412  if not isinstance(chk, str):
413  raise RuntimeError (
414  'could not create a shelve on the fly:\n%s'%chk[1])
415  import atexit
416  atexit.register (os.unlink, chk)
417 
418  for fname in (ref, chk):
419  if not _path.exists (fname):
420  raise RuntimeError ('file [%s] does not exist'%fname)
421 
422  @forking
423  def _dict_cfg (fname, cfgName):
424  #from PyUtils.odict import OrderedDict as odict
425  jar = loadFromPickle (fname, cfgName)
426  d = list()
427  all_cfgs = dict()
428  all_cfgs.setdefault (dict)
429  def _visit_cfg (cfg):
430  name = cfg.getJobOptName()
431  values = [name]
432  all_cfgs[name] = dict()
433  props = cfg.getProperties()
434  def _get_value (cfg, k, v):
435  _no_value = cfg.propertyNoValue
436  if v == _no_value:
437  v = cfg.getDefaultProperty(k)
438  return v
439 
440  from AthenaCommon.Configurable import Configurable
441  for k,v in props.items():
442  if not isinstance(v, Configurable):
443  all_cfgs[name][k] = _get_value(cfg,k,v)
444  else:
445  all_cfgs[name][k] = v.getJobOptName()
446  if v not in cfg.getAllChildren():
447  values.extend (_visit_cfg(v))
448 
449  if hasattr(cfg, 'getAllChildren'):
450  for c in cfg.getAllChildren():
451  values.extend (_visit_cfg(c))
452  return values
453 
454  for c in jar.athAlgSeq.getChildren():
455  d.extend (_visit_cfg(c))
456 
457  for c in jar.ServiceMgr.getChildren():
458  d.extend (_visit_cfg(c))
459  return d, all_cfgs
460 
461  ref_list, ref_cfgs = _dict_cfg (ref, refName)
462  chk_list, chk_cfgs = _dict_cfg (chk, chkName)
463 
464  report = []
465  addNames,subNames = [],[]
466  if ref_list != chk_list:
467  report.append ("::: configurable list in 'ref' and in 'chk' differ !")
468  addNames = sorted([ n for n in chk_cfgs if not (n in ref_cfgs) ])
469  if len (addNames) > 0:
470  report.append ("::: configurables in 'chk' and not in 'ref':")
471  report.extend ([" + %s" % n for n in addNames])
472  subNames = sorted([ n for n in ref_cfgs if not (n in chk_cfgs) ])
473  if len (subNames) > 0:
474  report.append ("::: configurables in 'ref' and not in 'chk':")
475  report.extend ([" - %s" % n for n in subNames])
476 
477  common_cfgs = set (
478  [n for n in ref_list if not (n in addNames or n in subNames)] +
479  [n for n in chk_list if not (n in addNames or n in subNames)]
480  )
481  for n in common_cfgs:
482  ref_cfg = ref_cfgs[n]
483  chk_cfg = chk_cfgs[n]
484  diff_props_report = []
485  for k in ref_cfg.iterkeys():
486  ref_val = str(ref_cfg.get(k, '**N/A**'))
487  chk_val = str(chk_cfg.get(k, '**N/A**'))
488 
489  if ref_val != chk_val:
490  diff_props_report.append (" ref: %s = %r" % (k, ref_val))
491  diff_props_report.append (" chk: %s = %r" % (k, chk_val))
492  if len(diff_props_report)>0:
493  report.append ("::: properties differing for [%s]:"%n)
494  report.extend (diff_props_report)
495  return (ref_list, ref_cfgs), (chk_list, chk_cfgs), report
python.ConfigurationShelve.ConfigurationJar.name
name
Definition: ConfigurationShelve.py:80
python.ConfigurationShelve.ConfigurationJar.__init__
def __init__(self, name)
Definition: ConfigurationShelve.py:79
python.ConfigurationShelve.loadFromAscii
def loadFromAscii(fileName, cfgName=None)
Definition: ConfigurationShelve.py:329
python.ConfigurationShelve.ConfigurationJar.AppMgr
AppMgr
Definition: ConfigurationShelve.py:90
python.ConfigurationShelve.ConfigurationShelve.store
def store(self, jar)
Definition: ConfigurationShelve.py:70
python.AlgSequence.AlgSequence
AlgSequence
Definition: PhysicsAnalysis/D3PDTools/AnaAlgorithm/python/AlgSequence.py:7
python.ConfigurationShelve.ConfigurationJar.getName
def getName(self)
Definition: ConfigurationShelve.py:94
python.ConfigurationShelve.ConfigurationJar.athAlgSeq
athAlgSeq
Definition: ConfigurationShelve.py:87
python.ConfigurationShelve.ConfigurationJar.athMasterSeq
athMasterSeq
Definition: ConfigurationShelve.py:86
python.ConfigurationShelve.ConfigurationJar.__setstate__
def __setstate__(self, d)
Definition: ConfigurationShelve.py:115
python.ConfigurationShelve.ConfigurationShelve
representation of job databases ==========================================
Definition: ConfigurationShelve.py:37
python.ConfigurationShelve.saveToAscii
def saveToAscii(out, cfgName=None)
further saving utilities =================================================
Definition: ConfigurationShelve.py:305
python.PyAthenaComps.services
services
Definition: PyAthenaComps.py:39
python.ConfigurationShelve.ConfigurationShelve._shelve
_shelve
Definition: ConfigurationShelve.py:42
python.ConfigurationShelve.ConfigurationShelve.__init__
def __init__(self, name)
Definition: ConfigurationShelve.py:40
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:797
python.ConfigurationShelve.ConfigurationJar
Definition: ConfigurationShelve.py:78
python.ConfigurationShelve.ConfigurationShelve.__getitem__
def __getitem__(self, key, refresh=True)
Definition: ConfigurationShelve.py:46
python.ConfigurationShelve.ConfigurationJar.ServiceMgr
ServiceMgr
Definition: ConfigurationShelve.py:91
grepfile.content
string content
Definition: grepfile.py:56
DiTauMassTools::ignore
void ignore(T &&)
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:54
python.ConfigurationShelve.ConfigurationShelve.__setitem__
def __setitem__(self, key, value)
Definition: ConfigurationShelve.py:67
PyAthena::Svc
Definition: PyAthenaSvc.h:33
python.ConfigurationShelve.ConfigurationShelve.__openShelves
dictionary __openShelves
Definition: ConfigurationShelve.py:38
PyAthena::Aud
Definition: PyAthenaAud.h:32
python.ConfigurationShelve.ConfigurationJar.__getstate__
def __getstate__(self)
Definition: ConfigurationShelve.py:100
python.ConfigurationShelve.ConfigurationShelve.retrieve
def retrieve(self, jarname, refresh=True)
Definition: ConfigurationShelve.py:73
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
Configurable
athena/gaudi ----------------------------------------------------------—
python.ConfigurationShelve.ConfigurationJar.content
def content(self)
Definition: ConfigurationShelve.py:97
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename T::value_type > sorted(T begin, T end)
Helper function to create a sorted vector from an unsorted one.
python.JsonUtils.properties
properties
Definition: JsonUtils.py:96
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
python.ConfigurationShelve.ConfigurationJar.__str__
def __str__(self)
Definition: ConfigurationShelve.py:126
Trk::open
@ open
Definition: BinningType.h:40
python.ConfigurationShelve.storeJobOptionsCatalogue
def storeJobOptionsCatalogue(cfg_fname)
Definition: ConfigurationShelve.py:134
python.ConfigurationShelve.loadFromPickle
def loadFromPickle(fileName, cfgName=None)
Definition: ConfigurationShelve.py:346
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
python.ConfigurationShelve.cmpConfigs
def cmpConfigs(ref, chk, refName=None, chkName=None)
Definition: ConfigurationShelve.py:358
pickleTool.object
object
Definition: pickleTool.py:30
str
Definition: BTagTrackIpAccessor.cxx:11
PyAthena::Alg
Definition: PyAthenaAlg.h:33
python.ConfigurationShelve.ConfigurationJar.JobProperties
JobProperties
Definition: ConfigurationShelve.py:92
python.ConfigurationShelve.ConfigurationJar.athOutSeq
athOutSeq
Definition: ConfigurationShelve.py:88
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
python.ConfigurationShelve.loadJobOptionsCatalogue
def loadJobOptionsCatalogue(cfg_fname)
Definition: ConfigurationShelve.py:245
python.ConfigurationShelve.saveToPickle
def saveToPickle(fileName, cfgName=None)
Definition: ConfigurationShelve.py:335