docstr: Mutate the set of user tags for a run.
  
  Removals logically get applied after additions.  Operations occur as a batch atomically.
  Parameters
  ----------
  flow_id : str
      Flow id, that the run belongs to.
  run_id: str
      Run id, together with flow_id, that identifies the specific Run whose tags to mutate
  tags_to_remove: iterable over str
      Iterable over tags to remove
  tags_to_add: iterable over str
      Iterable over tags to add
  
  Return
  ------
  Run tags after mutation operations
code: def mutate_user_tags_for_run(
      cls, flow_id, run_id, tags_to_remove=None, tags_to_add=None
  ):
      """
      Mutate the set of user tags for a run.
  
      Removals logically get applied after additions.  Operations occur as a batch atomically.
      Parameters
      ----------
      flow_id : str
          Flow id, that the run belongs to.
      run_id: str
          Run id, together with flow_id, that identifies the specific Run whose tags to mutate
      tags_to_remove: iterable over str
          Iterable over tags to remove
      tags_to_add: iterable over str
          Iterable over tags to add
  
      Return
      ------
      Run tags after mutation operations
      """
      # perform common validation, across all provider implementations
      if tags_to_remove is None:
          tags_to_remove = []
      if tags_to_add is None:
          tags_to_add = []
      if not tags_to_add and not tags_to_remove:
          raise MetaflowTaggingError(
              "Must add or remove at least one tag"
          )
  
      if is_stringish(tags_to_add):
          raise MetaflowTaggingError("tags_to_add may not be a string")
  
      if is_stringish(tags_to_remove):
          raise MetaflowTaggingError(
              "tags_to_remove may not be a string"
          )
  
      def _is_iterable(something):
          try:
              iter(something)
              return True
          except TypeError:
              return False
  
      if not _is_iterable(tags_to_add):
          raise MetaflowTaggingError("tags_to_add must be iterable")
      if not _is_iterable(tags_to_remove):
          raise MetaflowTaggingError("tags_to_remove must be iterable")
  
      # check each tag is valid
      for tag in chain(tags_to_add, tags_to_remove):
          validate_tag(tag)
  
      # onto subclass implementation
      final_user_tags = cls._mutate_user_tags_for_run(
          flow_id,
          run_id,
          tags_to_add=tags_to_add,
          tags_to_remove=tags_to_remove,
      )
      return final_user_tags
  
imports: import json
  import os
  import re
  import time
  from collections import namedtuple
  from itertools import chain
  from metaflow.exception import MetaflowInternalError, MetaflowTaggingError
  from metaflow.tagging_util import validate_tag
  from metaflow.util import get_username, resolve_identity_as_tuple, is_stringish
label_desc: Use image node_id 17 for calling the MetadataProvider obj's underlying member method code with example usage: obj.mutate_user_tags_for_run(cls, flow_id, run_id, tags_to_remove, tags_to_add) and returns: final_user_tags, True, False