13from AnalysisAlgorithmsConfig.ConfigSequence
import ConfigSequence
14from AnalysisAlgorithmsConfig.ConfigFactory
import ConfigFactory
15from AnalysisAlgorithmsConfig.ConfigAccumulator
import deprecationWarningCategory
17from AnaAlgorithm.DualUseConfig
import isAthena
18from AnaAlgorithm.Logging
import logging
19logCPAlgTextCfg = logging.getLogger(
'CPAlgTextCfg')
23 """Loads YAML file into a dictionary"""
24 if not os.path.isfile(yamlPath):
25 raise ValueError(f
"{yamlPath} is not a file.")
26 with open(yamlPath,
'r')
as f:
27 textConfig = yaml.safe_load(f)
32 """Prints a dictionary as YAML"""
33 print(yaml.dump(d, default_flow_style=jsonFormat, sort_keys=sort))
39class TextConfig(ConfigFactory):
40 def __init__(self, yamlPath=None, *, config=None, addDefaultBlocks=True):
41 super().
__init__(addDefaultBlocks=
False)
43 if yamlPath
and config:
44 raise ValueError(
"Cannot specify both yamlPath and config. Use one or the other.")
48 defaults={
'self': self})
56 if yamlPath
is not None or config
is not None:
63 """Print YAML configuration file."""
65 raise ValueError(
"Configuration has already been loaded.")
72 Preprocess the configuration dictionary.
73 Ensure blocks with only sub-blocks are initialized with an empty dictionary.
75 def processNode(node, algs):
76 if not isinstance(node, dict):
78 for blockName, blockContent
in list(node.items()):
82 if isinstance(blockContent, dict)
and not any(
83 key
in algs[blockName].options
for key
in blockContent
86 node[blockName] = {
'__placeholder__':
True, **blockContent}
88 processNode(node[blockName], algs[blockName].subAlgs)
91 processNode(config, algs)
96 Remove placeholder markers after initialization.
98 if not isinstance(config, dict):
100 if "__placeholder__" in config:
101 del config[
"__placeholder__"]
102 for key, value
in config.items():
107 read a YAML file. Will combine with any config blocks added using python
110 raise NotImplementedError(
"Mering multiple yaml files is not implemented.")
113 def merge(config, algs, path=''):
114 """Add to config block-by-block"""
115 if not isinstance(config, list):
118 for blocks
in config:
120 if blocks == {}
and path:
125 for blockName
in algs:
126 if blockName
in blocks:
127 subBlocks[blockName] = blocks.pop(blockName)
132 for subName, subBlock
in subBlocks.items():
133 newPath = f
'{path}.{subName}' if path
else subName
134 merge(subBlock, algs[subName].subAlgs, newPath)
137 logCPAlgTextCfg.debug(f
'loading {yamlPath}')
138 if configDict
is not None:
144 if "AddConfigBlocks" in config:
159 """Print YAML configuration file."""
161 raise ValueError(
"No configuration has been loaded.")
166 def saveYaml(self, filePath='config.yaml', default_flow_style=False,
169 Convert dictionary representation to yaml and save
171 logCPAlgTextCfg.info(f
"Saving configuration to {filePath}")
173 with open(filePath,
'w')
as outfile:
174 yaml.dump(config, outfile, default_flow_style=
False, **kwargs)
180 Create entry into dictionary representing the text configuration
182 def setEntry(name, config, opts):
184 if name
not in config:
186 elif isinstance(config[name], list):
187 config[name].append(opts)
189 config[name] = [config[name], opts]
193 name, rest = name[:name.index(
'.')], name[name.index(
'.') + 1:]
194 config = config[name]
195 if isinstance(config, list):
197 setEntry(rest, config, opts)
199 setEntry(name, self.
_config, dict(kwargs))
205 Set option(s) for the lsat block that was added. If an option
206 was added previously, will update value
208 if self.
_last is None:
209 raise TypeError(
"Cannot set options before adding a block")
211 self.
_last.update(**kwargs)
215 """Process YAML configuration file and confgure added algorithms."""
218 if blockName
not in self._order[self.ROOTNAME]:
220 blockName = list(self.
_config[blockName].keys())[0]
221 raise ValueError(f
"Unkown block {blockName} in yaml file")
224 configSeq = ConfigSequence()
225 for blockName
in self._order[self.ROOTNAME]:
226 if blockName ==
"AddConfigBlocks":
229 assert blockName
in self.
_algs
233 blockConfig = self.
_config[blockName]
234 alg = self.
_algs[blockName]
242 algName, defaults=None, pos=None, superBlocks=None):
244 Load <functionName> from <modulePath>
247 module = importlib.import_module(modulePath)
248 fxn = getattr(module, functionName)
249 except ModuleNotFoundError
as e:
250 raise ModuleNotFoundError(f
"{e}\nFailed to load {functionName} from {modulePath}")
252 sys.modules[functionName] = fxn
254 self.addAlgConfigBlock(algName=algName, alg=fxn,
256 superBlocks=superBlocks,
261 def _configureAlg(self, block, blockConfig, configSeq=None, containerName=None,
266 if block.algName ==
"AddConfigBlocks" and isinstance(blockConfig, dict):
267 blockConfig = [options | {
'algName': algName}
for algName, options
in blockConfig.items()]
269 elif not isinstance(blockConfig, list):
270 blockConfig = [blockConfig]
272 for options
in blockConfig:
274 if 'containerName' in options:
275 containerName = options[
'containerName']
276 elif containerName
is not None and 'containerName' not in options:
277 options[
'containerName'] = containerName
279 logCPAlgTextCfg.debug(f
"Configuring {block.algName}")
280 seq, funcOpts = block.makeConfig(options)
283 algOpts = seq.setOptions(options)
286 if containerName
is None:
288 if 'name' in opt
and opt[
'name'] ==
'containerName':
289 containerName = opt.get(
'value',
None)
292 if configSeq
is not None:
296 if extraOptions
is None:
297 extraOptionsList = [
"skipOnData",
"skipOnMC",
"onlyForDSIDs"]
299 if i[
'name']
in extraOptionsList
and i[
'defaultValue'] != i[
'value']:
300 if extraOptions
is None:
302 extraOptions[i[
'name']] = i[
'value']
304 algOpts = seq.setOptions(extraOptions.copy())
307 algOpts = [i[
'name']
for i
in algOpts]
308 expectedOptions =
set(funcOpts)
309 expectedOptions |=
set(algOpts)
310 expectedOptions |=
set(block.subAlgs)
312 difference =
set(options.keys()) - expectedOptions
313 difference.discard(
'__placeholder__')
315 difference =
"\n".join(difference)
316 raise ValueError(f
"There are options set that are not used for "
317 f
"{block.algName}:\n{difference}\n"
318 "Please check your configuration.")
321 for alg
in self._order.
get(block.algName, []):
323 subAlg = block.subAlgs[alg]
324 self.
_configureAlg(subAlg, options[alg], configSeq, containerName, extraOptions)
328def makeSequence(configPath, *, flags=None, algSeq=None, noSystematics=None, dataType=None, geometry=None, autoconfigFromFlags=None, isPhyslite=None, noPhysliteBroken=False):
336 if autoconfigFromFlags
is not None:
337 if flags
is not None:
338 raise ValueError(
"Cannot pass both flags and autoconfigFromFlags arguments")
339 flags = autoconfigFromFlags
340 warnings.warn (
'Using autoconfigFromFlags parameter is deprecated, use flags instead', category=deprecationWarningCategory, stacklevel=2)
342 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)
344 from AnalysisAlgorithmsConfig.ConfigAccumulator
import ConfigAccumulator
348 logCPAlgTextCfg.info(
"Configuration file read in:")
351 logCPAlgTextCfg.info(
"Default algorithms:")
352 config.printAlgs(printOpts=
True)
354 logCPAlgTextCfg.info(
"Configuring algorithms based on YAML file:")
355 configSeq = config.configure()
358 logCPAlgTextCfg.info(
"Configuration used:")
362 configAccumulator = ConfigAccumulator(algSeq=algSeq, dataType=dataType, isPhyslite=isPhyslite, geometry=geometry, autoconfigFromFlags=autoconfigFromFlags, flags=flags, noSystematics=noSystematics)
363 configSeq.fullConfigure(configAccumulator)
366 logCPAlgTextCfg.info(
"ConfigBlocks and their configuration:")
367 configSeq.printOptions()
369 return configAccumulator.CA
if isAthena
else None
378 Recursively combine configuration fragments into `local`.
380 - Looks for `fragment_key` at any dict node.
381 - If value is a string/path: merge that fragment.
382 - If value is a list: merge all fragments in order.
383 For conflicts between fragments, the **earlier** file in the list wins.
384 Local keys still override the merged fragments.
386 Returns True if any merging happened below this node.
391 if isinstance(local, dict):
392 to_combine = local.values()
393 elif isinstance(local, list):
399 for sub
in to_combine:
400 combined =
combineConfigFiles(sub, config_path, fragment_key=fragment_key)
or combined
403 if fragment_key
not in local:
407 if not isinstance(local, dict):
411 value = local[fragment_key]
412 if isinstance(value, (str, pathlib.Path)):
414 f
"{fragment_key} should be followed with a list of files",
419 elif isinstance(value, list):
422 raise TypeError(f
"'{fragment_key}' must be a string path or a list of paths, got {type(value).__name__}")
437 del local[fragment_key]
446 """Load a YAML or JSON fragment
448 This function is superfluous as of the yaml 1.2 spec (which
449 has not been implemented in ATLAS Yaml dependencies).
450 Once https://github.com/yaml/pyyaml/issues/173 is resolved
451 pyyaml will support yaml 1.2, which is compatable with json.
452 Until then yaml and json behave differently in some scientific
456 with open(fragment_path,
'r')
as fragment_file:
457 if fragment_path.suffix.lower() ==
'.json':
458 return json.load(fragment_file)
460 return yaml.safe_load(fragment_file)
465 config_path / fragment_path,
466 *[x / fragment_path
for x
in os.environ[
"DATAPATH"].
split(
":")]
468 for path
in paths_to_check:
472 raise FileNotFoundError(fragment_path)
477 if isinstance(local, list):
482 if isinstance(local, dict):
483 for key, value
in fragment.items():
void print(char *figname, TCanvas *c1)
_configureAlg(self, block, blockConfig, configSeq=None, containerName=None, extraOptions=None)
__init__(self, yamlPath=None, *, config=None, addDefaultBlocks=True)
saveYaml(self, filePath='config.yaml', default_flow_style=False, **kwargs)
setOptions(self, **kwargs)
_addNewConfigBlocks(self, modulePath, functionName, algName, defaults=None, pos=None, superBlocks=None)
cleanupPlaceholders(self, config)
loadConfig(self, yamlPath=None, *, configDict=None)
printConfig(self, sort=False, jsonFormat=False)
addBlock(self, name, **kwargs)
preprocessConfig(self, config, algs)
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
std::vector< std::string > split(const std::string &s, const std::string &t=":")
makeSequence(configPath, *, flags=None, algSeq=None, noSystematics=None, dataType=None, geometry=None, autoconfigFromFlags=None, isPhyslite=None, noPhysliteBroken=False)
_merge_dicts(local, fragment)
_load_fragment(pathlib.Path fragment_path)
printYaml(d, sort=False, jsonFormat=False)
combineConfigFiles(local, config_path, fragment_key="include")
_find_fragment(fragment_path, config_path)