13 from AnalysisAlgorithmsConfig.ConfigSequence
import ConfigSequence
14 from AnalysisAlgorithmsConfig.ConfigFactory
import ConfigFactory
15 from AnalysisAlgorithmsConfig.ConfigAccumulator
import deprecationWarningCategory
17 from AnaAlgorithm.Logging
import logging
18 logCPAlgTextCfg = logging.getLogger(
'CPAlgTextCfg')
22 """Loads YAML file into a dictionary"""
23 if not os.path.isfile(yamlPath):
24 raise ValueError(f
"{yamlPath} is not a file.")
25 with open(yamlPath,
'r')
as f:
26 textConfig = yaml.safe_load(f)
31 """Prints a dictionary as YAML"""
32 print(yaml.dump(d, default_flow_style=jsonFormat, sort_keys=sort))
36 def __init__(self, yamlPath=None, *, config=None, addDefaultBlocks=True):
37 super().
__init__(addDefaultBlocks=
False)
39 if yamlPath
and config:
40 raise ValueError(
"Cannot specify both yamlPath and config. Use one or the other.")
44 defaults={
'self': self})
52 if yamlPath
is not None or config
is not None:
59 """Print YAML configuration file."""
61 raise ValueError(
"Configuration has already been loaded.")
68 Preprocess the configuration dictionary.
69 Ensure blocks with only sub-blocks are initialized with an empty dictionary.
71 def processNode(node, algs):
72 if not isinstance(node, dict):
74 for blockName, blockContent
in list(node.items()):
78 if isinstance(blockContent, dict)
and not any(
79 key
in algs[blockName].options
for key
in blockContent
82 node[blockName] = {
'__placeholder__':
True, **blockContent}
84 processNode(node[blockName], algs[blockName].subAlgs)
87 processNode(config, algs)
92 Remove placeholder markers after initialization.
94 if not isinstance(config, dict):
96 if "__placeholder__" in config:
97 del config[
"__placeholder__"]
98 for key, value
in config.items():
103 read a YAML file. Will combine with any config blocks added using python
106 raise NotImplementedError(
"Mering multiple yaml files is not implemented.")
109 def merge(config, algs, path=''):
110 """Add to config block-by-block"""
111 if not isinstance(config, list):
114 for blocks
in config:
116 if blocks == {}
and path:
121 for blockName
in algs:
122 if blockName
in blocks:
123 subBlocks[blockName] = blocks.pop(blockName)
128 for subName, subBlock
in subBlocks.items():
129 newPath = f
'{path}.{subName}' if path
else subName
130 merge(subBlock, algs[subName].subAlgs, newPath)
133 logCPAlgTextCfg.info(f
'loading {yamlPath}')
134 if configDict
is not None:
140 if "AddConfigBlocks" in config:
141 self.
_configureAlg(self._algs[
"AddConfigBlocks"], config[
"AddConfigBlocks"])
146 merge(config, self._algs)
155 """Print YAML configuration file."""
157 raise ValueError(
"No configuration has been loaded.")
162 def saveYaml(self, filePath='config.yaml', default_flow_style=False,
165 Convert dictionary representation to yaml and save
167 logCPAlgTextCfg.info(f
"Saving configuration to {filePath}")
169 with open(filePath,
'w')
as outfile:
170 yaml.dump(config, outfile, default_flow_style=
False, **kwargs)
176 Create entry into dictionary representing the text configuration
178 def setEntry(name, config, opts):
180 if name
not in config:
182 elif isinstance(config[name], list):
185 config[name] = [config[name], opts]
189 name, rest = name[:name.index(
'.')], name[name.index(
'.') + 1:]
190 config = config[name]
191 if isinstance(config, list):
193 setEntry(rest, config, opts)
195 setEntry(name, self.
_config, dict(kwargs))
201 Set option(s) for the lsat block that was added. If an option
202 was added previously, will update value
204 if self.
_last is None:
205 raise TypeError(
"Cannot set options before adding a block")
207 self.
_last.update(**kwargs)
211 """Process YAML configuration file and confgure added algorithms."""
214 if blockName
not in self._order[self.ROOTNAME]:
217 raise ValueError(f
"Unkown block {blockName} in yaml file")
220 configSeq = ConfigSequence()
221 for blockName
in self._order[self.ROOTNAME]:
222 if blockName ==
"AddConfigBlocks":
225 assert blockName
in self._algs
229 blockConfig = self.
_config[blockName]
230 alg = self._algs[blockName]
238 algName, defaults=None, pos=None, superBlocks=None):
240 Load <functionName> from <modulePath>
243 module = importlib.import_module(modulePath)
244 fxn = getattr(module, functionName)
245 except ModuleNotFoundError
as e:
246 raise ModuleNotFoundError(f
"{e}\nFailed to load {functionName} from {modulePath}")
248 sys.modules[functionName] = fxn
250 self.addAlgConfigBlock(algName=algName, alg=fxn,
252 superBlocks=superBlocks,
257 def _configureAlg(self, block, blockConfig, configSeq=None, containerName=None,
259 if not isinstance(blockConfig, list):
260 blockConfig = [blockConfig]
262 for options
in blockConfig:
264 if 'containerName' in options:
265 containerName = options[
'containerName']
266 elif containerName
is not None and 'containerName' not in options:
267 options[
'containerName'] = containerName
269 logCPAlgTextCfg.info(f
"Configuring {block.algName}")
270 seq, funcOpts = block.makeConfig(options)
273 algOpts = seq.setOptions(options)
276 if containerName
is None:
278 if 'name' in opt
and opt[
'name'] ==
'containerName':
279 containerName = opt.get(
'value',
None)
282 if configSeq
is not None:
286 if extraOptions
is None:
287 extraOptionsList = [
"skipOnData",
"skipOnMC",
"onlyForDSIDs"]
289 if i[
'name']
in extraOptionsList
and i[
'defaultValue'] != i[
'value']:
290 if extraOptions
is None:
292 extraOptions[i[
'name']] = i[
'value']
294 algOpts = seq.setOptions(extraOptions.copy())
297 algOpts = [i[
'name']
for i
in algOpts]
298 expectedOptions =
set(funcOpts)
299 expectedOptions |=
set(algOpts)
300 expectedOptions |=
set(block.subAlgs)
302 difference =
set(options.keys()) - expectedOptions
303 difference.discard(
'__placeholder__')
305 difference =
"\n".
join(difference)
306 raise ValueError(f
"There are options set that are not used for "
307 f
"{block.algName}:\n{difference}\n"
308 "Please check your configuration.")
311 for alg
in self._order.
get(block.algName, []):
313 subAlg = block.subAlgs[alg]
314 self.
_configureAlg(subAlg, options[alg], configSeq, containerName, extraOptions)
318 def makeSequence(configPath, *, flags=None, algSeq=None, noSystematics=None, dataType=None, geometry=None, autoconfigFromFlags=None, isPhyslite=None, noPhysliteBroken=False):
326 if autoconfigFromFlags
is not None:
327 if flags
is not None:
328 raise ValueError(
"Cannot pass both flags and autoconfigFromFlags arguments")
329 flags = autoconfigFromFlags
330 warnings.warn (
'Using autoconfigFromFlags parameter is deprecated, use flags instead', category=deprecationWarningCategory, stacklevel=2)
332 warnings.warn (
'it is deprecated to configure meta-data for analysis configuration manually, please read the configuration flags via the meta-data reader', category=deprecationWarningCategory, stacklevel=2)
334 from AnalysisAlgorithmsConfig.ConfigAccumulator
import ConfigAccumulator
338 logCPAlgTextCfg.info(
"Configuration file read in:")
341 logCPAlgTextCfg.info(
"Default algorithms:")
342 config.printAlgs(printOpts=
True)
344 logCPAlgTextCfg.info(
"Configuring algorithms based on YAML file:")
345 configSeq = config.configure()
348 logCPAlgTextCfg.info(
"Configuration used:")
352 configAccumulator = ConfigAccumulator(algSeq=algSeq, dataType=dataType, isPhyslite=isPhyslite, geometry=geometry, autoconfigFromFlags=autoconfigFromFlags, flags=flags, noSystematics=noSystematics)
353 configSeq.fullConfigure(configAccumulator)
356 logCPAlgTextCfg.info(
"ConfigBlocks and their configuration:")
357 configSeq.printOptions()
359 from AnaAlgorithm.DualUseConfig
import isAthena, useComponentAccumulator
360 if isAthena
and useComponentAccumulator:
361 return configAccumulator.CA
374 if isinstance(local, dict):
375 to_combine = local.values()
376 elif isinstance(local, list):
382 for sub
in to_combine:
383 combined =
combineConfigFiles(sub, config_path, fragment_key=fragment_key)
or combined
386 if fragment_key
not in local:
390 pathlib.Path(local[fragment_key]),
393 with open(fragment_path)
as fragment_file:
398 if fragment_path.suffix ==
'.json':
399 fragment = json.load(fragment_file)
401 fragment = yaml.safe_load(fragment_file)
407 fragment_path.parent,
408 fragment_key=fragment_key
415 del local[fragment_key]
425 config_path / fragment_path,
426 *[x / fragment_path
for x
in os.environ[
"DATAPATH"].
split(
":")]
428 for path
in paths_to_check:
432 raise FileNotFoundError(fragment_path)
437 if isinstance(local, list):
442 if isinstance(local, dict):
443 for key, value
in fragment.items():