def find_cycle(start, graph):
  """Finds a path from `start` to itself in a directed graph.

  Note that if the graph has other cycles (that don't have `start` as a hop),
  they are ignored.

  Args:
    start: str name of the node to start.
    graph: {str => iterable of str} is adjacency map that defines the graph.

  Returns:
    A list or str with nodes that form a cycle or None if there's no cycle.
    When not None, the first and the last elements are always `start`.

  Raises:
    KeyError if there's a reference to a node that is not in the `graph` map.
  """
  explored = set()  # set of roots of totally explored subgraphs
  visiting = []     # stack of nodes currently being traversed

  def visit(node):
    if node in explored:
      return False  # been there, no cycles there that have `start` in them

    if node in visiting:
      # Found a cycle that starts and ends with `node`. Return True if it is
      # "start -> ... -> start" cycle or False if is some "inner" cycle. We are
      # not interested in the latter.
      return node == start

    visiting.append(node)
    for edge in graph[node]:
      if visit(edge):
        return True  # found a cycle!

    popped = visiting.pop()
    assert popped == node
    explored.add(node)  # don't visit this subgraph ever again

    return False

  if not visit(start):
    return None

  visiting.append(start)  # close the loop
  return visiting