https://github.com/python/cpython/commit/ba0c0e6be18ea56d00b4534cc9e32d1acaba96d6 commit: ba0c0e6be18ea56d00b4534cc9e32d1acaba96d6 branch: main author: Serhiy Storchaka <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2026-06-28T19:14:27+03:00 summary:
gh-78335: Complete the widget option lists in tkinter docstrings (GH-152485) Several widget __init__ docstrings omitted valid options, and Menubutton and Message had no option list at all. List every option supported by the widget, tagging those added in Tk 9.0 and 9.1. Add test_options_in_docstring, asserting that every option in OPTIONS is named in the widget's __init__ docstring. Options reported by keys() but not in the docstring are only printed in verbose mode, as some depend on the Tk version. Co-Authored-By: Claude Opus 4.8 <[email protected]> files: A Misc/NEWS.d/next/Library/2026-06-28-11-00-40.gh-issue-78335.Kp3mWq.rst M Lib/test/test_tkinter/test_geometry_managers.py M Lib/test/test_tkinter/widget_tests.py M Lib/test/test_ttk/test_extensions.py M Lib/tkinter/__init__.py M Lib/tkinter/ttk.py diff --git a/Lib/test/test_tkinter/test_geometry_managers.py b/Lib/test/test_tkinter/test_geometry_managers.py index 7fb0abff0049b5d..e98a6af060e80bc 100644 --- a/Lib/test/test_tkinter/test_geometry_managers.py +++ b/Lib/test/test_tkinter/test_geometry_managers.py @@ -19,6 +19,7 @@ class PackTest(AbstractWidgetTest, unittest.TestCase): test_keys = None + test_options_in_docstring = None def create2(self): pack = tkinter.Toplevel(self.root, name='pack') @@ -312,6 +313,7 @@ def test_pack_short_aliases(self): class PlaceTest(AbstractWidgetTest, unittest.TestCase): test_keys = None + test_options_in_docstring = None def create2(self): t = tkinter.Toplevel(self.root, width=300, height=200, bd=0) @@ -538,6 +540,7 @@ def test_place_method_aliases(self): class GridTest(AbstractWidgetTest, unittest.TestCase): test_keys = None + test_options_in_docstring = None def tearDown(self): cols, rows = self.root.grid_size() diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py index 94244a8b3fe2448..014906cba2902c3 100644 --- a/Lib/test/test_tkinter/widget_tests.py +++ b/Lib/test/test_tkinter/widget_tests.py @@ -8,6 +8,16 @@ _sentinel = object() +# Abbreviated option names accepted by Tk in addition to the full names. +_OPTION_ALIASES = { + 'bd': 'borderwidth', + 'bg': 'background', + 'bgimg': 'backgroundimage', + 'fg': 'foreground', + 'invcmd': 'invalidcommand', + 'vcmd': 'validatecommand', +} + # Options which accept all values allowed by Tk_GetPixels # borderwidth = bd @@ -214,23 +224,44 @@ def test_keys(self): widget[k] # Test if OPTIONS contains all keys if test.support.verbose: - aliases = { - 'bd': 'borderwidth', - 'bg': 'background', - 'bgimg': 'backgroundimage', - 'fg': 'foreground', - 'invcmd': 'invalidcommand', - 'vcmd': 'validatecommand', - } keys = set(keys) expected = set(self.OPTIONS) for k in sorted(keys - expected): - if not (k in aliases and - aliases[k] in keys and - aliases[k] in expected): + if not (k in _OPTION_ALIASES and + _OPTION_ALIASES[k] in keys and + _OPTION_ALIASES[k] in expected): print('%s.OPTIONS doesn\'t contain "%s"' % (self.__class__.__name__, k)) + def test_options_in_docstring(self): + # Every option in OPTIONS must be listed in the docstring of the + # __init__ method (see gh-78335). Options reported by keys() but + # missing from the docstring are only printed in verbose mode, as + # some of them depend on the Tk version. + widget = self.create() + doc = type(widget).__init__.__doc__ + if doc is None: + self.skipTest('docstrings are not available (run with -OO)') + # Look at the option list only, not the leading description. + start = doc.find('Valid option names') + if start < 0: + start = doc.find('OPTIONS') + if start < 0: + self.skipTest('the __init__ docstring does not list options') + documented = set(re.findall(r'[a-z][a-z0-9]+', doc[start:])) + def is_documented(option): + return (option in documented or + _OPTION_ALIASES.get(option) in documented) + missing = sorted(o for o in self.OPTIONS if not is_documented(o)) + self.assertEqual(missing, [], + '%s options missing from the __init__ docstring: %s' + % (type(widget).__name__, missing)) + if test.support.verbose: + for key in sorted(set(widget.keys())): + if not is_documented(key): + print('%s.__init__ docstring doesn\'t contain "%s"' + % (type(widget).__name__, key)) + class PixelOptionsTests: """Standard options that accept all formats acceptable to Tk_GetPixels. diff --git a/Lib/test/test_ttk/test_extensions.py b/Lib/test/test_ttk/test_extensions.py index 0460f1b9aa31172..fb6bd24b4b9147b 100644 --- a/Lib/test/test_ttk/test_extensions.py +++ b/Lib/test/test_ttk/test_extensions.py @@ -199,6 +199,10 @@ def test_resize(self): class OptionMenuTest(test_widgets.MenubuttonTest, unittest.TestCase): + # OptionMenu documents only its own options, not the inherited + # Menubutton options (like the classic tkinter.OptionMenu). + test_options_in_docstring = None + def setUp(self): super().setUp() self.textvar = tkinter.StringVar(self.root) diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 897a1ba093c2720..0f16757826e23c8 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -3040,10 +3040,11 @@ class Toplevel(BaseWidget, Wm): def __init__(self, master=None, cnf={}, **kw): """Construct a toplevel widget with the parent MASTER. - Valid option names: background, bd, bg, borderwidth, class, - colormap, container, cursor, height, highlightbackground, - highlightcolor, highlightthickness, menu, relief, screen, takefocus, - use, visual, width.""" + Valid option names: background, backgroundimage (Tk 9.0+), bd, bg, + bgimg (Tk 9.0+), borderwidth, class, colormap, container, + cursor, height, highlightbackground, highlightcolor, + highlightthickness, menu, padx, pady, relief, screen, + takefocus, tile (Tk 9.0+), use, visual, width.""" if kw: cnf = _cnfmerge((cnf, kw)) extra = () @@ -3442,12 +3443,13 @@ def __init__(self, master=None, cnf={}, **kw): """Construct a checkbutton widget with the parent MASTER. Valid option names: activebackground, activeforeground, anchor, - background, bd, bg, bitmap, borderwidth, command, cursor, - disabledforeground, fg, font, foreground, height, - highlightbackground, highlightcolor, highlightthickness, image, - indicatoron, justify, offvalue, onvalue, padx, pady, relief, - selectcolor, selectimage, state, takefocus, text, textvariable, - underline, variable, width, wraplength.""" + background, bd, bg, bitmap, borderwidth, command, compound, + cursor, disabledforeground, fg, font, foreground, height, + highlightbackground, highlightcolor, highlightthickness, + image, indicatoron, justify, offrelief, offvalue, onvalue, + overrelief, padx, pady, relief, selectcolor, selectimage, + state, takefocus, text, textvariable, tristateimage, + tristatevalue, underline, variable, width, wraplength.""" Widget.__init__(self, master, 'checkbutton', cnf, kw) def _setup(self, master, cnf): @@ -3491,13 +3493,15 @@ def __init__(self, master=None, cnf={}, **kw): """Construct an entry widget with the parent MASTER. Valid option names: background, bd, bg, borderwidth, cursor, - exportselection, fg, font, foreground, highlightbackground, - highlightcolor, highlightthickness, insertbackground, - insertborderwidth, insertofftime, insertontime, insertwidth, - invalidcommand, invcmd, justify, relief, selectbackground, - selectborderwidth, selectforeground, show, state, takefocus, - textvariable, validate, validatecommand, vcmd, width, - xscrollcommand.""" + disabledbackground, disabledforeground, exportselection, fg, + font, foreground, highlightbackground, highlightcolor, + highlightthickness, insertbackground, insertborderwidth, + insertofftime, insertontime, insertwidth, invalidcommand, + invcmd, justify, locale (Tk 9.1+), placeholder (Tk 9.0+), + placeholderforeground (Tk 9.0+), readonlybackground, relief, + selectbackground, selectborderwidth, selectforeground, show, + state, takefocus, textvariable, validate, validatecommand, + vcmd, width, xscrollcommand.""" Widget.__init__(self, master, 'entry', cnf, kw) def delete(self, first, last=None): @@ -3584,9 +3588,11 @@ class Frame(Widget): def __init__(self, master=None, cnf={}, **kw): """Construct a frame widget with the parent MASTER. - Valid option names: background, bd, bg, borderwidth, class, - colormap, container, cursor, height, highlightbackground, - highlightcolor, highlightthickness, relief, takefocus, visual, width.""" + Valid option names: background, backgroundimage (Tk 9.0+), bd, bg, + bgimg (Tk 9.0+), borderwidth, class, colormap, container, + cursor, height, highlightbackground, highlightcolor, + highlightthickness, padx, pady, relief, takefocus, tile (Tk + 9.0+), visual, width.""" cnf = _cnfmerge((cnf, kw)) extra = () if 'class_' in cnf: @@ -3616,7 +3622,8 @@ def __init__(self, master=None, cnf={}, **kw): WIDGET-SPECIFIC OPTIONS - height, state, width + compound, height, state, + textangle (Tk 9.1+), width """ Widget.__init__(self, master, 'label', cnf, kw) @@ -3628,11 +3635,14 @@ class Listbox(Widget, XView, YView): def __init__(self, master=None, cnf={}, **kw): """Construct a listbox widget with the parent MASTER. - Valid option names: background, bd, bg, borderwidth, cursor, - exportselection, fg, font, foreground, height, highlightbackground, - highlightcolor, highlightthickness, relief, selectbackground, - selectborderwidth, selectforeground, selectmode, setgrid, takefocus, - width, xscrollcommand, yscrollcommand, listvariable.""" + Valid option names: activestyle, background, bd, bg, borderwidth, + cursor, disabledforeground, exportselection, fg, font, + foreground, height, highlightbackground, highlightcolor, + highlightthickness, inactiveselectbackground (Tk 9.1+), + inactiveselectforeground (Tk 9.1+), justify, listvariable, + relief, selectbackground, selectborderwidth, selectforeground, + selectmode, setgrid, state, takefocus, width, xscrollcommand, + yscrollcommand.""" Widget.__init__(self, master, 'listbox', cnf, kw) def activate(self, index): @@ -3742,7 +3752,8 @@ def __init__(self, master=None, cnf={}, **kw): """Construct menu widget with the parent MASTER. Valid option names: activebackground, activeborderwidth, - activeforeground, background, bd, bg, borderwidth, cursor, + activeforeground, activerelief (Tk 9.0+), background, bd, bg, + borderwidth, cursor, disabledforeground, fg, font, foreground, postcommand, relief, selectcolor, takefocus, tearoff, tearoffcommand, title, type.""" Widget.__init__(self, master, 'menu', cnf, kw) @@ -3880,6 +3891,14 @@ class Menubutton(Widget): """Menubutton widget, obsolete since Tk8.0.""" def __init__(self, master=None, cnf={}, **kw): + """Construct a menubutton widget with the parent MASTER. + + Valid option names: activebackground, activeforeground, anchor, + background, bd, bg, bitmap, borderwidth, compound, cursor, + direction, disabledforeground, fg, font, foreground, height, + highlightbackground, highlightcolor, highlightthickness, + image, indicatoron, justify, menu, padx, pady, relief, state, + takefocus, text, textvariable, underline, width, wraplength.""" Widget.__init__(self, master, 'menubutton', cnf, kw) @@ -3887,6 +3906,12 @@ class Message(Widget): """Message widget to display multiline text. Obsolete since Label does it too.""" def __init__(self, master=None, cnf={}, **kw): + """Construct a message widget with the parent MASTER. + + Valid option names: anchor, aspect, background, bd, bg, borderwidth, + cursor, fg, font, foreground, highlightbackground, + highlightcolor, highlightthickness, justify, padx, pady, + relief, takefocus, text, textvariable, width.""" Widget.__init__(self, master, 'message', cnf, kw) @@ -3897,12 +3922,13 @@ def __init__(self, master=None, cnf={}, **kw): """Construct a radiobutton widget with the parent MASTER. Valid option names: activebackground, activeforeground, anchor, - background, bd, bg, bitmap, borderwidth, command, cursor, - disabledforeground, fg, font, foreground, height, - highlightbackground, highlightcolor, highlightthickness, image, - indicatoron, justify, padx, pady, relief, selectcolor, selectimage, - state, takefocus, text, textvariable, underline, value, variable, - width, wraplength.""" + background, bd, bg, bitmap, borderwidth, command, compound, + cursor, disabledforeground, fg, font, foreground, height, + highlightbackground, highlightcolor, highlightthickness, + image, indicatoron, justify, offrelief, overrelief, padx, + pady, relief, selectcolor, selectimage, state, takefocus, + text, textvariable, tristateimage, tristatevalue, underline, + value, variable, width, wraplength.""" Widget.__init__(self, master, 'radiobutton', cnf, kw) def deselect(self): @@ -4033,9 +4059,11 @@ def __init__(self, master=None, cnf={}, **kw): WIDGET-SPECIFIC OPTIONS - autoseparators, height, maxundo, - spacing1, spacing2, spacing3, - state, tabs, undo, width, wrap, + autoseparators, blockcursor, endline, + height, inactiveselectbackground, + insertunfocussed, locale (Tk 9.1+), maxundo, + spacing1, spacing2, spacing3, startline, + state, tabs, tabstyle, undo, width, wrap, """ Widget.__init__(self, master, 'text', cnf, kw) @@ -5002,9 +5030,12 @@ def __init__(self, master=None, cnf={}, **kw): buttondownrelief, buttonuprelief, command, disabledbackground, disabledforeground, format, from, - invalidcommand, increment, + invalidcommand, invcmd, increment, + locale (Tk 9.1+), + placeholder (Tk 9.0+), + placeholderforeground (Tk 9.0+), readonlybackground, state, to, - validate, validatecommand values, + validate, validatecommand, vcmd, values, width, wrap, """ Widget.__init__(self, master, 'spinbox', cnf, kw) @@ -5201,8 +5232,9 @@ def __init__(self, master=None, cnf={}, **kw): WIDGET-SPECIFIC OPTIONS handlepad, handlesize, opaqueresize, - sashcursor, sashpad, sashrelief, - sashwidth, showhandle, + proxybackground, proxyborderwidth, + proxyrelief, sashcursor, sashpad, + sashrelief, sashwidth, showhandle, """ Widget.__init__(self, master, 'panedwindow', cnf, kw) diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index 0cabc5c3140fd6b..69ac31c9e1244db 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -593,8 +593,8 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, compound, cursor, image, state, style, takefocus, - text, textvariable, underline, width + class, compound, cursor, image, justify (Tk 9.0+), padding, + state, style, takefocus, text, textvariable, underline, width WIDGET-SPECIFIC OPTIONS @@ -616,8 +616,8 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, compound, cursor, image, state, style, takefocus, - text, textvariable, underline, width + class, compound, cursor, image, justify (Tk 9.0+), padding, + state, style, takefocus, text, textvariable, underline, width WIDGET-SPECIFIC OPTIONS @@ -650,8 +650,10 @@ def __init__(self, master=None, widget=None, **kw): WIDGET-SPECIFIC OPTIONS - exportselection, invalidcommand, justify, show, state, - textvariable, validate, validatecommand, width + background, exportselection, font, foreground, invalidcommand, + justify, locale (Tk 9.1+), placeholder (Tk 9.0+), + placeholderforeground (Tk 9.0+), show, state, textvariable, + validate, validatecommand, width VALIDATION MODES @@ -688,12 +690,14 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, cursor, style, takefocus + class, cursor, style, takefocus, xscrollcommand WIDGET-SPECIFIC OPTIONS - exportselection, justify, height, postcommand, state, - textvariable, values, width + background, exportselection, font, foreground, height, + invalidcommand, justify, locale (Tk 9.1+), placeholder (Tk 9.0+), + placeholderforeground (Tk 9.0+), postcommand, show, state, + textvariable, validate, validatecommand, values, width """ Entry.__init__(self, master, "ttk::combobox", **kw) @@ -742,13 +746,13 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, compound, cursor, image, style, takefocus, text, - textvariable, underline, width + class, compound, cursor, image, state, style, takefocus, + text, textvariable, underline, width WIDGET-SPECIFIC OPTIONS - anchor, background, font, foreground, justify, padding, - relief, text, wraplength + anchor, background, borderwidth, font, foreground, justify, + padding, relief, text, textangle (Tk 9.1+), wraplength """ Widget.__init__(self, master, "ttk::label", kw) @@ -766,8 +770,9 @@ def __init__(self, master=None, **kw): class, cursor, style, takefocus WIDGET-SPECIFIC OPTIONS - labelanchor, text, underline, padding, labelwidget, width, - height + + borderwidth, height, labelanchor, labelwidget, padding, + relief, text, underline, width """ Widget.__init__(self, master, "ttk::labelframe", kw) @@ -783,8 +788,8 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, compound, cursor, image, state, style, takefocus, - text, textvariable, underline, width + class, compound, cursor, image, justify (Tk 9.0+), padding, + state, style, takefocus, text, textvariable, underline, width WIDGET-SPECIFIC OPTIONS @@ -997,7 +1002,9 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, cursor, style, takefocus + anchor (Tk 9.0+), class, cursor, font (Tk 9.0+), + foreground (Tk 9.0+), justify (Tk 9.0+), style, takefocus, + text (Tk 9.0+), wraplength (Tk 9.0+) WIDGET-SPECIFIC OPTIONS @@ -1036,8 +1043,8 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, compound, cursor, image, state, style, takefocus, - text, textvariable, underline, width + class, compound, cursor, image, justify (Tk 9.0+), padding, + state, style, takefocus, text, textvariable, underline, width WIDGET-SPECIFIC OPTIONS @@ -1064,7 +1071,7 @@ def __init__(self, master=None, **kw): STANDARD OPTIONS - class, cursor, style, takefocus + class, cursor, state, style, takefocus WIDGET-SPECIFIC OPTIONS @@ -1161,7 +1168,10 @@ def __init__(self, master=None, **kw): WIDGET-SPECIFIC OPTIONS - to, from_, increment, values, wrap, format, command + background, command, exportselection, font, foreground, + format, from_, increment, justify, locale (Tk 9.1+), + placeholder (Tk 9.0+), placeholderforeground (Tk 9.0+), show, + state, textvariable, to, values, width, wrap """ Entry.__init__(self, master, "ttk::spinbox", **kw) @@ -1188,7 +1198,10 @@ def __init__(self, master=None, **kw): WIDGET-SPECIFIC OPTIONS - columns, displaycolumns, height, padding, selectmode, show + columns, displaycolumns, headingheight (Tk 9.1+), height, + padding, rowheight (Tk 9.1+), selectmode, selecttype (Tk 9.0+), + show, striped (Tk 9.0+), titlecolumns (Tk 9.0+), + titleitems (Tk 9.0+) ITEM OPTIONS diff --git a/Misc/NEWS.d/next/Library/2026-06-28-11-00-40.gh-issue-78335.Kp3mWq.rst b/Misc/NEWS.d/next/Library/2026-06-28-11-00-40.gh-issue-78335.Kp3mWq.rst new file mode 100644 index 000000000000000..641889cfa9ef41d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-28-11-00-40.gh-issue-78335.Kp3mWq.rst @@ -0,0 +1,4 @@ +Update the docstrings of :mod:`tkinter` and :mod:`tkinter.ttk` widget classes +to list all supported widget options, including options added in Tk 9.0 and +9.1. ``tkinter.Menubutton`` and ``tkinter.Message`` previously had no option +list at all. _______________________________________________ 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]
