The Gaudi Framework  master (37c0b60a)
_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  f"- {name}: {p.cpp_type} ({p.default!r})\n {p.__doc__}\n"
104  for name, p in props.items()
105  ]
106  )
107  namespace["__doc__"] = doc
108  namespace["_descriptors"] = props
109  slots = set(namespace.get("__slots__", []))
110  slots.update(["_properties", "_name"])
111  namespace["__slots__"] = tuple(slots)
112  result = type.__new__(cls, name, bases, namespace)
113  return result
114 
115 
116 def opt_repr(value):
117  """
118  String representation of the value, such that it can be consumed be the
119  Gaudi option parsers.
120  """
121  if hasattr(value, "__opt_repr__"):
122  return value.__opt_repr__()
123  elif isinstance(value, str):
124  return '"{}"'.format(value.replace('"', '\\"'))
125  return repr(value)
126 
127 
128 class Configurable(metaclass=ConfigurableMeta):
129  """
130  Base class for all configurable instances.
131  """
132 
133  instances = {}
134 
135  def __init__(self, name=None, **kwargs):
136  self._name = None
137  self._properties = {}
138  if "parent" in kwargs:
139  parent = kwargs.pop("parent")
140  if isinstance(parent, str):
141  parent = self.instances[parent]
142  if not name:
143  raise TypeError("name is needed when a parent is specified")
144  name = f"{parent.name}.{name}"
145  if name:
146  self.name = name
147  elif not _GLOBAL_INSTANCES:
148  self.name = self.__cpp_type__
149  for key, value in kwargs.items():
150  setattr(self, key, value)
151 
152  @classmethod
153  def getInstance(cls, name):
154  return cls.instances.get(name) or cls(name)
155 
156  @property
157  def name(self):
158  if not self._name:
159  raise AttributeError(
160  f"{repr(type(self).__name__)} instance was not named yet"
161  )
162  return self._name
163 
164  @name.setter
165  def name(self, value):
166  if value == self._name:
167  return # it's already the name of the instance, nothing to do
168  if not isinstance(value, str) or not value:
169  raise TypeError(f"expected string, got {type(value).__name__} instead")
170  if _GLOBAL_INSTANCES:
171  if value in self.instances:
172  raise ValueError(f"name {repr(value)} already used")
173  if self._name in self.instances:
174  del self.instances[self._name]
175  self._name = value
176  self.instances[value] = self
177  else:
178  self._name = value
179 
180  @name.deleter
181  def name(self):
182  if _GLOBAL_INSTANCES:
183  # check if it was set
184  del self.instances[self.name]
185  self._name = None
186  else:
187  raise TypeError("name attribute cannot be deleted")
188 
189  def __repr__(self):
190  args = []
191  try:
192  args.append(repr(self.name))
193  except AttributeError:
194  pass # no name
195  args.extend(f"{k}={repr(v)}" for k, v in self._properties.items())
196  return "{}({})".format(type(self).__name__, ", ".join(args))
197 
198  def __getstate__(self):
199  state = {"properties": self._properties}
200  try:
201  state["name"] = self.name
202  except AttributeError:
203  pass # no name
204  return state
205 
206  def __setstate__(self, state):
207  self._name = None
208  self.name = state.get("name")
209  self._properties = state["properties"]
210 
211  def __opt_value__(self):
212  if self.__cpp_type__ == self.name:
213  return self.__cpp_type__
214  return f"{self.__cpp_type__}/{self.name}"
215 
216  def __opt_properties__(self, explicit_defaults=False):
217  name = self.name
218  out = {}
219  for p in self._descriptors.values():
220  if explicit_defaults or p.__is_set__(self, type(self)):
221  out[".".join([name, p.name])] = opt_repr(
222  p.__opt_value__(self, type(self))
223  )
224  return out
225 
226  def is_property_set(self, propname):
227  return self._descriptors[propname].__is_set__(self, type(self))
228 
229  @classmethod
230  def getGaudiType(cls):
231  return cls.__component_type__
232 
233  @classmethod
234  def getType(cls):
235  return cls.__cpp_type__
236 
237  def getName(self):
238  return self.name
239 
240  def getFullJobOptName(self):
241  return f"{self.__cpp_type__}/{self.name}"
242 
243  def toStringProperty(self):
244  return f"{self.__cpp_type__}/{self.name}"
245 
246  @classmethod
248  return {k: v.default for k, v in cls._descriptors.items()}
249 
250  @classmethod
251  def getDefaultProperty(cls, name):
252  return cls._descriptors[name].default
253 
254  def clone(self, newname=None):
255  """Clone instance with all its properties."""
256  return self.__class__(newname, **self._properties)
257 
258  def merge(self, other):
259  """
260  Merge the properties of the other instance into the current one.
261 
262  The two instances have to be of the same type, have the same name
263  (or both unnamed) and the settings must be mergable (according to
264  their semantics).
265  """
266  if self is other:
267  return self
268  if type(self) is not type(other):
269  raise TypeError(
270  f"cannot merge instance of {type(other).__name__} into an instance of { type(self).__name__}"
271  )
272  if hasattr(self, "name") != hasattr(other, "name"):
273  raise ValueError("cannot merge a named configurable with an unnamed one")
274  if hasattr(self, "name") and (self.name != other.name):
275  raise ValueError(
276  f"cannot merge configurables with different names ({self.name} and {other.name})"
277  )
278 
279  for name in other._properties:
280  if (
281  name in self._properties
282  and self._properties[name] == other._properties[name]
283  ):
284  continue
285  try:
286  self._properties[name] = self._descriptors[name].__merge__(
287  self, type(self), getattr(other, name)
288  )
289  except ValueError as err:
290  raise ValueError(
291  "conflicting settings for property {} of {}: {}".format(
292  name,
293  self.name if hasattr(self, "name") else type(self).__name__,
294  str(err),
295  )
296  )
297 
298  return self
299 
300 
301 def makeConfigurableClass(name, **namespace):
302  """
303  Create a Configurable specialization.
304  """
305  properties = namespace.pop("properties", {})
306  namespace.update({pname: Property(*pargs) for pname, pargs in properties.items()})
307 
308  return type(name, (Configurable,), namespace)
309 
310 
311 def all_options(explicit_defaults=False):
312  """
313  Return a dictionary with all explicitly set options, or with also the
314  defaults if explicit_defaults is set to True.
315  """
316  opts = {}
317  for c in Configurable.instances.values():
318  opts.update(c.__opt_properties__(explicit_defaults))
319  return opts
GaudiConfig2._configurables.Configurable.getName
def getName(self)
Definition: _configurables.py:237
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:128
GaudiConfig2._configurables.Property.__init__
def __init__(self, cpp_type, default, doc="undocumented", semantics=None)
Definition: _configurables.py:36
GaudiPartProp.decorators.get
get
decorate the vector of properties
Definition: decorators.py:283
GaudiConfig2._configurables.Configurable.is_property_set
def is_property_set(self, propname)
Definition: _configurables.py:226
GaudiConfig2._configurables.Configurable.__opt_properties__
def __opt_properties__(self, explicit_defaults=False)
Definition: _configurables.py:216
GaudiConfig2._configurables.Configurable.__cpp_type__
__cpp_type__
Definition: _configurables.py:212
GaudiConfig2._configurables.Configurable.getDefaultProperty
def getDefaultProperty(cls, name)
Definition: _configurables.py:251
GaudiConfig2._configurables.Configurable.instances
instances
Definition: _configurables.py:133
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:230
GaudiConfig2._configurables.Configurable.clone
def clone(self, newname=None)
Definition: _configurables.py:254
GaudiConfig2._configurables.Property.__merge__
def __merge__(self, instance, owner, value)
Definition: _configurables.py:77
GaudiConfig2.semantics.getSemanticsFor
def getSemanticsFor(cpp_type)
Definition: semantics.py:636
GaudiConfig2._configurables.Configurable.__init__
def __init__(self, name=None, **kwargs)
Definition: _configurables.py:135
GaudiConfig2._configurables.Configurable.__repr__
def __repr__(self)
Definition: _configurables.py:189
GaudiConfig2._configurables.Property.name
def name(self)
Definition: _configurables.py:48
GaudiConfig2._configurables.Property.__set__
def __set__(self, instance, value)
Definition: _configurables.py:56
GaudiConfig2._configurables.Configurable.getFullJobOptName
def getFullJobOptName(self)
Definition: _configurables.py:240
GaudiConfig2._configurables.Configurable.merge
def merge(self, other)
Definition: _configurables.py:258
GaudiConfig2._configurables.Configurable.name
name
Definition: _configurables.py:146
GaudiConfig2._configurables.opt_repr
def opt_repr(value)
Definition: _configurables.py:116
GaudiConfig2._configurables.all_options
def all_options(explicit_defaults=False)
Definition: _configurables.py:311
GaudiConfig2._configurables.Configurable.__getstate__
def __getstate__(self)
Definition: _configurables.py:198
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:243
GaudiConfig2._configurables.Configurable.getDefaultProperties
def getDefaultProperties(cls)
Definition: _configurables.py:247
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:137
GaudiConfig2._configurables.Property.__delete__
def __delete__(self, instance)
Definition: _configurables.py:59
GaudiConfig2._configurables.Configurable.getType
def getType(cls)
Definition: _configurables.py:234
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:43
GaudiConfig2._configurables.makeConfigurableClass
def makeConfigurableClass(name, **namespace)
Definition: _configurables.py:301
GaudiConfig2._configurables.Configurable.__opt_value__
def __opt_value__(self)
Definition: _configurables.py:211
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:136
GaudiConfig2._configurables.Configurable.getInstance
def getInstance(cls, name)
Definition: _configurables.py:153
GaudiConfig2._configurables.Configurable.__setstate__
def __setstate__(self, state)
Definition: _configurables.py:206