def main(args):
    if len(args) < 3:
        sys.stderr.write("usage: {} <directories> <output.json>\n".format(
            args[0]))
        sys.stderr.write(
            "\t<directories>: list of space-separated directories to examine\n"
        )
        sys.stderr.write(
            "\t<output>: json file describing graphs, interpreted by doc_grapher.html\n"
        )
        sys.exit(1)

    directories = args[1:-1]
    outfname = args[-1]

    # for each file in each directory, recursively on down,
    # search for doc annotations and create objects appropriately
    docnodes = collections.OrderedDict()
    filecount = 0
    for directory in directories:
        for root, dirs, files in os.walk(directory):
            for fname in files:
                filecount += 1

                path = os.path.join(root, fname)
                docnode = parse_docfile(path)

                if docnode is None:
                    # sys.stderr.write("Error! File is not annotated: {}\n"
                    #                  .format(path))
                    continue
                docnodes[docnode.name] = docnode

    # if any docnodes have auto import set up, take care of that
    import_manager = ImportManager()
    import_manager.add_auto_imports(list(docnodes.values()))

    # validate all parents & siblings - make sure they actually exist
    rejectedEdges = []
    for name in docnodes:
        docnode = docnodes[name]
        verified_edges = []
        for edge in docnode.edges:
            if edge['id'] in docnodes:
                verified_edges.append(edge)
            else:
                rejectedEdges.append(edge)
        docnode.edges = verified_edges
    # print any rejected edges
    print('Rejected {} edge{}'.format(len(rejectedEdges),
                                      's' if len(rejectedEdges) != 1 else ''))
    if len(rejectedEdges) > 0:
        print(rejectedEdges)

    ## assign colors to distinct segments
    ## we do this as follows:
    #### climb up chain of parents
    #### if parent has color assigned, assign same color to all children
    #### if we reach the top of the chain without having assigned a color, assign a color and bubble down
    #### IMPORTANT: remember to mark nodes as "seen" as we do this!
    ####            (because we don't necessary want to force links as a tree structure)
    assigner = ColorAssigner()
    assigner.assign_colors(docnodes)

    nodes = []
    edges = []
    node_config = {'size': 10}
    edge_config = {'size': 3}
    for name in docnodes:
        docnode = docnodes[name]

        nodes.append(docnode.graph_node(node_config))
        edges += docnode.graph_edges(edge_config)

    if len(nodes) == 0:
        sys.stderr.write(
            "No annotated files found! Not writing output file.\n")
        sys.exit(1)

    print("Extracted {} nodes with {} edges from {} files".format(
        len(nodes), len(edges), filecount))
    graph = {'nodes': nodes, 'edges': edges}
    # pprint(graph)

    with open(outfname, 'w') as f:
        json.dump(graph, f, indent=4)
def main(args):
    if len(args) < 3:
        sys.stderr.write("usage: {} <directories> <output.json>\n".format(args[0]))
        sys.stderr.write("\t<directories>: list of space-separated directories to examine\n")
        sys.stderr.write("\t<output>: json file describing graphs, interpreted by doc_grapher.html\n")
        sys.exit(1)

    directories = args[1:-1]
    outfname = args[-1]

    # for each file in each directory, recursively on down,
    # search for doc annotations and create objects appropriately
    docnodes = collections.OrderedDict()
    filecount = 0
    for directory in directories:
        for root, dirs, files in os.walk(directory):
            for fname in files:
                filecount += 1

                path = os.path.join(root, fname)
                docnode = parse_docfile(path)

                if docnode is None:
                    # sys.stderr.write("Error! File is not annotated: {}\n"
                    #                  .format(path))
                    continue
                docnodes[docnode.name] = docnode

    # if any docnodes have auto import set up, take care of that
    import_manager = ImportManager()
    import_manager.add_auto_imports(list(docnodes.values()))

    # validate all parents & siblings - make sure they actually exist
    rejectedEdges = []
    for name in docnodes:
        docnode = docnodes[name]
        verified_edges = []
        for edge in docnode.edges:
            if edge['id'] in docnodes:
                verified_edges.append(edge)
            else:
                rejectedEdges.append(edge)
        docnode.edges = verified_edges
    # print any rejected edges
    print('Rejected {} edge{}'.format(
        len(rejectedEdges),
        's' if len(rejectedEdges) != 1 else ''))
    if len(rejectedEdges) > 0:
        print(rejectedEdges)

    ## assign colors to distinct segments
    ## we do this as follows:
    #### climb up chain of parents
    #### if parent has color assigned, assign same color to all children
    #### if we reach the top of the chain without having assigned a color, assign a color and bubble down
    #### IMPORTANT: remember to mark nodes as "seen" as we do this!
    ####            (because we don't necessary want to force links as a tree structure)
    assigner = ColorAssigner()
    assigner.assign_colors(docnodes)

    nodes = []
    edges = []
    node_config = {'size': 10}
    edge_config = {'size': 3}
    for name in docnodes:
        docnode = docnodes[name]

        nodes.append(docnode.graph_node(node_config))
        edges += docnode.graph_edges(edge_config)

    if len(nodes) == 0:
        sys.stderr.write("No annotated files found! Not writing output file.\n")
        sys.exit(1)

    print("Extracted {} nodes with {} edges from {} files"
          .format(len(nodes), len(edges), filecount))
    graph = {'nodes': nodes, 'edges': edges}
    # pprint(graph)

    with open(outfname, 'w') as f:
        json.dump(graph, f, indent=4)