gh-157514: Add structured version info for tkinter (GH-157516)

Add constants tkinter.TCL_VERSION_INFO and tkinter.TK_VERSION_INFO,
named tuples with the versions of the Tcl and Tk libraries that were
used for building the module, and _tkinter.TCL_PATCH_LEVEL and
_tkinter.TK_PATCH_LEVEL strings.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serhiy StorchakaandClaude Opus 5 authored and GitHub committed 2026-09-29 15:55:12 +03:00
1 parent cbecd5edd9
commit 4a5a7b9f6d
7 files changed
+98 -25

No files matched your search

+29
View File
@@ -6898,3 +6898,32 @@ and :mod:`!tkinter.constants` namespaces.
DOTBOX
Other option values: ``'underline'``, ``'numeric'`` and ``'dotbox'``.
Version information
^^^^^^^^^^^^^^^^^^^
.. data:: TCL_VERSION_INFO
TK_VERSION_INFO
The versions of the Tcl and Tk libraries that were used for building
the :mod:`!_tkinter` module, as named tuples with the same five fields
as :data:`sys.version_info`: *major*, *minor*, *micro*, *releaselevel*
and *serial*.
*releaselevel* is ``'alpha'``, ``'beta'`` or ``'final'``.
Converting them to a string gives the version in the usual Tcl/Tk notation,
for example ``'9.0.3'`` for a final release or ``'9.1b2'`` for a
pre-release.
These may be different from the libraries actually used at runtime,
which are available as :meth:`Misc.info_patchlevel` (the Tcl version)
and the ``tk_patchLevel`` Tcl variable.
.. versionadded:: next
.. data:: TkVersion
TclVersion
The major and minor version of the Tk and Tcl libraries that were used
for building the :mod:`!_tkinter` module, as floats, for example ``8.6``.
Prefer :data:`TK_VERSION_INFO` and :data:`TCL_VERSION_INFO`, which are
more precise and compare correctly.
+5
View File
@@ -632,6 +632,11 @@ tkinter
methods (without recursion) also work on Tk older than 9.1.
(Contributed by Serhiy Storchaka in :gh:`151910`.)
* Added constants :const:`~tkinter.TCL_VERSION_INFO` and
:const:`~tkinter.TK_VERSION_INFO`, named tuples with the versions of
the Tcl and Tk libraries that were used for building the module.
(Contributed by Serhiy Storchaka in :gh:`157514`.)
* Added new :class:`!tkinter.Text` methods :meth:`~tkinter.Text.edit_canundo`
and :meth:`~tkinter.Text.edit_canredo` which return whether an undo or redo
is possible.
+1 -1
View File
@@ -491,7 +491,7 @@ def collect_tkinter(info_add):
except ImportError:
pass
else:
attributes = ('TK_VERSION', 'TCL_VERSION')
attributes = ('TK_PATCH_LEVEL', 'TCL_PATCH_LEVEL')
copy_attributes(info_add, _tkinter, 'tkinter.%s', attributes)
try:
+48 -24
View File
@@ -9,6 +9,7 @@ import textwrap
import time
import unittest
import weakref
import _tkinter
import tkinter
from tkinter import TclError, ttk
import enum
@@ -24,6 +25,49 @@ from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest,
support.requires('gui')
def check_version_info(test, vi):
# The following is almost a copy of tests for sys.version_info.
test.assertIsInstance(vi[:], tuple)
test.assertEqual(len(vi), 5)
test.assertIsInstance(vi[0], int)
test.assertIsInstance(vi[1], int)
test.assertIsInstance(vi[2], int)
test.assertIn(vi[3], ("alpha", "beta", "candidate", "final"))
test.assertIsInstance(vi[4], int)
test.assertIsInstance(vi.major, int)
test.assertIsInstance(vi.minor, int)
test.assertIsInstance(vi.micro, int)
test.assertIn(vi.releaselevel, ("alpha", "beta", "final"))
test.assertIsInstance(vi.serial, int)
test.assertEqual(vi[0], vi.major)
test.assertEqual(vi[1], vi.minor)
test.assertEqual(vi[2], vi.micro)
test.assertEqual(vi[3], vi.releaselevel)
test.assertEqual(vi[4], vi.serial)
test.assertTrue(vi > (1,0,0))
if vi.releaselevel == 'final':
test.assertEqual(vi.serial, 0)
else:
test.assertEqual(vi.micro, 0)
test.assertStartsWith(str(vi), f'{vi.major}.{vi.minor}')
class VersionTest(unittest.TestCase):
def test_version_info(self):
for vi, version, patchlevel in (
(tkinter.TCL_VERSION_INFO, _tkinter.TCL_VERSION, _tkinter.TCL_PATCH_LEVEL),
(tkinter.TK_VERSION_INFO, _tkinter.TK_VERSION, _tkinter.TK_PATCH_LEVEL),
):
with self.subTest(patchlevel=patchlevel):
check_version_info(self, vi)
self.assertEqual(str(vi), patchlevel)
self.assertEqual(f'{vi.major}.{vi.minor}', version)
self.assertEqual(tkinter.TclVersion,
float(f'{tkinter.TCL_VERSION_INFO.major}.'
f'{tkinter.TCL_VERSION_INFO.minor}'))
class MiscTest(AbstractTkTest, unittest.TestCase):
def test_all(self):
@@ -830,30 +874,10 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
vi = self.root.info_patchlevel()
f = tkinter.Frame(self.root)
self.assertEqual(f.info_patchlevel(), vi)
# The following is almost a copy of tests for sys.version_info.
self.assertIsInstance(vi[:], tuple)
self.assertEqual(len(vi), 5)
self.assertIsInstance(vi[0], int)
self.assertIsInstance(vi[1], int)
self.assertIsInstance(vi[2], int)
self.assertIn(vi[3], ("alpha", "beta", "candidate", "final"))
self.assertIsInstance(vi[4], int)
self.assertIsInstance(vi.major, int)
self.assertIsInstance(vi.minor, int)
self.assertIsInstance(vi.micro, int)
self.assertIn(vi.releaselevel, ("alpha", "beta", "final"))
self.assertIsInstance(vi.serial, int)
self.assertEqual(vi[0], vi.major)
self.assertEqual(vi[1], vi.minor)
self.assertEqual(vi[2], vi.micro)
self.assertEqual(vi[3], vi.releaselevel)
self.assertEqual(vi[4], vi.serial)
self.assertTrue(vi > (1,0,0))
if vi.releaselevel == 'final':
self.assertEqual(vi.serial, 0)
else:
self.assertEqual(vi.micro, 0)
self.assertStartsWith(str(vi), f'{vi.major}.{vi.minor}')
check_version_info(self, vi)
# The Tcl library loaded at runtime should be compatible with
# the one used for building the module.
self.assertEqual(vi[:2], tkinter.TCL_VERSION_INFO[:2])
def test_embedded_null(self):
widget = tkinter.Entry(self.root)
+3
View File
@@ -170,6 +170,9 @@ def _parse_version(version):
releaselevel = {'a': 'alpha', 'b': 'beta'}[releaselevel]
return _VersionInfoType(major, minor, micro, releaselevel, serial)
TCL_VERSION_INFO = _parse_version(_tkinter.TCL_PATCH_LEVEL)
TK_VERSION_INFO = _parse_version(_tkinter.TK_PATCH_LEVEL)
@enum._simple_enum(enum.StrEnum)
class EventType:
@@ -0,0 +1,4 @@
Add constants :const:`tkinter.TCL_VERSION_INFO` and
:const:`tkinter.TK_VERSION_INFO`, named tuples with the versions of the Tcl
and Tk libraries that were used for building the module, and
``_tkinter.TCL_PATCH_LEVEL`` and ``_tkinter.TK_PATCH_LEVEL`` strings.
+8
View File
@@ -3880,6 +3880,14 @@ PyInit__tkinter(void)
Py_DECREF(m);
return NULL;
}
if (PyModule_AddStringConstant(m, "TK_PATCH_LEVEL", TK_PATCH_LEVEL)) {
Py_DECREF(m);
return NULL;
}
if (PyModule_AddStringConstant(m, "TCL_PATCH_LEVEL", TCL_PATCH_LEVEL)) {
Py_DECREF(m);
return NULL;
}
Tkapp_Type = PyType_FromSpec(&Tkapp_Type_spec);
if (PyModule_AddObjectRef(m, "TkappType", Tkapp_Type)) {