def __setstate__(self, state): """ Rebuild schema upon unpickling since schema is unpickleable. Pickling happens during mpi broadcasts and also during testing where the test reactor is cached. See Also -------- armi.settings.setting.Setting.__getstate__ : removes schema """ from armi import getApp # pylint: disable=import-outside-toplevel self.__settings = getApp().getSettings() # restore non-setting instance attrs for key, val in state.items(): if key != "_Settings__settings": setattr(self, key, val) # with schema restored, restore all setting values for name, settingState in state["_Settings__settings"].items(): # pylint: disable=protected-access if name in self.__settings: self.__settings[name]._value = settingState.value elif isinstance(settingState, Setting): self.__settings[name] = copy(settingState) else: raise NonexistentSetting(name)
def _directAccessOfSettingAllowed(self, key): """ A way to check if specific settings can be grabbed out of the case settings. Could be updated with other specific instances as necessary. Notes ----- Checking the validity of grabbing specific settings at this point, as is done for the SIMPLE_CYCLES_INPUT's, feels a bit intrusive and out of place. In particular, the fact that the check is done every time that a setting is reached for, no matter if it is the setting in question, is quite clunky. In the future, it would be desirable if the settings system were more flexible to control this type of thing at a deeper level. """ if key not in self.__settings: return False, NonexistentSetting(key) if key in SIMPLE_CYCLES_INPUTS and self.__settings[ "cycles"].value != []: err = ValueError( "Cannot grab simple cycles information from the case settings" " when detailed cycles information is also entered.\n In general" " cycles information should be pulled off the operator or parsed" " using the appropriate getter in the utils.") return False, err return True, None
def getSetting(self, key, default=None): """ Return a copy of an actual Setting object, instead of just its value. NOTE: This is used very rarely, try to organize your code to only need a Setting value. """ if key in self.__settings: return copy(self.__settings[key]) elif default is not None: return default else: raise NonexistentSetting(key)
def copyInterfaceInputs(cs, destination: str, sourceDir: Optional[str] = None) -> Dict[str, str]: """ Copy sets of files that are considered "input" from each active interface. This enables developers to add new inputs in a plugin-dependent/ modular way. In parameter sweeps, these often have a sourceDir associated with them that is different from the cs.inputDirectory. Parameters ---------- cs : CaseSettings The source case settings to find input files destination: str The target directory to copy input files to sourceDir: str, optional The directory from which to copy files. Defaults to cs.inputDirectory Notes ----- This may seem a bit overly complex, but a lot of the behavior is important. Relative paths are copied into the target directory, which in some cases requires updating the setting that pointed to the file in the first place. This is necessary to avoid case dependencies in relavive locations above the input directory, which can lead to issues when cloneing case suites. In the future this could be simplified by adding a concept for a suite root directory, below which it is safe to copy files without needing to update settings that point with a relative path to files that are below it. """ activeInterfaces = interfaces.getActiveInterfaceInfo(cs) sourceDir = sourceDir or cs.inputDirectory sourceDirPath = pathlib.Path(sourceDir) destPath = pathlib.Path(destination) newSettings = {} assert destPath.is_dir() for klass, _ in activeInterfaces: interfaceFileNames = klass.specifyInputs(cs) # returned files can be absolute paths, relative paths, or even glob patterns. # Since we don't have an explicit way to signal about these, we sort of have to # guess. In future, it might be nice to have interfaces specify which # explicitly. for key, files in interfaceFileNames.items(): if not isinstance(key, settings.Setting): try: key = cs.getSetting(key) except NonexistentSetting(key): raise ValueError( "{} is not a valid setting. Ensure the relevant specifyInputs method uses a correct setting name." .format(key)) label = key.name for f in files: path = pathlib.Path(f) if path.is_absolute() and path.exists() and path.is_file(): # looks like an extant, absolute path; no need to do anything pass else: # An OSError can occur if a wildcard is included in the file name so # this is wrapped in a try/except to circumvent instances where an # interface requests to copy multiple files based on some prefix/suffix. try: if not (path.exists() and path.is_file()): runLog.extra( f"Input file `{f}` not found. Checking for file at path `{sourceDirPath}`" ) except OSError: pass # relative path/glob. Should be safe to just use glob resolution. # Note that `glob.glob` is being used here rather than `pathlib.glob` because # `pathlib.glob` for Python 3.7.2 does not handle case sensitivity for file and # path names. This is required for copying and using Python scripts (e.g., fuel management, # control logic, etc.). srcFiles = [ pathlib.Path(os.path.join(sourceDirPath, g)) for g in glob.glob(os.path.join(sourceDirPath, f)) ] for sourceFullPath in srcFiles: if not sourceFullPath: continue sourceName = os.path.basename(sourceFullPath.name) destFilePath = os.path.abspath(destPath / sourceName) pathTools.copyOrWarn(label, sourceFullPath, destFilePath) if len(srcFiles) == 0: runLog.warning( f"No input files for `{label}` could be resolved " f"with the following file path: `{f}`.") elif len(srcFiles) > 1: runLog.warning( f"Input files for `{label}` resolved to more " f"than one file; cannot update settings safely. " f"Discovered input files: {srcFiles}") elif len(srcFiles) == 1: newSettings[label] = str(destFilePath) return newSettings
def __setitem__(self, key, val): # TODO: This potentially allows for invisible settings mutations and should be removed. if key in self.__settings: self.__settings[key].setValue(val) else: raise NonexistentSetting(key)
def __getitem__(self, key): if key in self.__settings: return self.__settings[key].value else: raise NonexistentSetting(key)