The Gaudi Framework  v36r11 (bdb84f5f)
_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 merge(self, other):
277  """
278  Merge the properties of the other instance into the current one.
279 
280  The two instances have to be of the same type, have the same name
281  (or both unnamed) and the settings must be mergable (according to
282  their semantics).
283  """
284  if self is other:
285  return self
286  if type(self) is not type(other):
287  raise TypeError(
288  "cannot merge instance of {} into an instance of {}".format(
289  type(other).__name__, type(self).__name__
290  )
291  )
292  if hasattr(self, "name") != hasattr(other, "name"):
293  raise ValueError("cannot merge a named configurable with an unnamed one")
294  if hasattr(self, "name") and (self.name != other.name):
295  raise ValueError(
296  "cannot merge configurables with different names ({} and {})".format(
297  self.name, other.name
298  )
299  )
300 
301  for name in other._properties:
302  if (
303  name in self._properties
304  and self._properties[name] == other._properties[name]
305  ):
306  continue
307  try:
308  setattr(
309  self,
310  name,
311  self._descriptors[name].__merge__(
312  self, type(self), getattr(other, name)
313  ),
314  )
315  except ValueError as err:
316  raise ValueError(
317  "conflicting settings for property {} of {}: {}".format(
318  name,
319  self.name if hasattr(self, "name") else type(self).__name__,
320  str(err),
321  )
322  )
323 
324  return self
325 
326 
327 def makeConfigurableClass(name, **namespace):
328  """
329  Create a Configurable specialization.
330  """
331  properties = namespace.pop("properties", {})
332  namespace.update({pname: Property(*pargs) for pname, pargs in properties.items()})
333 
334  return type(name, (Configurable,), namespace)
335 
336 
337 def all_options(explicit_defaults=False):
338  """
339  Return a dictionary with all explicitly set options, or with also the
340  defaults if explicit_defaults is set to True.
341  """
342  opts = {}
343  for c in Configurable.instances.values():
344  opts.update(c.__opt_properties__(explicit_defaults))
345  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.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:276
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:337
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:327
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