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
56 if self.
name not in instance._properties
and hasattr(self.
semantics,
"default"):
64 del instance._properties[self.
name]
71 value = instance._properties[self.
name]
83 Return "merge" (according to the semantic) of the value
84 in this property and the incoming value.
93 Metaclass for Configurables.
96 def __new__(cls, name, bases, namespace, **kwds):
100 if isinstance(namespace[key], Property)
103 doc = namespace.get(
"__doc__",
"").rstrip()
104 doc +=
"\n\nProperties\n----------\n"
107 f
"- {name}: {p.cpp_type} ({p.default!r})\n {p.__doc__}\n"
108 for name, p
in props.items()
111 namespace[
"__doc__"] = doc
112 namespace[
"_descriptors"] = props
113 slots = set(namespace.get(
"__slots__", []))
114 slots.update([
"_properties",
"_name"])
115 namespace[
"__slots__"] = tuple(slots)
116 result = type.__new__(cls, name, bases, namespace)
122 String representation of the value, such that it can be consumed be the
123 Gaudi option parsers.
125 if hasattr(value,
"__opt_repr__"):
126 return value.__opt_repr__()
127 elif isinstance(value, str):
128 return '"{}"'.
format(value.replace(
'"',
'\\"'))
134 Base class for all configurable instances.
142 if "parent" in kwargs:
143 parent = kwargs.pop(
"parent")
144 if isinstance(parent, str):
147 raise TypeError(
"name is needed when a parent is specified")
148 name = f
"{parent.name}.{name}"
151 elif not _GLOBAL_INSTANCES:
153 for key, value
in kwargs.items():
154 setattr(self, key, value)
163 raise AttributeError(
164 f
"{repr(type(self).__name__)} instance was not named yet"
170 if value == self.
_name:
172 if not isinstance(value, str)
or not value:
173 raise TypeError(f
"expected string, got {type(value).__name__} instead")
174 if _GLOBAL_INSTANCES:
176 raise ValueError(f
"name {repr(value)} already used")
186 if _GLOBAL_INSTANCES:
191 raise TypeError(
"name attribute cannot be deleted")
196 args.append(repr(self.
name))
197 except AttributeError:
199 args.extend(f
"{k}={repr(v)}" for k, v
in self.
_properties.items())
200 return "{}({})".
format(
type(self).__name__,
", ".join(args))
205 state[
"name"] = self.
name
206 except AttributeError:
212 self.
name = state.get(
"name")
218 return f
"{self.__cpp_type__}/{self.name}"
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))
231 return self._descriptors[propname].__is_set__(self,
type(self))
235 return cls.__component_type__
245 return f
"{self.__cpp_type__}/{self.name}"
248 return f
"{self.__cpp_type__}/{self.name}"
252 return {k: v.default
for k, v
in cls._descriptors.items()}
256 return cls._descriptors[name].default
259 """Clone instance with all its properties."""
264 Merge the properties of the other instance into the current one.
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
274 f
"cannot merge instance of {type(other).__name__} into an instance of { type(self).__name__}"
276 if hasattr(self,
"name") != hasattr(other,
"name"):
277 raise ValueError(
"cannot merge a named configurable with an unnamed one")
278 if hasattr(self,
"name")
and (self.
name != other.name):
280 f
"cannot merge configurables with different names ({self.name} and {other.name})"
283 for name
in other._properties:
286 and self.
_properties[name] == other._properties[name]
290 self.
_properties[name] = self._descriptors[name].__merge__(
291 self,
type(self), getattr(other, name)
293 except ValueError
as err:
295 "conflicting settings for property {} of {}: {}".
format(
297 self.
name if hasattr(self,
"name")
else type(self).__name__,
307 Create a Configurable specialization.
309 properties = namespace.pop(
"properties", {})
310 namespace.update({pname:
Property(*pargs)
for pname, pargs
in properties.items()})
312 return type(name, (Configurable,), namespace)
317 Return a dictionary with all explicitly set options, or with also the
318 defaults if explicit_defaults is set to True.
321 for c
in Configurable.instances.values():
322 opts.update(c.__opt_properties__(explicit_defaults))