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