docs: def print_vessel_errors(retdict):
    """
    <Purpose>
      Prints out any errors that occurred while performing an action on vessels,
      in a human readable way.
  
      Errors will be printed out in the following format:
      description [reason]
      Affected vessels: nodelist
  
      To define a new error, add the following entry to ERROR_RESPONSES in this
      function:
        'error_identifier': {
          'error': 'description for the error',
          'reason': 'reason for the error' (optional).
      'error_identifier'
        This is the substring of the error that can be used to identify it.
        Longer identifiers will have a higher priority over shorter identifiers.
        For example, authentication errors could be identified using the string
        'Insufficient Permissions'.
      'error'
        This is where you put the description for the error to show to the user.
      'reason' (optional)
        This is where you put clarification for the error to ease the user.
        Additionally, you may put things that they could do to fix the error here,
        if applicable. If you don't want to show a reason, don't include this key
        in the dictionary.
        Examples when you would not put a reason is if you received a timeout,
        since the user can't do anything to fix them.
  
    <Arguments>
      retdict:
        A list of longnames mapped against a tuple (Success?, Message/Errortext).
  
    <Side Effects>
      Prints error messages onto the screen. See documentation for ERROR_RESPONSES
      for more information.
  
    <Exceptions>
      Exception
  
    <Return>
      None
  
    """
    ERROR_RESPONSES = {
      "Node Manager error 'Insufficient Permissions'": {
        'error': "You lack sufficient permissions to perform this action.",
        'reason': "Did you release the resource(s) by accident?"},
      'timed out': {
        'error':'Connection timed out.'},
      "file not found": {
        'error': "The specified file(s) could not be found.",
        'reason': "Please check the filename."},
      "Node Manager error 'Programming language platform is not supported.'": {
        'error': "Requested platform is not supported by the target vessel."},
      }
  
    # A dictionary mapping error identifiers to a list of vessels that share
    # that error.
    error_longnames = {}
  
    for longname in retdict:
      # if the first item is true, then there is no error.
      if not retdict[longname][0]:
        matches = []
        # Loop until we find the response
        for error_string in ERROR_RESPONSES:
          if error_string.lower() in retdict[longname][1].lower():
            # This is the first match
            if not matches:
              matches = [error_string]
            else:
              # This is a better match, forget about the previous matches
              if len(error_string) > len(matches[0]):
                matches = [error_string]
  
              elif len(error_string) == len(matches[0]):
                matches.append(error_string)
  
        # If there isn't a match, use the error string as an error identifier
        if not matches:
          errorid = retdict[longname][1]
  
        else:
          # There should not be more than 1 match for any error.
          # If there is, log the error to a file.
          if len(matches) != 1:
            errfile = open('seasherrors.txt', 'a')
            errorstring = "Multiple matches with same priority:" + '\n'.join(matches)
            errfile.write(errorstring)
            errfile.close()
            raise Exception(errorstring)
          errorid = matches[0]
  
        # Create the longname list if it doesn't already exist
        if errorid not in error_longnames:
          error_longnames[errorid] = []
        error_longnames[errorid].append(longname)
  
  
    # Print the errors out
    for errorid in error_longnames:
      # Represent the list of nodes in a human readable way.
      nodestring = ''
      for node in error_longnames[errorid]:
        # This is the first node
        if node == error_longnames[errorid][0]:
          divider = ''
        # This is a node in the middle
        elif node != error_longnames[errorid][-1]:
          divider = ', '
        # This is the last node
        else:
          # We will always have at least 2 nodes at this point, since if there
          # is only one node, it will be treated as the first node. Therefore,
          # we only have two cases, where there are exactly 2 nodes, or more than
          # 2 nodes.
          # If we have two nodes, we want: "node_1 and node_2".
          # Otherwise, we want: "node_1, node_2, ..., and node_n".
          divider = " and "
          if len(error_longnames[errorid]) > 2:
            divider = ',' + divider
        nodestring += divider + node
  
      if errorid in ERROR_RESPONSES:
        print ERROR_RESPONSES[errorid]['error'],
        if 'reason' in ERROR_RESPONSES[errorid]:
          print ERROR_RESPONSES[errorid]['reason']
        else:
          # Caret is still on the same line as the list of nodes
          print
      else:
        # Unknown error.
        print "An error occurred: " + errorid
      print "Affected vessels:", nodestring + '.'