122def conf2toConfigurable( comp, indent="", parent="", servicesOfThisCA=[], suppressDupes=False, propType="" ):
123 """
124 Method converts from Conf2 ( comp argument ) to old Configurable
125 If the Configurable of the same name exists, the properties merging process is invoked
126 """
127 _log = logging.getLogger( "conf2toConfigurable" )
128
129 if _isOldConfigurable(comp):
130 _log.debug( "%sComponent is already OLD Configurable object %s, no conversion",
131 indent, comp.getName() )
132 return comp
133
134 if isinstance(comp, str):
135 if comp and 'ServiceHandle' not in propType:
136 _log.warning( "%sComponent '%s' in '%s' is of type string, no conversion, "
137 "some properties possibly not set?", indent, comp, parent)
138 return comp
139
140 if comp.getType() == 'AthSequencer':
141 _log.debug( "%sComponent is a sequence %s, attempt to merge",
142 indent, comp.getName())
143 oldsequence = _fetchOldSeq(comp.getName())
144 _mergeSequences(oldsequence, comp, _log, indent)
145 return oldsequence
146
147 _log.debug( "%sConverting from GaudiConfig2 object %s type %s, parent %s",
148 indent, comp.getName(), comp.__class__.__name__ , parent)
149
150 def _alreadyConfigured( comp, parent ):
151 instanceName = comp.getName()
152 for conf in Configurable.allConfigurables.values():
153 conf_name = ''
154 try:
155 conf_name=conf.name()
156 except TypeError:
157
158 conf_name=conf
159
160 if conf_name==instanceName:
161 if conf.getParent() == parent:
162 _log.debug( "%s Matched component: '%s' with parent %s with same from allConfigurables match.",
163 indent, instanceName, parent if parent else "[not set]" )
164 return conf
165 else:
166 _log.debug( "%sComponent: '%s' had parent %s whilst this allConfigurables match had parent %s.",
167 indent, instanceName, parent if parent else "[not set]", conf.getParent() )
168 return None
169
170 def _createConf2Object( name ):
171 typename, instanceName = name.split("/") if "/" in name else (name,name)
172 return CompFactory.getComp( typename.replace("__", "::") )( instanceName )
173
174 def _configurableToConf2( comp, indent="" ):
175 _log.debug(
"%sConverting Conf2 to Configurable class %s, type %s", indent, comp.getFullName(),
type(comp) )
176 conf2Object = _createConf2Object( comp.getFullName() )
177 _getProperties( comp, conf2Object, _indent( indent ) )
178 return conf2Object
179
180 def _getProperties( src, dest, indent="" ):
181 """Read properties on src and set them on dest (GaudiConfig2) configurable"""
182 for prop, value in src.getProperties().items():
183 _log.debug( "%sDealing with class %s property %s value type %s",
184 indent, src.getFullJobOptName(), prop,
type(value) )
185 if "ServiceHandle" in str(
type( value ) ):
186 instance = _alreadyConfigured(value, src.getName())
187 if instance:
188 setattr( dest, prop, _configurableToConf2(instance, _indent(indent)) )
189 else:
190 if isinstance(value, _semanticsHelpers):
191 value=value.data
192 setattr( dest, prop, value )
193
194 def _findConfigurableClass( name ):
195 """Find old-style Configurable class for name"""
196 if "::" in name:
197 name = name.replace("::","__")
198
199 if "<" in name:
200 name=name.replace("<","_")
201 name=name.replace(">","_")
202 name=name.replace(", ","_")
203
204 classObj = getattr( CfgMgr, name )
205
206 if not classObj:
207 raise ConfigurationError(f"CAtoGlobalWrapper could not find the component of type {name}")
208
209 return classObj
210
211 def _areSettingsSame( conf1, conf2, indent="",servicesOfThisCA=[] ):
212 """Are the properties the same between old-style conf1 and new-style conf2 instance?"""
213 from AthenaCommon.AppMgr import ToolSvc
214
215 _log.debug( "%sChecking if settings are the same %s (%s) old(new)",
216 indent, conf1.getFullName(), conf2.getFullJobOptName() )
217
218 if conf1.getType() != conf2.__cpp_type__:
219 raise ConfigurationError("Old/new ({} | {}) cpp types are not the same for ({} | {}) !".format(
220 conf1.getType(), conf2.__cpp_type__,
221 conf1.getFullName(), conf2.getFullJobOptName() ) )
222
223 alreadySetProperties = conf1.getValuedProperties().copy()
224
225 _log.debug( "%sExisting properties: %s", indent, alreadySetProperties )
226 _log.debug( "%sNew properties: %s", indent, conf2._properties )
227
228 for pname, pvalue in conf2._properties.items():
229
230 if _isOldConfigurable( pvalue ):
231 _log.warning( "%sNew configuration object %s property %s has legacy configuration "
232 "components assigned to it %s. Skipping comparison, no guarantees "
233 "about configuration consistency.",
234 indent, conf2.getName(), pname, pvalue.getName() )
235 continue
236
237 propType = conf2._descriptors[pname].cpp_type
238 _log.debug( "%sComparing type: %s for: %s in: %s", indent, propType, pname, conf1.getFullJobOptName() )
239
240 if "PrivateToolHandleArray" in propType:
241 toolDict = {_.getName(): _ for _ in alreadySetProperties[pname]}
242 _log.debug('Private tool properties? %s', toolDict)
243 newCdict = {_.getName() : _ for _ in pvalue}
244 oldCset =
set(toolDict); newCset =
set(newCdict)
245 _log.debug('Private tool property names? %s %s', oldCset, newCset)
246 if ( not (oldCset == newCset) ):
247
248
249 _log.debug('%s PrivateToolHandleArray %s of %s does not have the same named components',indent, pname, conf1.getFullJobOptName() )
250 _log.debug('%s Old (conf1) %s for %s',indent, sorted(oldCset), conf1.getFullJobOptName())
251 _log.debug('%s New (conf2) %s for %s',indent, sorted(newCset), conf2.getFullJobOptName())
252 _log.debug('%s Will try to merge them, but this might go wrong!',indent)
253 for oldC in oldCset & newCset:
254 _areSettingsSame( toolDict[oldC], newCdict[oldC], _indent(indent),servicesOfThisCA)
255
256
257 for newC in sorted(newCset-oldCset):
258 className = newCdict[newC].getFullJobOptName().
split(
"/" )[0]
259 _log.debug('%s%s not in old config. Will try to create conf1 instance using '
260 'this className: %s, and merge.',indent, newC, className)
261 configurableClass = _findConfigurableClass( className )
262
263
264 tmpName = newC + className + str(len(indent))
265 instance = configurableClass( tmpName )
266
267
268 instance._name = newCdict[newC].name
269 if hasattr(instance, '_jobOptName'):
270 instance._jobOptName = instance._name
271
272 instance.allConfigurables[instance._name] = instance.allConfigurables.pop(tmpName)
273 instance.configurables[instance._name] = instance.configurables.pop(tmpName)
274
275 _setProperties( instance, newCdict[newC], _indent( indent ) )
276 _log.debug('%s will now add %s to array.',indent, instance)
277 conf1 += instance
278 del instance
279 alreadySetProperties[pname].append(conf1.getChildren()[-1])
280
281 elif "PublicToolHandleArray" in propType:
282 toolSet = {_.getName() for _ in alreadySetProperties[pname]}
283 _log.debug('Public tool handle array properties? %s %s', toolSet, pvalue)
284
285 for newC in pvalue:
286 if isinstance(newC, str):
287 pubtoolclass, pubtoolname = newC.split('/')
288 if pubtoolname not in toolSet:
289 klass = _findConfigurableClass( pubtoolclass )
290 instance = klass(pubtoolname)
291 ToolSvc += instance
292 alreadySetProperties[pname].append(instance)
293 else:
294 _log.warning('Not handling actual Configurable2s for public tool merging yet')
295 raise Exception()
296
297 elif ("PrivateToolHandle" in propType or
298 "GaudiConfig2.Configurables" in propType or
299 "ServiceHandle" in propType):
300 existingVal = getattr(conf1, pname)
301 if isinstance( pvalue, str ):
302
303 if "ServiceHandle" in propType and pvalue in servicesOfThisCA:
304 _log.debug("%sThe service %s is part of the CA. Consistency checks will be performed when the service is merged")
305 else:
306
307 _log.debug("%sThe %s '%s' of GaudiConfig2 component %s.%s is a string, "
308 "skipping deeper checks",
309 indent, propType, pvalue, conf2.name, pname)
310 elif pvalue is None:
311 _log.debug("%sThe property value for %s of %s is None. Skipping.", indent, pname, conf2.name )
312 continue
313 elif str(existingVal) == "":
314 className = pvalue.getFullJobOptName().
split(
"/" )[0]
315 pvalueCompName = pvalue.getFullJobOptName().
split(
"/" )[1]
316 _log.debug("%sThe existing value for %s of %s is an empty handle. "
317 "Will try to create conf1 instance using this className: %s, and merge.",
318 indent, pname, conf2.name, className )
319 configurableClass = _findConfigurableClass( className )
320
321
322 tmpName = pvalueCompName + className + str(len(indent))
323 instance = configurableClass( tmpName )
324
325 instance._name = pvalueCompName
326 if hasattr(instance, '_jobOptName'):
327 instance._jobOptName = instance._name
328
329 instance.allConfigurables[instance._name] = instance.allConfigurables.pop(tmpName)
330 instance.configurables[instance._name] = instance.configurables.pop(tmpName)
331
332 setattr(conf1, pname, instance)
333 existingVal = getattr(conf1, pname)
334 _areSettingsSame( existingVal, pvalue, indent,servicesOfThisCA)
335 else:
336 _log.debug( "%sSome kind of handle and, object type %s existing %s",
337 indent,
type(pvalue),
type(existingVal) )
338 _areSettingsSame( existingVal, pvalue, indent,servicesOfThisCA)
339 else:
340 if isinstance(pvalue, _semanticsHelpers):
341 pvalue = _conf2HelperToBuiltin(pvalue)
342
343 if pname not in alreadySetProperties:
344 _log.debug( "%sAdding property: %s for %s", indent, pname, conf2.getName() )
345 try:
346 setattr(conf1, pname, pvalue)
347 except AttributeError:
348 _log.info(
"%sCould not set attribute. Type of conf1 %s.",indent,
type(conf1) )
349 raise
350
351 elif alreadySetProperties[pname] != pvalue:
352
353
354 merge = True
355
356
357
358 if (isinstance(pvalue, str) and isinstance(alreadySetProperties[pname], str)):
359 if ('/' in pvalue \
360 and pvalue.split('/')[-1] == alreadySetProperties[pname]):
361
362 merge = False
363 _log.warning( "%sProperties here are strings and not exactly the same. "
364 "ASSUMING they match types but we cannot check. %s for %s",
365 indent, pname, conf2.getName() )
366 try:
367 if ('+' in alreadySetProperties[pname].toStringProperty() and
368 alreadySetProperties[pname].toStringProperty().
split(
'+')[-1] == pvalue):
369
370 merge = False
371 except AttributeError :
372
373 pass
374
375
376 if merge:
377 _log.debug( "%sMerging property: %s for new config: %s", indent, pname, conf2.getName() )
378
379 clone = conf2.getInstance("Clone")
380 setattr(clone, pname, alreadySetProperties[pname])
381 try:
382 updatedPropValue = _conf2HelperToBuiltin( conf2._descriptors[pname].semantics.merge(
383 getattr(conf2, pname), getattr(clone, pname)) )
384 except (TypeError, ValueError):
385 err_message = f"Failed merging new config value ({getattr(conf2, pname)}) and old config value ({getattr(clone, pname)}) for the ({pname}) property of {conf1.getFullJobOptName() } ({conf2.getFullJobOptName()}) old (new)."
386 _log.fatal( err_message )
387 raise ConfigurationError(err_message)
388
389 _log.debug("existingConfigurable.name: %s, pname: %s, updatedPropValue: %s",
390 conf1.name(), pname, updatedPropValue )
391
392 setattr(conf1, pname, updatedPropValue)
393 del clone
394 _log.debug("%sInvoked GaudiConf2 semantics to merge the %s and the %s to %s "
395 "for property %s of %s",
396 indent, alreadySetProperties[pname], pvalue, pname,
397 updatedPropValue, existingConfigurable.getFullName())
398
399 _log.debug( "%sConf2 Full name: %s ", indent, comp.getFullJobOptName() )
400 existingConfigurable = _alreadyConfigured( comp, parent )
401
402 if existingConfigurable:
403 _log.debug( "%sPre-existing configurable %s was found, checking if has the same properties",
404 indent, existingConfigurable.getFullJobOptName() )
405 _areSettingsSame( existingConfigurable, comp, indent, servicesOfThisCA )
406 _log.debug( "%sPre-existing configurable %s was found to have the same properties",
407 indent, comp.name )
408 instance = existingConfigurable if not suppressDupes else None
409
410 else:
411 _log.debug( "%sExisting Conf1 not found. Creating component configurable %s",
412 indent, comp.getFullJobOptName() )
413 configurableClass = _findConfigurableClass( comp.getFullJobOptName().
split(
"/" )[0] )
414 instance = configurableClass( comp.name )
415 _setProperties( instance, comp, _indent( indent ) )
416
417 return instance
418
419
std::vector< std::string > split(const std::string &s, const std::string &t=":")