The Gaudi Framework  v41r0 (bdbe7744)
Loading...
Searching...
No Matches
GaudiHandles.py
Go to the documentation of this file.
11
12import logging
13from collections.abc import MutableSequence
14
15# explicit list for wildcard imports
16__all__ = [
17 "GaudiHandle",
18 "PublicToolHandle",
19 "PrivateToolHandle",
20 "ServiceHandle",
21 "GaudiHandleArray",
22 "ServiceHandleArray",
23 "PublicToolHandleArray",
24 "PrivateToolHandleArray",
25]
26__doc__ = """The python module holding python bindings to XyzHandles"""
27
28_log = logging.getLogger("GaudiHandles")
29
30
32 componentType = "Unspecified" # must be overridden by derived class
33 isPublic = True # can be overridden by derived class
34
35 __slots__ = ("typeAndName",)
36
37 def __init__(self, typeAndName):
38 object.__init__(self)
39 if hasattr(typeAndName, "toStringProperty"):
40 # this is a GaudiHandle or equivalent
41 typeAndName = typeAndName.toStringProperty()
42 if not isinstance(typeAndName, str):
43 raise TypeError(
44 f"Argument to {self.__class__.__name__} must be a string. Got a {type(typeAndName).__name__} instead"
45 )
46 self.typeAndName = typeAndName
47
48 def __repr__(self):
49 return f"{self.__class__.__name__}({self.toStringProperty()!r})"
50
51 def __str__(self):
52 # FIXME: (Patch #1668) this creates problem with 2.5
53 # return "%s:%s" % (self.__class__.__name__, self.toStringProperty())
54 return self.toStringProperty()
55
56 def __opt_value__(self):
57 return self.toStringProperty()
58
59 #
60 # Several member functions which are the same as Configurables
61 #
63 return self.typeAndName
64
65 def getType(self):
66 """Get the 'type' part of the \"type/name\" string."""
67 slash = self.typeAndName.find("/")
68 if slash != -1:
69 # includes name. Return part before /
70 return self.typeAndName[0:slash]
71 else:
72 # only type is given, return full string
73 return self.typeAndName
74
75 def getName(self):
76 """Get the 'name' part of the \"type/name\" string.
77 If only a type is given, this will be returned as the name.
78 If the \"type/name\" string is empty, it will return an emtpy string."""
79 slash = self.typeAndName.find("/")
80 if slash != -1:
81 # explicit name. Return part after the /
82 return self.typeAndName[slash + 1 :]
83 else:
84 # only type is given. return type as default name
85 return self.typeAndName
86
87 def getGaudiHandle(self):
88 return self
89
90 def getFullName(self):
91 return self.toStringProperty()
92
93 def __eq__(self, other):
94 return type(self) is type(other) and self.typeAndName == other.typeAndName
95
96
98 __slots__ = ()
99 componentType = "AlgTool"
100 isPublic = True
101
102 def __init__(self, toolTypeAndName=""):
103 GaudiHandle.__init__(self, toolTypeAndName)
104
105
107 __slots__ = ()
108 componentType = "AlgTool"
109 isPublic = False
110
111 def __init__(self, toolTypeAndName=""):
112 GaudiHandle.__init__(self, toolTypeAndName)
113
114
116 __slots__ = ()
117 componentType = "Service"
118 isPublic = True
119
120 def __init__(self, serviceName=""):
121 GaudiHandle.__init__(self, serviceName)
122
123
124#
125# The HandleArrays
126#
127
128
129class GaudiHandleArray(MutableSequence):
130 """A unique list of GaudiHandles. Only handles of one type are allowed,
131 as specified by self.__class__.handleType"""
132
133 __slots__ = ("_items",)
134 handleType = None # must be set by derived class to the handle type
135
136 def __init__(self, typesAndNames=None):
137 # Use of dictionary as storage for faster duplicate finding
138 self._items = {} # name : handle
139
140 # check the type
141 if typesAndNames is None:
142 typesAndNames = []
143 elif not isinstance(typesAndNames, list):
144 raise TypeError(
145 f"Argument to {self.__class__.__name__} must be a list. Got a {type(typesAndNames).__name__} instead"
146 )
147 # add entries to list
148 self.extend(typesAndNames)
149
150 def __len__(self):
151 return len(self._items)
152
153 def __eq__(self, other):
154 try:
155 return self._items == other._items
156 except AttributeError:
157 # other is e.g. plain list
158 return list(self._items.values()) == other
159
160 def __repr__(self):
161 """Return class name with list of type/name strings as argument"""
162 return (
163 self.__class__.__name__
164 + "(["
165 + ",".join(repr(h.toStringProperty()) for h in self._items.values())
166 + "])"
167 )
168
169 def __str__(self):
170 """Print in a form which can be parsed"""
171 return self.toStringProperty()
172
173 def __opt_value__(self):
174 return self.toStringProperty()
175
176 def _indexToKey(self, index):
177 """Convert list index to dictionary key"""
178 return list(self._items.keys())[index]
179
180 def __copy__(self):
181 inst = self.__class__.__new__(self.__class__)
182 inst._items = self._items.copy()
183 return inst
184
185 def __setitem__(self, index, value):
186 try:
187 self._items[self._indexToKey(index)] = value
188 except TypeError as e:
189 if isinstance(index, slice):
190 raise TypeError(
191 f"Setting elements of {self.__class__.__name__} via slice is not supported"
192 )
193 else:
194 raise e
195
196 def __getitem__(self, index):
197 if isinstance(index, str):
198 # search by instance name
199 try:
200 return self._items[index]
201 except KeyError:
202 raise IndexError(
203 f"{self.__class__.__name__} does not have a {self.handleType.componentType} with instance name {index}"
204 )
205 else:
206 if isinstance(index, slice):
207 return [self._items[k] for k in self._indexToKey(index)]
208 else:
209 return self._items[self._indexToKey(index)]
210
211 def __iter__(self):
212 yield from self._items.values()
213
214 def __delitem__(self, index):
215 if isinstance(index, slice):
216 for k in self._indexToKey(index):
217 self._items.pop(k)
218 else:
219 self._items.pop(self._indexToKey(index))
220
221 def __contains__(self, value):
222 if isinstance(value, str):
223 return value in self._items
224 else:
225 return value.getName() in self._items
226
227 def __add__(self, other):
228 return self.__class__(list(self) + list(other))
229
230 def index(self, value, *args):
231 return list(self._items.keys()).index(
232 value if isinstance(value, str) else value.getName(), *args
233 )
234
235 def insert(self, index, value):
236 """Only allow inserting compatible types. It accepts a string, a handle or a configurable."""
237 if isinstance(value, str):
238 # convert string to handle
239 value = self.__class__.handleType(value)
240 elif isinstance(value, self.__class__.handleType):
241 pass # leave handle as-is
242 elif isinstance(value, GaudiHandle):
243 # do not allow different type of handles
244 raise TypeError(
245 f"Can not add a {value.__class__.__name__} to a {self.__class__.__name__}"
246 )
247 elif value.getGaudiType() != self.__class__.handleType.componentType:
248 # assume it is a configurable: allow only correct types
249 raise TypeError(
250 f"Can not append {value.__class__.__name__} ({value.getGaudiType()}) to a {self.__class__.__name__}"
251 )
252 elif hasattr(value, "isPublic"):
253 # check public vs private if applicable for this configurable
254 pop = value.isPublic() and "Public" or "Private"
255 if value.isPublic() != self.__class__.handleType.isPublic:
256 raise TypeError(
257 f"Can not append {value.__class__.__name__} ({pop} {value.getGaudiType()}) to a {self.__class__.__name__}"
258 )
259
260 # check that an instance name appears only once in the list
261 if value.getName() not in self._items:
262 if index == len(self._items): # append
263 self._items[value.getName()] = value
264 else:
265 # for `insert` we need to rebuild the dictionary in the correct order
266 values = list(self._items.items())
267 values.insert(index, (value.getName(), value))
268 self._items = dict(values)
269 else:
270 oldValue = self._items[value.getName()]
271 _log.warning(
272 "%r with instance name %r already in %s. Not adding %r.",
273 oldValue,
274 oldValue.getName(),
275 self.__class__.__name__,
276 value,
277 )
278
279 def isPublic(self):
280 return self.__class__.handleType.isPublic
281
282 #
283 # Member functions which are the same as Configurables
284 #
286 return (
287 "["
288 + ",".join(repr(v.toStringProperty()) for v in self._items.values())
289 + "]"
290 )
291
292
294 __slots__ = ()
295 handleType = ServiceHandle
296
297 def __init__(self, serviceTypesAndNames=None):
298 GaudiHandleArray.__init__(self, serviceTypesAndNames)
299
300
302 __slots__ = ()
303 handleType = PublicToolHandle
304
305 def __init__(self, toolTypesAndNames=None):
306 GaudiHandleArray.__init__(self, toolTypesAndNames)
307
308
310 __slots__ = ()
311 handleType = PrivateToolHandle
312
313 def __init__(self, toolTypesAndNames=None):
314 GaudiHandleArray.__init__(self, toolTypesAndNames)
__init__(self, serviceTypesAndNames=None)