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, *, typetype, info='', noneAction='ignore', required=False, expertMode=None, meta=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 subsequent 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 subsequent 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 93 of file ConfigBlock.py.

Constructor & Destructor Documentation

◆ __init__()

python.ConfigBlock.ConfigBlock.__init__ ( self)

Definition at line 148 of file ConfigBlock.py.

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

Member Function Documentation

◆ __eq__()

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

Definition at line 454 of file ConfigBlock.py.

454 def __eq__(self, blockName):
455 """
456 Implementation of == operator. Used for searching configSeq.
457 E.g. if blockName in configSeq:
458 """
459 return self._blockName == blockName
460
461

◆ __new__()

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

Definition at line 54 of file ConfigBlock.py.

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

◆ __str__()

python.ConfigBlock.ConfigBlock.__str__ ( self)

Definition at line 462 of file ConfigBlock.py.

462 def __str__(self):
463 return self._blockName if self._blockName else self.factoryName()
464
465

◆ _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 471 of file ConfigBlock.py.

471 def _is_expert_value(self, rule, value):
472 """
473 Check whether value matches an expert mode rule.
474 Rule can be:
475 - A literal (compared with ==)
476 - A callable predicate (called with value)
477 - A special marker string (common callable)
478 """
479 if callable(rule):
480 return rule(value)
481
482 if isinstance(rule, str):
483 if rule == "nonemptystring":
484 return isinstance(value, str) and value != ""
485 if rule == "nonemptylist":
486 return isinstance(value, list) and value != []
487 if rule == "positiveint":
488 return isinstance(value, int) and value > 0
489
490 # Fallback: direct value comparison
491 return value == rule
492

◆ 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 297 of file ConfigBlock.py.

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

◆ addOption()

python.ConfigBlock.ConfigBlock.addOption ( self,
name,
defaultValue,
* ,
type,
info = '',
noneAction = 'ignore',
required = False,
expertMode = None,
meta = 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 320 of file ConfigBlock.py.

322 expertMode=None, meta=None) :
323 """declare the given option on the configuration block
324
325 This should only be called in the constructor of the
326 configuration block.
327
328 NOTE: The backend to option handling is slated to be replaced
329 at some point. This particular function should essentially
330 stay the same, but some behavior may change.
331 """
332 if name in self._options :
333 raise KeyError (f'duplicate option: {name}')
334 if type not in [str, bool, int, float, list, dict, None] :
335 raise TypeError (f'unknown option type: {type}')
336 noneActions = ['error', 'set', 'ignore']
337 if noneAction not in noneActions :
338 raise ValueError (f'invalid noneAction: {noneAction} [allowed values: {noneActions}]')
339
340 # Store expert mode settings if provided
341 if expertMode is not None:
342 if expertMode is True:
343 # in this case we will just check against the default value
344 self._expertModeSettings[name] = True
345 elif not isinstance(expertMode, list):
346 raise TypeError (f'expertMode must be a list, got {expertMode.__class__}')
347 else:
348 # here we will check against a list of custom values
349 self._expertModeSettings[name] = expertMode
350
351 if meta is not None:
352 if not isinstance(meta, dict):
353 raise TypeError(f'meta must be a dictionary, got {meta.__class__}')
354
355 unknown = set(meta) - {'choices', 'role'}
356 if unknown:
357 raise ValueError(f'meta received unknown keys: {unknown}')
358
359 if 'choices' in meta:
360 choices = meta['choices']
361 if (not isinstance(choices, tuple)
362 or len(choices) != 2
363 or not isinstance(choices[0], list)
364 or not all(isinstance(choice, str) for choice in choices[0])
365 or (choices[1] is not None and not isinstance(choices[1], int))
366 ):
367 raise TypeError("meta['choices'] must be a (list[str], int | None) tuple")
368
369 if 'role' in meta:
370 role = meta['role']
371 if role not in {'container', 'containerRef', 'selection', 'region'}:
372 # container: defines a new container name
373 # containerRef: expects a container and possibly a selection, i.e. 'container' or 'container.selection'
374 # selection: defines a new selection name
375 # region: specifically for event selections
376 raise ValueError(f"meta['role'] must be one of 'container', 'containerRef', 'selection', 'region', got '{role}'")
377
378 setattr (self, name, defaultValue)
379 self._options[name] = ConfigBlockOption(type=type, info=info,
380 noneAction=noneAction, required=required,
381 default=defaultValue, meta=meta)
382
383
STL class.

◆ 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 273 of file ConfigBlock.py.

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

◆ 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 493 of file ConfigBlock.py.

493 def checkExpertSettings(self, config):
494 """
495 Check if any settings require expert mode and validate accordingly.
496 If any setting is set to a value that requires expert mode but we're
497 not in expert mode, raise an error.
498 """
499 for option_name, expert_rule in self._expertModeSettings.items():
500 current_value = self.getOptionValue(option_name)
501 default_value = self._options[option_name].default
502
503 if expert_rule is True:
504 # Any deviation from the default requires expert mode
505 if current_value != default_value:
506 warnings.warn(
507 f"Block '{self.factoryName()}' option '{option_name}' "
508 f"set to '{current_value}' (default '{default_value}'), "
509 f"requires expert mode.",
510 ExpertModeWarning, stacklevel=2
511 )
512
513 else: # it's a list of expert values/markers/predicates
514 for ev in expert_rule:
515 if self._is_expert_value(ev, current_value):
516 warnings.warn(
517 f"Block '{self.factoryName()}' option '{option_name}' "
518 f"set to expert-only value '{current_value}'. "
519 f"Requires expert mode.",
520 ExpertModeWarning, stacklevel=2
521 )
522 # All checks passed
523 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 212 of file ConfigBlock.py.

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

◆ get_instance_count()

python.ConfigBlock.ConfigBlock.get_instance_count ( cls)

Definition at line 467 of file ConfigBlock.py.

467 def get_instance_count(cls):
468 # Access the current count for this class
469 return ConfigBlock.instance_counts.get(cls, 0)
470

◆ getBlockName()

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

Definition at line 208 of file ConfigBlock.py.

208 def getBlockName(self):
209 """Get blockName"""
210 return self._blockName
211

◆ getDependencies()

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

Definition at line 316 of file ConfigBlock.py.

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

◆ getOptions()

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

Definition at line 417 of file ConfigBlock.py.

417 def getOptions(self):
418 """Return a copy of the options associated with the block"""
419 return self._options.copy()
420
421

◆ getOptionValue()

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

Definition at line 411 of file ConfigBlock.py.

411 def getOptionValue(self, name):
412 """Returns config option value, if present; otherwise return None"""
413 if name in self._options:
414 return getattr(self, name)
415
416

◆ hasDependencies()

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

Definition at line 312 of file ConfigBlock.py.

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

◆ 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 444 of file ConfigBlock.py.

444 def hasOption (self, name) :
445 """whether the configuration block has the given option
446
447 WARNING: The backend to option handling is slated to be
448 replaced at some point. This particular function may change
449 behavior, interface or be removed/replaced entirely.
450 """
451 return name in self._options
452
453

◆ 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 239 of file ConfigBlock.py.

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

◆ 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 256 of file ConfigBlock.py.

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

◆ printOptions()

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

Definition at line 422 of file ConfigBlock.py.

422 def printOptions(self, verbose=False, width=60, indent=" "):
423 """
424 Prints options and their values
425 """
426 def printWrap(text, width=60, indent=" "):
427 wrapper = textwrap.TextWrapper(width=width, initial_indent=indent,
428 subsequent_indent=indent)
429 for line in wrapper.wrap(text=text):
430 logCPAlgCfgBlock.info(line)
431
432 for opt, vals in self.getOptions().items():
433 if verbose:
434 logCPAlgCfgBlock.info(indent + f"\033[4m{opt}\033[0m: {self.getOptionValue(opt)}")
435 logCPAlgCfgBlock.info(indent*2 + f"\033[4mtype\033[0m: {vals.type}")
436 logCPAlgCfgBlock.info(indent*2 + f"\033[4mdefault\033[0m: {vals.default}")
437 logCPAlgCfgBlock.info(indent*2 + f"\033[4mrequired\033[0m: {vals.required}")
438 logCPAlgCfgBlock.info(indent*2 + f"\033[4mnoneAction\033[0m: {vals.noneAction}")
439 printWrap(f"\033[4minfo\033[0m: {vals.info}", indent=indent*2)
440 else:
441 logCPAlgCfgBlock.info(indent + f"{ opt}: {self.getOptionValue(opt)}")
442
443

◆ setBlockName()

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

Definition at line 204 of file ConfigBlock.py.

204 def setBlockName(self, name):
205 """Set blockName"""
206 self._blockName = name
207

◆ 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 230 of file ConfigBlock.py.

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

◆ 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 384 of file ConfigBlock.py.

384 def setOptionValue (self, name, value) :
385 """set the given option on the configuration block
386
387 NOTE: The backend to option handling is slated to be replaced
388 at some point. This particular function should essentially
389 stay the same, but some behavior may change.
390 """
391
392 if name not in self._options :
393 raise KeyError (f'unknown option "{name}" in block "{self.__class__.__name__}"')
394 noneAction = self._options[name].noneAction
395 if value is not None or noneAction == 'set' :
396 # check type if specified
397 optType = self._options[name].type
398 # convert int to float to prevent crash
399 if optType is float and type(value) is int:
400 value = float(value)
401 if optType is not None and optType != type(value):
402 raise ValueError(f'{name} for block {self.__class__.__name__} should '
403 f'be of type {optType} not {type(value)}')
404 setattr (self, name, value)
405 elif noneAction == 'ignore' :
406 pass
407 elif noneAction == 'error' :
408 raise ValueError (f'passed None for setting option {name} with noneAction=error')
409
410

Member Data Documentation

◆ _blockName

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

Definition at line 149 of file ConfigBlock.py.

◆ _dependencies

python.ConfigBlock.ConfigBlock._dependencies = []
protected

Definition at line 151 of file ConfigBlock.py.

◆ _expertModeSettings

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

Definition at line 153 of file ConfigBlock.py.

◆ _factoryName

python.ConfigBlock.ConfigBlock._factoryName = None
protected

Definition at line 150 of file ConfigBlock.py.

◆ _options

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

Definition at line 152 of file ConfigBlock.py.

◆ instance_counts

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

Definition at line 146 of file ConfigBlock.py.

◆ onlyForDSIDs

python.ConfigBlock.ConfigBlock.onlyForDSIDs

Definition at line 269 of file ConfigBlock.py.


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