12 _GLOBAL_INSTANCES =
False
17 Enable or disable the global instances database.
19 By default global instances are enabled.
21 global _GLOBAL_INSTANCES
22 if enable == _GLOBAL_INSTANCES:
26 not Configurable.instances
27 ),
"Configurable instances DB not empty, cannot be disabled"
28 _GLOBAL_INSTANCES = enable
33 Descriptor class to implement validation of Configurable properties.
36 def __init__(self, cpp_type, default, doc="undocumented", semantics=None):
37 from .semantics
import getSemanticsFor
52 if self.
name not in instance._properties
and hasattr(self.
semantics,
"default"):
60 del instance._properties[self.
name]
67 value = instance._properties[self.
name]
79 Return "merge" (according to the semantic) of the value
80 in this property and the incoming value.
89 Metaclass for Configurables.
92 def __new__(cls, name, bases, namespace, **kwds):
96 if isinstance(namespace[key], Property)
99 doc = namespace.get(
"__doc__",
"").rstrip()
100 doc +=
"\n\nProperties\n----------\n"
103 f
"- {name}: {p.cpp_type} ({p.default!r})\n {p.__doc__}\n"
104 for name, p
in props.items()
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)
118 String representation of the value, such that it can be consumed be the
119 Gaudi option parsers.
121 if hasattr(value,
"__opt_repr__"):
122 return value.__opt_repr__()
123 elif isinstance(value, str):
124 return '"{}"'.
format(value.replace(
'"',
'\\"'))
130 Base class for all configurable instances.
138 if "parent" in kwargs:
139 parent = kwargs.pop(
"parent")
140 if isinstance(parent, str):
143 raise TypeError(
"name is needed when a parent is specified")
144 name = f
"{parent.name}.{name}"
147 elif not _GLOBAL_INSTANCES:
149 for key, value
in kwargs.items():
150 setattr(self, key, value)
159 raise AttributeError(
160 f
"{repr(type(self).__name__)} instance was not named yet"
166 if value == self.
_name:
168 if not isinstance(value, str)
or not value:
169 raise TypeError(f
"expected string, got {type(value).__name__} instead")
170 if _GLOBAL_INSTANCES:
172 raise ValueError(f
"name {repr(value)} already used")
182 if _GLOBAL_INSTANCES:
187 raise TypeError(
"name attribute cannot be deleted")
192 args.append(repr(self.
name))
193 except AttributeError:
195 args.extend(f
"{k}={repr(v)}" for k, v
in self.
_properties.items())
196 return "{}({})".
format(
type(self).__name__,
", ".join(args))
201 state[
"name"] = self.
name
202 except AttributeError:
208 self.
name = state.get(
"name")
214 return f
"{self.__cpp_type__}/{self.name}"
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))
227 return self._descriptors[propname].__is_set__(self,
type(self))
231 return cls.__component_type__
241 return f
"{self.__cpp_type__}/{self.name}"
244 return f
"{self.__cpp_type__}/{self.name}"
248 return {k: v.default
for k, v
in cls._descriptors.items()}
252 return cls._descriptors[name].default
255 """Clone instance with all its properties."""
260 Merge the properties of the other instance into the current one.
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
270 f
"cannot merge instance of {type(other).__name__} into an instance of { type(self).__name__}"
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):
276 f
"cannot merge configurables with different names ({self.name} and {other.name})"
279 for name
in other._properties:
282 and self.
_properties[name] == other._properties[name]
286 self.
_properties[name] = self._descriptors[name].__merge__(
287 self,
type(self), getattr(other, name)
289 except ValueError
as err:
291 "conflicting settings for property {} of {}: {}".
format(
293 self.
name if hasattr(self,
"name")
else type(self).__name__,
303 Create a Configurable specialization.
305 properties = namespace.pop(
"properties", {})
306 namespace.update({pname:
Property(*pargs)
for pname, pargs
in properties.items()})
308 return type(name, (Configurable,), namespace)
313 Return a dictionary with all explicitly set options, or with also the
314 defaults if explicit_defaults is set to True.
317 for c
in Configurable.instances.values():
318 opts.update(c.__opt_properties__(explicit_defaults))