The Gaudi Framework  v36r12 (27a178af)
_configurables.py
Go to the documentation of this file.
1 
12 from __future__ import absolute_import
13 
14 import sys
15 
16 _GLOBAL_INSTANCES = False
17 
18 
19 def useGlobalInstances(enable):
20  """
21  Enable or disable the global instances database.
22 
23  By default global instances are enabled.
24  """
25  global _GLOBAL_INSTANCES
26  if enable == _GLOBAL_INSTANCES:
27  return
28  if not enable:
29  assert (
30  not Configurable.instances
31  ), "Configurable instances DB not empty, cannot be disabled"
32  _GLOBAL_INSTANCES = enable
33 
34 
35 class Property(object):
36  """
37  Descriptor class to implement validation of Configurable properties.
38  """
39 
40  def __init__(self, cpp_type, default, doc="undocumented", semantics=None):
41  from .semantics import getSemanticsFor
42 
43  self.semantics = getSemanticsFor(semantics or cpp_type)
44  self.default = default
45  self.__doc__ = doc
46 
47  @property
48  def cpp_type(self):
49  return self.semantics.cpp_type
50 
51  @property
52  def name(self):
53  return self.semantics.name
54 
55  def __get__(self, instance, owner):
56  if self.name not in instance._properties and hasattr(self.semantics, "default"):
57  instance._properties[self.name] = self.semantics.default(self.default)
58  return self.semantics.load(instance._properties.get(self.name, self.default))
59 
60  def __set__(self, instance, value):
61  instance._properties[self.name] = self.semantics.store(value)
62 
63  def __delete__(self, instance):
64  del instance._properties[self.name]
65 
66  def __set_name__(self, owner, name):
67  self.semantics.name = name
68 
69  def __is_set__(self, instance, owner):
70  try:
71  value = instance._properties[self.name]
72  return self.semantics.is_set(value)
73  except KeyError:
74  return False
75 
76  def __opt_value__(self, instance, owner):
77  return self.semantics.opt_value(
78  instance._properties.get(self.name, self.default)
79  )
80 
81  def __merge__(self, instance, owner, value):
82  """
83  Return "merge" (according to the semantic) of the value
84  in this property and the incoming value.
85  """
86  if not self.__is_set__(instance, owner):
87  return value
88  return self.semantics.merge(self.__get__(instance, owner), value)
89 
90 
92  """
93  Metaclass for Configurables.
94  """
95 
96  def __new__(cls, name, bases, namespace, **kwds):
97  props = {
98  key: namespace[key]
99  for key in namespace
100  if isinstance(namespace[key], Property)
101  }
102  if props:
103  doc = namespace.get("__doc__", "").rstrip()
104  doc += "\n\nProperties\n----------\n"
105  doc += "\n".join(
106  [
107  "- {name}: {p.cpp_type} ({p.default!r})\n {p.__doc__}\n".format(
108  name=n, p=props[n]
109  )
110  for n in props
111  ]
112  )
113  namespace["__doc__"] = doc
114  if sys.version_info < (3, 6): # pragma no cover
115  for n in props:
116  namespace[n].__set_name__(None, n)
117  namespace["_descriptors"] = props
118  slots = set(namespace.get("__slots__", []))
119  slots.update(["_properties", "_name"])
120  namespace["__slots__"] = tuple(slots)
121  result = type.__new__(cls, name, bases, namespace)
122  return result
123 
124 
125 def opt_repr(value):
126  """
127  String representation of the value, such that it can be consumed be the
128  Gaudi option parsers.
129  """
130  if hasattr(value, "__opt_repr__"):
131  return value.__opt_repr__()
132  elif isinstance(value, str):
133  return '"{}"'.format(value.replace('"', '\\"'))
134  return repr(value)
135 
136 
137 if sys.version_info >= (3,): # pragma no cover
138  exec("class ConfigMetaHelper(metaclass=ConfigurableMeta):\n pass")
139 else: # pragma no cover
140 
141  class ConfigMetaHelper(object):
142  __metaclass__ = ConfigurableMeta
143 
144 
146  """
147  Base class for all configurable instances.
148  """
149 
150  instances = {}
151 
152  def __init__(self, name=None, **kwargs):
153  self._name = None
154  self._properties = {}
155  if "parent" in kwargs:
156  parent = kwargs.pop("parent")
157  if isinstance(parent, basestring if sys.version_info[0] == 2 else str):
158  parent = self.instances[parent]
159  if not name:
160  raise TypeError("name is needed when a parent is specified")
161  name = "{}.{}".format(parent.name, name)
162  if name:
163  self.name = name
164  elif not _GLOBAL_INSTANCES:
165  self.name = self.__cpp_type__
166  for key, value in kwargs.items():
167  setattr(self, key, value)
168 
169  @classmethod
170  def getInstance(cls, name):
171  return cls.instances.get(name) or cls(name)
172 
173  @property
174  def name(self):
175  if not self._name:
176  raise AttributeError(
177  "{!r} instance was not named yet".format(type(self).__name__)
178  )
179  return self._name
180 
181  @name.setter
182  def name(self, value):
183  if value == self._name:
184  return # it's already the name of the instance, nothing to do
185  if (
186  not isinstance(value, basestring if sys.version_info[0] == 2 else str)
187  or not value
188  ):
189  raise TypeError(
190  "expected string, got {} instead".format(type(value).__name__)
191  )
192  if _GLOBAL_INSTANCES:
193  if value in self.instances:
194  raise ValueError("name {!r} already used".format(value))
195  if self._name in self.instances:
196  del self.instances[self._name]
197  self._name = value
198  self.instances[value] = self
199  else:
200  self._name = value
201 
202  @name.deleter
203  def name(self):
204  if _GLOBAL_INSTANCES:
205  # check if it was set
206  del self.instances[self.name]
207  self._name = None
208  else:
209  raise TypeError("name attribute cannot be deleted")
210 
211  def __repr__(self):
212  args = []
213  try:
214  args.append(repr(self.name))
215  except AttributeError:
216  pass # no name
217  args.extend("{}={!r}".format(*item) for item in self._properties.items())
218  return "{}({})".format(type(self).__name__, ", ".join(args))
219 
220  def __getstate__(self):
221  state = {"properties": self._properties}
222  try:
223  state["name"] = self.name
224  except AttributeError:
225  pass # no name
226  return state
227 
228  def __setstate__(self, state):
229  self._name = None
230  self.name = state.get("name")
231  self._properties = state["properties"]
232 
233  def __opt_value__(self):
234  if self.__cpp_type__ == self.name:
235  return self.__cpp_type__
236  return "{}/{}".format(self.__cpp_type__, self.name)
237 
238  def __opt_properties__(self, explicit_defaults=False):
239  name = self.name
240  out = {}
241  for p in self._descriptors.values():
242  if explicit_defaults or p.__is_set__(self, type(self)):
243  out[".".join([name, p.name])] = opt_repr(
244  p.__opt_value__(self, type(self))
245  )
246  return out
247 
248  def is_property_set(self, propname):
249  return self._descriptors[propname].__is_set__(self, type(self))
250 
251  @classmethod
252  def getGaudiType(cls):
253  return cls.__component_type__
254 
255  @classmethod
256  def getType(cls):
257  return cls.__cpp_type__
258 
259  def getName(self):
260  return self.name
261 
262  def getFullJobOptName(self):
263  return "{}/{}".format(self.__cpp_type__, self.name)
264 
265  def toStringProperty(self):
266  return "{}/{}".format(self.__cpp_type__, self.name)
267 
268  @classmethod
270  return {k: v.default for k, v in cls._descriptors.items()}
271 
272  @classmethod
273  def getDefaultProperty(cls, name):
274  return cls._descriptors[name].default
275 
276  def clone(self, newname=None):
277  """Clone instance with all its properties."""
278  return self.__class__(newname, **self._properties)
279 
280  def merge(self, other):
281  """
282  Merge the properties of the other instance into the current one.
283 
284  The two instances have to be of the same type, have the same name
285  (or both unnamed) and the settings must be mergable (according to
286  their semantics).
287  """
288  if self is other:
289  return self
290  if type(self) is not type(other):
291  raise TypeError(
292  "cannot merge instance of {} into an instance of {}".format(
293  type(other).__name__, type(self).__name__
294  )
295  )
296  if hasattr(self, "name") != hasattr(other, "name"):
297  raise ValueError("cannot merge a named configurable with an unnamed one")
298  if hasattr(self, "name") and (self.name != other.name):
299  raise ValueError(
300  "cannot merge configurables with different names ({} and {})".format(
301  self.name, other.name
302  )
303  )
304 
305  for name in other._properties:
306  if (
307  name in self._properties
308  and self._properties[name] == other._properties[name]
309  ):
310  continue
311  try:
312  setattr(
313  self,
314  name,
315  self._descriptors[name].__merge__(
316  self, type(self), getattr(other, name)
317  ),
318  )
319  except ValueError as err:
320  raise ValueError(
321  "conflicting settings for property {} of {}: {}".format(
322  name,
323  self.name if hasattr(self, "name") else type(self).__name__,
324  str(err),
325  )
326  )
327 
328  return self
329 
330 
331 def makeConfigurableClass(name, **namespace):
332  """
333  Create a Configurable specialization.
334  """
335  properties = namespace.pop("properties", {})
336  namespace.update({pname: Property(*pargs) for pname, pargs in properties.items()})
337 
338  return type(name, (Configurable,), namespace)
339 
340 
341 def all_options(explicit_defaults=False):
342  """
343  Return a dictionary with all explicitly set options, or with also the
344  defaults if explicit_defaults is set to True.
345  """
346  opts = {}
347  for c in Configurable.instances.values():
348  opts.update(c.__opt_properties__(explicit_defaults))
349  return opts
GaudiConfig2._configurables.Configurable.getName
def getName(self)
Definition: _configurables.py:259
GaudiConfig2._configurables.Property.__doc__
__doc__
Definition: _configurables.py:45
GaudiConfig2._configurables.Property.default
default
Definition: _configurables.py:44
GaudiConfig2._configurables.ConfigurableMeta
Definition: _configurables.py:91
GaudiConfig2._configurables.Configurable
Definition: _configurables.py:145
GaudiConfig2._configurables.Property.__init__
def __init__(self, cpp_type, default, doc="undocumented", semantics=None)
Definition: _configurables.py:40
GaudiConfig2._configurables.Configurable.is_property_set
def is_property_set(self, propname)
Definition: _configurables.py:248
GaudiConfig2._configurables.Configurable.__opt_properties__
def __opt_properties__(self, explicit_defaults=False)
Definition: _configurables.py:238
GaudiConfig2._configurables.Configurable.__cpp_type__
__cpp_type__
Definition: _configurables.py:234
GaudiConfig2._configurables.Configurable.getDefaultProperty
def getDefaultProperty(cls, name)
Definition: _configurables.py:273
GaudiConfig2._configurables.Property.cpp_type
def cpp_type(self)
Definition: _configurables.py:48
GaudiConfig2._configurables.Property.__is_set__
def __is_set__(self, instance, owner)
Definition: _configurables.py:69
GaudiConfig2._configurables.Configurable.getGaudiType
def getGaudiType(cls)
Definition: _configurables.py:252
GaudiConfig2._configurables.Configurable.clone
def clone(self, newname=None)
Definition: _configurables.py:276
GaudiConfig2._configurables.Property.__merge__
def __merge__(self, instance, owner, value)
Definition: _configurables.py:81
GaudiConfig2._configurables.Configurable.__init__
def __init__(self, name=None, **kwargs)
Definition: _configurables.py:152
GaudiConfig2._configurables.Configurable.__repr__
def __repr__(self)
Definition: _configurables.py:211
GaudiConfig2._configurables.Property.name
def name(self)
Definition: _configurables.py:52
GaudiConfig2.semantics.getSemanticsFor
def getSemanticsFor(cpp_type, name=None)
Definition: semantics.py:510
GaudiConfig2._configurables.Property.__set__
def __set__(self, instance, value)
Definition: _configurables.py:60
GaudiConfig2._configurables.Configurable.getFullJobOptName
def getFullJobOptName(self)
Definition: _configurables.py:262
GaudiConfig2._configurables.ConfigMetaHelper
Definition: _configurables.py:141
Gaudi::Functional::details::get
auto get(const Handle &handle, const Algo &, const EventContext &) -> decltype(details::deref(handle.get()))
Definition: FunctionalDetails.h:444
GaudiConfig2._configurables.Configurable.merge
def merge(self, other)
Definition: _configurables.py:280
GaudiConfig2._configurables.Configurable.name
name
Definition: _configurables.py:163
GaudiConfig2._configurables.opt_repr
def opt_repr(value)
Definition: _configurables.py:125
GaudiConfig2._configurables.all_options
def all_options(explicit_defaults=False)
Definition: _configurables.py:341
GaudiConfig2._configurables.Configurable.__getstate__
def __getstate__(self)
Definition: _configurables.py:220
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
GaudiConfig2._configurables.Configurable.toStringProperty
def toStringProperty(self)
Definition: _configurables.py:265
GaudiConfig2._configurables.Configurable.getDefaultProperties
def getDefaultProperties(cls)
Definition: _configurables.py:269
gaudirun.type
type
Definition: gaudirun.py:160
merge
int merge(const char *target, const char *source, bool fixup=false, bool dbg=true)
Definition: merge.C:430
GaudiConfig2._configurables.Configurable._properties
_properties
Definition: _configurables.py:154
GaudiConfig2._configurables.Property.__delete__
def __delete__(self, instance)
Definition: _configurables.py:63
GaudiConfig2._configurables.Configurable.getType
def getType(cls)
Definition: _configurables.py:256
GaudiConfig2._configurables.Property.__set_name__
def __set_name__(self, owner, name)
Definition: _configurables.py:66
GaudiConfig2._configurables.ConfigurableMeta.__new__
def __new__(cls, name, bases, namespace, **kwds)
Definition: _configurables.py:96
GaudiConfig2._configurables.Property.semantics
semantics
Definition: _configurables.py:43
GaudiConfig2._configurables.Property
Definition: _configurables.py:35
GaudiConfig2._configurables.Property.__opt_value__
def __opt_value__(self, instance, owner)
Definition: _configurables.py:76
Gaudi.CommonGaudiConfigurables.cls
cls
Definition: CommonGaudiConfigurables.py:44
GaudiConfig2._configurables.makeConfigurableClass
def makeConfigurableClass(name, **namespace)
Definition: _configurables.py:331
GaudiConfig2._configurables.Configurable.__opt_value__
def __opt_value__(self)
Definition: _configurables.py:233
GaudiConfig2._configurables.useGlobalInstances
def useGlobalInstances(enable)
Definition: _configurables.py:19
GaudiConfig2._configurables.Property.__get__
def __get__(self, instance, owner)
Definition: _configurables.py:55
GaudiConfig2._configurables.Configurable._name
_name
Definition: _configurables.py:153
GaudiConfig2._configurables.Configurable.getInstance
def getInstance(cls, name)
Definition: _configurables.py:170
GaudiConfig2._configurables.Configurable.instances
dictionary instances
Definition: _configurables.py:150
GaudiConfig2._configurables.Configurable.__setstate__
def __setstate__(self, state)
Definition: _configurables.py:228
GaudiPython.Pythonizations.items
items
Definition: Pythonizations.py:546