diff --git a/CODEOWNERS b/CODEOWNERS index 39541807..b5662f11 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,7 +3,7 @@ # These owners will be the default owners for everything in the repo. #* @defunkt -* @climbfuji @grantfirl @gold2718 @mkavulich +* @climbfuji @gold2718 @dustinswales @mwaxmonsky @peverwhee @grantfirl @mkavulich # Order is important. The last matching pattern has the most precedence. # So if a pull request only touches javascript files, only these owners diff --git a/doc/HelloWorld/CMakeLists.txt b/doc/HelloWorld/CMakeLists.txt index 4b0cb208..7b480203 100644 --- a/doc/HelloWorld/CMakeLists.txt +++ b/doc/HelloWorld/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +CMAKE_MINIMUM_REQUIRED(VERSION 3.10) PROJECT(HelloWorld) ENABLE_LANGUAGE(Fortran) @@ -166,7 +166,8 @@ list(APPEND DTABLE_CMD "${CCPP_CAP_FILES}/datatable.xml") list(APPEND DTABLE_CMD "--ccpp-files") list(APPEND DTABLE_CMD "--separator=\\;") string(REPLACE ";" " " DTABLE_STRING "${DTABLE_CMD}") -MESSAGE(STATUS "Running: ${DTABLE_STRING}") +string(STRIP ${DTABLE_STRING} DTABLE_STRING) +MESSAGE(STATUS "Running: ${DTABLE_STRING};") EXECUTE_PROCESS(COMMAND ${DTABLE_CMD} OUTPUT_VARIABLE CCPP_CAPS RESULT_VARIABLE RES OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) diff --git a/scripts/ccpp_capgen.py b/scripts/ccpp_capgen.py index 7a12729a..dfa2e211 100755 --- a/scripts/ccpp_capgen.py +++ b/scripts/ccpp_capgen.py @@ -15,6 +15,7 @@ import logging import re # CCPP framework imports +from ccpp_database_obj import CCPPDatabaseObj from ccpp_datafile import generate_ccpp_datatable from ccpp_suite import API from file_utils import check_for_writeable_file, remove_dir, replace_paths @@ -559,7 +560,7 @@ def clean_capgen(cap_output_file, logger): set_log_level(logger, log_level) ############################################################################### -def capgen(run_env): +def capgen(run_env, return_db=False): ############################################################################### """Parse indicated host, scheme, and suite files. Generate code to allow host model to run indicated CCPP suites.""" @@ -650,6 +651,10 @@ def capgen(run_env): generate_ccpp_datatable(run_env, host_model, ccpp_api, scheme_headers, scheme_tdict, host_files, cap_filenames, kinds_file, src_dir) + if return_db: + return CCPPDatabaseObj(run_env, host_model=host_model, api=ccpp_api) + # end if + return None ############################################################################### def _main_func(): @@ -665,7 +670,7 @@ def _main_func(): if framework_env.clean: clean_capgen(framework_env.datatable_file, framework_env.logger) else: - capgen(framework_env) + _ = capgen(framework_env) # end if (clean) ############################################################################### diff --git a/scripts/ccpp_database_obj.py b/scripts/ccpp_database_obj.py new file mode 100644 index 00000000..24579750 --- /dev/null +++ b/scripts/ccpp_database_obj.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +""" +Define the CCPPDatabaseObj object +Object definition and methods to provide information from a run of capgen. +""" + +from host_model import HostModel +from ccpp_suite import API + +class CCPPDatabaseObjError(ValueError): + """Error class specific to CCPPDatabaseObj. + All uses of this error should be internal (i.e., programmer error, + not user error).""" + + def __init__(self, message): + """Initialize this exception""" + super().__init__(message) + +class CCPPDatabaseObj: + """Object with data and methods to provide information from a run of capgen. + """ + + def __init__(self, run_env, host_model=None, api=None, database_file=None): + """Initialize this CCPPDatabaseObj. + If is not None, all other inputs MUST be None and + the object is created from the database table created by capgen. + To initialize the object from an in-memory capgen run, ALL other + inputs MUST be passed (i.e., not None) and it is an error to pass + a value for . + """ + + runtime_obj = all([host_model is not None, api is not None]) + self.__host_model = None + self.__api = None + self.__database_file = None + if runtime_obj and database_file: + emsg = "Cannot provide both runtime arguments and database_file." + elif (not runtime_obj) and (not database_file): + emsg = "Must provide either database_file or all runtime arguments." + else: + emsg = "" + # end if + if emsg: + raise CCPPDatabaseObjError(f"ERROR: {emsg}") + # end if + if runtime_obj: + self.__host_model = host_model + self.__api = api + else: + self.db_from_file(run_env, database_file) + # end if + + def db_from_file(self, run_env, database_file): + """Create the necessary internal data structures from a CCPP + datatable.xml file created by capgen. + """ + metadata_tables = {} + host_name = "host" + self.__host_model = HostModel(metadata_tables, host_name, run_env) + self.__api = API(sdfs, host_model, scheme_headers, run_env) + raise CCPPDatabaseObjError("ERROR: not supported") + + def host_model_dict(self): + """Return the host model dictionary for this CCPP DB object""" + if self.__host_model is not None: + return self.__host_model + # end if + raise CCPPDatabaseObjError("ERROR: not supported") + + def suite_list(self): + """Return a list of suites built into the API""" + if self.__api is not None: + return list(self.__api.suites) + # end if + raise CCPPDatabaseObjError("ERROR: not supported") + + def constituent_dictionary(self, suite): + """Return the constituent dictionary for """ + return suite.constituent_dictionary() + + def call_list(self, phase): + """Return the API call list for """ + if self.__api is not None: + return self.__api.call_list(phase) + # end if + raise CCPPDatabaseObjError("ERROR: not supported") diff --git a/scripts/ccpp_datafile.py b/scripts/ccpp_datafile.py index 158d9dec..562a8f4b 100755 --- a/scripts/ccpp_datafile.py +++ b/scripts/ccpp_datafile.py @@ -27,10 +27,6 @@ from parse_tools import read_xml_file, PrettyElementTree from suite_objects import VerticalLoop, Subcycle -# Find python version -PY3 = sys.version_info[0] > 2 -PYSUBVER = sys.version_info[1] - # Global data _INDENT_STR = " " @@ -652,7 +648,7 @@ def _new_var_entry(parent, var, full_entry=True): """Create a variable sub-element of with information from . If is False, only include standard name and intent. """ - prop_list = ["intent"] + prop_list = ["intent", "local_name"] if full_entry: prop_list.extend(["allocatable", "active", "default_value", "diagnostic_name", "diagnostic_name_fixed", @@ -671,9 +667,13 @@ def _new_var_entry(parent, var, full_entry=True): if full_entry: dims = var.get_dimensions() if dims: - dim_entry = ET.SubElement(ventry, "dimensions") - dim_entry.text = " ".join(dims) + v_entry = ET.SubElement(ventry, "dimensions") + v_entry.text = " ".join(dims) # end if + v_entry = ET.SubElement(ventry, "source_type") + v_entry.text = var.source.ptype.lower() + v_entry = ET.SubElement(ventry, "source_name") + v_entry.text = var.source.name.lower() # end if ############################################################################### diff --git a/scripts/ccpp_suite.py b/scripts/ccpp_suite.py index 8b9a1aeb..c4d7fbab 100644 --- a/scripts/ccpp_suite.py +++ b/scripts/ccpp_suite.py @@ -17,8 +17,7 @@ from fortran_tools import FortranWriter from framework_env import CCPPFrameworkEnv from metavar import Var, VarDictionary, ccpp_standard_var -from metavar import CCPP_CONSTANT_VARS, CCPP_LOOP_VAR_STDNAMES -from parse_tools import ParseContext, ParseSource, context_string +from parse_tools import ParseContext, ParseSource from parse_tools import ParseInternalError, CCPPError from parse_tools import read_xml_file, validate_xml_file, find_schema_version from parse_tools import init_log, set_log_to_null @@ -31,12 +30,12 @@ ############################################################################### # Source for internally generated variables. -_API_SOURCE_NAME = "CCPP_API" +API_SOURCE_NAME = "CCPP_API" # Use the constituent source type for consistency _API_SUITE_VAR_NAME = ConstituentVarDict.constitutent_source_type() _API_SCHEME_VAR_NAME = "scheme" _API_CONTEXT = ParseContext(filename="ccpp_suite.py") -_API_SOURCE = ParseSource(_API_SOURCE_NAME, _API_SCHEME_VAR_NAME, _API_CONTEXT) +_API_SOURCE = ParseSource(API_SOURCE_NAME, _API_SCHEME_VAR_NAME, _API_CONTEXT) _API_LOGGING = init_log('ccpp_suite') set_log_to_null(_API_LOGGING) _API_DUMMY_RUN_ENV = CCPPFrameworkEnv(_API_LOGGING, @@ -299,7 +298,7 @@ def find_variable(self, standard_name=None, source_var=None, # Remove this entry to avoid looping back here del self.__gvar_stdnames[standard_name] # Let everyone know this is now a Suite variable - var.source = ParseSource(_API_SOURCE_NAME, + var.source = ParseSource(API_SOURCE_NAME, _API_SUITE_VAR_NAME, var.context) self.add_variable(var, self.__run_env) @@ -730,9 +729,8 @@ def write_var_set_loop(ofile, varlist_name, var_list, indent, def write_suite_part_list_sub(self, ofile, errmsg_name, errcode_name): """Write the suite-part list subroutine""" - oline = "suite_name, part_list, {errmsg}, {errcode}" - inargs = oline.format(errmsg=errmsg_name, errcode=errcode_name) - ofile.write("\nsubroutine {}({})".format(API.__part_fname, inargs), 1) + inargs = f"suite_name, part_list, {errmsg_name}, {errcode_name}" + ofile.write(f"subroutine {API.__part_fname}({inargs})", 1) oline = "character(len=*), intent(in) :: suite_name" ofile.write(oline, 2) oline = "character(len=*), allocatable, intent(out) :: part_list(:)" @@ -741,9 +739,9 @@ def write_suite_part_list_sub(self, ofile, errmsg_name, errcode_name): self._errcode_var.write_def(ofile, 2, self) else_str = '' ename = self._errcode_var.get_prop_value('local_name') - ofile.write("{} = 0".format(ename), 2) + ofile.write(f"{ename} = 0", 2) ename = self._errmsg_var.get_prop_value('local_name') - ofile.write("{} = ''".format(ename), 2) + ofile.write(f"{ename} = ''", 2) for suite in self.suites: oline = "{}if(trim(suite_name) == '{}') then" ofile.write(oline.format(else_str, suite.name), 2) @@ -751,12 +749,12 @@ def write_suite_part_list_sub(self, ofile, errmsg_name, errcode_name): else_str = 'else ' # end for ofile.write("else", 2) - emsg = "write({errmsg}, '(3a)')".format(errmsg=errmsg_name) + emsg = f"write({errmsg_name}, '(3a)')" emsg += "'No suite named ', trim(suite_name), ' found'" ofile.write(emsg, 3) - ofile.write("{errcode} = 1".format(errcode=errcode_name), 3) + ofile.write(f"{errcode_name} = 1", 3) ofile.write("end if", 2) - ofile.write("end subroutine {}".format(API.__part_fname), 1) + ofile.write(f"end subroutine {API.__part_fname}", 1) def write_req_vars_sub(self, ofile, errmsg_name, errcode_name): """Write the required variables subroutine""" @@ -807,9 +805,9 @@ def write_req_vars_sub(self, ofile, errmsg_name, errcode_name): parent = suite.parent # Collect all the suite variables oline = "{}if(trim(suite_name) == '{}') then" - input_vars = [set(), set(), set()] # leaves, arrrays, leaf elements - inout_vars = [set(), set(), set()] # leaves, arrrays, leaf elements - output_vars = [set(), set(), set()] # leaves, arrrays, leaf elements + input_vars = [set(), set(), set()] # leaves, arrays, leaf elements + inout_vars = [set(), set(), set()] # leaves, arrays, leaf elements + output_vars = [set(), set(), set()] # leaves, arrays, leaf elements for part in suite.groups: for var in part.call_list.variable_list(): stdname = var.get_prop_value("standard_name") diff --git a/scripts/common.py b/scripts/common.py index a4b43eb7..a1bc0efd 100755 --- a/scripts/common.py +++ b/scripts/common.py @@ -169,14 +169,7 @@ def escape_tex(text): def isstring(s): """Return true if a variable is a string""" - # We use Python 3 - if (sys.version_info.major == 3): - return isinstance(s, str) - # We use Python 2 - elif (sys.version_info.major == 2): - return isinstance(s, basestring) - else: - raise Exception('Unknown Python version') + return isinstance(s, str) def string_to_python_identifier(string): """Replaces forbidden characters in strings with standard substitutions diff --git a/scripts/constituents.py b/scripts/constituents.py index be8d4774..659f997f 100644 --- a/scripts/constituents.py +++ b/scripts/constituents.py @@ -398,7 +398,7 @@ def constituent_module_name(self): if not ((self.parent is not None) and hasattr(self.parent.parent, "constituent_module")): emsg = "ConstituentVarDict parent not HostModel?" - emsg += "\nparent is '{}'".format(type(self.parent.parent)) + emsg += f"\nparent is '{type_name(self.parent.parent)}'" raise ParseInternalError(emsg) # end if return self.parent.parent.constituent_module diff --git a/scripts/file_utils.py b/scripts/file_utils.py index 8cdfd023..669922b9 100644 --- a/scripts/file_utils.py +++ b/scripts/file_utils.py @@ -13,11 +13,6 @@ import os # CCPP framework imports from parse_tools import CCPPError, ParseInternalError -#XXgoldyXX: v Crap required to support python 2 -import sys -# Find python version -PY3 = sys.version_info[0] > 2 -#XXgoldyXX: ^ Crap required to support python 2 # Standardize name of generated kinds file and module KINDS_MODULE = 'ccpp_kinds' @@ -300,13 +295,7 @@ def move_modified_files(src_dir, dest_dir, overwrite=False, remove_src=False): fmove = True # end if if fmove: -#XXgoldyXX: v Crap required to support python 2 - if PY3: - os.replace(src_path, dest_path) - else: - os.rename(src_path, dest_path) - # end if -#XXgoldyXX: ^ Crap required to support python 2 + os.replace(src_path, dest_path) else: os.remove(src_path) # end if diff --git a/scripts/fortran_tools/parse_fortran.py b/scripts/fortran_tools/parse_fortran.py index 624f02cb..2310e13f 100644 --- a/scripts/fortran_tools/parse_fortran.py +++ b/scripts/fortran_tools/parse_fortran.py @@ -532,7 +532,8 @@ def class_match(cls, line): @classmethod def type_def_line(cls, line): """Return a type information if represents the start - of a type definition""" + of a type definition. + Otherwise, return None""" type_def = None if not cls.type_match(line): if '!' in line: @@ -629,7 +630,8 @@ def ftype_factory(line, context): def fortran_type_definition(line): ######################################################################## """Return a type information if represents the start - of a type definition""" + of a type definition. + Otherwise, return None.""" return FtypeTypeDecl.type_def_line(line) ######################################################################## @@ -720,8 +722,8 @@ def parse_fortran_var_decl(line, source, run_env): varprops = Ftype.parse_attr_specs(elements[0].strip(), context) for prop in varprops: if prop[0:6] == 'intent': - if source.type != 'scheme': - typ = source.type + if source.ptype != 'scheme': + typ = source.ptype errmsg = 'Invalid variable declaration, {}, intent' errmsg = errmsg + ' not allowed in {} variable' if run_env.logger is not None: diff --git a/scripts/framework_env.py b/scripts/framework_env.py index 8ee553f4..6456134e 100644 --- a/scripts/framework_env.py +++ b/scripts/framework_env.py @@ -103,8 +103,8 @@ def __init__(self, logger, ndict=None, verbose=0, clean=False, # String of definitions, separated by spaces preproc_list = [x.strip() for x in preproc_defs.split(' ') if x] else: - wmsg = "Error: Bad preproc list type, '{}'" - emsg += esep + wmsg.format(type(preproc_defs)) + wmsg = f"Error: Bad preproc list type, '{type_name(preproc_defs)}'" + emsg += esep + wmsg esep = '\n' # end if # Turn the list into a dictionary diff --git a/scripts/host_cap.py b/scripts/host_cap.py index 9c88cf34..f7447de2 100644 --- a/scripts/host_cap.py +++ b/scripts/host_cap.py @@ -8,7 +8,7 @@ import logging import os # CCPP framework imports -from ccpp_suite import API +from ccpp_suite import API, API_SOURCE_NAME from ccpp_state_machine import CCPP_STATE_MACH from constituents import ConstituentVarDict, CONST_DDT_NAME, CONST_DDT_MOD from ddt_library import DDTLibrary @@ -32,9 +32,7 @@ end subroutine {host_model}_ccpp_physics_{stage} ''' -_API_SRC_NAME = "CCPP_API" - -_API_SOURCE = ParseSource(_API_SRC_NAME, "MODULE", +_API_SOURCE = ParseSource(API_SOURCE_NAME, "MODULE", ParseContext(filename="host_cap.F90")) _API_DUMMY_RUN_ENV = CCPPFrameworkEnv(None, ndict={'host_files':'', @@ -60,7 +58,7 @@ 'suites':''}) # Used to prevent loop substitution lookups -_BLANK_DICT = VarDictionary(_API_SRC_NAME, _MVAR_DUMMY_RUN_ENV) +_BLANK_DICT = VarDictionary(API_SOURCE_NAME, _MVAR_DUMMY_RUN_ENV) ############################################################################### def suite_part_list(suite, stage): @@ -475,7 +473,7 @@ def write_host_cap(host_model, api, output_dir, run_env): subst_dict['intent'] = 'inout' # End if hdvars.append(hvar.clone(subst_dict, - source_name=_API_SRC_NAME)) + source_name=API_SOURCE_NAME)) # End for lnames = [x.get_prop_value('local_name') for x in apivars + hdvars] api_vlist = ", ".join(lnames) diff --git a/scripts/metadata_table.py b/scripts/metadata_table.py index 2ed71c2e..5ccf34b3 100755 --- a/scripts/metadata_table.py +++ b/scripts/metadata_table.py @@ -158,7 +158,7 @@ def blank_metadata_line(line): def _parse_config_line(line, context): """Parse a config line and return a list of keyword value pairs.""" - parse_items = list() + parse_items = [] if line is None: pass # No properties on this line elif blank_metadata_line(line): @@ -182,8 +182,8 @@ def _parse_config_line(line, context): def parse_metadata_file(filename, known_ddts, run_env): """Parse and return list of parsed metadata tables""" # Read all lines of the file at once - meta_tables = list() - table_titles = list() # Keep track of names in file + meta_tables = [] + table_titles = [] # Keep track of names in file with open(filename, 'r') as infile: fin_lines = infile.readlines() for index, fin_line in enumerate(fin_lines): @@ -225,7 +225,7 @@ def find_scheme_names(filename): """Find and return a list of all the physics scheme names in . A scheme is identified by its ccpp-table-properties name. """ - scheme_names = list() + scheme_names = [] with open(filename, 'r') as infile: fin_lines = infile.readlines() # end with @@ -283,7 +283,7 @@ def __init__(self, run_env, table_name_in=None, table_type_in=None, self.__pobj = parse_object self.__dependencies = dependencies self.__relative_path = relative_path - self.__sections = list() + self.__sections = [] self.__run_env = run_env if parse_object is None: if table_name_in is not None: @@ -339,7 +339,7 @@ def __init__(self, run_env, table_name_in=None, table_type_in=None, raise ParseInternalError(perr) # end if if known_ddts is None: - known_ddts = list() + known_ddts = [] # end if self.__start_context = ParseContext(context=self.__pobj) self.__init_from_file(known_ddts, self.__run_env) @@ -351,7 +351,7 @@ def __init_from_file(self, known_ddts, run_env): curr_line, _ = self.__pobj.next_line() in_properties_header = True skip_rest_of_section = False - self.__dependencies = list() # Default is no dependencies + self.__dependencies = [] # Default is no dependencies # Process lines until the end of the file or start of the next table. while ((curr_line is not None) and (not MetadataTable.table_start(curr_line))): @@ -440,7 +440,7 @@ def __init_from_file(self, known_ddts, run_env): known_ddts.append(self.table_name) # end if if self.__dependencies is None: - self.__dependencies = list() + self.__dependencies = [] # end if def start_context(self, with_comma=True, nodir=True): @@ -686,7 +686,7 @@ def __init__(self, table_name, table_type, run_env, parse_object=None, self.__start_context = None else: if known_ddts is None: - known_ddts = list() + known_ddts = [] # end if self.__start_context = ParseContext(context=self.__pobj) self.__init_from_file(table_name, table_type, known_ddts, run_env) @@ -696,7 +696,7 @@ def __init__(self, table_name, table_type, run_env, parse_object=None, register_fortran_ddt_name(self.title) # end if # Categorize the variables - self._var_intents = {'in' : list(), 'out' : list(), 'inout' : list()} + self._var_intents = {'in' : [], 'out' : [], 'inout' : []} for var in self.variable_list(): intent = var.get_prop_value('intent') if intent is not None: @@ -706,7 +706,7 @@ def __init__(self, table_name, table_type, run_env, parse_object=None, def _default_module(self): """Set a default module for this header""" - mfile = self.__pobj.file_name + mfile = self.__pobj.filename if mfile[-5:] == '.meta': # Default value is a Fortran module that matches the filename def_mod = os.path.basename(mfile)[:-5] @@ -901,7 +901,7 @@ def parse_variable(self, curr_line, known_ddts): # Special case for dimensions, turn them into ranges if pname == 'dimensions': porig = pval - pval = list() + pval = [] for dim in porig: if ':' in dim: pval.append(dim) @@ -988,7 +988,7 @@ def check_array_reference(local_name, var_dict, context): local_name, colon_rank, ctx)) # end if - sub_dims = list() + sub_dims = [] sindex = 0 for rind in rdims: if rind == ':': @@ -1023,7 +1023,7 @@ def convert_dims_to_standard_names(self, var, logger=None, context=None): """Convert the dimension elements in to standard names by by using other variables in this header. """ - std_dims = list() + std_dims = [] vdims = var.get_dimensions() # Check for bad dimensions if vdims is None: @@ -1049,7 +1049,7 @@ def convert_dims_to_standard_names(self, var, logger=None, context=None): raise CCPPError("{}".format(errmsg)) # end if for dim in vdims: - std_dim = list() + std_dim = [] if ':' not in dim: # Metadata dimensions always have an explicit start var_one = CCPP_CONSTANT_VARS.find_local_name('1') @@ -1069,7 +1069,7 @@ def convert_dims_to_standard_names(self, var, logger=None, context=None): # Some non-standard integer value dname = item # end if - except ValueError: + except ValueError as verr: # Not an integer, try to find the standard_name if not item: # Naked colons are okay @@ -1083,15 +1083,15 @@ def convert_dims_to_standard_names(self, var, logger=None, context=None): # end if # end if if dname is None: - errmsg = "Unknown dimension element, {}, in {}{}" std = var.get_prop_value('local_name') - ctx = context_string(context) + errmsg = f"Unknown dimension element, {item}, in {std}" + errmsg += context_string(context) if logger is not None: errmsg = "ERROR: " + errmsg logger.error(errmsg.format(item, std, ctx)) dname = unique_standard_name() else: - raise CCPPError(errmsg.format(item, std, ctx)) + raise CCPPError(errmsg) from verr # end if # end if # end try diff --git a/scripts/metavar.py b/scripts/metavar.py index 71609580..64067dd1 100755 --- a/scripts/metavar.py +++ b/scripts/metavar.py @@ -20,7 +20,7 @@ from parse_tools import check_units, check_dimensions, check_cf_standard_name from parse_tools import check_diagnostic_id, check_diagnostic_fixed from parse_tools import check_default_value, check_valid_values -from parse_tools import ParseContext, ParseSource +from parse_tools import ParseContext, ParseSource, type_name from parse_tools import ParseInternalError, ParseSyntaxError, CCPPError from var_props import CCPP_LOOP_DIM_SUBSTS, VariableProperty, VarCompatObj from var_props import find_horizontal_dimension, find_vertical_dimension @@ -261,7 +261,7 @@ def __init__(self, prop_dict, source, run_env, context=None, if isinstance(prop_dict, Var): prop_dict = prop_dict.copy_prop_dict() # end if - if source.type == 'scheme': + if source.ptype == 'scheme': self.__required_props = Var.__required_var_props # XXgoldyXX: v don't fill in default properties? # mstr_propdict = Var.__var_propdict @@ -272,7 +272,7 @@ def __init__(self, prop_dict, source, run_env, context=None, mstr_propdict = Var.__spec_propdict # XXgoldyXX: ^ don't fill in default properties? # end if - self._source = source + self.__source = source # Grab a frozen copy of the context if context is None: self._context = ParseContext(context=source.context) @@ -476,7 +476,7 @@ def clone(self, subst_dict=None, remove_intent=False, source_name = self.source.name # end if if source_type is None: - source_type = self.source.type + source_type = self.source.ptype # end if if context is None: context = self._context @@ -822,7 +822,7 @@ def parent(self, parent_var): else: emsg = 'Attempting to set parent for {}, bad parent type, {}' lname = self.get_prop_value('local_name') - raise ParseInternalError(emsg.format(lname, type(parent_var))) + raise ParseInternalError(emsg.format(lname, type_name(parent_var))) # end if def add_child(self, cvar): @@ -852,13 +852,13 @@ def context(self): @property def source(self): """Return the source object for this variable""" - return self._source + return self.__source @source.setter def source(self, new_source): """Reset this Var's source if seems legit""" if isinstance(new_source, ParseSource): - self._source = new_source + self.__source = new_source else: errmsg = 'Attemping to set source of {} ({}) to "{}"' stdname = self.get_prop_value('standard_name') @@ -874,7 +874,7 @@ def clone_source(self): @property def host_interface_var(self): """True iff self is included in the host model interface calls""" - return self.source.type == 'host' + return self.source.ptype == 'host' @property def run_env(self): @@ -1427,7 +1427,7 @@ def __init__(self, name, run_env, variables=None, self[stdname] = variables[key] # end for elif variables is not None: - raise ParseInternalError('Illegal type for variables, {} in {}'.format(type(variables), self.name)) + raise ParseInternalError(f'Illegal type for variables, {type_name(variables)} in {self.name}') # end if @property @@ -1608,7 +1608,7 @@ def add_variable_dimensions(self, var, ignore_sources, to_dict=None, # end if if not present: dvar = self.find_variable(standard_name=dimname, any_scope=True) - if dvar and (dvar.source.type not in ignore_sources): + if dvar and (dvar.source.ptype not in ignore_sources): if to_dict: to_dict.add_variable(dvar, self.__run_env, exists_ok=True, @@ -1623,15 +1623,13 @@ def add_variable_dimensions(self, var, ignore_sources, to_dict=None, else: ctx = context_string(var.context) # end if - err_ret += "{}: ".format(self.name) - err_ret += "Cannot find variable for dimension, {}, of {}{}" vstdname = var.get_prop_value('standard_name') - err_ret = err_ret.format(dimname, vstdname, ctx) + err_ret += f"{self.name}: " + err_ret += f"Cannot find variable for dimension, {dimname}, of {vstdname}{ctx}" if dvar: - err_ret += "\nFound {} from excluded source, '{}'{}" + err_ret += f"\nFound {lname} from excluded source, '{dvar.source.ptype}'{dctx}" lname = dvar.get_prop_value('local_name') dctx = context_string(dvar.context) - err_ret = err_ret.format(lname, dvar.source.type, dctx) # end if # end if # end if diff --git a/scripts/parse_tools/__init__.py b/scripts/parse_tools/__init__.py index 03dd0429..4f888fb1 100644 --- a/scripts/parse_tools/__init__.py +++ b/scripts/parse_tools/__init__.py @@ -8,7 +8,7 @@ # pylint: disable=wrong-import-position from parse_source import ParseContext, ParseSource from parse_source import ParseSyntaxError, ParseInternalError -from parse_source import CCPPError, context_string +from parse_source import CCPPError, context_string, type_name from parse_source import unique_standard_name, reset_standard_name_counter from parse_object import ParseObject from parse_checkers import check_fortran_id, FORTRAN_ID @@ -71,6 +71,7 @@ 'set_log_to_file', 'set_log_to_null', 'set_log_to_stdout', + 'type_name', 'unique_standard_name', 'validate_xml_file' ] diff --git a/scripts/parse_tools/parse_checkers.py b/scripts/parse_tools/parse_checkers.py index 487478e6..ba8722d9 100755 --- a/scripts/parse_tools/parse_checkers.py +++ b/scripts/parse_tools/parse_checkers.py @@ -12,7 +12,13 @@ ######################################################################## -_UNITS_RE = re.compile(r"^[^/@#$%^&*()\|<>\[\]{}?,.]+$") +_UNITLESS_REGEX = "1" +_NON_LEADING_ZERO_NUM = "[1-9]\d*" +_NEGATIVE_NON_LEADING_ZERO_NUM = f"[-]{_NON_LEADING_ZERO_NUM}" +_UNIT_EXPONENT = f"({_NEGATIVE_NON_LEADING_ZERO_NUM}|{_NON_LEADING_ZERO_NUM})" +_UNIT_REGEX = f"[a-zA-Z]+{_UNIT_EXPONENT}?" +_UNITS_REGEX = f"^({_UNIT_REGEX}(\s{_UNIT_REGEX})*|{_UNITLESS_REGEX})$" +_UNITS_RE = re.compile(_UNITS_REGEX) def check_units(test_val, prop_dict, error): """Return if a valid unit, otherwise, None diff --git a/scripts/parse_tools/parse_object.py b/scripts/parse_tools/parse_object.py index 4518d75c..f141b298 100644 --- a/scripts/parse_tools/parse_object.py +++ b/scripts/parse_tools/parse_object.py @@ -35,7 +35,6 @@ class ParseObject(ParseContext): def __init__(self, filename, lines_in, line_start=0): """Initialize this ParseObject""" - self.__filename = filename self.__lines = lines_in self.__line_start = line_start self.__line_end = line_start @@ -43,7 +42,7 @@ def __init__(self, filename, lines_in, line_start=0): self.__num_lines = len(self.__lines) self.__error_message = "" self.__num_errors = 0 - super(ParseObject, self).__init__(linenum=line_start, filename=filename) + super().__init__(linenum=line_start, filename=filename) @property def first_line_num(self): @@ -59,11 +58,6 @@ def valid_line(self): """Return True if the current line is valid""" return (self.line_num >= 0) and (self.line_num < self.__num_lines) - @property - def file_name(self): - """Return this object's filename""" - return self.__filename - @property def error_message(self): """Return this object's error message""" diff --git a/scripts/parse_tools/parse_source.py b/scripts/parse_tools/parse_source.py index 6d28b694..f96bbb2d 100644 --- a/scripts/parse_tools/parse_source.py +++ b/scripts/parse_tools/parse_source.py @@ -2,24 +2,18 @@ """Classes to aid the parsing process""" -import sys -# Find python version -PY3 = sys.version_info[0] > 2 -# pylint: disable=wrong-import-position # Python library imports -if PY3: - from collections.abc import Iterable -else: - from collections import Iterable +from collections.abc import Iterable # end if import copy +import sys import os.path import logging # CCPP framework imports # pylint: enable=wrong-import-position -class _StdNameCounter(object): +class _StdNameCounter(): """Class to hold a global counter to avoid using global keyword""" __SNAME_NUM = 0 # Counter for unique standard names @@ -117,6 +111,12 @@ def context_string(context=None, with_comma=True, nodir=False): # End if return cstr.format(comma=comma, where_str=where_str, ctx=context) +############################################################################### +def type_name(obj): +############################################################################### + """Return the name of the type of """ + return type(obj).__name__ + ############################################################################### class CCPPError(ValueError): """Class so programs can log user errors without backtrace""" @@ -198,7 +198,7 @@ def __getitem__(self, index): ######################################################################## -class ParseContext(object): +class ParseContext(): """A class for keeping track of a parsing position >>> ParseContext(32, "source.F90") #doctest: +ELLIPSIS <__main__.ParseContext object at 0x...> @@ -216,13 +216,6 @@ class ParseContext(object): """ - # python 2/3 difference - try: - __pstr_types = (str, unicode) - except NameError: - __pstr_types = (str,) - # End try - def __init__(self, linenum=None, filename=None, context=None): """Initialize this ParseContext""" # Set regions first in case of exception @@ -245,27 +238,27 @@ def __init__(self, linenum=None, filename=None, context=None): filename = context.filename elif filename is None: filename = "" - elif not isinstance(filename, ParseContext.__pstr_types): + elif not isinstance(filename, str): raise CCPPError('ParseContext filename must be a string') # No else, everything is okay # End if - self._linenum = linenum - self._filename = filename + self.__linenum = linenum + self.__filename = filename @property def line_num(self): """Return the current line""" - return self._linenum + return self.__linenum @line_num.setter def line_num(self, newnum): """Set a new line number for this context""" - self._linenum = newnum + self.__linenum = newnum @property def filename(self): """Return the object's filename""" - return self._filename + return self.__filename @property def regions(self): @@ -274,19 +267,19 @@ def regions(self): def __format__(self, spec): """Return a string representing the location in a file - Note that self._linenum is zero based. + Note that self.__linenum is zero based. can be 'dir' (show filename directory) or 'nodir' filename only. Any other spec entry is ignored. """ if spec == 'dir': - fname = self._filename + fname = self.__filename elif spec == 'nodir': - fname = os.path.basename(self._filename) + fname = os.path.basename(self.__filename) else: - fname = self._filename + fname = self.__filename # End if - if self._linenum >= 0: - fmt_str = "{}:{}".format(fname, self._linenum+1) + if self.__linenum >= 0: + fmt_str = "{}:{}".format(fname, self.__linenum+1) else: fmt_str = "{}".format(fname) # End if @@ -294,21 +287,21 @@ def __format__(self, spec): def __str__(self): """Return a string representing the location in a file - Note that self._linenum is zero based. + Note that self.__linenum is zero based. """ - if self._linenum >= 0: - retstr = "{}:{}".format(self._filename, self._linenum+1) + if self.__linenum >= 0: + retstr = "{}:{}".format(self.__filename, self.__linenum+1) else: - retstr = "{}".format(self._filename) + retstr = "{}".format(self.__filename) # End if return retstr def increment(self, inc=1): """Increment the location within a file""" - if self._linenum < 0: - self._linenum = 0 + if self.__linenum < 0: + self.__linenum = 0 # End if - self._linenum = self._linenum + inc + self.__linenum = self.__linenum + inc def enter_region(self, region_type, region_name=None, nested_ok=True): """Mark the entry of a region (e.g., DDT, module, function). @@ -378,12 +371,12 @@ def region_str(self): ######################################################################## -class ParseSource(object): +class ParseSource(): """ A simple object for providing source information >>> ParseSource("myname", "mytype", ParseContext(13, "foo.F90")) #doctest: +ELLIPSIS <__main__.ParseSource object at 0x...> - >>> ParseSource("myname", "mytype", ParseContext(13, "foo.F90")).type + >>> ParseSource("myname", "mytype", ParseContext(13, "foo.F90")).ptype 'mytype' >>> ParseSource("myname", "mytype", ParseContext(13, "foo.F90")).name 'myname' @@ -393,24 +386,24 @@ class ParseSource(object): def __init__(self, name_in, type_in, context_in): """Initialize this ParseSource object.""" - self._name = name_in - self._type = type_in - self._context = context_in + self.__name = name_in + self.__type = type_in + self.__context = context_in @property - def type(self): + def ptype(self): """Return this source's type""" - return self._type + return self.__type @property def name(self): """Return this source's name""" - return self._name + return self.__name @property def context(self): """Return this source's context""" - return self._context + return self.__context ######################################################################## diff --git a/scripts/suite_objects.py b/scripts/suite_objects.py index 472556cb..cc34a750 100644 --- a/scripts/suite_objects.py +++ b/scripts/suite_objects.py @@ -361,7 +361,7 @@ def register_action(self, vaction): @classmethod def is_suite_variable(cls, var): """Return True iff belongs to our Suite""" - return var and (var.source.type == _API_SUITE_VAR_NAME) + return var and (var.source.ptype == _API_SUITE_VAR_NAME) def is_local_variable(self, var): """Return the local variable matching if one is found belonging @@ -433,7 +433,7 @@ def add_call_list_variable(self, newvar, exists_ok=False, if dvar is None: emsg = "{}: Could not find dimension {} in {}" raise ParseInternalError(emsg.format(self.name, - stdname, vardim)) + vardim, stdname)) # end if elif self.parent is None: errmsg = 'No call_list found for {}'.format(newvar) @@ -831,7 +831,7 @@ def match_variable(self, var, vstdname=None, vdims=None): found_var = self.parent.add_variable_to_call_tree(dict_var, vmatch=vmatch) new_vdims = vdims - elif dict_var.source.type in _API_LOCAL_VAR_TYPES: + elif dict_var.source.ptype in _API_LOCAL_VAR_TYPES: # We cannot change the dimensions of locally-declared variables # Using a loop substitution is invalid because the loop variable # value has not yet been set. @@ -1716,7 +1716,7 @@ def find_variable(self, standard_name=None, source_var=None, search_call_list=search_call_list, loop_subst=loop_subst) if fvar and fvar.is_constituent(): - if fvar.source.type == ConstituentVarDict.constitutent_source_type(): + if fvar.source.ptype == ConstituentVarDict.constitutent_source_type(): # We found this variable in the constituent dictionary, # add it to our call list self.add_call_list_variable(fvar, exists_ok=True) diff --git a/test/advection_test/cld_ice.F90 b/test/advection_test/cld_ice.F90 index 5a91282f..a7da57e5 100644 --- a/test/advection_test/cld_ice.F90 +++ b/test/advection_test/cld_ice.F90 @@ -19,7 +19,7 @@ MODULE cld_ice !> \section arg_table_cld_ice_run Argument Table !! \htmlinclude arg_table_cld_ice_run.html !! - subroutine cld_ice_run(ncol, timestep, temp, qv, ps, cld_ice, & + subroutine cld_ice_run(ncol, timestep, temp, qv, ps, cld_ice_array, & errmsg, errflg) integer, intent(in) :: ncol @@ -27,7 +27,7 @@ subroutine cld_ice_run(ncol, timestep, temp, qv, ps, cld_ice, & real(kind_phys), intent(inout) :: temp(:,:) real(kind_phys), intent(inout) :: qv(:,:) real(kind_phys), intent(in) :: ps(:) - REAL(kind_phys), intent(inout) :: cld_ice(:,:) + REAL(kind_phys), intent(inout) :: cld_ice_array(:,:) character(len=512), intent(out) :: errmsg integer, intent(out) :: errflg !---------------------------------------------------------------- @@ -44,7 +44,7 @@ subroutine cld_ice_run(ncol, timestep, temp, qv, ps, cld_ice, & do ilev = 1, size(temp, 2) if (temp(icol, ilev) < tcld) then frz = MAX(qv(icol, ilev) - 0.5_kind_phys, 0.0_kind_phys) - cld_ice(icol, ilev) = cld_ice(icol, ilev) + frz + cld_ice_array(icol, ilev) = cld_ice_array(icol, ilev) + frz qv(icol, ilev) = qv(icol, ilev) - frz if (frz > 0.0_kind_phys) then temp(icol, ilev) = temp(icol, ilev) + 1.0_kind_phys @@ -58,16 +58,16 @@ END SUBROUTINE cld_ice_run !> \section arg_table_cld_ice_init Argument Table !! \htmlinclude arg_table_cld_ice_init.html !! - subroutine cld_ice_init(tfreeze, cld_ice, errmsg, errflg) + subroutine cld_ice_init(tfreeze, cld_ice_array, errmsg, errflg) real(kind_phys), intent(in) :: tfreeze - real(kind_phys), intent(inout) :: cld_ice(:,:) + real(kind_phys), intent(inout) :: cld_ice_array(:,:) character(len=512), intent(out) :: errmsg integer, intent(out) :: errflg errmsg = '' errflg = 0 - cld_ice = 0.0_kind_phys + cld_ice_array = 0.0_kind_phys tcld = tfreeze - 20.0_kind_phys end subroutine cld_ice_init diff --git a/test/advection_test/cld_ice.meta b/test/advection_test/cld_ice.meta index e4a961ab..f66888e0 100644 --- a/test/advection_test/cld_ice.meta +++ b/test/advection_test/cld_ice.meta @@ -41,7 +41,7 @@ units = Pa dimensions = (horizontal_loop_extent) intent = in -[ cld_ice ] +[ cld_ice_array ] standard_name = cloud_ice_dry_mixing_ratio advected = .true. units = kg kg-1 @@ -73,7 +73,7 @@ dimensions = () type = real | kind = kind_phys intent = in -[ cld_ice ] +[ cld_ice_array ] standard_name = cloud_ice_dry_mixing_ratio advected = .true. units = kg kg-1 diff --git a/test/advection_test/cld_liq.F90 b/test/advection_test/cld_liq.F90 index 2e1e5a57..9411e0cb 100644 --- a/test/advection_test/cld_liq.F90 +++ b/test/advection_test/cld_liq.F90 @@ -16,7 +16,7 @@ MODULE cld_liq !> \section arg_table_cld_liq_run Argument Table !! \htmlinclude arg_table_cld_liq_run.html !! - subroutine cld_liq_run(ncol, timestep, tcld, temp, qv, ps, cld_liq, & + subroutine cld_liq_run(ncol, timestep, tcld, temp, qv, ps, cld_liq_array, & errmsg, errflg) integer, intent(in) :: ncol @@ -25,7 +25,7 @@ subroutine cld_liq_run(ncol, timestep, tcld, temp, qv, ps, cld_liq, & real(kind_phys), intent(inout) :: temp(:,:) real(kind_phys), intent(inout) :: qv(:,:) real(kind_phys), intent(in) :: ps(:) - REAL(kind_phys), intent(inout) :: cld_liq(:,:) + REAL(kind_phys), intent(inout) :: cld_liq_array(:,:) character(len=512), intent(out) :: errmsg integer, intent(out) :: errflg !---------------------------------------------------------------- @@ -43,7 +43,7 @@ subroutine cld_liq_run(ncol, timestep, tcld, temp, qv, ps, cld_liq, & if ( (qv(icol, ilev) > 0.0_kind_phys) .and. & (temp(icol, ilev) <= tcld)) then cond = MIN(qv(icol, ilev), 0.1_kind_phys) - cld_liq(icol, ilev) = cld_liq(icol, ilev) + cond + cld_liq_array(icol, ilev) = cld_liq_array(icol, ilev) + cond qv(icol, ilev) = qv(icol, ilev) - cond if (cond > 0.0_kind_phys) then temp(icol, ilev) = temp(icol, ilev) + (cond * 5.0_kind_phys) @@ -57,10 +57,10 @@ END SUBROUTINE cld_liq_run !> \section arg_table_cld_liq_init Argument Table !! \htmlinclude arg_table_cld_liq_init.html !! - subroutine cld_liq_init(tfreeze, cld_liq, tcld, errmsg, errflg) + subroutine cld_liq_init(tfreeze, cld_liq_array, tcld, errmsg, errflg) real(kind_phys), intent(in) :: tfreeze - real(kind_phys), intent(out) :: cld_liq(:,:) + real(kind_phys), intent(out) :: cld_liq_array(:,:) real(kind_phys), intent(out) :: tcld character(len=512), intent(out) :: errmsg integer, intent(out) :: errflg @@ -69,7 +69,7 @@ subroutine cld_liq_init(tfreeze, cld_liq, tcld, errmsg, errflg) errmsg = '' errflg = 0 - cld_liq = 0.0_kind_phys + cld_liq_array = 0.0_kind_phys tcld = tfreeze - 20.0_kind_phys end subroutine cld_liq_init diff --git a/test/advection_test/cld_liq.meta b/test/advection_test/cld_liq.meta index 1186d741..4c87f4b7 100644 --- a/test/advection_test/cld_liq.meta +++ b/test/advection_test/cld_liq.meta @@ -47,7 +47,7 @@ units = Pa dimensions = (horizontal_loop_extent) intent = in -[ cld_liq ] +[ cld_liq_array ] standard_name = cloud_liquid_dry_mixing_ratio advected = .true. units = kg kg-1 @@ -79,7 +79,7 @@ dimensions = () type = real | kind = kind_phys intent = in -[ cld_liq ] +[ cld_liq_array ] standard_name = cloud_liquid_dry_mixing_ratio advected = .true. units = kg kg-1