ATLAS Offline Software
Loading...
Searching...
No Matches
python.ConfigBlock.ConfigBlock Class Reference
Inheritance diagram for python.ConfigBlock.ConfigBlock:
Collaboration diagram for python.ConfigBlock.ConfigBlock:

Public Member Functions

 __init__ (self)
 setBlockName (self, name)
 getBlockName (self)
 factoryName (self)
 setFactoryName (self, name)
 instanceName (self)
 isUsedForConfig (self, config)
 applyConfigOverrides (self, config)
 addDependency (self, dependencyName, required=True)
 hasDependencies (self)
 getDependencies (self)
 addOption (self, name, defaultValue, *, type, info='', noneAction='ignore', required=False, expertMode=None)
 setOptionValue (self, name, value)
 getOptionValue (self, name)
 getOptions (self)
 printOptions (self, verbose=False, width=60, indent=" ")
 hasOption (self, name)
 __eq__ (self, blockName)
 __str__ (self)
 get_instance_count (cls)
 checkExpertSettings (self, config)
 __new__ (cls, name, bases, dct)

Public Attributes

 onlyForDSIDs

Static Public Attributes

dict instance_counts = {}

Protected Member Functions

 _is_expert_value (self, rule, value)

Protected Attributes

str _blockName = ''
 _factoryName = None
list _dependencies = []
dict _options = {}
dict _expertModeSettings = {}

Detailed Description

the base class for classes implementing individual blocks of
configuration

A configuration block is a sequence of one or more algorithms that
should always be scheduled together, e.g. the muon four momentum
corrections could be a single block, muon selection could then be
another block.  The blocks themselves generally have their own
configuration options/properties specific to the block, and will
perform a dynamic configuration based on those options as well as
the overall job.

The actual configuration of the algorithms in the block will
depend on what other blocks are scheduled before and afterwards,
most importantly some algorithms will introduce shallow copies
that subsequent algorithms will need to run on, and some
algorithms will add selection decorations that subquent algorithms
should use as preselections.

The algorithms get created in a multi-step process (that may be
extended in the future): As a first step each block retrieves
references to the containers it uses (essentially marking its spot
in the processing chain) and also registering any shallow copies
that will be made.  In the second/last step each block then
creates the fully configured algorithms.

One goal is that when the algorithms get created they will have
their final configuration and there needs to be no
meta-configuration data attached to the algorithms, essentially an
inversion of the approach in AnaAlgSequence in which the
algorithms got created first with associated meta-configuration
and then get modified in susequent configuration steps.

For now this is mostly an empty base class, but another goal of
this approach is to make it easier to build another configuration
layer on top of this one, and this class will likely be extended
and get data members at that point.

The child class needs to implement the method `makeAlgs` which is
given a single `ConfigAccumulator` type argument. This is meant to
create the sequence of algorithms that this block configures. This
is currently (28 Jul 2025) called twice and should do the same thing
during both calls, but the plan is to change that to a single call.

The child class should also implement the method `getInstanceName`
which should return a string that is used to distinguish between
multiple instances of the same block. This is used to append the
instance name to the names of all algorithms created by this block,
and may in the future also be used to distinguish between multiple
instances of the block.

Definition at line 92 of file ConfigBlock.py.

Constructor & Destructor Documentation

◆ __init__()

python.ConfigBlock.ConfigBlock.__init__ ( self)

Definition at line 147 of file ConfigBlock.py.

147 def __init__ (self) :
148 self._blockName = ''
149 self._factoryName = None
150 self._dependencies = []
151 self._options = {} # used with block configuration to set arbitrary option
152 self._expertModeSettings = {} # dictionary to track expert mode requirements for each option
153 self.addOption('groupName', '', type=str,
154 info=('Used to specify this block when setting an'
155 ' option at an arbitrary location.'))
156 self.addOption('skipOnData', False, type=bool,
157 info=('User option to prevent the block from running'
158 ' on data. This only affects blocks that are'
159 ' intended to run on data.'))
160 self.addOption('skipOnMC', False, type=bool,
161 info=('User option to prevent the block from running'
162 ' on MC. This only affects blocks that are'
163 ' intended to run on MC.'))
164 self.addOption('skipWithSystematics', False, type=bool,
165 info=('User option to prevent the block from running with systematics.'))
166 self.addOption('onlyForDSIDs', [], type=list,
167 info=('Used to specify which MC DSIDs to allow this'
168 ' block to run on. Each element of the list'
169 ' can be a full DSID (e.g. 410470), or a regex'
170 ' (e.g. 410.* to select all 410xxx DSIDs, or'
171 ' ^(?!410) to veto them). An empty list means no'
172 ' DSID restriction.'))
173 self.addOption('propertyOverrides', {}, type=dict,
174 info=('EXPERT USE ONLY: A dictionary of properties to'
175 ' override at the end of configuration. This should'
176 ' take the form'
177 ' {"algName.toolName.propertyName": value, ...},'
178 ' without any automatically applied postfixes for'
179 ' the algorithm name. THIS IS MEANT TO BE EXPERT'
180 ' USAGE ONLY. Properties that need to be set by'
181 ' the user should be declared as options on the'
182 ' block itself. EXPERT USE ONLY!'),
183 expertMode=True)
184 # Increment the instance count for the current class
185 cls = type(self) # Get the actual class of the instance (also derived!)
186 if cls not in ConfigBlock.instance_counts:
187 ConfigBlock.instance_counts[cls] = 0
188 # Note: we do need to check in the call stack that we are
189 # in a real makeConfig situation, and not e.g. printAlgs
190 stack = inspect.stack()
191 for frame_info in stack:
192 # Get the class name (if any) from the frame
193 parent_cls = frame_info.frame.f_locals.get('self', None)
194 if parent_cls is None or not isinstance(parent_cls, ConfigBlock):
195 # If the frame does not belong to an instance of ConfigBlock, it's an external caller
196 if frame_info.function == "makeConfig":
197 ConfigBlock.instance_counts[cls] += 1
198 break
199
200

Member Function Documentation

◆ __eq__()

python.ConfigBlock.ConfigBlock.__eq__ ( self,
blockName )
Implementation of == operator. Used for seaching configSeque.
E.g. if blockName in configSeq:

Definition at line 423 of file ConfigBlock.py.

423 def __eq__(self, blockName):
424 """
425 Implementation of == operator. Used for seaching configSeque.
426 E.g. if blockName in configSeq:
427 """
428 return self._blockName == blockName
429
430

◆ __new__()

python.ConfigBlock.BlockNameProcessorMeta.__new__ ( cls,
name,
bases,
dct )
inherited

Definition at line 53 of file ConfigBlock.py.

53 def __new__(cls, name, bases, dct):
54 # Automatically apply alphanumeric-only decorator to 'instanceName()' method
55 if 'instanceName' in dct and callable(dct['instanceName']):
56 dct['instanceName'] = alphanumeric_block_name(dct['instanceName'])
57 return super().__new__(cls, name, bases, dct)
58

◆ __str__()

python.ConfigBlock.ConfigBlock.__str__ ( self)

Definition at line 431 of file ConfigBlock.py.

431 def __str__(self):
432 return self._blockName if self._blockName else self.factoryName()
433
434

◆ _is_expert_value()

python.ConfigBlock.ConfigBlock._is_expert_value ( self,
rule,
value )
protected
Check whether value matches an expert mode rule.
Rule can be:
- A literal (compared with ==)
- A callable predicate (called with value)
- A special marker string (common callable)

Definition at line 440 of file ConfigBlock.py.

440 def _is_expert_value(self, rule, value):
441 """
442 Check whether value matches an expert mode rule.
443 Rule can be:
444 - A literal (compared with ==)
445 - A callable predicate (called with value)
446 - A special marker string (common callable)
447 """
448 if callable(rule):
449 return rule(value)
450
451 if isinstance(rule, str):
452 if rule == "nonemptystring":
453 return isinstance(value, str) and value != ""
454 if rule == "nonemptylist":
455 return isinstance(value, list) and value != []
456 if rule == "positiveint":
457 return isinstance(value, int) and value > 0
458
459 # Fallback: direct value comparison
460 return value == rule
461

◆ addDependency()

python.ConfigBlock.ConfigBlock.addDependency ( self,
dependencyName,
required = True )
Add a dependency for the block. Dependency is corresponds to the
blockName of another block. If required is True, will throw an
error if dependency is not present; otherwise will move this
block after the required block. If required is False, will do
nothing if required block is not present; otherwise, it will
move block after required block.

Definition at line 295 of file ConfigBlock.py.

295 def addDependency(self, dependencyName, required=True):
296 """
297 Add a dependency for the block. Dependency is corresponds to the
298 blockName of another block. If required is True, will throw an
299 error if dependency is not present; otherwise will move this
300 block after the required block. If required is False, will do
301 nothing if required block is not present; otherwise, it will
302 move block after required block.
303 """
304 if not self.hasDependencies():
305 # add option to block ignore dependencies
306 self.addOption('ignoreDependencies', [], type=list,
307 info='List of dependencies defined in the ConfigBlock to ignore.')
308 self._dependencies.append(ConfigBlockDependency(dependencyName, required))
309

◆ addOption()

python.ConfigBlock.ConfigBlock.addOption ( self,
name,
defaultValue,
* ,
type,
info = '',
noneAction = 'ignore',
required = False,
expertMode = None )
declare the given option on the configuration block

This should only be called in the constructor of the
configuration block.

NOTE: The backend to option handling is slated to be replaced
at some point.  This particular function should essentially
stay the same, but some behavior may change.

Definition at line 318 of file ConfigBlock.py.

319 type, info='', noneAction='ignore', required=False, expertMode=None) :
320 """declare the given option on the configuration block
321
322 This should only be called in the constructor of the
323 configuration block.
324
325 NOTE: The backend to option handling is slated to be replaced
326 at some point. This particular function should essentially
327 stay the same, but some behavior may change.
328 """
329 if name in self._options :
330 raise KeyError (f'duplicate option: {name}')
331 if type not in [str, bool, int, float, list, dict, None] :
332 raise TypeError (f'unknown option type: {type}')
333 noneActions = ['error', 'set', 'ignore']
334 if noneAction not in noneActions :
335 raise ValueError (f'invalid noneAction: {noneAction} [allowed values: {noneActions}]')
336
337 # Store expert mode settings if provided
338 if expertMode is not None:
339 if expertMode is True:
340 # in this case we will just check against the default value
341 self._expertModeSettings[name] = True
342 elif not isinstance(expertMode, list):
343 raise TypeError (f'expertMode must be a list, got {type(expertMode)}')
344 else:
345 # here we will check against a list of custom values
346 self._expertModeSettings[name] = expertMode
347
348 setattr (self, name, defaultValue)
349 self._options[name] = ConfigBlockOption(type=type, info=info,
350 noneAction=noneAction, required=required, default=defaultValue)
351
352

◆ applyConfigOverrides()

python.ConfigBlock.ConfigBlock.applyConfigOverrides ( self,
config )
Apply any configuration overrides specified in the block's
`propertyOverrides` option. This is meant to be called at the
end of the configuration process, after all algorithms have been
created and configured.

Definition at line 270 of file ConfigBlock.py.

270 def applyConfigOverrides(self, config):
271 """
272 Apply any configuration overrides specified in the block's
273 `propertyOverrides` option. This is meant to be called at the
274 end of the configuration process, after all algorithms have been
275 created and configured.
276 """
277 for key, value in self.propertyOverrides.items():
278 # Split the key into algorithm name, tool name, and property name
279 parts = key.split('.')
280 if len(parts) < 2:
281 raise Exception(f"Invalid override key format: {key}")
282 alg = config.getAlgorithm(parts[0])
283 if alg is None:
284 raise Exception(f"Algorithm {parts[0]} not found in config for override: {key}")
285 for name in parts[1:-1]:
286 # Navigate through tools if necessary
287 if hasattr(alg, name):
288 alg = getattr(alg, name)
289 else:
290 raise Exception(f"Tool {name} not found for override: {key}")
291 # Set the property on the algorithm/tool. This is probably a
292 # horrible hack, but `setattr` didn't work for me.
293 alg.__setattr__(parts[-1], value)
294

◆ checkExpertSettings()

python.ConfigBlock.ConfigBlock.checkExpertSettings ( self,
config )
Check if any settings require expert mode and validate accordingly.
If any setting is set to a value that requires expert mode but we're
not in expert mode, raise an error.

Definition at line 462 of file ConfigBlock.py.

462 def checkExpertSettings(self, config):
463 """
464 Check if any settings require expert mode and validate accordingly.
465 If any setting is set to a value that requires expert mode but we're
466 not in expert mode, raise an error.
467 """
468 for option_name, expert_rule in self._expertModeSettings.items():
469 current_value = self.getOptionValue(option_name)
470 default_value = self._options[option_name].default
471
472 if expert_rule is True:
473 # Any deviation from the default requires expert mode
474 if current_value != default_value:
475 warnings.warn(
476 f"Block '{self.factoryName()}' option '{option_name}' "
477 f"set to '{current_value}' (default '{default_value}'), "
478 f"requires expert mode.",
479 ExpertModeWarning, stacklevel=2
480 )
481
482 else: # it's a list of expert values/markers/predicates
483 for ev in expert_rule:
484 if self._is_expert_value(ev, current_value):
485 warnings.warn(
486 f"Block '{self.factoryName()}' option '{option_name}' "
487 f"set to expert-only value '{current_value}'. "
488 f"Requires expert mode.",
489 ExpertModeWarning, stacklevel=2
490 )
491 # All checks passed
492 return

◆ factoryName()

python.ConfigBlock.ConfigBlock.factoryName ( self)
get the factory name for this block

This is mostly to give a reliable means of identifying the type
of block we have in error messages. This is meant to be
automatically set by the factory based on the requested block
name, but there are a number of fallbacks. It is best not to
assume a specific format, this is mostly meant to be used as an
identifier in output messages.

Definition at line 209 of file ConfigBlock.py.

209 def factoryName(self):
210 """get the factory name for this block
211
212 This is mostly to give a reliable means of identifying the type
213 of block we have in error messages. This is meant to be
214 automatically set by the factory based on the requested block
215 name, but there are a number of fallbacks. It is best not to
216 assume a specific format, this is mostly meant to be used as an
217 identifier in output messages.
218 """
219 if self._factoryName is not None and self._factoryName != '':
220 return self._factoryName
221 # If no factory name is set and the block has a name, use that
222 if self._blockName is not None and self._blockName != '':
223 return self._blockName
224 # Use the class name as a fallback
225 return self.__class__.__name__
226

◆ get_instance_count()

python.ConfigBlock.ConfigBlock.get_instance_count ( cls)

Definition at line 436 of file ConfigBlock.py.

436 def get_instance_count(cls):
437 # Access the current count for this class
438 return ConfigBlock.instance_counts.get(cls, 0)
439

◆ getBlockName()

python.ConfigBlock.ConfigBlock.getBlockName ( self)
Get blockName

Definition at line 205 of file ConfigBlock.py.

205 def getBlockName(self):
206 """Get blockName"""
207 return self._blockName
208

◆ getDependencies()

python.ConfigBlock.ConfigBlock.getDependencies ( self)
Return the list of dependencies. 

Definition at line 314 of file ConfigBlock.py.

314 def getDependencies(self):
315 """Return the list of dependencies. """
316 return self._dependencies
317

◆ getOptions()

python.ConfigBlock.ConfigBlock.getOptions ( self)
Return a copy of the options associated with the block

Definition at line 386 of file ConfigBlock.py.

386 def getOptions(self):
387 """Return a copy of the options associated with the block"""
388 return self._options.copy()
389
390

◆ getOptionValue()

python.ConfigBlock.ConfigBlock.getOptionValue ( self,
name )
Returns config option value, if present; otherwise return None

Definition at line 380 of file ConfigBlock.py.

380 def getOptionValue(self, name):
381 """Returns config option value, if present; otherwise return None"""
382 if name in self._options:
383 return getattr(self, name)
384
385

◆ hasDependencies()

python.ConfigBlock.ConfigBlock.hasDependencies ( self)
Return True if there is a dependency.

Definition at line 310 of file ConfigBlock.py.

310 def hasDependencies(self):
311 """Return True if there is a dependency."""
312 return bool(self._dependencies)
313

◆ hasOption()

python.ConfigBlock.ConfigBlock.hasOption ( self,
name )
whether the configuration block has the given option

WARNING: The backend to option handling is slated to be
replaced at some point.  This particular function may change
behavior, interface or be removed/replaced entirely.

Definition at line 413 of file ConfigBlock.py.

413 def hasOption (self, name) :
414 """whether the configuration block has the given option
415
416 WARNING: The backend to option handling is slated to be
417 replaced at some point. This particular function may change
418 behavior, interface or be removed/replaced entirely.
419 """
420 return name in self._options
421
422

◆ instanceName()

python.ConfigBlock.ConfigBlock.instanceName ( self)
Get the name of the instance

The name of the instance is used to distinguish between multiple
instances of the same block. Most importantly, this will be
appended to the names of all algorithms created by this block.
This defaults to an empty string, but block implementations
should override it with an appropriate name based on identifying
options set on this instance. A typical example would be the
name of the (main) container, plus potentially the selection or
working point.

Ideally all blocks should override this method, but for backward
compatibility (28 Jul 25) it defaults to an empty string.

Definition at line 236 of file ConfigBlock.py.

236 def instanceName(self):
237 """Get the name of the instance
238
239 The name of the instance is used to distinguish between multiple
240 instances of the same block. Most importantly, this will be
241 appended to the names of all algorithms created by this block.
242 This defaults to an empty string, but block implementations
243 should override it with an appropriate name based on identifying
244 options set on this instance. A typical example would be the
245 name of the (main) container, plus potentially the selection or
246 working point.
247
248 Ideally all blocks should override this method, but for backward
249 compatibility (28 Jul 25) it defaults to an empty string.
250 """
251 return ''
252

◆ isUsedForConfig()

python.ConfigBlock.ConfigBlock.isUsedForConfig ( self,
config )
whether this block should be used for the given configuration

This is used by `ConfigSequence` to determine whether this block
should be included in the configuration.

Definition at line 253 of file ConfigBlock.py.

253 def isUsedForConfig(self, config):
254 """
255 whether this block should be used for the given configuration
256
257 This is used by `ConfigSequence` to determine whether this block
258 should be included in the configuration.
259 """
260 if self.skipWithSystematics and not config.noSystematics():
261 return False
262 if self.skipOnData and config.dataType() is DataType.Data:
263 return False
264 if self.skipOnMC and config.dataType() is not DataType.Data:
265 return False
266 if not filter_dsids(self.onlyForDSIDs, config):
267 return False
268 return True
269

◆ printOptions()

python.ConfigBlock.ConfigBlock.printOptions ( self,
verbose = False,
width = 60,
indent = "    " )
Prints options and their values

Definition at line 391 of file ConfigBlock.py.

391 def printOptions(self, verbose=False, width=60, indent=" "):
392 """
393 Prints options and their values
394 """
395 def printWrap(text, width=60, indent=" "):
396 wrapper = textwrap.TextWrapper(width=width, initial_indent=indent,
397 subsequent_indent=indent)
398 for line in wrapper.wrap(text=text):
399 logCPAlgCfgBlock.info(line)
400
401 for opt, vals in self.getOptions().items():
402 if verbose:
403 logCPAlgCfgBlock.info(indent + f"\033[4m{opt}\033[0m: {self.getOptionValue(opt)}")
404 logCPAlgCfgBlock.info(indent*2 + f"\033[4mtype\033[0m: {vals.type}")
405 logCPAlgCfgBlock.info(indent*2 + f"\033[4mdefault\033[0m: {vals.default}")
406 logCPAlgCfgBlock.info(indent*2 + f"\033[4mrequired\033[0m: {vals.required}")
407 logCPAlgCfgBlock.info(indent*2 + f"\033[4mnoneAction\033[0m: {vals.noneAction}")
408 printWrap(f"\033[4minfo\033[0m: {vals.info}", indent=indent*2)
409 else:
410 logCPAlgCfgBlock.info(indent + f"{ opt}: {self.getOptionValue(opt)}")
411
412

◆ setBlockName()

python.ConfigBlock.ConfigBlock.setBlockName ( self,
name )
Set blockName

Definition at line 201 of file ConfigBlock.py.

201 def setBlockName(self, name):
202 """Set blockName"""
203 self._blockName = name
204

◆ setFactoryName()

python.ConfigBlock.ConfigBlock.setFactoryName ( self,
name )
set the factory name for this block

This is meant to be called automatically by the factory based on
the requested block name. If you are creating a block without a factory,
you can call this method to set the factory name manually.

Definition at line 227 of file ConfigBlock.py.

227 def setFactoryName(self, name):
228 """set the factory name for this block
229
230 This is meant to be called automatically by the factory based on
231 the requested block name. If you are creating a block without a factory,
232 you can call this method to set the factory name manually.
233 """
234 self._factoryName = name
235

◆ setOptionValue()

python.ConfigBlock.ConfigBlock.setOptionValue ( self,
name,
value )
set the given option on the configuration block

NOTE: The backend to option handling is slated to be replaced
at some point.  This particular function should essentially
stay the same, but some behavior may change.

Definition at line 353 of file ConfigBlock.py.

353 def setOptionValue (self, name, value) :
354 """set the given option on the configuration block
355
356 NOTE: The backend to option handling is slated to be replaced
357 at some point. This particular function should essentially
358 stay the same, but some behavior may change.
359 """
360
361 if name not in self._options :
362 raise KeyError (f'unknown option "{name}" in block "{self.__class__.__name__}"')
363 noneAction = self._options[name].noneAction
364 if value is not None or noneAction == 'set' :
365 # check type if specified
366 optType = self._options[name].type
367 # convert int to float to prevent crash
368 if optType is float and type(value) is int:
369 value = float(value)
370 if optType is not None and optType != type(value):
371 raise ValueError(f'{name} for block {self.__class__.__name__} should '
372 f'be of type {optType} not {type(value)}')
373 setattr (self, name, value)
374 elif noneAction == 'ignore' :
375 pass
376 elif noneAction == 'error' :
377 raise ValueError (f'passed None for setting option {name} with noneAction=error')
378
379

Member Data Documentation

◆ _blockName

str python.ConfigBlock.ConfigBlock._blockName = ''
protected

Definition at line 148 of file ConfigBlock.py.

◆ _dependencies

python.ConfigBlock.ConfigBlock._dependencies = []
protected

Definition at line 150 of file ConfigBlock.py.

◆ _expertModeSettings

dict python.ConfigBlock.ConfigBlock._expertModeSettings = {}
protected

Definition at line 152 of file ConfigBlock.py.

◆ _factoryName

python.ConfigBlock.ConfigBlock._factoryName = None
protected

Definition at line 149 of file ConfigBlock.py.

◆ _options

dict python.ConfigBlock.ConfigBlock._options = {}
protected

Definition at line 151 of file ConfigBlock.py.

◆ instance_counts

dict python.ConfigBlock.ConfigBlock.instance_counts = {}
static

Definition at line 145 of file ConfigBlock.py.

◆ onlyForDSIDs

python.ConfigBlock.ConfigBlock.onlyForDSIDs

Definition at line 266 of file ConfigBlock.py.


The documentation for this class was generated from the following file: