Printing a plain text dependency graph

We populate a custom working set and compute its dependency graph, arranging for one subtree to appear more than once in the graph:

>>> anton_1 = make_dist("anton-1.egg", depends="""berta
...                                               charlie[extra]""")
>>> berta_2 = make_dist("berta-2.egg", depends="charlie[artxe, extra]")
>>> charlie_1_4 = make_dist("charlie-1.4.egg", depends="""[extra]
...                                                       dora
...                                                       [artxe]
...                                                       dora
...                                                       emil""")
>>> dora_0_5 = make_dist("dora-0.5.egg")
>>> emil_1 = make_dist("emil-1.egg")
>>> ws = make_working_set(anton_1, berta_2, charlie_1_4, dora_0_5, emil_1)
>>> from tl.eggdeps.graph import Graph
>>> graph = Graph(working_set=ws)
>>> graph.from_working_set()
>>> sprint(graph)
{'anton': {'berta': {None: set([])},
           'charlie': {'extra': set([])}},
 'berta': {'charlie': {'artxe': set([]), 'extra': set([])}},
 'charlie': {'dora': {None: set(['artxe', 'extra'])},
             'emil': {None: set(['artxe'])}},
 'dora': {},
 'emil': {}}

Full trees

Full trees are still somewhat abbreviated representations of the complete dependency structure but include enough information to reconstruct the latter: they spell out recurring subtrees only once but print the subtree root distributions in all other places and add ellipses to hint at omissions.

>>> from tl.eggdeps.plaintext import print_graph
>>> print_graph(graph, Options())
anton
    berta
        charlie [artxe, extra] ...
    charlie [extra]
      [artxe]
        dora
        emil
      [extra]
        dora

Version numbers may be printed next to the distribution names:

>>> print_graph(graph, Options(version_numbers=True))
anton 1
    berta 2
        charlie 1.4 [artxe, extra] ...
    charlie 1.4 [extra]
      [artxe]
        dora 0.5
        emil 1
      [extra]
        dora 0.5

Reduced output

The representation can be shortened in several ways. The terse option omits ellipses for left-out subtrees:

>>> print_graph(graph, Options(terse=True))
anton
    berta
        charlie [artxe, extra]
    charlie [extra]
      [artxe]
        dora
        emil
      [extra]
        dora

Alternatively, the once option hides omitted subtrees completely so that even the names of root distributions of subtrees are printed in only one place. Which extras of each distribution are required is then not shown anymore. This means that the full information cannot be reconstructed from the output anymore. Still, an ellipsis indicates where one or more subtrees have been left out:

>>> print_graph(graph, Options(once=True))
anton
    berta
        ...
    charlie
      [artxe]
        dora
        emil
      [extra]
        ...

These two options may be combined to remove even those ellipses:

>>> print_graph(graph, Options(once=True, terse=True))
anton
    berta
    charlie
      [artxe]
        dora
        emil
      [extra]

Determining which occurrence of a subtree to render

If a subgraph of the dependency structure occurs multiple times, a deterministic algorithm is employed to decide at which point in the printed tree the subgraph is to be rendered.

A subgraph will be rendered as close to the root as possible in order to avoid printing a deep tree:

>>> anton = make_dist("anton-1.egg", depends="""berta
...                                             charlie""")
>>> berta = make_dist("berta-2.egg", depends="charlie")
>>> charlie = make_dist("charlie-1.4.egg", depends="dora")
>>> dora = make_dist("dora-0.5.egg")
>>> ws = make_working_set(anton, berta, charlie, dora)
>>> graph = Graph(working_set=ws)
>>> graph.from_specifications("anton")
>>> print_graph(graph, Options())
anton
    berta
        charlie ...
    charlie
        dora

In particular, this prevents infinite recursion with cyclic dependency graphs:

>>> anton = make_dist("anton-1.egg", depends="anton")
>>> ws = make_working_set(anton)
>>> graph = Graph(working_set=ws)
>>> graph.from_specifications("anton")
>>> print_graph(graph, Options())
anton
    anton ...

If a subgraph can be reached from a root node both by a route consisting only of mandatory dependency edges and by a route including extra dependencies, the subgraph will be rendered at the point reached without considering extras:

>>> anton = make_dist("anton-1.egg", depends="""berta
...                                             [extra]
...                                             charlie""")
>>> ws = make_working_set(anton, berta, charlie, dora)
>>> graph = Graph(working_set=ws)
>>> graph.from_specifications("anton[extra]")
>>> print_graph(graph, Options())
anton
    berta
        charlie
            dora
  [extra]
    charlie ...

However, if a distribution is required more than once with different sets of extras, the dependencies of all extras involved will be rendered at the same occurrence of that distribution:

>>> anton = make_dist("anton-1.egg", depends="""berta
...                                             charlie""")
>>> berta = make_dist("berta-2.egg", depends="charlie[foo]")
>>> charlie = make_dist("charlie-1.4.egg", depends="""dora
...                                                   [foo]
...                                                   emil""")
>>> dora = make_dist("dora-0.5.egg")
>>> emil = make_dist("emil-1.egg")
>>> ws = make_working_set(anton, berta, charlie, dora, emil)
>>> graph = Graph(working_set=ws)
>>> graph.from_specifications("anton")
>>> print_graph(graph, Options())
anton
    berta
        charlie [foo] ...
    charlie
        dora
      [foo]
        emil