https://github.com/python/cpython/commit/7edd8bd62579f39a06084837df170f770968e449
commit: 7edd8bd62579f39a06084837df170f770968e449
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: terryjreedy <[email protected]>
date: 2026-08-23T06:03:18Z
summary:
[3.13] gh-155648: In IDLE tests, call unittest.main without exit arg
(GH-156249) (#156253)
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 ee368255f9b5c7e..5d0176af42f03ee 100644
--- a/Lib/idlelib/idle_test/test_query.py
+++ b/Lib/idlelib/idle_test/test_query.py
@@ -449,4 +449,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]