Skip to content

Commit c1b42db

Browse files
authoredMar 18, 2025··
gh-130914: Make graphlib.TopologicalSorter.prepare() idempotent (#131317)
Closes #130914: Make graphlib.TopologicalSorter.prepare() idempotent Relax the rules so that `.prepare()` can be called multiple times, provided that no work has been passed out by `.get_ready()` yet.
1 parent f819900 commit c1b42db

File tree

6 files changed

+43
-6
lines changed

6 files changed

+43
-6
lines changed
 

‎Doc/library/graphlib.rst

+8
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@
106106
function, the graph cannot be modified, and therefore no more nodes can be
107107
added using :meth:`~TopologicalSorter.add`.
108108

109+
A :exc:`ValueError` will be raised if the sort has been started by
110+
:meth:`~.static_order` or :meth:`~.get_ready`.
111+
112+
.. versionchanged:: next
113+
114+
``prepare()`` can now be called more than once as long as the sort has
115+
not started. Previously this raised :exc:`ValueError`.
116+
109117
.. method:: is_active()
110118

111119
Returns ``True`` if more progress can be made and ``False`` otherwise.

‎Doc/whatsnew/3.14.rst

+9
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,15 @@ getopt
607607
* Add support for returning intermixed options and non-option arguments in order.
608608
(Contributed by Serhiy Storchaka in :gh:`126390`.)
609609

610+
611+
graphlib
612+
--------
613+
614+
* Allow :meth:`graphlib.TopologicalSorter.prepare` to be called more than once
615+
as long as sorting has not started.
616+
(Contributed by Daniel Pope in :gh:`130914`)
617+
618+
610619
http
611620
----
612621

‎Lib/graphlib.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,17 @@ def prepare(self):
9090
still be used to obtain as many nodes as possible until cycles block more
9191
progress. After a call to this function, the graph cannot be modified and
9292
therefore no more nodes can be added using "add".
93+
94+
Raise ValueError if nodes have already been passed out of the sorter.
95+
9396
"""
94-
if self._ready_nodes is not None:
95-
raise ValueError("cannot prepare() more than once")
97+
if self._npassedout > 0:
98+
raise ValueError("cannot prepare() after starting sort")
9699

97-
self._ready_nodes = [
98-
i.node for i in self._node2info.values() if i.npredecessors == 0
99-
]
100+
if self._ready_nodes is None:
101+
self._ready_nodes = [
102+
i.node for i in self._node2info.values() if i.npredecessors == 0
103+
]
100104
# ready_nodes is set before we look for cycles on purpose:
101105
# if the user wants to catch the CycleError, that's fine,
102106
# they can continue using the instance to grab as many

‎Lib/test/test_graphlib.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,21 @@ def test_calls_before_prepare(self):
140140
def test_prepare_multiple_times(self):
141141
ts = graphlib.TopologicalSorter()
142142
ts.prepare()
143-
with self.assertRaisesRegex(ValueError, r"cannot prepare\(\) more than once"):
143+
ts.prepare()
144+
145+
def test_prepare_after_pass_out(self):
146+
ts = graphlib.TopologicalSorter({'a': 'bc'})
147+
ts.prepare()
148+
self.assertEqual(set(ts.get_ready()), {'b', 'c'})
149+
with self.assertRaisesRegex(ValueError, r"cannot prepare\(\) after starting sort"):
144150
ts.prepare()
145151

152+
def test_prepare_cycleerror_each_time(self):
153+
ts = graphlib.TopologicalSorter({'a': 'b', 'b': 'a'})
154+
for attempt in range(1, 4):
155+
with self.assertRaises(graphlib.CycleError, msg=f"{attempt=}"):
156+
ts.prepare()
157+
146158
def test_invalid_nodes_in_done(self):
147159
ts = graphlib.TopologicalSorter()
148160
ts.add(1, 2, 3, 4)

‎Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,7 @@ Michael Pomraning
14831483
Martin Pool
14841484
Iustin Pop
14851485
Claudiu Popa
1486+
Daniel Pope
14861487
Nick Pope
14871488
John Popplewell
14881489
Matheus Vieira Portela
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Allow :meth:`graphlib.TopologicalSorter.prepare` to be called more than once
2+
as long as sorting has not started.
3+
Patch by Daniel Pope.

0 commit comments

Comments
 (0)
Please sign in to comment.