def _sort_nodes(self):
        """Sort nodes.

        Nodes are sorted so that the ones which depend on the input of other
        nodes appear after them.

        Uses a breadth-first topological sort algorithm, gathering nodes in
        different groups called generations.

        The first generation is formed by nodes that don't have incoming
        edges. Each subsequent generation is formed by nodes which expect
        inputs solely from the nodes on the previous generation(s) and so on.
        """
        ### clear list where generations will be stored in order
        self.clear_node_generations()

        ### reference objects locally for quicker and easier access

        ## operation to append a node generation
        append_node_generation = self.append_node_generation

        ## set of nodes to sort into generations
        nodes_to_sort = self.nodes_to_sort

        ### send ids of the nodes to their direct children, so the
        ### children can keep track of their indegree

        for node in nodes_to_sort:
            node.send_id_to_direct_children()

        ### while there's nodes to sort, keep creating and appending
        ### new node generations from the nodes with indegree == 0, always
        ### decrementing the indegree of their direct children

        while nodes_to_sort:

            ## create generation from nodes whose indegree == 0, that is,
            ## nodes with no incoming edges;
            ##
            ## however, the meaning of indegree == 0 was extended here to
            ## also mean nodes whose incoming edges come from nodes in
            ## previous generation(s)

            node_generation = [
                node
                for node in nodes_to_sort
                if len(node.input_source_ids) == 0
            ]

            ## now that we defined a new generation, append it to the
            ## dedicated list
            append_node_generation(node_generation)

            ## and remove them from the set of nodes to sort
            nodes_to_sort.difference_update(node_generation)

            ## create a set of ids from the nodes in this generation
            ## and use it to remove such ids from the remaining nodes
            ## to be sorted
            ##
            ## this is a way to signal the remaining nodes that the
            ## nodes with those ids are already stored in previous
            ## generation(s)

            ids_to_remove = {node.id for node in node_generation}

            for node in nodes_to_sort:
                node.remove_source_ids(ids_to_remove)
Sort nodes.

        Nodes are sorted so that the ones which depend on the input of other
        nodes appear after them.

        Uses a breadth-first topological sort algorithm, gathering nodes in
        different groups called generations.

        The first generation is formed by nodes that don't have incoming
        edges. Each subsequent generation is formed by nodes which expect
        inputs solely from the nodes on the previous generation(s) and so on.
        
nodezator/graphman/execution.py