from collections import deque


def shortest_hops(graph, source, target):
    nodes = set(graph)
    for neighbors in graph.values():
        nodes.update(neighbors)
    if source not in nodes or target not in nodes:
        raise ValueError('unknown vertex')
    root = object()
    parent = {source: root}
    queue = deque([source])
    while queue:
        vertex = queue.popleft()
        if vertex == target:
            path = []
            while vertex is not root:
                path.append(vertex)
                vertex = parent[vertex]
            return path[::-1]
        for neighbor in graph.get(vertex, ()):
            if neighbor not in parent:
                parent[neighbor] = vertex
                queue.append(neighbor)
    return None


if __name__ == '__main__':
    print(shortest_hops({'a': ['b', 'c'], 'b': ['d'], 'c': ['d'], 'd': []}, 'a', 'd'))
