The Gaudi Framework  v36r3 (83a1ddab)
_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  return repr(value)
133 
134 
135 if sys.version_info >= (3,): # pragma no cover
136  exec("class ConfigMetaHelper(metaclass=ConfigurableMeta):\n pass")
137 else: # pragma no cover
138 
139  class ConfigMetaHelper(object):
140  __metaclass__ = ConfigurableMeta
141 
142 
144  """
145  Base class for all configurable instances.
146  """
147 
148  instances = {}
149 
150  def __init__(self, name=None, **kwargs):
151  self._name = None
152  self._properties = {}
153  if "parent" in kwargs:
154  parent = kwargs.pop("parent")
155  if isinstance(parent, basestring if sys.version_info[0] == 2 else str):
156  parent = self.instances[parent]
157  if not name:
158  raise TypeError("name is needed when a parent is specified")
159  name = "{}.{}".format(parent.name, name)
160  if name:
161  self.name = name
162  elif not _GLOBAL_INSTANCES:
163  self.name = self.__cpp_type__
164  for key, value in kwargs.items():
165  setattr(self, key, value)
166 
167  @classmethod
168  def getInstance(cls, name):
169  return cls.instances.get(name) or cls(name)
170 
171  @property
172  def name(self):
173  if not self._name:
174  raise AttributeError(
175  "{!r} instance was not named yet".format(type(self).__name__)
176  )
177  return self._name
178 
179  @name.setter
180  def name(self, value):
181  if value == self._name:
182  return # it's already the name of the instance, nothing to do
183  if (
184  not isinstance(value, basestring if sys.version_info[0] == 2 else str)
185  or not value
186  ):
187  raise TypeError(
188  "expected string, got {} instead".format(type(value).__name__)
189  )
190  if _GLOBAL_INSTANCES:
191  if value in self.instances:
192  raise ValueError("name {!r} already used".format(value))
193  if self._name in self.instances:
194  del self.instances[self._name]
195  self._name = value
196  self.instances[value] = self
197  else:
198  self._name = value
199 
200  @name.deleter
201  def name(self):
202  if _GLOBAL_INSTANCES:
203  # check if it was set
204  del self.instances[self.name]
205  self._name = None
206  else:
207  raise TypeError("name attribute cannot be deleted")
208 
209  def __repr__(self):
210  args = []
211  try:
212  args.append(repr(self.name))
213  except AttributeError:
214  pass # no name
215  args.extend("{}={!r}".format(*item) for item in self._properties.items())
216  return "{}({})".format(type(self).__name__, ", ".join(args))
217 
218  def __getstate__(self):
219  state = {"properties": self._properties}
220  try:
221  state["name"] = self.name
222  except AttributeError:
223  pass # no name
224  return state
225 
226  def __setstate__(self, state):
227  self._name = None
228  self.name = state.get("name")
229  self._properties = state["properties"]
230 
231  def __opt_value__(self):
232  if self.__cpp_type__ == self.name:
233  return self.__cpp_type__
234  return "{}/{}".format(self.__cpp_type__, self.name)
235 
236  def __opt_properties__(self, explicit_defaults=False):
237  name = self.name
238  out = {}
239  for p in self._descriptors.values():
240  if explicit_defaults or p.__is_set__(self, type(self)):
241  out[".".join([name, p.name])] = opt_repr(
242  p.__opt_value__(self, type(self))
243  )
244  return out
245 
246  def is_property_set(self, propname):
247  return self._descriptors[propname].__is_set__(self, type(self))
248 
249  @classmethod
250  def getGaudiType(cls):
251  return cls.__component_type__
252 
253  @classmethod
254  def getType(cls):
255  return cls.__cpp_type__
256 
257  def getName(self):
258  return self.name
259 
260  def getFullJobOptName(self):
261  return "{}/{}".format(self.__cpp_type__, self.name)
262 
263  def toStringProperty(self):
264  return "{}/{}".format(self.__cpp_type__, self.name)
265 
266  @classmethod
268  return {k: v.default for k, v in cls._descriptors.items()}
269 
270  @classmethod
271  def getDefaultProperty(cls, name):
272  return cls._descriptors[name].default
273 
274  def merge(self, other):
275  """
276  Merge the properties of the other instance into the current one.
277 
278  The two instances have to be of the same type, have the same name
279  (or both unnamed) and the settings must be mergable (according to
280  their semantics).
281  """
282  if type(self) is not type(other):
283  raise TypeError(
284  "cannot merge instance of {} into an instance of {}".format(
285  type(other).__name__, type(self).__name__
286  )
287  )
288  if hasattr(self, "name") != hasattr(other, "name"):
289  raise ValueError("cannot merge a named configurable with an unnamed one")
290  if hasattr(self, "name") and (self.name != other.name):
291  raise ValueError(
292  "cannot merge configurables with different names ({} and {})".format(
293  self.name, other.name
294  )
295  )
296 
297  for name in other._descriptors:
298  if not other.is_property_set(name):
299  continue
300  try:
301  setattr(
302  self,
303  name,
304  self._descriptors[name].__merge__(
305  self, type(self), getattr(other, name)
306  ),
307  )
308  except ValueError as err:
309  raise ValueError(
310  "conflicting settings for property {} of {}: {}".format(
311  name,
312  self.name if hasattr(self, "name") else type(self).__name__,
313  str(err),
314  )
315  )
316 
317  return self
318 
319 
320 def makeConfigurableClass(name, **namespace):
321  """
322  Create a Configurable specialization.
323  """
324  properties = namespace.pop("properties", {})
325  namespace.update({pname: Property(*pargs) for pname, pargs in properties.items()})
326 
327  return type(name, (Configurable,), namespace)
328 
329 
330 def all_options(explicit_defaults=False):
331  """
332  Return a dictionary with all explicitly set options, or with also the
333  defaults if explicit_defaults is set to True.
334  """
335  opts = {}
336  for c in Configurable.instances.values():
337  opts.update(c.__opt_properties__(explicit_defaults))
338  return opts
GaudiConfig2._configurables.Configurable.getName
def getName(self)
Definition: _configurables.py:257
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:143
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:246
GaudiConfig2._configurables.Configurable.__opt_properties__
def __opt_properties__(self, explicit_defaults=False)
Definition: _configurables.py:236
GaudiConfig2._configurables.Configurable.__cpp_type__
__cpp_type__
Definition: _configurables.py:232
GaudiConfig2._configurables.Configurable.getDefaultProperty
def getDefaultProperty(cls, name)
Definition: _configurables.py:271
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:250
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:150
GaudiConfig2._configurables.Configurable.__repr__
def __repr__(self)
Definition: _configurables.py:209
GaudiConfig2._configurables.Property.name
def name(self)
Definition: _configurables.py:52
GaudiConfig2.semantics.getSemanticsFor
def getSemanticsFor(cpp_type, name=None)
Definition: semantics.py:474
GaudiConfig2._configurables.Property.__set__
def __set__(self, instance, value)
Definition: _configurables.py:60
GaudiConfig2._configurables.Configurable.getFullJobOptName
def getFullJobOptName(self)
Definition: _configurables.py:260
GaudiConfig2._configurables.ConfigMetaHelper
Definition: _configurables.py:139
Gaudi::Functional::details::get
auto get(const Handle &handle, const Algo &, const EventContext &) -> decltype(details::deref(handle.get()))
Definition: FunctionalDetails.h:412
GaudiConfig2._configurables.Configurable.merge
def merge(self, other)
Definition: _configurables.py:274
GaudiConfig2._configurables.Configurable.name
name
Definition: _configurables.py:161
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:330
GaudiConfig2._configurables.Configurable.__getstate__
def __getstate__(self)
Definition: _configurables.py:218
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:263
GaudiConfig2._configurables.Configurable.getDefaultProperties
def getDefaultProperties(cls)
Definition: _configurables.py:267
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:152
GaudiConfig2._configurables.Property.__delete__
def __delete__(self, instance)
Definition: _configurables.py:63
GaudiConfig2._configurables.Configurable.getType
def getType(cls)
Definition: _configurables.py:254
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:320
GaudiConfig2._configurables.Configurable.__opt_value__
def __opt_value__(self)
Definition: _configurables.py:231
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:151
GaudiConfig2._configurables.Configurable.getInstance
def getInstance(cls, name)
Definition: _configurables.py:168
GaudiConfig2._configurables.Configurable.instances
dictionary instances
Definition: _configurables.py:148
GaudiConfig2._configurables.Configurable.__setstate__
def __setstate__(self, state)
Definition: _configurables.py:226
GaudiPython.Pythonizations.items
items
Definition: Pythonizations.py:546