docs: def _topo_sort(self, forward=True):
          """
          Topological sort.
  
          Returns a list of nodes where the successors (based on outgoing and
          incoming edges selected by the forward parameter) of any given node
          appear in the sequence after that node.
          """
          topo_list = []
          queue = deque()
          indeg = {}
  
          # select the operation that will be performed
          if forward:
              get_edges = self.out_edges
              get_degree = self.inc_degree
              get_next = self.tail
          else:
              get_edges = self.inc_edges
              get_degree = self.out_degree
              get_next = self.head
  
          for node in self.node_list():
              degree = get_degree(node)
              if degree:
                  indeg[node] = degree
              else:
                  queue.append(node)
  
          while queue:
              curr_node = queue.popleft()
              topo_list.append(curr_node)
              for edge in get_edges(curr_node):
                  tail_id = get_next(edge)
                  if tail_id in indeg:
                      indeg[tail_id] -= 1
                      if indeg[tail_id] == 0:
                          queue.append(tail_id)
  
          if len(topo_list) == len(self.node_list()):
              valid = True
          else:
              # the graph has cycles, invalid topological sort
              valid = False
  
          return (valid, topo_list)