Пример #1
0
def read_xml_file(filename, logger=None):
###############################################################################
    """Read the XML file, <filename>, and return its tree and root"""
    if os.path.isfile(filename) and os.access(filename, os.R_OK):
        if PY3:
            file_open = (lambda x: open(x, 'r', encoding='utf-8'))
        else:
            file_open = (lambda x: open(x, 'r'))
        # End if
        with file_open(filename) as file_:
            try:
                tree = ET.parse(file_)
                root = tree.getroot()
            except ET.ParseError as perr:
                emsg = "read_xml_file: Cannot read {}, {}"
                raise CCPPError(emsg.format(filename, perr))
    elif not os.access(filename, os.R_OK):
        raise CCPPError("read_xml_file: Cannot open '{}'".format(filename))
    else:
        emsg = "read_xml_file: Filename, '{}', does not exist"
        raise CCPPError(emsg.format(filename))
    # End if
    if logger:
        logger.debug("Read XML file, '{}'".format(filename))
    # End if
    return tree, root
Пример #2
0
def check_diagnostic_fixed(test_val, prop_dict, error):
    """Return <test_val> if a valid descriptor for a CCPP diagnostic,
         otherwise, None.
    If <error> is True, raise an Exception if <value> is not valid.
    A fixed diagnostic name is any Fortran identifier, however, it is
    an error to specify both 'diagnostic_name' and 'diagnostic_name_fixed'.
    >>> check_diagnostic_fixed("foo", {'diagnostic_name_fixed' : 'foo'}, False)
    'foo'
    >>> check_diagnostic_fixed("foo", {'diagnostic_name_fixed' : 'foo'}, True)
    'foo'
    >>> check_diagnostic_fixed("foo", {'diagnostic_name' : 'foo'}, False)

    >>> check_diagnostic_fixed("foo", {'diagnostic_name':'','local_name':'hi','standard_name':'mom'}, True)
    'foo'
    >>> check_diagnostic_fixed("foo", {'diagnostic_name':'foo','local_name':'hi','standard_name':'mom'}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: hi (mom) cannot have both 'diagnostic_name' and 'diagnostic_name_fixed' attributes
    >>> check_diagnostic_fixed("2foo", {'diagnostic_name_fixed':'foo','local_name':'hi','standard_name':'mom'}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: '2foo' (hi) is not a valid fixed diagnostic name
    """
    valid = test_val
    if (prop_dict and ('diagnostic_name' in prop_dict)
            and prop_dict['diagnostic_name']):
        valid = None
        if error:
            emsg = "{} ({}) cannot have both 'diagnostic_name' and "
            emsg += "'diagnostic_name_fixed' attributes"
            if 'local_name' in prop_dict:
                lname = prop_dict['local_name']
            else:
                lname = 'UNKNOWN'
            # end if
            if 'standard_name' in prop_dict:
                sname = prop_dict['standard_name']
            else:
                sname = 'UNKNOWN'
            # end if
            raise CCPPError(emsg.format(lname, sname))
        # end if
    elif check_fortran_id(test_val, prop_dict, False) is None:
        valid = None
        if error:
            emsg = "'{}' ({}) is not a valid fixed diagnostic name"
            if 'local_name' in prop_dict:
                lname = prop_dict['local_name']
            else:
                lname = 'UNKNOWN'
            # end if
            raise CCPPError(emsg.format(test_val, lname))
        # end if
    # end if
    return valid
Пример #3
0
def validate_xml_file(filename,
                      schema_root,
                      version,
                      logger,
                      schema_path=None,
                      error_on_noxmllint=False):
    ###############################################################################
    """
    Find the appropriate schema and validate the XML file, <filename>,
    against it using xmllint
    """
    # Check the filename
    if not os.path.isfile(filename):
        raise CCPPError(
            "validate_xml_file: Filename, '{}', does not exist".format(
                filename))
    # End if
    if not os.access(filename, os.R_OK):
        raise CCPPError("validate_xml_file: Cannot open '{}'".format(filename))
    # End if
    if not schema_path:
        # Find the schema, based on the model version
        thispath = os.path.abspath(__file__)
        pdir = os.path.dirname(os.path.dirname(os.path.dirname(thispath)))
        schema_path = os.path.join(pdir, 'schema')
    # End if
    schema_file = find_schema_file(schema_root, version, schema_path)
    if not (schema_file and os.path.isfile(schema_file)):
        verstring = '.'.join([str(x) for x in version])
        emsg = """validate_xml_file: Cannot find schema for version {},
        {} does not exist"""
        raise CCPPError(emsg.format(verstring, schema_file))
    # End if
    if not os.access(schema_file, os.R_OK):
        emsg = "validate_xml_file: Cannot open schema, '{}'"
        raise CCPPError(emsg.format(schema_file))
    # End if
    if _XMLLINT is not None:
        logger.debug("Checking file {} against schema {}".format(
            filename, schema_file))
        cmd = [_XMLLINT, '--noout', '--schema', schema_file, filename]
        result = call_command(cmd, logger)
        return result
    # End if
    lmsg = "xmllint not found, could not validate file {}"
    if error_on_noxmllint:
        raise CCPPError("validate_xml_file: " + lmsg.format(filename))
    # End if
    logger.warning(lmsg.format(filename))
    return True  # We could not check but still need to proceed
Пример #4
0
def call_command(commands, logger, silent=False):
    ###############################################################################
    """
    Try a command line and return the output on success (None on failure)
    >>> call_command(['ls', 'really__improbable_fffilename.foo'], _LOGGER) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: Execution of 'ls really__improbable_fffilename.foo' failed:
    [Errno 2] No such file or directory
    >>> call_command(['ls', 'really__improbable_fffilename.foo'], _LOGGER, silent=True)
    False
    >>> call_command(['ls'], _LOGGER)
    True
    """
    result = False
    outstr = ''
    try:
        if PY3:
            if PYSUBVER > 6:
                cproc = subprocess.run(commands,
                                       check=True,
                                       capture_output=True)
                if not silent:
                    logger.debug(cproc.stdout)
                # End if
                result = cproc.returncode == 0
            elif PYSUBVER >= 5:
                cproc = subprocess.run(commands,
                                       check=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
                if not silent:
                    logger.debug(cproc.stdout)
                # End if
                result = cproc.returncode == 0
            else:
                raise ValueError("Python 3 must be at least version 3.5")
            # End if
        else:
            pproc = subprocess.Popen(commands,
                                     stdin=None,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
            output, _ = pproc.communicate()
            if not silent:
                logger.debug(output)
            # End if
            result = pproc.returncode == 0
        # End if
    except (OSError, CCPPError, subprocess.CalledProcessError) as err:
        if silent:
            result = False
        else:
            cmd = ' '.join(commands)
            emsg = "Execution of '{}' failed with code:\n"
            outstr = emsg.format(cmd, err.returncode)
            outstr += "{}".format(err.output)
            raise CCPPError(outstr)
        # End if
    # End of try
    return result
Пример #5
0
 def add_syntax_err(self, token_type, token=None, skip_context=False):
     """Add a ParseSyntaxError-type message to this object's error
     log, separating it from any previous messages with a newline."""
     if self.__error_message:
         if self.__num_errors == self._max_errors:
             self.__error_message += '\nMaximum number of errors exceeded'
             self.line_num = self.__num_lines  # Intentionally walk off end
             self.__line_next = self.line_num
         elif self.__num_errors > self._max_errors:
             # Oops, something went wrong, panic!
             raise CCPPError(self.error_message)
         # end if
         self.__error_message += '\n'
     # end if
     if self.__num_errors < self._max_errors:
         if skip_context:
             cstr = ""
         else:
             cstr = context_string(self)
         # end if
         if token is None:
             self.__error_message += "{}{}".format(token_type, cstr)
         else:
             self.__error_message += "Invalid {}, '{}'{}".format(
                 token_type, token, cstr)
         # end if
     # end if
     self.__num_errors += 1
Пример #6
0
 def write_line(self, line_num, line):
     """Overwrite line, <line_num> with <line>.
     If <line_start> is out of bounds, raise an exception."""
     if (line_num < 0) or (line_num >= len(self.__lines)):
         emsg = 'Attempt to write non-existent line, {}'
         raise CCPPError(emsg.format(line_num))
     # end if
     self.__lines[line_num] = line
Пример #7
0
def check_units(test_val, prop_dict, error):
    """Return <test_val> if a valid unit, otherwise, None
    if <error> is True, raise an Exception if <test_val> is not valid.
    >>> check_units('m/s', None, True)
    'm/s'
    >>> check_units('kg m-3', None, True)
    'kg m-3'
    >>> check_units('1', None, True)
    '1'
    >>> check_units('', None, False)

    >>> check_units('', None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: '' is not a valid unit
    >>> check_units(' ', None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: '' is not a valid unit
    >>> check_units(['foo'], None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: ['foo'] is invalid; not a string
    """
    if not isinstance(test_val, str):
        if error:
            raise CCPPError("'{}' is invalid; not a string".format(test_val))
        else:
            test_val = None
        # end if
    else:
        if not test_val.strip():
            if error:
                raise CCPPError("'{}' is not a valid unit".format(test_val))
            else:
                test_val = None
            # end if
        # end if
    # end if

    # DH* 20210812
    # Temporary workaround to convert unit 'none' (used for
    # dimensionless quantities in ccpp-physics/UFS/SCM) to '1'
    if test_val.lower() == 'none':
        test_val = '1'
    # *DH 20210812

    return test_val
Пример #8
0
 def reset_pos(self, line_start=0):
     """Attempt to set the current file position to <line_start>.
     If <line_start> is out of bounds, raise an exception."""
     if (line_start < 0) or (line_start >= self.__num_lines):
         emsg = 'Attempt to reset_pos to non-existent line, {}'
         raise CCPPError(emsg.format(line_start))
     # end if
     self.line_num = line_start
     self.__line_next = line_start
Пример #9
0
def check_fortran_id(test_val, prop_dict, error, max_len=0):
    """Return <test_val> if a valid Fortran identifier, otherwise, None
    If <max_len> > 0, <test_val> must not be longer than <max_len>.
    if <error> is True, raise an Exception if <test_val> is not valid.
    >>> check_fortran_id("hi_mom", None, False)
    'hi_mom'
    >>> check_fortran_id("hi_mom", None, False, max_len=5)

    >>> check_fortran_id("hi_mom", None, True, max_len=5) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'hi_mom' is too long (> 5 chars)
    >>> check_fortran_id("hi mom", None, False)

    >>> check_fortran_id("hi mom", None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'hi_mom' is not a valid Fortran identifier
    >>> check_fortran_id("", None, False)

    >>> check_fortran_id("_hi_mom", None, False)

    >>> check_fortran_id("2pac", None, False)

    >>> check_fortran_id("Agood4tranID", None, False)
    'Agood4tranID'
    """
    match = __FID_RE.match(test_val)
    if match is None:
        if error:
            raise CCPPError(
                "'{}' is not a valid Fortran identifier".format(test_val))
        else:
            test_val = None
        # end if
    elif (max_len > 0) and (len(test_val) > max_len):
        if error:
            raise CCPPError("'{}' is too long (> {} chars)".format(
                test_val, max_len))
        else:
            test_val = None
        # end if
    # end if
    return test_val
Пример #10
0
def check_cf_standard_name(test_val, prop_dict, error):
    """Return <test_val> if a valid CF Standard Name, otherwise, None
    http://cfconventions.org/Data/cf-standard-names/docs/guidelines.html
    if <error> is True, raise an Exception if <test_val> is not valid.
    >>> check_cf_standard_name("hi_mom", None, False)
    'hi_mom'
    >>> check_cf_standard_name("hi mom", None, False)

    >>> check_cf_standard_name("hi mom", None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'hi_mom' is not a valid CF Standard Name
    >>> check_cf_standard_name("", None, False) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: CCPP Standard Name cannot be blank
    >>> check_cf_standard_name("_hi_mom", None, False)

    >>> check_cf_standard_name("2pac", None, False)

    >>> check_cf_standard_name("Agood4tranID", None, False)
    'agood4tranid'
    >>> check_cf_standard_name("agoodcfid", None, False)
    'agoodcfid'
    """
    if len(test_val) == 0:
        raise CCPPError("CCPP Standard Name cannot be blank")
    else:
        match = __CFID_RE.match(test_val)
    # end if
    if match is None:
        if error:
            errmsg = "'{}' is not a valid CCPP Standard Name"
            raise CCPPError(errmsg.format(test_val))
        else:
            test_val = None
        # end if
    else:
        test_val = test_val.lower()
    # end if
    return test_val
Пример #11
0
def check_fortran_type(typestr, prop_dict, error):
    """Return <typestr> if a valid Fortran type, otherwise, None
    if <error> is True, raise an Exception if <typestr> is not valid.
    >>> check_fortran_type("real", None, False)
    'real'
    >>> check_fortran_type("integer", None, False)
    'integer'
    >>> check_fortran_type("InteGer", None, False)
    'InteGer'
    >>> check_fortran_type("character", None, False)
    'character'
    >>> check_fortran_type("double precision", None, False)
    'double precision'
    >>> check_fortran_type("double   precision", None, False)
    'double   precision'
    >>> check_fortran_type("doubleprecision", None, False)
    'doubleprecision'
    >>> check_fortran_type("complex", None, False)
    'complex'
    >>> check_fortran_type("char", {}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'char' is not a valid Fortran type
    >>> check_fortran_type("int", None, False)

    >>> check_fortran_type("char", {}, False)

    >>> check_fortran_type("type", None, False)

    >>> check_fortran_type("type", {}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'type' is not a valid derived Fortran type
    >>> check_fortran_type("type(hi mom)", {}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'type(hi mom)' is not a valid derived Fortran type
    """
    dt = ""
    match = check_fortran_intrinsic(typestr, error=False)
    if match is None:
        match = registered_fortran_ddt_name(typestr)
        dt = " derived"
    # end if
    if match is None:
        if error:
            emsg = "'{}' is not a valid{} Fortran type"
            raise CCPPError(emsg.format(typestr, dt))
        else:
            typestr = None
        # end if
    # end if
    return typestr
Пример #12
0
def check_fortran_intrinsic(typestr, error=False):
    """Return <test_val> if a valid Fortran intrinsic type, otherwise, None
    if <error> is True, raise an Exception if <test_val> is not valid.
    >>> check_fortran_intrinsic("real", error=False)
    'real'
    >>> check_fortran_intrinsic("complex")
    'complex'
    >>> check_fortran_intrinsic("integer")
    'integer'
    >>> check_fortran_intrinsic("InteGer")
    'InteGer'
    >>> check_fortran_intrinsic("logical")
    'logical'
    >>> check_fortran_intrinsic("character")
    'character'
    >>> check_fortran_intrinsic("double precision")
    'double precision'
    >>> check_fortran_intrinsic("double   precision")
    'double   precision'
    >>> check_fortran_intrinsic("doubleprecision")
    'doubleprecision'
    >>> check_fortran_intrinsic("char", error=True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'char' is not a valid Fortran type
    >>> check_fortran_intrinsic("int")

    >>> check_fortran_intrinsic("char", error=False)

    >>> check_fortran_intrinsic("type")

    >>> check_fortran_intrinsic("complex(kind=r8)")

    """
    chk_type = typestr.strip().lower()
    match = chk_type in FORTRAN_INTRINSIC_TYPES
    if (not match) and (chk_type[0:6] == 'double'):
        # Special case for double precision
        match = FORTRAN_DP_RE.match(chk_type) is not None
    # End if
    if not match:
        if error:
            raise CCPPError("'{}' is not a valid Fortran type".format(typestr))
        else:
            typestr = None
        # end if
    # end if
    return typestr
Пример #13
0
def find_schema_version(root):
###############################################################################
    """
    Find the version of the host registry file represented by root
    >>> find_schema_version(ET.fromstring('<model name="CAM" version="1.0"></model>'))
    [1, 0]
    >>> find_schema_version(ET.fromstring('<model name="CAM" version="1.a"></model>')) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: Illegal version string, '1.a'
    Format must be <integer>.<integer>
    >>> find_schema_version(ET.fromstring('<model name="CAM" version="0.0"></model>')) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: Illegal version string, '0.0'
    Major version must be at least 1
    >>> find_schema_version(ET.fromstring('<model name="CAM" version="0.-1"></model>')) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: Illegal version string, '0.0'
    Minor version must be at least 0
    """
    verbits = None
    if 'version' not in root.attrib:
        raise CCPPError("version attribute required")
    # End if
    version = root.attrib['version']
    versplit = version.split('.')
    try:
        if len(versplit) != 2:
            raise CCPPError('oops')
        # End if (no else needed)
        try:
            verbits = [int(x) for x in versplit]
        except ValueError as verr:
            raise CCPPError(verr)
        # End try
        if verbits[0] < 1:
            raise CCPPError('Major version must be at least 1')
        # End if
        if verbits[1] < 0:
            raise CCPPError('Minor version must be non-negative')
        # End if
    except CCPPError as verr:
        errstr = """Illegal version string, '{}'
        Format must be <integer>.<integer>"""
        ve_str = str(verr)
        if ve_str:
            errstr = ve_str + '\n' + errstr
        # End if
        raise CCPPError(errstr.format(version))
    # End try
    return verbits
Пример #14
0
def check_dimensions(test_val, max_len=0, error=False):
    """Return <test_val> if a valid dimensions list, otherwise, None
    If <max_len> > 0, each string in <test_val> must not be longer than
    <max_len>.
    if <error> is True, raise an Exception if <test_val> is not valid.
    >>> check_dimensions(["dim1", "dim2name"])
    ['dim1', 'dim2name']
    >>> check_dimensions([":", ":"])
    [':', ':']
    >>> check_dimensions([":", "dim2"])
    [':', 'dim2']
    >>> check_dimensions(["dim1", ":"])
    ['dim1', ':']
    >>> check_dimensions(["8", "::"])
    ['8', '::']
    >>> check_dimensions(['start1:end1', 'start2:end2'])
    ['start1:end1', 'start2:end2']
    >>> check_dimensions(['start1:', 'start2:end2'])
    ['start1:', 'start2:end2']
    >>> check_dimensions(["dim1", "dim2name"], max_len=5)

    >>> check_dimensions(["dim1", "dim2name"], error=True, max_len=5)
    Traceback (most recent call last):
    CCPPError: 'dim2name' is too long (> 5 chars)
    >>> check_dimensions("hi_mom", error=True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'hi_mom' is invalid; not a list
    """
    if type(test_val) != list:
        if error:
            raise CCPPError("'{}' is invalid; not a list".format(test_val))
        else:
            test_val = None
        # End if
    else:
        for item in test_val:
            isplit = item.split(':')
            # Check for too many colons
            if (len(isplit) > 3):
                if error:
                    raise CCPPError(
                        "'{}' is an invalid dimension range".format(item))
                else:
                    test_val = None
                # End if
                break
            # End if
            # Check possible dim styles (a, a:b, a:, :b, :, ::, a:b:c, a::c)
            tdims = [x for x in isplit if len(x) > 0]
            for tdim in tdims:
                # Check numeric value first
                try:
                    valid = isinstance(int(tdim), int)
                except ValueError as ve:
                    # Not an integer, try a Fortran ID
                    valid = check_fortran_id(tdim,
                                             max_len=max_len,
                                             error=error) is not None
                # End try
                if not valid:
                    if error:
                        raise CCPPError(
                            "'{}' is an invalid dimension name".format(item))
                    else:
                        test_val = None
                    # End if
                    break
                # End if
            # End for
        # End for
    # End if
    return test_val
Пример #15
0
 def reset_pos(self, line_start=0):
     if (line_start < 0) or (line_start >= len(self._lines)):
         raise CCPPError('Attempt to reset_pos to non-existent line, {}'.format(line_start))
     else:
         self.line_num = line_start
         self._line_next = line_start
Пример #16
0
 def write_line(self, line_num, line):
     "Overwrite line, <line_num> with <line>"
     if (line_num < 0) or (line_num >= len(self._lines)):
         raise CCPPError('Attempt to write non-existent line, {}'.format(line_num))
     else:
         self._lines[line_num] = line
Пример #17
0
def check_balanced_paren(string, start=0, error=False):
    """Return <string> indices delineating a balance set of parentheses.
    Parentheses in character context do not count.
    Left parenthesis search begins at <start>.
    Return start and end indices if found
    If no parentheses are found, return (-1, -1).
    If a left parenthesis is found but no balancing right, return (begin, -1)
    where begin is the index where the left parenthesis was found.
    If error is True, raise a CCPPError.
    >>> check_balanced_paren("foo")
    (-1, -1)
    >>> check_balanced_paren("(foo, bar)")
    (0, 9)
    >>> check_balanced_paren("( (foo, bar) )", start=1)
    (2, 11)
    >>> check_balanced_paren("(size(foo,1), qux)")
    (0, 17)
    >>> check_balanced_paren("(foo('bar()'))")
    (0, 13)
    >>> check_balanced_paren("(foo('bar()')")
    (0, -1)
    >>> check_balanced_paren("(foo('bar()')", error=True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: ERROR: Unbalanced parenthesis in '(foo('bar()')'
    """
    index = start
    begin = -1
    end = -1
    depth = 0
    inchar = None
    str_len = len(string)
    while index < str_len:
        if (string[index] == '"') or (string[index] == "'"):
            if inchar == string[index]:
                inchar = None
            elif inchar is None:
                inchar = string[index]
            # else in character context, keep going
            # end if
        elif inchar is not None:
            # In character context, keep going
            pass
        elif string[index] == '(':
            if depth == 0:
                begin = index
            # end if
            depth = depth + 1
            if depth == 0:
                break
            # end if
        elif string[index] == ')':
            depth = depth - 1
            if depth == 0:
                end = index
                break
            # end if
        # else just keep going
        # end if
        index = index + 1
    # End while
    if (begin >= 0) and (end < 0) and error:
        raise CCPPError("ERROR: Unbalanced parenthesis in '{}'".format(string))
    # end if
    return begin, end
Пример #18
0
def check_diagnostic_id(test_val, prop_dict, error):
    """Return <test_val> if a valid descriptor for a CCPP diagnostic,
        otherwise, None.
    If <error> is True, raise an Exception if <value> is not valid.
    A diagnostic name is a Fortran identifier with the optional
       addition of one variable substitution.
    A variable substitution is a substring of the form of either:
       ${process}: The scheme process name will be substituted for this
          substring. If this substring is included, it is an error for
          there to be no process specified by the scheme (although this
          error cannot be detected by this routine).
       ${scheme_name}: The scheme name will be substituted for this substring.
    It is an error to specify both 'diagnostic_name' and
       'diagnostic_name_fixed'.
    >>> check_diagnostic_id("foo", {'diagnostic_name' : 'foo'}, False)
    'foo'
    >>> check_diagnostic_id("foo", {'diagnostic_name' : 'foo'}, True)
    'foo'
    >>> check_diagnostic_id("foo", {'diagnostic_name_fixed' : 'foo'}, False)

    >>> check_diagnostic_id("foo_${process}", {}, False)
    'foo_${process}'
    >>> check_diagnostic_id("foo_${process}_2bad", {}, False)
    'foo_${process}_2bad'
    >>> check_diagnostic_id("${process}_2bad", {}, False)
    '${process}_2bad'
    >>> check_diagnostic_id("foo_${scheme_name}", {}, False)
    'foo_${scheme_name}'
    >>> check_diagnostic_id("foo_${scheme_name}_2bad", {}, False)
    'foo_${scheme_name}_2bad'
    >>> check_diagnostic_id("${scheme_name}_suff", {}, False)
    '${scheme_name}_suff'
    >>> check_diagnostic_id("pref_${scheme}_suff", {}, False)

    >>> check_diagnostic_id("pref_${scheme_name_suff", {}, False)

    >>> check_diagnostic_id("pref_$scheme_name}_suff", {}, False)

    >>> check_diagnostic_id("pref_{scheme_name}_suff", {}, False)

    >>> check_diagnostic_id("foo", {'diagnostic_name_fixed':'','local_name':'hi','standard_name':'mom'}, True)
    'foo'
    >>> check_diagnostic_id("foo", {'diagnostic_name_fixed':'foo','local_name':'hi','standard_name':'mom'}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: hi (mom) cannot have both 'diagnostic_name' and 'diagnostic_name_fixed' attributes
    >>> check_diagnostic_id("2foo", {'diagnostic_name':'foo','local_name':'hi','standard_name':'mom'}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: '2foo' (hi) is not a valid diagnostic name
    """
    if (prop_dict and ('diagnostic_name_fixed' in prop_dict)
            and prop_dict['diagnostic_name_fixed']):
        valid = None
        if error:
            emsg = "{} ({}) cannot have both 'diagnostic_name' and "
            emsg += "'diagnostic_name_fixed' attributes"
            if 'local_name' in prop_dict:
                lname = prop_dict['local_name']
            else:
                lname = 'UNKNOWN'
            # end if
            if 'standard_name' in prop_dict:
                sname = prop_dict['standard_name']
            else:
                sname = 'UNKNOWN'
            # end if
            raise CCPPError(emsg.format(lname, sname))
        # end if
    else:
        match = _DIAG_RE.match(test_val)
        if match is None:
            valid = None
            if error:
                emsg = "'{}' is not a valid diagnostic_name value"
                raise CCPPError(emsg.format(test_val))
            # end if
        else:
            valid = test_val
        # end if
    # end if
    return valid
Пример #19
0
def check_fortran_ref(test_val, prop_dict, error, max_len=0):
    """Return <test_val> if a valid simple Fortran variable reference,
    otherwise, None. A simple Fortran variable reference is defined as
    a scalar id or a scalar array reference.
    if <error> is True, raise an Exception if <test_val> is not valid.
    >>> FORTRAN_SCALAR_REF_RE.match("foo( bar, baz )").group(1)
    'foo'
    >>> FORTRAN_SCALAR_REF_RE.match("foo( bar, baz )").group(2)
    'bar, baz '
    >>> FORTRAN_SCALAR_REF_RE.match("foo( bar, baz )").group(2).split(',')[0].strip()
    'bar'
    >>> FORTRAN_SCALAR_REF_RE.match("foo( :, baz )").group(2).split(',')[0].strip()
    ':'
    >>> FORTRAN_SCALAR_REF_RE.match("foo( bar, baz )").group(2).split(',')[1].strip()
    'baz'
    >>> check_fortran_ref("hi_mom", None, False)
    'hi_mom'
    >>> check_fortran_ref("hi_mom", None, False, max_len=5)

    >>> check_fortran_ref("hi_mom", None, True, max_len=5) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'hi_mom' is too long (> 5 chars)
    >>> check_fortran_ref("hi mom", None, False)

    >>> check_fortran_ref("hi mom", None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'hi_mom' is not a valid Fortran identifier
    >>> check_fortran_ref("", None, False)

    >>> check_fortran_ref("_hi_mom", None, False)

    >>> check_fortran_ref("2pac", None, False)

    >>> check_fortran_ref("Agood4tranID", None, False)
    'Agood4tranID'
    >>> check_fortran_ref("foo(bar)", None, False)
    'foo(bar)'
    >>> check_fortran_ref("foo( bar, baz )", None, False)
    'foo( bar, baz )'
    >>> check_fortran_ref("foo( :, baz )", None, False)
    'foo( :, baz )'
    >>> check_fortran_ref("foo( bar, )", None, False)

    >>> check_fortran_ref("foo( bar, )", None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'foo( bar, )' is not a valid Fortran scalar reference
    >>> check_fortran_ref("foo()", None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'foo()' is not a valid Fortran scalar reference
    >>> check_fortran_ref("foo(bar, bazz)", None, True, max_len=3) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'bazz' is too long (> 3 chars) in foo(bar, bazz)
    >>> check_fortran_ref("foo(barr, baz)", None, True, max_len=3) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'bazr' is too long (> 3 chars) in foo(barr, baz)
    >>> check_fortran_ref("fooo(bar, baz)", None, True, max_len=3) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'foo' is too long (> 3 chars) in fooo(bar, baz)
    """
    idval = check_fortran_id(test_val, prop_dict, False, max_len=max_len)
    if idval is None:
        match = FORTRAN_SCALAR_REF_RE.match(test_val)
        if match is None:
            if error:
                emsg = "'{}' is not a valid Fortran scalar reference"
                raise CCPPError(emsg.format(test_val))
            else:
                test_val = None
            # end if
        elif max_len > 0:
            tokens = test_val.strip().rstrip(')').split('(')
            tokens = [tokens[0].strip()
                      ] + [x.strip() for x in tokens[1].split(',')]
            for token in tokens:
                if len(token) > max_len:
                    if error:
                        emsg = "'{}' is too long (> {} chars) in {}"
                        raise CCPPError(emsg.format(token, max_len, test_val))
                    else:
                        test_val = None
                        break
                    # end if
                # end if
            # end for
        # end if
    # end if
    return test_val
Пример #20
0
def check_default_value(test_val, prop_dict, error):
    """Return <test_val> if a valid default value for a CCPP field,
         otherwise, None.
    If <error> is True, raise an Exception if <value> is not valid.
    A valid value is determined by the 'type' of the variable. It is an
    error for there to be no 'type' property in <prop_dict>.
    >>> check_default_value('314', {'type':'integer'}, False)
    '314'
    >>> check_default_value('314', {'type':'integer'}, True)
    '314'
    >>> check_default_value('314', {'type':'integer', 'kind':'ikind'}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 314 is not a valid Fortran integer of kind, ikind
    >>> check_default_value('314_ikind', {'type':'integer', 'kind':'ikind'}, True)
    '314_ikind'
    >>> check_default_value('314', {'type':'real'}, False)

    >>> check_default_value('314', {'type':'real'}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 314 is not a valid Fortran real
    >>> check_default_value('3.14', {'type':'real'}, False)
    '3.14'
    >>> check_default_value('314', {'tipe':'integer'}, False)

    >>> check_default_value('314', {'local_name':'foo'}, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: foo does not have a 'type' attribute
    >>> check_default_value('314', {'tipe':'integer'}, False)

    >>> check_default_value('314', None, True)
    '314'
    """
    valid = None
    if prop_dict and ('type' in prop_dict):
        valid = test_val
        var_type = prop_dict['type'].lower().strip()
        if 'kind' in prop_dict:
            vkind = prop_dict['kind'].lower().strip()
        else:
            vkind = ''
        # end if
        if not check_fortran_literal(test_val, var_type, vkind):
            valid = None
            if error:
                emsg = '{} is not a valid Fortran {}'
                if vkind:
                    emsg += ' of kind, {}'
                raise CCPPError(emsg.format(test_val, var_type, vkind))
            # end if
        # end if (no else, <test_val> is okay)
    elif prop_dict is None:
        # Special case for checks during parsing, always pass
        valid = test_val
    elif error:
        emsg = "{} does not have a 'type' attribute"
        if 'local_name' in prop_dict:
            lname = prop_dict['local_name']
        else:
            lname = 'UNKNOWN'
        # end if
        raise CCPPError(emsg.format(lname))
    # end if
    return valid
Пример #21
0
def check_dimensions(test_val, prop_dict, error, max_len=0):
    """Return <test_val> if a valid dimensions list, otherwise, None
    If <max_len> > 0, each string in <test_val> must not be longer than
    <max_len>.
    if <error> is True, raise an Exception if <test_val> is not valid.
    >>> check_dimensions(["dim1", "dim2name"], None, False)
    ['dim1', 'dim2name']
    >>> check_dimensions([":", ":"], None, False)
    [':', ':']
    >>> check_dimensions([":", "dim2"], None, False)
    [':', 'dim2']
    >>> check_dimensions(["dim1", ":"], None, False)
    ['dim1', ':']
    >>> check_dimensions(["8", "::"], None, False)
    ['8', '::']
    >>> check_dimensions(['start1:end1', 'start2:end2'], None, False)
    ['start1:end1', 'start2:end2']
    >>> check_dimensions(['start1:', 'start2:end2'], None, False)
    ['start1:', 'start2:end2']
    >>> check_dimensions(['start1 :end1', 'start2: end2'], None, False)
    ['start1 :end1', 'start2: end2']
    >>> check_dimensions(['size(foo)'], None, False)
    ['size(foo)']
    >>> check_dimensions(['size(foo,1) '], None, False)
    ['size(foo,1) ']
    >>> check_dimensions(['size(foo,1'], None, False) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: Invalid dimension component, size(foo,1
    >>> check_dimensions(["dim1", "dim2name"], None, False, max_len=5)

    >>> check_dimensions(["dim1", "dim2name"], None, True, max_len=5) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'dim2name' is too long (> 5 chars)
    >>> check_dimensions("hi_mom", None, True) #doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    CCPPError: 'hi_mom' is invalid; not a list
    """
    if not isinstance(test_val, list):
        if error:
            raise CCPPError("'{}' is invalid; not a list".format(test_val))
        else:
            test_val = None
        # end if
    else:
        for item in test_val:
            isplit = item.split(':')
            # Check for too many colons
            if (len(isplit) > 3):
                if error:
                    errmsg = "'{}' is an invalid dimension range"
                    raise CCPPError(errmsg.format(item))
                else:
                    test_val = None
                # end if
                break
            # end if
            # Check possible dim styles (a, a:b, a:, :b, :, ::, a:b:c, a::c)
            tdims = [x.strip() for x in isplit if len(x) > 0]
            for tdim in tdims:
                # Check numeric value first
                try:
                    valid = isinstance(int(tdim), int)
                except ValueError as ve:
                    # Not an integer, try a Fortran ID
                    valid = check_fortran_id(tdim,
                                             None,
                                             error,
                                             max_len=max_len) is not None
                    if not valid:
                        # Check for size entry -- simple check
                        tcheck = tdim.strip().lower()
                        if tcheck[0:4] == 'size':
                            ploc = check_balanced_paren(tdim[4:])
                            if -1 in ploc:
                                emsg = 'Invalid dimension component, {}'
                                raise CCPPError(emsg.format(tdim))
                            else:
                                valid = tdim
                            # end if
                        # end if
                    # end if
                # End try
                if not valid:
                    if error:
                        errmsg = "'{}' is an invalid dimension name"
                        raise CCPPError(errmsg.format(item))
                    else:
                        test_val = None
                    # end if
                    break
                # end if
            # end for
        # end for
    # end if
    return test_val