An Implementation of a Depth-first search-based algorithm for finding a Topological order for a Directed acyclic graph in Python.

from random import choice
from itertools import count
from dataclasses import dataclass, field
from typing import Mapping, List, TypeVar, Generic

T = TypeVar('T')
VertexId = int

@dataclass
class Vertex(Generic[T]):
    value: T
    id: VertexId = field(default_factory=count().__next__)

def topological_sort(adjacency_list: Mapping[VertexId, List[VertexId]]) -> List[VertexId]:
    """Return a topological ordering of ADJACENCY_LIST."""

    evaluated = set()
    evaluating = set()
    topological_order = []

    def visit(id: VertexId) -> None:
        if id in evaluated:
            return

        if id in evaluating:
            raise Exception(f"Provided adjacency list contains a cycle including {id}")

        evaluating.add(id)

        if id in adjacency_list:
            for neighbor_id in adjacency_list[id]:
                visit(neighbor_id)

        evaluating.remove(id)
        evaluated.add(id)
        topological_order.insert(0, id)

    for id in adjacency_list:
        visit(id)

    return topological_order

a = Vertex(value='a') # 0
b = Vertex(value='b') # 1
c = Vertex(value='c') # 2
d = Vertex(value='d') # 3
e = Vertex(value='e') # 4
f = Vertex(value='f') # 5
adjacency_list = {
    a.id: [d.id],
    b.id: [d.id],
    c.id: [d.id],
    d.id: [e.id],
    e.id: [f.id],
}
print(topological_sort(adjacency_list), 'should be [(2,1,0 in any order), 3, 4, 5]')

Bibliography