docs: 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