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.
This commit is contained in:
Daniel Pope 2025-03-18 21:28:00 +00:00 committed by GitHub
parent f819900245
commit c1b42db9e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 44 additions and 7 deletions

View file

@ -106,6 +106,14 @@
function, the graph cannot be modified, and therefore no more nodes can be function, the graph cannot be modified, and therefore no more nodes can be
added using :meth:`~TopologicalSorter.add`. added using :meth:`~TopologicalSorter.add`.
A :exc:`ValueError` will be raised if the sort has been started by
:meth:`~.static_order` or :meth:`~.get_ready`.
.. versionchanged:: next
``prepare()`` can now be called more than once as long as the sort has
not started. Previously this raised :exc:`ValueError`.
.. method:: is_active() .. method:: is_active()
Returns ``True`` if more progress can be made and ``False`` otherwise. Returns ``True`` if more progress can be made and ``False`` otherwise.

View file

@ -607,6 +607,15 @@ getopt
* Add support for returning intermixed options and non-option arguments in order. * Add support for returning intermixed options and non-option arguments in order.
(Contributed by Serhiy Storchaka in :gh:`126390`.) (Contributed by Serhiy Storchaka in :gh:`126390`.)
graphlib
--------
* Allow :meth:`graphlib.TopologicalSorter.prepare` to be called more than once
as long as sorting has not started.
(Contributed by Daniel Pope in :gh:`130914`)
http http
---- ----

View file

@ -90,13 +90,17 @@ class TopologicalSorter:
still be used to obtain as many nodes as possible until cycles block more still be used to obtain as many nodes as possible until cycles block more
progress. After a call to this function, the graph cannot be modified and progress. After a call to this function, the graph cannot be modified and
therefore no more nodes can be added using "add". therefore no more nodes can be added using "add".
"""
if self._ready_nodes is not None:
raise ValueError("cannot prepare() more than once")
self._ready_nodes = [ Raise ValueError if nodes have already been passed out of the sorter.
i.node for i in self._node2info.values() if i.npredecessors == 0
] """
if self._npassedout > 0:
raise ValueError("cannot prepare() after starting sort")
if self._ready_nodes is None:
self._ready_nodes = [
i.node for i in self._node2info.values() if i.npredecessors == 0
]
# ready_nodes is set before we look for cycles on purpose: # ready_nodes is set before we look for cycles on purpose:
# if the user wants to catch the CycleError, that's fine, # if the user wants to catch the CycleError, that's fine,
# they can continue using the instance to grab as many # they can continue using the instance to grab as many

View file

@ -140,9 +140,21 @@ class TestTopologicalSort(unittest.TestCase):
def test_prepare_multiple_times(self): def test_prepare_multiple_times(self):
ts = graphlib.TopologicalSorter() ts = graphlib.TopologicalSorter()
ts.prepare() ts.prepare()
with self.assertRaisesRegex(ValueError, r"cannot prepare\(\) more than once"): ts.prepare()
def test_prepare_after_pass_out(self):
ts = graphlib.TopologicalSorter({'a': 'bc'})
ts.prepare()
self.assertEqual(set(ts.get_ready()), {'b', 'c'})
with self.assertRaisesRegex(ValueError, r"cannot prepare\(\) after starting sort"):
ts.prepare() ts.prepare()
def test_prepare_cycleerror_each_time(self):
ts = graphlib.TopologicalSorter({'a': 'b', 'b': 'a'})
for attempt in range(1, 4):
with self.assertRaises(graphlib.CycleError, msg=f"{attempt=}"):
ts.prepare()
def test_invalid_nodes_in_done(self): def test_invalid_nodes_in_done(self):
ts = graphlib.TopologicalSorter() ts = graphlib.TopologicalSorter()
ts.add(1, 2, 3, 4) ts.add(1, 2, 3, 4)

View file

@ -1483,6 +1483,7 @@ Michael Pomraning
Martin Pool Martin Pool
Iustin Pop Iustin Pop
Claudiu Popa Claudiu Popa
Daniel Pope
Nick Pope Nick Pope
John Popplewell John Popplewell
Matheus Vieira Portela Matheus Vieira Portela

View file

@ -0,0 +1,3 @@
Allow :meth:`graphlib.TopologicalSorter.prepare` to be called more than once
as long as sorting has not started.
Patch by Daniel Pope.