All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sbndcode/sbndcode/gallery/python/LArSoftUtils.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 from __future__ import print_function
4 
5 __doc__ = """
6 Collection of utilities to interface LArSoft with python and gallery.
7 
8 This module requires ROOT.
9 """
10 
11 __all__ = [
12  'readHeader',
13  'SourceCode',
14  'make_getValidHandle',
15  'makeFileList',
16  'forEach',
17  'eventLoop',
18  'findFHiCL',
19  'loadConfiguration',
20  'ConfigurationClass',
21  'startMessageFacility',
22  'ServiceRegistryClass',
23  'ConfigurationHelper',
24  'loadGeometry',
25  'justLoadGeometry',
26  'loadSimpleService',
27  ]
28 
29 import sys, os
30 import ROOTutils
31 from ROOTutils import ROOT
32 import galleryUtils
33 from galleryUtils import (
34  make_getValidHandle, makeFileList, forEach, eventLoop,
35  findFHiCL, loadConfiguration, ConfigurationHelper, ConfigurationClass,
36  startMessageFacility, ServiceRegistryClass,
37  )
38 
39 
40 ################################################################################
41 ### Pass-through
42 ###
43 SourceCode = galleryUtils.SourceCode
44 readHeader = galleryUtils.readHeader
45 
46 
47 ################################################################################
48 ### LArSoft
49 ################################################################################
50 def loadGeometry(config=None, registry=None, mapping=None):
51  """The argument `config` is an instance of `ConfigurationClass`.
52 
53  If a config object is provided, configurations will be read from there.
54  Otherwise, they will be read from the registry.
55  If a registry is provided, the services will be registered in there.
56  """
57  assert(config or registry)
58  serviceName = 'Geometry'
59 
60  # deal with messagefacility first
61  if not (registry and registry.has("message")):
62  messageConfig = config.service("message") if config else registry.config("message")
63  startMessageFacility(messageConfig) # use default name
64  if registry: registry.register("message", None) # there is no direct access, sorry
65  # if need to load message facility
66 
67  geometryConfig = config.service(serviceName) if config else registry.config(serviceName)
68  if geometryConfig is None:
69  raise RuntimeError("Failed to retrieve the configuration for %s service" % serviceName)
70 
71  if not mapping:
72  SourceCode.loadHeaderFromUPS('larcorealg/Geometry/ChannelMapStandardAlg.h')
73  mapping = ROOT.geo.ChannelMapStandardAlg
74  SourceCode.loadHeaderFromUPS("larcorealg/Geometry/StandaloneGeometrySetup.h")
75  SourceCode.loadLibrary("larcorealg_Geometry")
76  service = ROOT.lar.standalone.SetupGeometry[mapping](geometryConfig)
77  if registry: registry.register(serviceName, service)
78 
79  # make it easy to print points and vectors in python
80  for varName in ( 'Point_t', 'Vector_t', ):
81  try: klass = getattr(ROOT.geo, varName)
82  except AttributeError: continue
83  klass.__str__ = ROOTutils.TVector3ToString
84  # ... and IDs...
85  for varName in ( 'CryostatID', 'TPCID', 'PlaneID', 'WireID', ):
86  try: klass = getattr(ROOT.geo, varName)
87  except AttributeError: continue
88  klass.__str__ = klass.toString
89  # for ID
90  for varName in ( 'CryostatID', 'TPCsetID', 'ROPID', ):
91  try: klass = getattr(ROOT.readout, varName)
92  except AttributeError: continue
93  klass.__str__ = klass.toString
94  # for ID
95  # ... and geometry objects
96  for varName in ( 'CryostatGeo', 'TPCGeo', 'PlaneGeo', 'WireGeo', 'OpDetGeo', 'AuxDetGeo', ):
97  try: klass = getattr(ROOT.geo, varName)
98  except AttributeError: continue
99  klass.__str__ = getattr(klass, varName[:-3] + "Info")
100  # for geo object
101 
102  return service
103 # loadGeometry()
104 
105 
106 ################################################################################
107 def justLoadGeometry(configFile, mapping=None):
108  """Loads and returns the geometry from the specified configuration file.
109 
110  This is a one-stop procedure recommended only when running interactively.
111  """
112  return loadGeometry(config=ConfigurationClass(configFile), mapping=mapping)
113 # justLoadGeometry()
114 
115 
116 ################################################################################
117 def loadSimpleService \
118  (serviceClass, config=None, registry=None, interfaceClass=None, args = []):
119  """Loads a service assuming some simple requirements:
120  * no dependency from other services
121  * constructor accepts a FHiCL parameter set
122 
123  If a config object is provided, configurations will be read from there.
124  Otherwise, they will be read from the registry.
125  If a registry is provided, the services will be registered in there.
126 
127  The service configuration is read from an item called as `interfaceClass`,
128  or `serviceClass` itself if `interfaceClass` is None, with "Service" appended.
129 
130  As a first attempt, the test helpers are attempted to load the service, and
131  the arguments are used to construct a `providers_type` object.
132  If there is no test helper for `serviceClass`, then direct construction is
133  attempted using all the specified arguments.
134  """
135 
136  serviceName = (interfaceClass if interfaceClass else serviceClass).__name__
137  configKey = serviceName + "Service"
138 
139  if not isinstance(config, ROOT.fhicl.ParameterSet):
140  config = config.service(configKey) if config else registry.config(configKey)
141  if config is None:
142  raise RuntimeError("Failed to retrieve the configuration for %s service" % serviceName)
143 
144  TestSetupClass = ROOT.testing.ProviderSetupClass(serviceClass)
145  if TestSetupClass:
146  serviceSetup = TestSetupClass.setup
147  try: providers = [ serviceClass.providers_type(*args), ]
148  except:
149  # we received arguments, called expected them to be used, we did not:
150  if args: raise # there must have been some problem
151  providers = []
152  service = serviceSetup(config, *providers)
153  else:
154  service = serviceClass(config, *args)
155 
156  if registry: registry.register(serviceName, service)
157  return service
158 # loadSimpleService()
159 
160 
161 ################################################################################
162 ### special known services
163 ###
164 ###
165 ###
166 
167 def makeStringList(l): return [ l, ] if isinstance(l, str) else l
168 
169 
170 class SimpleServiceLoader:
171  """Class storing the parameters needed to load a "simple" service.
172 
173  So far a "simple" service is one that can be loaded with `loadSimpleService()`
174  allowing service provider dependencies and simple configuration adaptions.
175  """
176  def __init__(self,
177  serviceClass,
178  interfaceClass = None,
179  headers = [],
180  libraries = [],
181  dependencies = [],
182  purgeConfig = [],
183  addConfig = {}
184  ):
185  assert serviceClass is not None
186  self.serviceClass = serviceClass
187  self.interfaceClass = interfaceClass
188  self.headers = makeStringList(headers)
189  self.libraries = makeStringList(libraries)
190  self.serviceDependencies = makeStringList(dependencies)
191  self.purgeConfig = makeStringList(purgeConfig)
192  self.addConfig = addConfig
193  # __init__()
194 
195  def _needsSpecialConfig(self): return self.addConfig or self.purgeConfig
196 
197  def _makeConfig(self, registry):
198  for configKey in ( self.serviceKey(), self.serviceKey() + "Service", ):
199  try:
200  config = registry.config(configKey)
201  break
202  except Exception: pass
203  else: config = None
204  if not config:
205  raise RuntimeError \
206  ("No configuration for service '{}'".format(self.serviceKey()))
207  # if
208  for key in self.purgeConfig: config.erase(key)
209  for key, value in self.addConfig.items(): config.put(key, str(value))
210  return config
211  # _makeConfig()
212 
213  def serviceKey(self):
214  source = (self.interfaceClass if self.interfaceClass else self.serviceClass)
215  if not isinstance(source, str): source = source.__name__
216  return source.replace('::', '.').split('.')[-1]
217  # serviceKey()
218 
219  def load(self, manager):
220  # load the required service dependencies
221  dependencies = [
222  manager(dependency) for dependency in self.serviceDependencies
223  ]
224 
225  # load the required headers and libraries
226  for header in self.headers:
227  galleryUtils.SourceCode.loadHeaderFromUPS(header)
228  for library in self.libraries:
229  galleryUtils.SourceCode.loadLibrary(library)
230 
231  # loads the actual classes from ROOT
232  self.expandClass('serviceClass')
233  if self.interfaceClass is not None: self.expandClass('interfaceClass')
234 
235  # if we don't need a special configuration,
236  # we let loadSimpleService() find it
237  registry = manager.registry()
238  config = self._makeConfig(registry) if self._needsSpecialConfig() else None
239 
240  return loadSimpleService(
241  self.serviceClass, config=config, registry=registry,
242  interfaceClass=self.interfaceClass,
243  args=dependencies,
244  )
245  # get()
246 
247  def __call__(self, manager): return self.load(manager)
248 
249  def expandClass(self, attrName):
250  classOrName = getattr(self, attrName)
251  if isinstance(classOrName, str):
252  classOrName = ROOTutils.getROOTclass(classOrName)
253  setattr(self, attrName, classOrName)
254  return classOrName
255  # expandClass()
256 
257 
258 # class SimpleServiceLoader
259 
260 
262 
263  def serviceKey(self): return 'Geometry'
264 
265  def load(self, manager): return loadGeometry(registry=manager.registry())
266 
267  def get(self, manager):
268  try: return manager.registry().get(self.serviceKey())
269  except KeyError:
270  return self.load(manager)
271  # get()
272 
273  def __call__(self, manager): return self.get(manager)
274 
275 # class GeometryServiceGetter
276 
277 
278 
280  """The interface of a service manager implementation."""
281 
282  def registry(self):
283  """Returns the service registry."""
284  return NotImplementedError
285  # registry()
286 
287  def loaders(self):
288  """Returns the service registry."""
289  return NotImplementedError
290  # loaders()
291 
292 
293  def loaded(self):
294  """Returns a list names of service already loaded."""
295  return self.registry().registeredServiceNames()
296  # loaded()
297 
298 
299  def supported(self):
300  """Returns a list names of services known to be supported."""
301  return self.loaders().keys()
302  # supported()
303 
304 
305  def registerLoader(self, serviceKey, loader):
306  """Registers a service provider loader, that can then be invoked to create
307  the service.
308  """
309  self.loaders()[serviceKey] = loader
310  return self
311  # registerLoader()
312 
313 
314  def get(self, serviceName, interfaceClass = None):
315  """Return (and load first when needed) the specified service.
316 
317  The service can be specified by name or by class.
318  In the former case, if the service is not already configured, an exception
319  will be raised.
320  """
321  return NotImplementedError
322  # get()
323 
324  def __call__(self, serviceName, interfaceClass = None):
325  return self.get(serviceName, interfaceClass=interfaceClass)
326 
327 
328 # class ServiceManagerInterface
329 
330 
332 
333  def __init__(self, config, loadingTable = {}, preload = []):
334 
335  #
336  # prepare the service registry
337  #
339 
340  #
341  # register the standard services
342  #
343  self.loadingTable = loadingTable.copy()
344 
345  #
346  # message facility
347  #
348  galleryUtils.startMessageFacility(self.registry().config("message"))
349  self.registry().register("message", None) # there is no direct access, sorry
350 
351  #
352  # preload services
353  #
354  for serviceKey in preload: self.get(serviceKey)
355 
356  # __init__()
357 
358  def registry(self): return self.registry_
359 
360  def loaders(self): return self.loadingTable
361 
362 
363  def get(self, serviceKey, interfaceClass = None):
364  """Return the specified service.
365 
366  The service can be specified by name or by class.
367  In the former case, if the service is not already configured, an exception
368  will be raised.
369  If the service is specified by class instead, an attempt is made to load it,
370  in which case `interfaceClass` is also passed to
371  `LArSoftUtils.loadSimpleService()`.
372 
373  """
374 
375  # if it is already cached, let's use it
376  try: return self.registry().get(serviceKey)
377  except KeyError: pass
378 
379  # first try to see if there is a loader available for this service
380  try:
381  loader = self.loadingTable[serviceKey]
382  except KeyError:
383  loader = SimpleServiceLoader(serviceKey, interfaceClass=interfaceClass)
384 
385  print("Loading service provider: '{}'".format(serviceKey))
386  return loader(self)
387 
388  # get()
389 
390 
391 # class ServiceManagerClass
392 
393 
394 
395 ################################################################################
397  """A service manager with a lazy setup.
398 
399  """
400 
401  StandardLoadingTable = {
402 
403  'Geometry': GeometryServiceGetter(),
404 
405  'LArProperties': SimpleServiceLoader(
406  "detinfo::LArPropertiesStandard",
407  interfaceClass="detinfo::LArProperties",
408  headers = 'lardataalg/DetectorInfo/LArPropertiesStandardTestHelpers.h',
409  libraries = 'lardataalg_DetectorInfo',
410  ),
411 
412  'DetectorClocks': SimpleServiceLoader(
413  "detinfo::DetectorClocksStandard",
414  interfaceClass="detinfo::DetectorClocks",
415  headers = 'lardataalg/DetectorInfo/DetectorClocksStandardTestHelpers.h',
416  libraries = 'lardataalg_DetectorInfo',
417  ),
418 
419  'DetectorProperties': SimpleServiceLoader(
420  "detinfo::DetectorPropertiesStandard",
421  interfaceClass="detinfo::DetectorProperties",
422  headers = 'lardataalg/DetectorInfo/DetectorPropertiesStandardTestHelpers.h',
423  libraries = 'lardataalg_DetectorInfo',
424  dependencies = [ 'Geometry', 'LArProperties' ],
425  ),
426 
427  } # StandardLoadingTable
428 
429 
430  class ConfigurationInfo:
431  __slots__ = [ 'configPath', 'serviceTable' ]
432  def __init__(self, configPath = None, serviceTable = None):
433  self.configPath = configPath
434  self.serviceTable = serviceTable
435  def fullConfig(self): return self.serviceTable is None
436  def isValid(self): return self.configPath is not None
437 
438  def __str__(self):
439  if self.fullConfig(): return self.configPath
440  else: return "{{services={}}}@{}".format(self.serviceTable, self.configPath)
441  # __str__()
442 
443  # class ConfigurationInfo
444 
445 
446  def __init__(self):
447  self.manager = None
448  self.configuration = None
449 
450  def registry(self):
451  """Returns the service registry."""
452  return self.manager.registry()
453  # registry()
454 
455  def registerLoader(self, serviceKey, loader):
456  """Registers a service provider loader, that can then be invoked to create
457  the service.
458 
459  It returns the service manager.
460  """
461  return self.manager.registerLoader(serviceKey, loader)
462  # registerLoader()
463 
464 
465  def get(self, serviceKey, interfaceClass = None):
466  """Return (and load first when needed) the specified service.
467 
468  The service can be specified by name or by class.
469  In the former case, if the service is not already configured, an exception
470  will be raised.
471  """
472  if not self.manager: self.setup()
473  return self.manager(serviceKey)
474  # get()
475 
476 
477  def defaultConfiguration(self): return None
478 
479  def setConfiguration(self, configFile, serviceTable = None):
480  """Sets which configuration to use for setup.
481 
482  If `serviceTable` is not `None`, a new configuration is created with the
483  service table as `serviceTable`, and `configPath` is included in that
484  configuration (presumably to define `serviceTable`).
485 
486  If `serviceTable` is `None` instead, the configuration file in
487  `configPath` is included directly, and it is assumed that it already
488  properly defines a `services` table.
489  """
490  assert configFile is not None
491  self.configuration \
492  = ServiceManagerInstance.ConfigurationInfo(configFile, serviceTable)
493  # setConfiguration()
494 
495  def setup(self):
496  """Prepares for service provider access in python/Gallery."""
497 
498  if self.manager is not None: return self.manager
499 
500  #
501  # configuration "file"
502  #
503  configurationInfo = self.defaultConfiguration() \
504  if self.configuration is None else self.configuration
505 
506  # if assertion fails, then `setConfiguration()` was not correctly called.
507  assert self.configuration.isValid()
508 
509  if configurationInfo.fullConfig():
510  config = configurationInfo.configPath
511  else:
513  '#include "{configPath}"'
514  '\n'
515  '\nservices: @local::{serviceTable}'
516  '\n'
517  .format(
518  configPath=configurationInfo.configPath,
519  serviceTable=configurationInfo.serviceTable
520  )
521  )
522  # if ... else
523 
524  #
525  # prepare the service registry and manager
526  #
527  self.manager = ServiceManagerClass \
528  (config, loadingTable=ServiceManagerInstance.StandardLoadingTable)
529 
530  #
531  # register the services we know about;
532  # some are already known
533  # (`LArSoftUtils.ServiceManagerClass.StandardLoadingTable`), including
534  # 'Geometry', 'LArProperties', 'DetectorClocks' and 'DetectorProperties',
535  # but se override the former with our flavor of it
536  #
537 
538  return self.manager
539 
540  # setup()
541 
542 # class ServiceManagerInstance
543 
544 
545 ################################################################################
static std::string format(PyObject *obj, unsigned int pos, unsigned int indent, unsigned int maxlen, unsigned int depth)
Definition: fclmodule.cxx:374
tuple loader
Definition: demo.py:7
do one_file $F done echo for F in find $TOP name CMakeLists txt print