[3.14] gh-155648: In IDLE tests, call unittest.main without exit arg (GH-156249) (#156252)

terryjreedy <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/63bec1a6d9bf60e199cb036b9355bcb9d855ff82
commit: 63bec1a6d9bf60e199cb036b9355bcb9d855ff82
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: terryjreedy <[email protected]>
date: 2026-08-23T06:06:04Z
summary:

[3.14] gh-155648: In IDLE tests, call unittest.main without exit arg (GH-156249) (#156252)

gh-155648: In IDLE tests, call unittest.main without exit arg (GH-156249)

* gh-155648: In IDLE tests, call unittest.main without exit arg

DD bug 73: In idlelib.idle_test, test_xyz.py files should end with

if __name__ == '__main__':
    unittest.main(verbosity=2)

The default exit is True. This need not and should not be added.
5 files add the confusing equivalent exit=2 ("why the weird value?"),

4 files add exit=False. This is nonsensical when there is nothing more to run;
main will immediately exit anyway. When running a test file from an IDLE editor,
this argument has no visible effect. However, a Claude-based
bug finder claims that in other circumstances (such as a program running
the test in a shell), the good test may falsely fail. Even if this is
not true, it can only confuse a reader.

(The only place in idlelib for exit=False is in idlelib/abc.py files where the unittest
is followed by an htest. The default exit=True exits the process, skipping the htest.)

As part of editing the discussion of this in idle_test/htest.py, I clarified other things.
(cherry picked from commit c20318bf09e59946f805ace2bd15f70524009edc)

Co-authored-by: Terry Jan Reedy <[email protected]>

files:
M Lib/idlelib/idle_test/README.txt
M Lib/idlelib/idle_test/htest.py
M Lib/idlelib/idle_test/test_delegator.py
M Lib/idlelib/idle_test/test_format.py
M Lib/idlelib/idle_test/test_history.py
M Lib/idlelib/idle_test/test_pathbrowser.py
M Lib/idlelib/idle_test/test_query.py
M Lib/idlelib/idle_test/test_search.py
M Lib/idlelib/idle_test/test_searchbase.py
M Lib/idlelib/idle_test/test_text.py
M Lib/idlelib/idle_test/test_undo.py

diff --git a/Lib/idlelib/idle_test/README.txt b/Lib/idlelib/idle_test/README.txt
index cacd06db873d039..242de2225248178 100644
--- a/Lib/idlelib/idle_test/README.txt
+++ b/Lib/idlelib/idle_test/README.txt
@@ -33,9 +33,9 @@ insert the import and main lines before the htest lines.
 
 if __name__ == "__main__":
     from unittest import main
-    main('idlelib.idle_test.test_abc', verbosity=2, exit=False)
+    main('idlelib.idle_test.test_abc', verbosity=2)
 
-The ', exit=False' is only needed if an htest follows.
+Add ', exit=False' to the main call if and only if an htest follows.
 
 
 
diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py
index 778e5c3d84e4963..0bd0378fbfa07a9 100644
--- a/Lib/idlelib/idle_test/htest.py
+++ b/Lib/idlelib/idle_test/htest.py
@@ -1,48 +1,51 @@
 """Run human tests of Idle's window, dialog, and popup widgets.
 
-run(*tests) Create a master Tk() htest window.  Within that, run each
-callable in tests after finding the matching test spec in this file.  If
-tests is empty, run an htest for each spec dict in this file after
-finding the matching callable in the module named in the spec.  Close
-the master window to end testing.
-
-In a tested module, let X be a global name bound to a callable (class or
-function) whose .__name__ attribute is also X (the usual situation). The
-first parameter of X must be 'parent' or 'master'.  When called, the
-first argument will be the root window.  X must create a child
-Toplevel(parent/master) (or subclass thereof).  The Toplevel may be a
-test widget or dialog, in which case the callable is the corresponding
-class.  Or the Toplevel may contain the widget to be tested or set up a
-context in which a test widget is invoked.  In this latter case, the
-callable is a wrapper function that sets up the Toplevel and other
-objects.  Wrapper function names, such as _editor_window', should start
-with '_' and be lowercase.
-
+The main function, `run(*tests)`, is defined at the end of this file.
+Argument `tests` is a possibly empty tuple of callables defined in some
+idlelib.abc module (or possibly modules).  Its steps:
+1. Create a master Tk() htest window.  Within that window ...
+2a. If tuple `tests` is not empty, run was likely called from one
+    module.  Run each callable in `tests` after finding the matching
+    callable_spec test spec in this file.
+2b. If tests is empty, run was likely called from this file.
+    Run an htest for each spec dict in this file after finding the
+    matching callable in the module named in the spec.
+3. Close the master window to end testing.
+
+In a tested module, let X be a global name bound to a callable (class
+or function) whose .__name__ attribute (its `class` or `def` definition
+name) is also X.  X must expect exactly 1 positional argument, a
+parent toplevel window. Run passes the htest window.  X must create a
+child Toplevel(parent/master).  The callable may be either a runtime
+object or a wrapper function written just for the test.  In the latter
+case, its name should start with '_' and be lowercase (such as '_ttt').
 
 End the module with
-
+```
 if __name__ == '__main__':
-    <run unittest.main with 'exit=False'>
+    from unittest import main
+    main("idlelib.idle_test.test_xyz", verbosity=2, exit=False)
+
     from idlelib.idle_test.htest import run
-    run(callable)  # There could be multiple comma-separated callables.
+    run(callable)
+```
+Replace 'xyz' as appropriate and 'callable' with the callable name or
+comma-separated names (multiple names is rare).  'exit=False' is needed
+for the htest to run.
 
 To have wrapper functions ignored by coverage reports, tag the def
-header like so: "def _wrapper(parent):  # htest #".  Use the same tag
-for htest lines in widget code.  Make sure that the 'if __name__' line
-matches the above.  Then have make sure that .coveragerc includes the
-following:
-
+header like so: "def _wrapper(root):  # htest #".  Use the same tag
+for htest-only lines in the main code. To ignore the 'if __name__'
+statement, match the example above.  Add the below to coveragerc.
+```
 [report]
 exclude_lines =
     .*# htest #
     if __name__ == .__main__.:
-
-(The "." instead of "'" is intentional and necessary.)
-
+```
 
 To run any X, this file must contain a matching instance of the
 following template, with X.__name__ prepended to '_spec'.
-When all tests are run, the prefix is use to get X.
 
 callable_spec = {
     'file': '',
@@ -51,11 +54,10 @@
     }
 
 file (no .py): run() imports file.py.
-kwds: augmented with {'parent':root} and passed to X as **kwds.
+kwds: run() augments with {'parent':root} and passes to X as **kwds.
 title: an example kwd; some widgets need this, delete line if not.
 msg: master window hints about testing the widget.
 
-
 TODO test these modules and classes:
   autocomplete_w.AutoCompleteWindow
   debugger.Debugger
diff --git a/Lib/idlelib/idle_test/test_delegator.py b/Lib/idlelib/idle_test/test_delegator.py
index 922416297a42e02..c4273deee7ffbdd 100644
--- a/Lib/idlelib/idle_test/test_delegator.py
+++ b/Lib/idlelib/idle_test/test_delegator.py
@@ -41,4 +41,4 @@ def test_mydel(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=2)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_format.py b/Lib/idlelib/idle_test/test_format.py
index e5e903688597aa7..6550e9765f290d3 100644
--- a/Lib/idlelib/idle_test/test_format.py
+++ b/Lib/idlelib/idle_test/test_format.py
@@ -665,4 +665,4 @@ def test_rstrip_end(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=2)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_history.py b/Lib/idlelib/idle_test/test_history.py
index 675396514447514..e1031579c3d8210 100644
--- a/Lib/idlelib/idle_test/test_history.py
+++ b/Lib/idlelib/idle_test/test_history.py
@@ -169,4 +169,4 @@ def test_history_prev_next(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=2)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_pathbrowser.py b/Lib/idlelib/idle_test/test_pathbrowser.py
index 13d8b9e1ba9572a..a198978d5c1ef72 100644
--- a/Lib/idlelib/idle_test/test_pathbrowser.py
+++ b/Lib/idlelib/idle_test/test_pathbrowser.py
@@ -83,4 +83,4 @@ def test_PathBrowserTreeItem(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=False)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py
index a6ef858a8c954a2..58c173723a5adac 100644
--- a/Lib/idlelib/idle_test/test_query.py
+++ b/Lib/idlelib/idle_test/test_query.py
@@ -448,4 +448,4 @@ def test_click_args(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=False)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_search.py b/Lib/idlelib/idle_test/test_search.py
index de703c195cd2290..2b0a9d483bfc0ff 100644
--- a/Lib/idlelib/idle_test/test_search.py
+++ b/Lib/idlelib/idle_test/test_search.py
@@ -77,4 +77,4 @@ def test_find_selection(self):
         text.delete('2.0', 'end')
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=2)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_searchbase.py b/Lib/idlelib/idle_test/test_searchbase.py
index 8c9c410ebaf47c0..1780cab6527dd94 100644
--- a/Lib/idlelib/idle_test/test_searchbase.py
+++ b/Lib/idlelib/idle_test/test_searchbase.py
@@ -157,4 +157,4 @@ def test_create_command_buttons(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=2)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_text.py b/Lib/idlelib/idle_test/test_text.py
index 43a9ba02c3d3c9a..8ee1c9f2d768131 100644
--- a/Lib/idlelib/idle_test/test_text.py
+++ b/Lib/idlelib/idle_test/test_text.py
@@ -233,4 +233,4 @@ def setUp(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=False)
+    unittest.main(verbosity=2)
diff --git a/Lib/idlelib/idle_test/test_undo.py b/Lib/idlelib/idle_test/test_undo.py
index beb5b582039f884..0488a2c9809b48c 100644
--- a/Lib/idlelib/idle_test/test_undo.py
+++ b/Lib/idlelib/idle_test/test_undo.py
@@ -132,4 +132,4 @@ def test_addcmd(self):
 
 
 if __name__ == '__main__':
-    unittest.main(verbosity=2, exit=False)
+    unittest.main(verbosity=2)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.