Source code for pcse.base.config_loader

import os
from pathlib import Path
from .. import exceptions as exc
import textwrap


[docs] class ConfigurationLoader(object): """Class for loading the model configuration from a PCSE configuration files :param config: string given file name containing model configuration """ _required_attr = ("CROP", "SOIL", "AGROMANAGEMENT", "OUTPUT_VARS", "OUTPUT_INTERVAL", "OUTPUT_INTERVAL_DAYS", "SUMMARY_OUTPUT_VARS") defined_attr = [] model_config_file = None description = None def __init__(self, config): if not isinstance(config, (str, Path)): msg = ("Keyword 'config' should provide the name of the file (string or pathlib.Path)" + "storing the configuration of the model PCSE should run.") raise exc.PCSEError(msg) # check if model configuration file is an absolute or relative path. If # not assume that it is located in the 'conf/' folder in the PCSE # distribution config = Path(config) if config.is_absolute(): mconf = config else: this_dir = Path(__file__).parent pcse_dir = this_dir.parent mconf = pcse_dir / "conf" / config model_config_file = mconf.resolve() # check that configuration file exists if not model_config_file.exists(): msg = "PCSE model configuration file does not exist: %s" % model_config_file raise exc.PCSEError(msg) # store for later use self.model_config_file = model_config_file # Load file using execfile try: loc = {} bytecode = compile(open(model_config_file).read(), model_config_file, 'exec') exec(bytecode, {}, loc) except Exception as e: msg = "Failed to load configuration from file '%s' due to: %s" msg = msg % (model_config_file, e) raise exc.PCSEError(msg) # Add the descriptive header for later use if "__doc__" in loc: desc = loc.pop("__doc__") if len(desc) > 0: self.description = desc if self.description[-1] != "\n": self.description += "\n" # Loop through the attributes in the configuration file for key, value in list(loc.items()): if key.isupper(): self.defined_attr.append(key) setattr(self, key, value) # Check for any missing compulsary attributes req = set(self._required_attr) diff = req.difference(set(self.defined_attr)) if diff: msg = "One or more compulsory configuration items missing: %s" % list(diff) raise exc.PCSEError(msg) def __str__(self): msg = "PCSE ConfigurationLoader from file:\n" msg += " %s\n\n" % self.model_config_file if self.description is not None: msg += ("%s Header of configuration file %s\n"% ("-"*20, "-"*20)) msg += self.description if msg[-1] != "\n": msg += "\n" msg += ("%s Contents of configuration file %s\n"% ("-"*19, "-"*19)) for k in self.defined_attr: r = "%s: %s" % (k, getattr(self, k)) msg += (textwrap.fill(r, subsequent_indent=" ") + "\n") return msg
[docs] def update_output_variable_lists(self, output_vars=None, summary_vars=None, terminal_vars=None): """Updates the lists of output variables that are defined in the configuration file. This is useful because sometimes you want the flexibility to get access to an additional model variable which is not in the standard list of variables defined in the model configuration file. The more elegant way is to define your own configuration file, but this adds some flexibility particularly for use in jupyter notebooks and exploratory analysis. Note that there is a different behaviour given the type of the variable provided. List and string inputs will extend the list of variables, while set/tuple inputs will replace the current list. :param output_vars: the variable names to add/replace for the OUTPUT_VARS configuration variable :param summary_vars: the variable names to add/replace for the SUMMARY_OUTPUT_VARS configuration variable :param terminal_vars: the variable names to add/replace for the TERMINAL_OUTPUT_VARS configuration variable """ config_varnames = ["OUTPUT_VARS", "SUMMARY_OUTPUT_VARS", "TERMINAL_OUTPUT_VARS"] for varitems, config_varname in zip([output_vars, summary_vars, terminal_vars], config_varnames): if varitems is None: continue else: if isinstance(varitems, str): # A string: we extend the current list getattr(self, config_varname).extend(varitems.split()) elif isinstance(varitems, list): # a list: we extend the current list getattr(self, config_varname).extend(varitems) elif isinstance(varitems, (tuple, set)): # tuple/set we replace the current list setattr(self, config_varname, list(varitems)) else: msg = "Unrecognized input for `output_vars` to engine(): %s" % output_vars print(msg)