https://github.com/python/cpython/commit/c42f36ac53c9f54c182d70fabe243a04934a8363
commit: c42f36ac53c9f54c182d70fabe243a04934a8363
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-22T12:52:50Z
summary:

gh-151878: Add tkinter Entry and Spinbox validate methods (GH-151879)

Wrap the Tk "validate" widget command of the entry and spinbox widgets as
the methods Entry.validate() and Spinbox.validate(), forcing an evaluation
of the validatecommand independently of the validate option and returning
whether the value is valid.

Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-06-22-01-02-33.gh-issue-151878.IFIA5C.rst
M Doc/library/tkinter.rst
M Doc/whatsnew/3.16.rst
M Lib/test/test_tkinter/test_widgets.py
M Lib/tkinter/__init__.py

diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst
index 249b1602a711ad..13fe3514b5ebc8 100644
--- a/Doc/library/tkinter.rst
+++ b/Doc/library/tkinter.rst
@@ -4254,6 +4254,14 @@ Widget classes
       Typically associated with mouse motion events, to produce the effect of
       dragging the entry at high speed through the window.
 
+   .. method:: validate()
+
+      Force an evaluation of the command given by the *validatecommand* option,
+      independently of the conditions specified by the *validate* option, and
+      return whether the value is considered valid.
+
+      .. versionadded:: next
+
 
 .. class:: Frame(master=None, cnf={}, **kw)
 
@@ -5206,6 +5214,14 @@ Widget classes
 
       .. versionadded:: 3.8
 
+   .. method:: validate()
+
+      Force an evaluation of the command given by the *validatecommand* option,
+      independently of the conditions specified by the *validate* option, and
+      return whether the value is considered valid.
+
+      .. versionadded:: next
+
 
 
 .. class:: Text(master=None, cnf={}, **kw)
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 8abc4d0af8d19f..3c625801c46a1c 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -163,6 +163,11 @@ tkinter
   synchronization of the displayed view with the underlying text.
   (Contributed by Serhiy Storchaka in :gh:`151675`.)
 
+* Added a :meth:`!validate` method to the :class:`!tkinter.Entry` and
+  :class:`!tkinter.Spinbox` widgets, which forces an evaluation of the
+  validation command.
+  (Contributed by Serhiy Storchaka in :gh:`151878`.)
+
 * Added new window-management methods :meth:`~tkinter.Misc.winfo_isdark`
   (dark mode detection), :meth:`~tkinter.Wm.wm_iconbadge` (application icon
   badge) and :meth:`~tkinter.Wm.wm_stackorder` (toplevel stacking order).
diff --git a/Lib/test/test_tkinter/test_widgets.py 
b/Lib/test/test_tkinter/test_widgets.py
index 4b51d219d87e5b..d0305562a0cb05 100644
--- a/Lib/test/test_tkinter/test_widgets.py
+++ b/Lib/test/test_tkinter/test_widgets.py
@@ -589,6 +589,25 @@ def test_select_aliases(self):
         self.assertRaisesRegex(TclError, 'bad entry index "xyz"',
                                widget.select_range, 'xyz', 'end')
 
+    def test_validate(self):
+        calls = []
+        def validatecommand(value):
+            calls.append(value)
+            return value.isdigit()
+        # validate='none' means validation is never triggered automatically,
+        # so validate() exercises the forced evaluation.
+        widget = self.create(validate='none',
+                validatecommand=(self.root.register(validatecommand), '%P'))
+        widget.insert(0, '123')
+        result = widget.validate()
+        self.assertIs(result, True)
+        self.assertEqual(calls, ['123'])
+        widget.delete(0, 'end')
+        widget.insert(0, 'abc')
+        calls.clear()
+        self.assertIs(widget.validate(), False)
+        self.assertEqual(calls, ['abc'])
+
 
 @add_configure_tests(StandardOptionsTests)
 class SpinboxTest(EntryTest, unittest.TestCase):
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index 68de4a35689691..974e386be1bb49 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -3445,6 +3445,14 @@ def selection_to(self, index):
 
     select_to = selection_to
 
+    def validate(self):
+        """Force an evaluation of the validation command.
+
+        This evaluates the command given by the validatecommand option,
+        independently of the conditions specified by the validate option.
+        Return whether the value is considered valid."""
+        return self.tk.getboolean(self.tk.call(self._w, 'validate'))
+
 
 class Frame(Widget):
     """Frame widget which may contain other widgets and can have a 3D 
border."""
@@ -4957,6 +4965,14 @@ def selection_to(self, index):
         """Set the variable end of a selection to INDEX."""
         self.selection('to', index)
 
+    def validate(self):
+        """Force an evaluation of the validation command.
+
+        This evaluates the command given by the validatecommand option,
+        independently of the conditions specified by the validate option.
+        Return whether the value is considered valid."""
+        return self.tk.getboolean(self.tk.call(self._w, 'validate'))
+
 ###########################################################################
 
 
diff --git 
a/Misc/NEWS.d/next/Library/2026-06-22-01-02-33.gh-issue-151878.IFIA5C.rst 
b/Misc/NEWS.d/next/Library/2026-06-22-01-02-33.gh-issue-151878.IFIA5C.rst
new file mode 100644
index 00000000000000..af0d2c25136c45
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-22-01-02-33.gh-issue-151878.IFIA5C.rst
@@ -0,0 +1,3 @@
+Add the :meth:`!validate` method to the :class:`tkinter.Entry` and
+:class:`tkinter.Spinbox` widgets, forcing an evaluation of the validation
+command.

_______________________________________________
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]

Reply via email to