]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-130914: Make graphlib.TopologicalSorter.prepare() idempotent (#131317)
authorDaniel Pope <lordmauve@users.noreply.github.com>
Tue, 18 Mar 2025 21:28:00 +0000 (21:28 +0000)
committerGitHub <noreply@github.com>
Tue, 18 Mar 2025 21:28:00 +0000 (16:28 -0500)
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.

Doc/library/graphlib.rst
Doc/whatsnew/3.14.rst
Lib/graphlib.py
Lib/test/test_graphlib.py
Misc/ACKS
Misc/NEWS.d/next/Library/2025-03-16-08-00-29.gh-issue-130914.6z883_.rst [new file with mode: 0644]

index a0b16576fad219c5260d8e46fc2d9448fe84cce1..49424700303fdd341c49e6d7787a4f92444ec7ea 100644 (file)
       function, the graph cannot be modified, and therefore no more nodes can be
       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()
 
       Returns ``True`` if more progress can be made and ``False`` otherwise.
index 9a25f27ca57257a9114c417242f583992add4d60..79b219dd72651c22302df9fbe4aaafc7d7feac5c 100644 (file)
@@ -607,6 +607,15 @@ getopt
 * Add support for returning intermixed options and non-option arguments in order.
   (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
 ----
 
index 82f33fb5cf312c2807c43948254f2e428d067388..7961c9c5cac2d66d04082bbf34e98b5438100ccc 100644 (file)
@@ -90,13 +90,17 @@ class TopologicalSorter:
         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
         therefore no more nodes can be added using "add".
+
+        Raise ValueError if nodes have already been passed out of the sorter.
+
         """
-        if self._ready_nodes is not None:
-            raise ValueError("cannot prepare() more than once")
+        if self._npassedout > 0:
+            raise ValueError("cannot prepare() after starting sort")
 
-        self._ready_nodes = [
-            i.node for i in self._node2info.values() if i.npredecessors == 0
-        ]
+        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:
         # if the user wants to catch the CycleError, that's fine,
         # they can continue using the instance to grab as many
index 5f38af4024c5b07f6191a3b146e449d546561f73..66722e0b0498a6696b3776b1014ad2ef34a3ba9e 100644 (file)
@@ -140,9 +140,21 @@ class TestTopologicalSort(unittest.TestCase):
     def test_prepare_multiple_times(self):
         ts = graphlib.TopologicalSorter()
         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()
 
+    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):
         ts = graphlib.TopologicalSorter()
         ts.add(1, 2, 3, 4)
index c623d69e086b639c1a3352e4136ef2899d7b2e7d..b0b4ea0db44b64b8ec99e1af86a0c0ae6b0d3a2f 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1483,6 +1483,7 @@ Michael Pomraning
 Martin Pool
 Iustin Pop
 Claudiu Popa
+Daniel Pope
 Nick Pope
 John Popplewell
 Matheus Vieira Portela
diff --git a/Misc/NEWS.d/next/Library/2025-03-16-08-00-29.gh-issue-130914.6z883_.rst b/Misc/NEWS.d/next/Library/2025-03-16-08-00-29.gh-issue-130914.6z883_.rst
new file mode 100644 (file)
index 0000000..956cf83
--- /dev/null
@@ -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.