def topology_sort(members: List[Dict[str, Any]]) -> Iterator[Dict[str, Any]]:
    """Sort *members* according to their level in the replication topology tree.

    :param members: list of members in the cluster. Each item should contain at least these keys:

        * ``name``: name of the node, according to ``name`` configuration;
        * ``role``: ``leader``, ``standby_leader`` or ``replica``.

        Cascading replicas are identified through ``tags`` -> ``replicatefrom`` value -- if that is set, and they are
        in fact attached to another replica.

        Besides ``name``, ``role`` and ``tags`` keys, it may contain other keys, which although ignored by this
        function, will be yielded as part of the resulting object. The value of key ``name`` is changed through
        :func:`generate_topology`.

    :yields: *members* sorted by level in the topology, and with a new ``name`` value according to their level
        in the topology.
    """
    topology: Dict[Optional[str], List[Dict[str, Any]]] = defaultdict(list)
    leader = next((m for m in members if m['role'].endswith('leader')), {'name': None})
    replicas = set(member['name'] for member in members if not member['role'].endswith('leader'))
    for member in members:
        if not member['role'].endswith('leader'):
            parent = member.get('tags', {}).get('replicatefrom')
            parent = parent if parent and parent != member['name'] and parent in replicas else leader['name']
            topology[parent].append(member)
    for member in generate_topology(0, leader, topology):
        yield member
Sort *members* according to their level in the replication topology tree.

    :param members: list of members in the cluster. Each item should contain at least these keys:

        * ``name``: name of the node, according to ``name`` configuration;
        * ``role``: ``leader``, ``standby_leader`` or ``replica``.

        Cascading replicas are identified through ``tags`` -> ``replicatefrom`` value -- if that is set, and they are
        in fact attached to another replica.

        Besides ``name``, ``role`` and ``tags`` keys, it may contain other keys, which although ignored by this
        function, will be yielded as part of the resulting object. The value of key ``name`` is changed through
        :func:`generate_topology`.

    :yields: *members* sorted by level in the topology, and with a new ``name`` value according to their level
        in the topology.
    
patroni/ctl.py