nodes: 0, module; 1, function_definition; 2, function_name:toposorted; 3, parameters; 4, block; 5, identifier:nodes; 6, identifier:edges; 7, expression_statement; 8, expression_statement; 9, expression_statement; 10, for_statement; 11, expression_statement; 12, expression_statement; 13, while_statement; 14, return_statement; 15, comment:"""
      Perform a topological sort on the input resources.
  
      The topological sort uses Kahn's algorithm, which is a stable sort and will preserve this
      ordering; note that a DFS will produce a worst case ordering from the perspective of batching.
  
      """; 16, assignment; 17, assignment; 18, identifier:edge; 19, identifier:edges; 20, block; 21, assignment; 22, assignment; 23, identifier:working_set; 24, block; 25, identifier:results; 26, identifier:incoming; 27, call; 28, identifier:outgoing; 29, call; 30, expression_statement; 31, expression_statement; 32, identifier:working_set; 33, call; 34, identifier:results; 35, list; 36, expression_statement; 37, for_statement; 38, if_statement; 39, expression_statement; 40, identifier:defaultdict; 41, argument_list; 42, identifier:defaultdict; 43, argument_list; 44, call; 45, call; 46, identifier:list; 47, argument_list; 48, assignment; 49, identifier:node; 50, identifier:working_set; 51, block; 52, comparison_operator:len(working_set) == len(remaining); 53, block; 54, assignment; 55, identifier:set; 56, identifier:set; 57, attribute; 58, argument_list; 59, attribute; 60, argument_list; 61, call; 62, identifier:remaining; 63, list; 64, if_statement; 65, expression_statement; 66, for_statement; 67, call; 68, call; 69, raise_statement; 70, identifier:working_set; 71, identifier:remaining; 72, subscript; 73, identifier:add; 74, attribute; 75, subscript; 76, identifier:add; 77, attribute; 78, attribute; 79, argument_list; 80, subscript; 81, comment:# node still has incoming edges; 82, block; 83, call; 84, identifier:child; 85, subscript; 86, block; 87, identifier:len; 88, argument_list; 89, identifier:len; 90, argument_list; 91, call; 92, identifier:incoming; 93, attribute; 94, identifier:edge; 95, identifier:from_id; 96, identifier:outgoing; 97, attribute; 98, identifier:edge; 99, identifier:to_id; 100, identifier:nodes; 101, identifier:values; 102, identifier:incoming; 103, attribute; 104, expression_statement; 105, continue_statement; 106, attribute; 107, argument_list; 108, identifier:outgoing; 109, attribute; 110, expression_statement; 111, identifier:working_set; 112, identifier:remaining; 113, identifier:Exception; 114, argument_list; 115, identifier:edge; 116, identifier:to_id; 117, identifier:edge; 118, identifier:from_id; 119, identifier:node; 120, identifier:id; 121, call; 122, identifier:results; 123, identifier:append; 124, identifier:node; 125, identifier:node; 126, identifier:id; 127, call; 128, string:"Cycle detected"; 129, attribute; 130, argument_list; 131, attribute; 132, argument_list; 133, identifier:remaining; 134, identifier:append; 135, identifier:node; 136, subscript; 137, identifier:remove; 138, attribute; 139, identifier:incoming; 140, identifier:child; 141, identifier:node; 142, identifier:id
code: def toposorted(nodes, edges):
      """
      Perform a topological sort on the input resources.
  
      The topological sort uses Kahn's algorithm, which is a stable sort and will preserve this
      ordering; note that a DFS will produce a worst case ordering from the perspective of batching.
  
      """
      incoming = defaultdict(set)
      outgoing = defaultdict(set)
      for edge in edges:
          incoming[edge.to_id].add(edge.from_id)
          outgoing[edge.from_id].add(edge.to_id)
  
      working_set = list(nodes.values())
      results = []
      while working_set:
          remaining = []
          for node in working_set:
              if incoming[node.id]:
                  # node still has incoming edges
                  remaining.append(node)
                  continue
  
              results.append(node)
              for child in outgoing[node.id]:
                  incoming[child].remove(node.id)
  
          if len(working_set) == len(remaining):
              raise Exception("Cycle detected")
          working_set = remaining
  
      return results