6 from AnaAlgorithm.Logging
import logging
7 logCPAlgCfgBlock = logging.getLogger(
'CPAlgCfgBlock')
11 """the information for a single option on a configuration block"""
13 def __init__ (self, type=None, info='', noneAction='ignore', required=False,
24 """Class encoding a blocks dependence on other blocks."""
40 return f
'ConfigBlockDependency(blockName="{self.blockName}", required={self.required})'
44 """the base class for classes implementing individual blocks of
47 A configuration block is a sequence of one or more algorithms that
48 should always be scheduled together, e.g. the muon four momentum
49 corrections could be a single block, muon selection could then be
50 another block. The blocks themselves generally have their own
51 configuration options/properties specific to the block, and will
52 perform a dynamic configuration based on those options as well as
55 The actual configuration of the algorithms in the block will
56 depend on what other blocks are scheduled before and afterwards,
57 most importantly some algorithms will introduce shallow copies
58 that subsequent algorithms will need to run on, and some
59 algorithms will add selection decorations that subquent algorithms
60 should use as preselections.
62 The algorithms get created in a multi-step process (that may be
63 extended in the future): As a first step each block retrieves
64 references to the containers it uses (essentially marking its spot
65 in the processing chain) and also registering any shallow copies
66 that will be made. In the second/last step each block then
67 creates the fully configured algorithms.
69 One goal is that when the algorithms get created they will have
70 their final configuration and there needs to be no
71 meta-configuration data attached to the algorithms, essentially an
72 inversion of the approach in AnaAlgSequence in which the
73 algorithms got created first with associated meta-configuration
74 and then get modified in susequent configuration steps.
76 For now this is mostly an empty base class, but another goal of
77 this approach is to make it easier to build another configuration
78 layer on top of this one, and this class will likely be extended
79 and get data members at that point.
81 The child class needs to implement two methods,
82 `collectReferences` and `makeAlgs` which are each given a single
83 `ConfigAccumulator` type argument. The first is for the first
84 configuration step, and should only collect references to the
85 containers to be used. The second is for the second configuration
86 step, and should create the actual algorithms.
99 info=(
'Used to specify this block when setting an'
100 ' option at an arbitrary location.'))
101 self.
addOption(
'skipOnData',
False, type=bool,
102 info=(
'User option to prevent the block from running'
103 ' on data. This only affects blocks that are'
104 ' intended to run on data.'))
105 self.
addOption(
'skipOnMC',
False, type=bool,
106 info=(
'User option to prevent the block from running'
107 ' on MC. This only affects blocks that are'
108 ' intended to run on MC.'))
109 self.
addOption(
'onlyForDSIDs', [], type=list,
110 info=(
'Used to specify which MC DSIDs to allow this'
111 ' block to run on. Each element of the list'
112 ' can be a full DSID (e.g. 410470), or a regex'
113 ' (e.g. 410.* to select all 410xxx DSIDs, or'
114 ' ^(?!410) to veto them). An empty list means no'
115 ' DSID restriction.'))
118 if cls
not in ConfigBlock.instance_counts:
119 ConfigBlock.instance_counts[cls] = 0
122 stack = inspect.stack()
123 for frame_info
in stack:
125 parent_cls = frame_info.frame.f_locals.get(
'self',
None)
126 if parent_cls
is None or not isinstance(parent_cls, ConfigBlock):
128 if frame_info.function ==
"makeConfig":
129 ConfigBlock.instance_counts[cls] += 1
143 Add a dependency for the block. Dependency is corresponds to the
144 blockName of another block. If required is True, will throw an
145 error if dependency is not present; otherwise will move this
146 block after the required block. If required is False, will do
147 nothing if required block is not present; otherwise, it will
148 move block after required block.
152 self.
addOption(
'ignoreDependencies', [], type=list,
153 info=
'List of dependencies defined in the ConfigBlock to ignore.')
157 """Return True if there is a dependency."""
161 """Return the list of dependencies. """
165 type, info='', noneAction='ignore', required=False) :
166 """declare the given option on the configuration block
168 This should only be called in the constructor of the
171 NOTE: The backend to option handling is slated to be replaced
172 at some point. This particular function should essentially
173 stay the same, but some behavior may change.
176 raise KeyError (f
'duplicate option: {name}')
177 if type
not in [str, bool, int, float, list,
None] :
178 raise TypeError (f
'unknown option type: {type}')
179 noneActions = [
'error',
'set',
'ignore']
180 if noneAction
not in noneActions :
181 raise ValueError (f
'invalid noneAction: {noneAction} [allowed values: {noneActions}]')
182 setattr (self, name, defaultValue)
184 noneAction=noneAction, required=required, default=defaultValue)
188 """set the given option on the configuration block
190 NOTE: The backend to option handling is slated to be replaced
191 at some point. This particular function should essentially
192 stay the same, but some behavior may change.
196 raise KeyError (f
'unknown option "{name}" in block "{self.__class__.__name__}"')
197 noneAction = self.
_options[name].noneAction
198 if value
is not None or noneAction ==
'set' :
202 if optType
is float
and type(value)
is int:
204 if optType
is not None and optType !=
type(value):
205 raise ValueError(f
'{name} for block {self.__class__.__name__} should '
206 f
'be of type {optType} not {type(value)}')
207 setattr (self, name, value)
208 elif noneAction ==
'ignore' :
210 elif noneAction ==
'error' :
211 raise ValueError (f
'passed None for setting option {name} with noneAction=error')
215 """Returns config option value, if present; otherwise return None"""
217 return getattr(self, name)
221 """Return a copy of the options associated with the block"""
227 Prints options and their values
229 def printWrap(text, width=60, indent=" "):
230 wrapper = textwrap.TextWrapper(width=width, initial_indent=indent,
231 subsequent_indent=indent)
232 for line
in wrapper.wrap(text=text):
233 logCPAlgCfgBlock.info(line)
237 logCPAlgCfgBlock.info(indent + f
"\033[4m{opt}\033[0m: {self.getOptionValue(opt)}")
238 logCPAlgCfgBlock.info(indent*2 + f
"\033[4mtype\033[0m: {vals.type}")
239 logCPAlgCfgBlock.info(indent*2 + f
"\033[4mdefault\033[0m: {vals.default}")
240 logCPAlgCfgBlock.info(indent*2 + f
"\033[4mrequired\033[0m: {vals.required}")
241 logCPAlgCfgBlock.info(indent*2 + f
"\033[4mnoneAction\033[0m: {vals.noneAction}")
242 printWrap(f
"\033[4minfo\033[0m: {vals.info}", indent=indent*2)
244 logCPAlgCfgBlock.info(indent + f
"{ opt}: {self.getOptionValue(opt)}")
248 """whether the configuration block has the given option
250 WARNING: The backend to option handling is slated to be
251 replaced at some point. This particular function may change
252 behavior, interface or be removed/replaced entirely.
259 Implementation of == operator. Used for seaching configSeque.
260 E.g. if blockName in configSeq:
272 return ConfigBlock.instance_counts.get(cls, 0)