def invoke_editor(before_editing: str, cluster_name: str) -> Tuple[str, Dict[str, Any]]:
    """Start editor command to edit configuration in human readable format.

    .. note::
        Requires an editor program, and uses first found among:
            * Program given by ``EDITOR`` environment variable; or
            * ``editor``; or
            * ``vi``.

    :param before_editing: human representation before editing.
    :param cluster_name: name of the Patroni cluster.

    :returns: tuple of human-readable, parsed data structure after changes.

    :raises:
        :class:`PatroniCtlException`: if
            * No suitable editor can be found; or
            * Editor call exits with unexpected return code.
    """
    editor_cmd = os.environ.get('EDITOR')
    if not editor_cmd:
        for editor in ('editor', 'vi'):
            editor_cmd = shutil.which(editor)
            if editor_cmd:
                logging.debug('Setting fallback editor_cmd=%s', editor)
                break
    if not editor_cmd:
        raise PatroniCtlException('EDITOR environment variable is not set. editor or vi are not available')

    safe_cluster_name = cluster_name.replace("/", "_")
    with temporary_file(contents=before_editing.encode('utf-8'),
                        suffix='.yaml',
                        prefix='{0}-config-'.format(safe_cluster_name)) as tmpfile:
        ret = subprocess.call([editor_cmd, tmpfile])
        if ret:
            raise PatroniCtlException("Editor exited with return code {0}".format(ret))

        with codecs.open(tmpfile, encoding='utf-8') as fd:
            after_editing = fd.read()

        return after_editing, yaml.safe_load(after_editing)
Start editor command to edit configuration in human readable format.

    .. note::
        Requires an editor program, and uses first found among:
            * Program given by ``EDITOR`` environment variable; or
            * ``editor``; or
            * ``vi``.

    :param before_editing: human representation before editing.
    :param cluster_name: name of the Patroni cluster.

    :returns: tuple of human-readable, parsed data structure after changes.

    :raises:
        :class:`PatroniCtlException`: if
            * No suitable editor can be found; or
            * Editor call exits with unexpected return code.
    
patroni/ctl.py