<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def attr_check(kvp, ds, priority, ret_val, gname=None):
 """ Handles attribute checks for simple presence of an attribute, presence of one of several attributes, and passing a validation function. Returns a status along with an error message in the event of a failure. Mutates ret_val parameter :param tuple(str, func) or str l: the attribute being checked :param netCDF4 dataset ds : dataset being checked :param int priority : priority level of check :param list ret_val : result to be returned :param str or None gname : group name assigned to a group of attribute Results """


    msgs = []
    name, other = kvp
    if other is None:
        res = std_check(ds, name)
        if not res:
            msgs = ["%s not present" % name]
        else:
            try:
                # see if this attribute is a string, try stripping
                # whitespace, and return an error if empty
                att_strip = getattr(ds, name).strip()
                if not att_strip:
                    res = False
                    msgs = ["%s is empty or completely whitespace" % name]
            # if not a string/has no strip method we should be OK
            except AttributeError:
                pass

        # gname arg allows the global attrs to be grouped together
        ret_val.append(Result(
            priority,
            value=res,
            name=gname if gname else name,
            msgs=msgs
        ))
    elif hasattr(other, '__iter__'):
        # redundant, we could easily do this with a hasattr
        # check instead
        res = std_check_in(ds, name, other)
        if res == 0:
            msgs.append("%s not present" % name)
        elif res == 1:
            msgs.append("%s present, but not in expected value list (%s)" % (name, other))

        ret_val.append(
            Result(
                priority,
                (res, 2),
                gname if gname else name, # groups Globals if supplied
                msgs
            )
        )
    # if we have an XPath expression, call it on the document
    elif type(other) is etree.XPath:
        # TODO: store tree instead of creating it each time?
        res = xpath_check(ds._root, other)
        if not res:
            msgs = ["XPath for {} not found".format(name)]
        ret_val.append(
            Result(
                priority,
                res,
                gname if gname else name,
                msgs
            )
        )
    # if the attribute is a function, call it
    # right now only supports single attribute
    # important note: current magic approach uses all functions
    # starting with "check".  Avoid naming check functions
    # starting with check if you want to pass them in with
    # a tuple to avoid them being checked more than once
    elif hasattr(other, '__call__'):
        # check that the attribute is actually present.
        # This reduces boilerplate in functions by not needing
        # to check whether the attribute is present every time
        # and instead focuses on the core functionality of the
        # test

        res = other(ds) # call the method on the dataset
        if not res:
            msgs = ["%s not present" % name]
            ret_val.append(
                Result(
                    priority,
                    res,
                    gname if gname else name,
                    msgs
                )
            )
        else:
            ret_val.append(res(priority))
    # unsupported second type in second
    else:
        raise TypeError("Second arg in tuple has unsupported type: {}".format(type(other)))


    return ret_val