======================================
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


.. Local Variables:
.. mode: rst
.. End:
