def addNamelist(self, name, full_name, variable_list, trigger=None): """ Adds a namelist to the input file that will be parsed. Variable list must be an array of arrays. Each array element must be an array that has the information [varname, datatype, default, chars to match]. The 'trigger' is the logical variable that gets set to true if this namelist is specified in the input file. """ if name in self.ordered_namelist_keys: raise InputError('Namelist %s defined multiple times' % name) self.ordered_namelist_keys.append(name) self.namelists[name] = Namelist(trigger, full_name) for var in variable_list: if not isinstance(var, (list, tuple)) or len(var) not in [4, 5]: raise InputError( 'variables in variable_list must be lists of ' + 'length 4 or 5. [varname, datatype, default, description, internal_datatype (' 'Optional)]') if len(var) == 4: self.namelists[name].addVariable(var[0], var[1], var[2], var[3]) else: self.namelists[name].addVariable(var[0], var[1], var[2], var[3], var[4])
def __init__(self, varname, dat_type=int, default=None, chars_to_match=4, case_sensitive=False, description=''): """ Initializes the variable type. Sets the default value as well as specifying how many characters are required by the parser to trigger recognition """ # Catch illegalities if not dat_type in (int, str, float): raise InputError('Variable has unknown data type %s' % dat_type.__name__) # You can't match more characters than you have characters! chars_to_match = min(chars_to_match, len(varname)) self.name = varname self.datatype = dat_type if default is not None: if self.datatype is str: self.value = default.replace("'", '').replace('"', '') else: self.value = self.datatype(default) else: self.value = None self.tomatch = chars_to_match self.case_sensitive = case_sensitive self.description = description
def Open(self): """ Signifies that the namelist is open """ if self.open: raise InputError('Namelist already open. Cannot open before closing') if self.trigger: self.variables[self.trigger] = True self.open = True
def _full_namelist_name(self, nml): """ Determines what the full namelist name is. We try to make as many allowances as possible. We will match the first 3 characters and replace all _'s with """ nml = nml.replace(' ', '_') # replaces spaces with _'s for key in self.ordered_namelist_keys: if self.namelists[key] == nml: return key raise InputError('Unrecognized namelist %s' % nml)
def __init__(self, varname, dat_type=int, default=None, description='', int_dat_type=str): """ Initializes the variable type. Sets the default value as well as specifying how many characters are required by the parser to trigger recognition """ # Catch illegalities if dat_type not in (int, str, float, list, tuple): raise InputError('Variable has unknown data type %s' % dat_type.__name__) if int_dat_type not in (int, str, float): raise InputError('Variable has unknown internal data type %s' % int_dat_type) self.name = varname self.datatype = dat_type self.int_datatype = int_dat_type if default is None: self.value = None elif self.datatype is str: self.value = default.replace("'", '').replace('"', '') elif self.datatype in [list, tuple]: if isinstance(default, str): self.value = [ self.int_datatype(x.strip()) for x in re.split(';\s*|,\s*', default.replace('"', '')) ] else: self.value = default else: self.value = self.datatype(default) self.description = description
def Parse(self, filename): """ This subroutine parses the input file. Only data in namelists are parsed, and all namelists must be set prior to calling this routine. It will create a dictionary of Input variables for all variables in all namelists. They all flood the same namespace. If there are any conflicts between variables in namelists, an error will be raised. Make sure all input variables are unique! """ from os.path import exists # Make sure our file exists if filename is None: raise InputError("No input file was provided!") if not exists(filename): raise InputError("Can't find input file (%s)" % filename) # Load the whole thing into memory. This should be plenty short enough. lines = open(filename, 'r').readlines() # Save the text of the input file so we can echo it back later self.text = ''.join(lines) # We will loop through the input file three times: # # 1st: Load all of the data into an array (namelist_fields) # 2nd: Combine multi-element values (to allow commas in input variables) # 3rd: Loop through the fields to change the values of the variables. declared_namelists = [ ] # keep track of the namelists we've found so far namelist_fields = [] # entries in a given namelist innml = False # are we in a namelist now? Don't enter multiple # split up the input file into separate fields by comma for line in lines: # Skip title lines (we are flexible here) and comments if not innml and not line.strip().startswith('&'): continue if line.strip().startswith('#') or line.strip().startswith('!'): continue # Catch some errors if innml and line.strip().startswith('&'): raise InputError( 'Invalid input. Terminate each namelist prior ' + 'to starting another one.') # End of a namelist elif innml and line.strip() in ['/', '&end']: innml = False # Now if we finally find a namelist elif not innml and line.strip().startswith('&'): innml = True namelist = line.strip()[1:].lower() namelist = self._full_namelist_name(namelist) if namelist in declared_namelists: raise InputError('Namelist %s specified multiple times' % namelist) self.namelists[namelist].Open() declared_namelists.append(namelist) namelist_fields.append([]) # We are in a namelist here, now fill in the fields elif innml: items = line.strip().split(',') # Screen any blank fields j = 0 while j < len(items): items[j] = items[j].strip() if len(items[j]) == 0: items.pop(j) else: j += 1 namelist_fields[len(namelist_fields) - 1].extend(items) # end if [elif innml] # end for line in lines # Combine any multi-element fields into the last field that has a = in it begin_field = -1 for i in range(len(namelist_fields)): for j in range(len(namelist_fields[i])): if not '=' in namelist_fields[i][j]: if begin_field == -1: raise InputError('Invalid input file! Error reading ' + 'namelist %s' % declared_namelists[i]) else: namelist_fields[i][begin_field] += \ ',%s' % namelist_fields[i][j] else: begin_field = j # Now parse through the items to add them to the master dictionary. Note # that thanks to the last step, all data in namelist_fields will be # contained within fields that have a '='. All others can be ignored for i in range(len(namelist_fields)): for j in range(len(namelist_fields[i])): if not '=' in namelist_fields[i][j]: continue else: var = namelist_fields[i][j].split('=') var[0] = var[0].strip() var[1] = var[1].strip() # Now we have to loop through all variables in that namelist to # see if this is the variable we want. found = False for key in list(self.namelists[ declared_namelists[i]].variables.keys()): if self.namelists[declared_namelists[i]].variables[key] == \ var[0]: self.namelists[declared_namelists[i]].variables[key]. \ SetValue(var[1]) found = True break if not found: raise InputError('Unknown variable %s in &%s' % (var[0], declared_namelists[i])) # Now it's time to fill the INPUT dictionary INPUT = {} for nml in self.ordered_namelist_keys: for var in list(self.namelists[nml].variables.keys()): # Here, the triggers are just bool types, so protect from accessing # an attribute that doesn't exist! We only allow Variable types and # bool types var_object = self.namelists[nml].variables[var] try: INPUT[var] = self.namelists[nml].variables[var].value except AttributeError: if isinstance(var_object, bool): INPUT[var] = var_object else: raise InputError('Disallowed namelist variable type') return INPUT