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 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 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 stack = inspect.stack()
192 for frame_info in stack:
193 # Get the class name (if any) from the frame
194 parent_cls = frame_info.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_info.function == "makeConfig":
198 ConfigBlock.instance_counts[cls] += 1
199 break
200
201

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

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

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

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

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

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

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

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

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

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

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

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

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

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

◆ get_instance_count()

python.ConfigBlock.ConfigBlock.get_instance_count ( cls)

Definition at line 466 of file ConfigBlock.py.

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

◆ getBlockName()

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

Definition at line 206 of file ConfigBlock.py.

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

◆ getDependencies()

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

Definition at line 315 of file ConfigBlock.py.

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

◆ getOptions()

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

Definition at line 416 of file ConfigBlock.py.

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

◆ getOptionValue()

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

Definition at line 410 of file ConfigBlock.py.

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

◆ hasDependencies()

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

Definition at line 311 of file ConfigBlock.py.

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

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

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

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

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

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

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

◆ printOptions()

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

Definition at line 421 of file ConfigBlock.py.

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

◆ setBlockName()

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

Definition at line 202 of file ConfigBlock.py.

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

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

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

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

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

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


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