12 from AnalysisAlgorithmsConfig.ConfigSequence
import ConfigSequence
13 from AnalysisAlgorithmsConfig.ConfigFactory
import ConfigFactory
15 from AnaAlgorithm.Logging
import logging
16 logCPAlgTextCfg = logging.getLogger(
'CPAlgTextCfg')
20 """Loads YAML file into a dictionary"""
21 if not os.path.isfile(yamlPath):
22 raise ValueError(f
"{yamlPath} is not a file.")
23 with open(yamlPath,
'r')
as f:
24 textConfig = yaml.safe_load(f)
29 """Prints a dictionary as YAML"""
30 print(yaml.dump(d, default_flow_style=jsonFormat, sort_keys=sort))
34 def __init__(self, yamlPath=None, *, addDefaultBlocks=True):
35 super().
__init__(addDefaultBlocks=
False)
39 defaults={
'self': self})
47 if yamlPath
is not None:
54 """Print YAML configuration file."""
56 raise ValueError(
"Configuration has already been loaded.")
63 Preprocess the configuration dictionary.
64 Ensure blocks with only sub-blocks are initialized with an empty dictionary.
66 def processNode(node, algs):
67 if not isinstance(node, dict):
69 for blockName, blockContent
in list(node.items()):
73 if isinstance(blockContent, dict)
and not any(
74 key
in algs[blockName].options
for key
in blockContent
77 node[blockName] = {
'__placeholder__':
True, **blockContent}
79 processNode(node[blockName], algs[blockName].subAlgs)
82 processNode(config, algs)
87 Remove placeholder markers after initialization.
89 if not isinstance(config, dict):
91 if "__placeholder__" in config:
92 del config[
"__placeholder__"]
93 for key, value
in config.items():
98 read a YAML file. Will combine with any config blocks added using python
101 raise NotImplementedError(
"Mering multiple yaml files is not implemented.")
104 def merge(config, algs, path=''):
105 """Add to config block-by-block"""
106 if not isinstance(config, list):
109 for blocks
in config:
111 if blocks == {}
and path:
116 for blockName
in algs:
117 if blockName
in blocks:
118 subBlocks[blockName] = blocks.pop(blockName)
123 for subName, subBlock
in subBlocks.items():
124 newPath = f
'{path}.{subName}' if path
else subName
125 merge(subBlock, algs[subName].subAlgs, newPath)
128 logCPAlgTextCfg.info(f
'loading {yamlPath}')
131 if "AddConfigBlocks" in config:
132 self.
_configureAlg(self._algs[
"AddConfigBlocks"], config[
"AddConfigBlocks"])
137 merge(config, self._algs)
146 """Print YAML configuration file."""
148 raise ValueError(
"No configuration has been loaded.")
153 def saveYaml(self, filePath='config.yaml', default_flow_style=False,
156 Convert dictionary representation to yaml and save
158 logCPAlgTextCfg.info(f
"Saving configuration to {filePath}")
160 with open(filePath,
'w')
as outfile:
161 yaml.dump(config, outfile, default_flow_style=
False, **kwargs)
167 Create entry into dictionary representing the text configuration
169 def setEntry(name, config, opts):
171 if name
not in config:
173 elif isinstance(config[name], list):
176 config[name] = [config[name], opts]
180 name, rest = name[:name.index(
'.')], name[name.index(
'.') + 1:]
181 config = config[name]
182 if isinstance(config, list):
184 setEntry(rest, config, opts)
186 setEntry(name, self.
_config, dict(kwargs))
192 Set option(s) for the lsat block that was added. If an option
193 was added previously, will update value
195 if self.
_last is None:
196 raise TypeError(
"Cannot set options before adding a block")
202 """Process YAML configuration file and confgure added algorithms."""
205 if blockName
not in self._order[self.ROOTNAME]:
208 raise ValueError(f
"Unkown block {blockName} in yaml file")
211 configSeq = ConfigSequence()
212 for blockName
in self._order[self.ROOTNAME]:
213 if blockName ==
"AddConfigBlocks":
216 assert blockName
in self._algs
220 blockConfig = self.
_config[blockName]
221 alg = self._algs[blockName]
229 algName, defaults=None, pos=None, superBlocks=None):
231 Load <functionName> from <modulePath>
234 module = importlib.import_module(modulePath)
235 fxn = getattr(module, functionName)
236 except ModuleNotFoundError
as e:
237 raise ModuleNotFoundError(f
"{e}\nFailed to load {functionName} from {modulePath}")
239 sys.modules[functionName] = fxn
241 self.addAlgConfigBlock(algName=algName, alg=fxn,
243 superBlocks=superBlocks,
248 def _configureAlg(self, block, blockConfig, configSeq=None, containerName=None,
250 if not isinstance(blockConfig, list):
251 blockConfig = [blockConfig]
253 for options
in blockConfig:
255 if 'containerName' in options:
256 containerName = options[
'containerName']
257 elif containerName
is not None and 'containerName' not in options:
258 options[
'containerName'] = containerName
260 logCPAlgTextCfg.info(f
"Configuring {block.algName}")
261 seq, funcOpts = block.makeConfig(options)
264 algOpts = seq.setOptions(options)
267 if containerName
is None:
269 if 'name' in opt
and opt[
'name'] ==
'containerName':
270 containerName = opt.get(
'value',
None)
273 if configSeq
is not None:
277 if extraOptions
is None:
278 extraOptionsList = [
"skipOnData",
"skipOnMC",
"onlyForDSIDs"]
280 if i[
'name']
in extraOptionsList
and i[
'defaultValue'] != i[
'value']:
281 if extraOptions
is None:
283 extraOptions[i[
'name']] = i[
'value']
285 algOpts = seq.setOptions(extraOptions)
288 algOpts = [i[
'name']
for i
in algOpts]
289 expectedOptions =
set(funcOpts)
290 expectedOptions |=
set(algOpts)
291 expectedOptions |=
set(block.subAlgs)
293 difference =
set(options.keys()) - expectedOptions
294 difference.discard(
'__placeholder__')
296 difference =
"\n".
join(difference)
297 raise ValueError(f
"There are options set that are not used for "
298 f
"{block.algName}:\n{difference}\n"
299 "Please check your configuration.")
302 for alg
in self._order.
get(block.algName, []):
304 subAlg = block.subAlgs[alg]
305 self.
_configureAlg(subAlg, options[alg], configSeq, containerName, extraOptions)
309 def makeSequence(configPath, dataType, algSeq, geometry=None, autoconfigFromFlags=None,
310 isPhyslite=False, noPhysliteBroken=False, noSystematics=None):
314 from AnalysisAlgorithmsConfig.ConfigAccumulator
import ConfigAccumulator
318 logCPAlgTextCfg.info(
"Configuration file read in:")
321 logCPAlgTextCfg.info(
"Default algorithms:")
322 config.printAlgs(printOpts=
True)
324 logCPAlgTextCfg.info(
"Configuring algorithms based on YAML file:")
325 configSeq = config.configure()
328 logCPAlgTextCfg.info(
"Configuration used:")
332 configAccumulator = ConfigAccumulator(algSeq, dataType, isPhyslite=isPhyslite, geometry=geometry, autoconfigFromFlags=autoconfigFromFlags, noSystematics=noSystematics)
333 configSeq.fullConfigure(configAccumulator)
336 logCPAlgTextCfg.info(
"ConfigBlocks and their configuration:")
337 configSeq.printOptions()
339 from AnaAlgorithm.DualUseConfig
import isAthena, useComponentAccumulator
340 if isAthena
and useComponentAccumulator:
341 return configAccumulator.CA
353 if isinstance(local, dict):
354 to_combine = local.values()
355 elif isinstance(local, list):
361 for sub
in to_combine:
365 if fragment_key
not in local:
369 pathlib.Path(local[fragment_key]),
372 with open(fragment_path)
as fragment_file:
377 if fragment_path.suffix ==
'.json':
378 fragment = json.load(fragment_file)
380 fragment = yaml.safe_load(fragment_file)
386 fragment_path.parent,
387 fragment_key=fragment_key
394 del local[fragment_key]
400 config_path / fragment_path,
401 *[x / fragment_path
for x
in os.environ[
"DATAPATH"].
split(
":")]
403 for path
in paths_to_check:
407 raise FileNotFoundError(fragment_path)
412 if isinstance(local, list):
417 if isinstance(local, dict):
418 for key, value
in fragment.items():