docs: def to_nodename(string, invalid=None, raise_exc=False):
      """Makes a Quilt Node name (perhaps an ugly one) out of any string.
  
      This should match whatever the current definition of a node name is, as
      defined in is_nodename().
  
      This isn't an isomorphic change, the original name can't be recovered
      from the change in all cases, so it must be stored separately (`FileNode`
      metadata)
  
      If `invalid` is given, it should be an iterable of names that the returned
      string cannot match -- for example, other node names.
  
      If `raise_exc` is True, an exception is raised when the converted string
      is present in `invalid`.  Otherwise, the converted string will have a
      number appended to its name.
  
      Example:
      # replace special chars -> remove prefix underscores -> rename keywords
      # '!if' -> '_if' -> 'if' -> 'if_'
      >>> to_nodename('!if') -> 'if_'
      >>> to_nodename('if', ['if_']) -> 'if__2'
      >>> to_nodename('9#blah') -> 'n9_blah'
      >>> to_nodename('9:blah', ['n9_blah', 'n9_blah_2']) -> 'n9_blah_3'
  
      :param string: string to convert to a nodename
      :param invalid: Container of names to avoid.  Efficiency: Use set or dict
      :param raise_exc: Raise an exception on name conflicts if truthy.
      :returns: valid node name
      """
      string = to_identifier(string)
  
      #TODO: Remove this stanza once keywords are permissible in nodenames
      if keyword.iskeyword(string):
          string += '_'   # there are no keywords ending in "_"
  
      # Done if no deduplication needed
      if invalid is None:
          return string
  
      # Deduplicate
      if string in invalid and raise_exc:
          raise QuiltException("Conflicting node name after string conversion: {!r}".format(string))
  
      result = string
      counter = 1
      while result in invalid:
          # first conflicted name will be "somenode_2"
          # The result is "somenode", "somenode_2", "somenode_3"..
          counter += 1
          result = "{}_{}".format(string, counter)
  
      return result