Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Doc/library/tkinter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 5 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,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.
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,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:
Expand Down
72 changes: 48 additions & 24 deletions Lib/test/test_tkinter/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import time
import unittest
import weakref
import _tkinter
import tkinter
from tkinter import TclError, ttk
import enum
Expand All @@ -22,6 +23,49 @@

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):
Expand Down Expand Up @@ -828,30 +872,10 @@ def test_info_patchlevel(self):
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)
Expand Down
3 changes: 3 additions & 0 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions Modules/_tkinter.c
Original file line number Diff line number Diff line change
Expand Up @@ -3848,6 +3848,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)) {
Expand Down
Loading