Skip to content

Commit 65ecc39

Browse files
authored
bpo-17005: Minor improvements to the documentation of TopologicalSorter (GH-18155)
1 parent 7142df5 commit 65ecc39

File tree

1 file changed

+67
-75
lines changed

1 file changed

+67
-75
lines changed

Doc/library/functools.rst

Lines changed: 67 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -522,54 +522,46 @@ The :mod:`functools` module defines the following functions:
522522

523523
Provides functionality to topologically sort a graph of hashable nodes.
524524

525-
A topological order is a linear ordering of the vertices in a graph such that for
526-
every directed edge u -> v from vertex u to vertex v, vertex u comes before vertex
527-
v in the ordering. For instance, the vertices of the graph may represent tasks to
528-
be performed, and the edges may represent constraints that one task must be
529-
performed before another; in this example, a topological ordering is just a valid
530-
sequence for the tasks. A complete topological ordering is possible if and only if
531-
the graph has no directed cycles, that is, if it is a directed acyclic graph.
532-
533-
If the optional *graph* argument is provided it must be a dictionary representing
534-
a directed acyclic graph where the keys are nodes and the values are iterables of
535-
all predecessors of that node in the graph (the nodes that have edges that point
536-
to the value in the key). Additional nodes can be added to the graph using the
537-
:meth:`~TopologicalSorter.add` method.
538-
539-
In the general case, the steps required to perform the sorting of a given graph
540-
are as follows:
541-
542-
* Create an instance of the :class:`TopologicalSorter` with an optional initial graph.
525+
A topological order is a linear ordering of the vertices in a graph such that
526+
for every directed edge u -> v from vertex u to vertex v, vertex u comes
527+
before vertex v in the ordering. For instance, the vertices of the graph may
528+
represent tasks to be performed, and the edges may represent constraints that
529+
one task must be performed before another; in this example, a topological
530+
ordering is just a valid sequence for the tasks. A complete topological
531+
ordering is possible if and only if the graph has no directed cycles, that
532+
is, if it is a directed acyclic graph.
533+
534+
If the optional *graph* argument is provided it must be a dictionary
535+
representing a directed acyclic graph where the keys are nodes and the values
536+
are iterables of all predecessors of that node in the graph (the nodes that
537+
have edges that point to the value in the key). Additional nodes can be added
538+
to the graph using the :meth:`~TopologicalSorter.add` method.
539+
540+
In the general case, the steps required to perform the sorting of a given
541+
graph are as follows:
542+
543+
* Create an instance of the :class:`TopologicalSorter` with an optional
544+
initial graph.
543545
* Add additional nodes to the graph.
544546
* Call :meth:`~TopologicalSorter.prepare` on the graph.
545-
* While :meth:`~TopologicalSorter.is_active` is ``True``, iterate over the
546-
nodes returned by :meth:`~TopologicalSorter.get_ready` and process them.
547-
Call :meth:`~TopologicalSorter.done` on each node as it finishes processing.
547+
* While :meth:`~TopologicalSorter.is_active` is ``True``, iterate over
548+
the nodes returned by :meth:`~TopologicalSorter.get_ready` and
549+
process them. Call :meth:`~TopologicalSorter.done` on each node as it
550+
finishes processing.
548551

549552
In case just an immediate sorting of the nodes in the graph is required and
550-
no parallelism is involved, the convenience method :meth:`TopologicalSorter.static_order`
551-
can be used directly. For example, this method can be used to implement a simple
552-
version of the C3 linearization algorithm used by Python to calculate the Method
553-
Resolution Order (MRO) of a derived class:
553+
no parallelism is involved, the convenience method
554+
:meth:`TopologicalSorter.static_order` can be used directly:
554555

555556
.. doctest::
556557

557-
>>> class A: pass
558-
>>> class B(A): pass
559-
>>> class C(A): pass
560-
>>> class D(B, C): pass
561-
562-
>>> D.__mro__
563-
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
564-
565-
>>> graph = {D: {B, C}, C: {A}, B: {A}, A:{object}}
558+
>>> graph = {"D": {"B", "C"}, "C": {"A"}, "B": {"A"}}
566559
>>> ts = TopologicalSorter(graph)
567-
>>> topological_order = tuple(ts.static_order())
568-
>>> tuple(reversed(topological_order))
569-
(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
560+
>>> tuple(ts.static_order())
561+
('A', 'C', 'B', 'D')
570562

571-
The class is designed to easily support parallel processing of the nodes as they
572-
become ready. For instance::
563+
The class is designed to easily support parallel processing of the nodes as
564+
they become ready. For instance::
573565

574566
topological_sorter = TopologicalSorter()
575567

@@ -595,39 +587,39 @@ The :mod:`functools` module defines the following functions:
595587

596588
.. method:: add(node, *predecessors)
597589

598-
Add a new node and its predecessors to the graph. Both the *node* and
599-
all elements in *predecessors* must be hashable.
590+
Add a new node and its predecessors to the graph. Both the *node* and all
591+
elements in *predecessors* must be hashable.
600592

601-
If called multiple times with the same node argument, the set of dependencies
602-
will be the union of all dependencies passed in.
593+
If called multiple times with the same node argument, the set of
594+
dependencies will be the union of all dependencies passed in.
603595

604596
It is possible to add a node with no dependencies (*predecessors* is not
605597
provided) or to provide a dependency twice. If a node that has not been
606-
provided before is included among *predecessors* it will be automatically added
607-
to the graph with no predecessors of its own.
598+
provided before is included among *predecessors* it will be automatically
599+
added to the graph with no predecessors of its own.
608600

609601
Raises :exc:`ValueError` if called after :meth:`~TopologicalSorter.prepare`.
610602

611603
.. method:: prepare()
612604

613-
Mark the graph as finished and check for cycles in the graph. If any cycle is
614-
detected, :exc:`CycleError` will be raised, but
615-
:meth:`~TopologicalSorter.get_ready` can still be used to obtain as many nodes
616-
as possible until cycles block more progress. After a call to this function,
617-
the graph cannot be modified, and therefore no more nodes can be added using
618-
:meth:`~TopologicalSorter.add`.
605+
Mark the graph as finished and check for cycles in the graph. If any cycle
606+
is detected, :exc:`CycleError` will be raised, but
607+
:meth:`~TopologicalSorter.get_ready` can still be used to obtain as many
608+
nodes as possible until cycles block more progress. After a call to this
609+
function, the graph cannot be modified, and therefore no more nodes can be
610+
added using :meth:`~TopologicalSorter.add`.
619611

620612
.. method:: is_active()
621613

622-
Returns ``True`` if more progress can be made and ``False`` otherwise. Progress
623-
can be made if cycles do not block the resolution and either there are still
624-
nodes ready that haven't yet been returned by
614+
Returns ``True`` if more progress can be made and ``False`` otherwise.
615+
Progress can be made if cycles do not block the resolution and either
616+
there are still nodes ready that haven't yet been returned by
625617
:meth:`TopologicalSorter.get_ready` or the number of nodes marked
626-
:meth:`TopologicalSorter.done` is less than the number that have been returned
627-
by :meth:`TopologicalSorter.get_ready`.
618+
:meth:`TopologicalSorter.done` is less than the number that have been
619+
returned by :meth:`TopologicalSorter.get_ready`.
628620

629-
The :meth:`~TopologicalSorter.__bool__` method of this class defers to this
630-
function, so instead of::
621+
The :meth:`~TopologicalSorter.__bool__` method of this class defers to
622+
this function, so instead of::
631623

632624
if ts.is_active():
633625
...
@@ -637,29 +629,28 @@ The :mod:`functools` module defines the following functions:
637629
if ts:
638630
...
639631

640-
Raises :exc:`ValueError` if called without calling :meth:`~TopologicalSorter.prepare`
641-
previously.
632+
Raises :exc:`ValueError` if called without calling
633+
:meth:`~TopologicalSorter.prepare` previously.
642634

643635
.. method:: done(*nodes)
644636

645637
Marks a set of nodes returned by :meth:`TopologicalSorter.get_ready` as
646-
processed, unblocking any successor of each node in *nodes* for being returned
647-
in the future by a call to :meth:`TopologicalSorter.get_ready`.
638+
processed, unblocking any successor of each node in *nodes* for being
639+
returned in the future by a call to :meth:`TopologicalSorter.get_ready`.
648640

649641
Raises :exc:`ValueError` if any node in *nodes* has already been marked as
650-
processed by a previous call to this method or if a node was not added to the
651-
graph by using :meth:`TopologicalSorter.add`, if called without calling
652-
:meth:`~TopologicalSorter.prepare` or if node has not yet been returned by
653-
:meth:`~TopologicalSorter.get_ready`.
642+
processed by a previous call to this method or if a node was not added to
643+
the graph by using :meth:`TopologicalSorter.add`, if called without
644+
calling :meth:`~TopologicalSorter.prepare` or if node has not yet been
645+
returned by :meth:`~TopologicalSorter.get_ready`.
654646

655647
.. method:: get_ready()
656648

657-
Returns a ``tuple`` with all the nodes that are ready. Initially it returns all
658-
nodes with no predecessors, and once those are marked as processed by calling
659-
:meth:`TopologicalSorter.done`, further calls will return all new nodes that
660-
have all their predecessors already processed. Once no more progress can be
661-
made, empty tuples are returned.
662-
made.
649+
Returns a ``tuple`` with all the nodes that are ready. Initially it
650+
returns all nodes with no predecessors, and once those are marked as
651+
processed by calling :meth:`TopologicalSorter.done`, further calls will
652+
return all new nodes that have all their predecessors already processed.
653+
Once no more progress can be made, empty tuples are returned.
663654

664655
Raises :exc:`ValueError` if called without calling
665656
:meth:`~TopologicalSorter.prepare` previously.
@@ -694,9 +685,10 @@ The :mod:`functools` module defines the following functions:
694685
>>> print([*ts2.static_order()])
695686
[0, 2, 1, 3]
696687

697-
This is due to the fact that "0" and "2" are in the same level in the graph (they
698-
would have been returned in the same call to :meth:`~TopologicalSorter.get_ready`)
699-
and the order between them is determined by the order of insertion.
688+
This is due to the fact that "0" and "2" are in the same level in the
689+
graph (they would have been returned in the same call to
690+
:meth:`~TopologicalSorter.get_ready`) and the order between them is
691+
determined by the order of insertion.
700692

701693

702694
If any cycle is detected, :exc:`CycleError` will be raised.

0 commit comments

Comments
 (0)