From c2e3c06139e9468efb32629d147d99a1672d9e19 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 17 Mar 2022 13:05:52 +0200 Subject: [PATCH] bpo-46996: Remove support of Tcl/Tk < 8.5.12 (GH-31839) --- Doc/library/tkinter.ttk.rst | 4 +- Doc/whatsnew/3.11.rst | 3 + Lib/test/test_tcl.py | 72 ++++++----------- .../test_tkinter/test_geometry_managers.py | 37 ++++----- Lib/tkinter/test/test_tkinter/test_widgets.py | 77 ++++++------------- Lib/tkinter/test/test_ttk/test_widgets.py | 18 ++--- Lib/tkinter/test/widget_tests.py | 41 +++------- Lib/tkinter/ttk.py | 25 ------ .../2022-03-12-18-09-31.bpo-46996.SygzVz.rst | 1 + Modules/_tkinter.c | 28 +------ 10 files changed, 90 insertions(+), 216 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2022-03-12-18-09-31.bpo-46996.SygzVz.rst diff --git a/Doc/library/tkinter.ttk.rst b/Doc/library/tkinter.ttk.rst index 2db4c0f9143..d50ea99aa46 100644 --- a/Doc/library/tkinter.ttk.rst +++ b/Doc/library/tkinter.ttk.rst @@ -13,9 +13,7 @@ -------------- The :mod:`tkinter.ttk` module provides access to the Tk themed widget set, -introduced in Tk 8.5. If Python has not been compiled against Tk 8.5, this -module can still be accessed if *Tile* has been installed. The former -method using Tk 8.5 provides additional benefits including anti-aliased font +introduced in Tk 8.5. It provides additional benefits including anti-aliased font rendering under X11 and window transparency (requiring a composition window manager on X11). diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 9fbf46791c2..391423407ee 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -712,6 +712,9 @@ Build Changes be removed at some point in the future. (Contributed by Mark Dickinson in :issue:`45569`.) +* The :mod:`tkinter` package now requires Tcl/Tk version 8.5.12 or newer. + (Contributed by Serhiy Storchaka in :issue:`46996`.) + C API Changes ============= diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py index 581c31ccb72..e73ad596fe6 100644 --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -143,27 +143,18 @@ def testUnsetVarException(self): self.assertRaises(TclError,tcl.unsetvar,'a') def get_integers(self): - integers = (0, 1, -1, 2**31-1, -2**31, 2**31, -2**31-1, 2**63-1, -2**63) - # bignum was added in Tcl 8.5, but its support is able only since 8.5.8. - # Actually it is determined at compile time, so using get_tk_patchlevel() - # is not reliable. - # TODO: expose full static version. - if tcl_version >= (8, 5): - v = get_tk_patchlevel() - if v >= (8, 6, 0, 'final') or (8, 5, 8) <= v < (8, 6): - integers += (2**63, -2**63-1, 2**1000, -2**1000) - return integers + return (0, 1, -1, + 2**31-1, -2**31, 2**31, -2**31-1, + 2**63-1, -2**63, 2**63, -2**63-1, + 2**1000, -2**1000) def test_getint(self): tcl = self.interp.tk for i in self.get_integers(): self.assertEqual(tcl.getint(' %d ' % i), i) - if tcl_version >= (8, 5): - self.assertEqual(tcl.getint(' %#o ' % i), i) + self.assertEqual(tcl.getint(' %#o ' % i), i) self.assertEqual(tcl.getint((' %#o ' % i).replace('o', '')), i) self.assertEqual(tcl.getint(' %#x ' % i), i) - if tcl_version < (8, 5): # bignum was added in Tcl 8.5 - self.assertRaises(TclError, tcl.getint, str(2**1000)) self.assertEqual(tcl.getint(42), 42) self.assertRaises(TypeError, tcl.getint) self.assertRaises(TypeError, tcl.getint, '42', '10') @@ -317,8 +308,7 @@ def check(expr, expected): check('"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\0b"', 'a\x00b') - if tcl_version >= (8, 5): # bignum was added in Tcl 8.5 - check('2**64', str(2**64)) + check('2**64', str(2**64)) def test_exprdouble(self): tcl = self.interp @@ -349,8 +339,7 @@ def check(expr, expected): check('[string length "a\xbd\u20ac"]', 3.0) check(r'[string length "a\xbd\u20ac"]', 3.0) self.assertRaises(TclError, tcl.exprdouble, '"abc"') - if tcl_version >= (8, 5): # bignum was added in Tcl 8.5 - check('2**64', float(2**64)) + check('2**64', float(2**64)) def test_exprlong(self): tcl = self.interp @@ -381,8 +370,7 @@ def check(expr, expected): check('[string length "a\xbd\u20ac"]', 3) check(r'[string length "a\xbd\u20ac"]', 3) self.assertRaises(TclError, tcl.exprlong, '"abc"') - if tcl_version >= (8, 5): # bignum was added in Tcl 8.5 - self.assertRaises(TclError, tcl.exprlong, '2**64') + self.assertRaises(TclError, tcl.exprlong, '2**64') def test_exprboolean(self): tcl = self.interp @@ -422,10 +410,8 @@ def check(expr, expected): check('[string length "a\xbd\u20ac"]', True) check(r'[string length "a\xbd\u20ac"]', True) self.assertRaises(TclError, tcl.exprboolean, '"abc"') - if tcl_version >= (8, 5): # bignum was added in Tcl 8.5 - check('2**64', True) + check('2**64', True) - @unittest.skipUnless(tcl_version >= (8, 5), 'requires Tcl version >= 8.5') def test_booleans(self): tcl = self.interp def check(expr, expected): @@ -455,8 +441,6 @@ def test_expr_bignum(self): else: self.assertEqual(result, str(i)) self.assertIsInstance(result, str) - if get_tk_patchlevel() < (8, 5): # bignum was added in Tcl 8.5 - self.assertRaises(TclError, tcl.call, 'expr', str(2**1000)) def test_passing_values(self): def passValue(value): @@ -485,8 +469,6 @@ def passValue(value): b'str\xbding' if self.wantobjects else 'str\xbding') for i in self.get_integers(): self.assertEqual(passValue(i), i if self.wantobjects else str(i)) - if tcl_version < (8, 5): # bignum was added in Tcl 8.5 - self.assertEqual(passValue(2**1000), str(2**1000)) for f in (0.0, 1.0, -1.0, 1/3, sys.float_info.min, sys.float_info.max, -sys.float_info.min, -sys.float_info.max): @@ -552,8 +534,6 @@ def float_eq(actual, expected): check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac') for i in self.get_integers(): check(i, str(i)) - if tcl_version < (8, 5): # bignum was added in Tcl 8.5 - check(2**1000, str(2**1000)) for f in (0.0, 1.0, -1.0): check(f, repr(f)) for f in (1/3.0, sys.float_info.min, sys.float_info.max, @@ -600,16 +580,14 @@ def test_splitlist(self): ('1', '2', '3.4')), ] tk_patchlevel = get_tk_patchlevel() - if tcl_version >= (8, 5): - if not self.wantobjects or tk_patchlevel < (8, 5, 5): - # Before 8.5.5 dicts were converted to lists through string - expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4') - else: - expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,)) - testcases += [ - (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), - expected), - ] + if not self.wantobjects: + expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4') + else: + expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,)) + testcases += [ + (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), + expected), + ] dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s' % (self.wantobjects, tcl_version, tk_patchlevel)) for arg, res in testcases: @@ -642,15 +620,13 @@ def test_splitdict(self): {'a': (1, 2, 3) if self.wantobjects else '1 2 3', 'something': 'foo', 'status': ''}) - if tcl_version >= (8, 5): - arg = tcl.call('dict', 'create', - '-a', (1, 2, 3), '-something', 'foo', 'status', ()) - if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): - # Before 8.5.5 dicts were converted to lists through string - expected = {'a': '1 2 3', 'something': 'foo', 'status': ''} - else: - expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''} - self.assertEqual(splitdict(tcl, arg), expected) + arg = tcl.call('dict', 'create', + '-a', (1, 2, 3), '-something', 'foo', 'status', ()) + if not self.wantobjects: + expected = {'a': '1 2 3', 'something': 'foo', 'status': ''} + else: + expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''} + self.assertEqual(splitdict(tcl, arg), expected) def test_join(self): join = tkinter._join diff --git a/Lib/tkinter/test/test_tkinter/test_geometry_managers.py b/Lib/tkinter/test/test_tkinter/test_geometry_managers.py index c645d430079..c89bc8dbf85 100644 --- a/Lib/tkinter/test/test_tkinter/test_geometry_managers.py +++ b/Lib/tkinter/test/test_tkinter/test_geometry_managers.py @@ -4,7 +4,7 @@ from tkinter import TclError from test.support import requires -from tkinter.test.support import pixels_conv, tcl_version, requires_tcl +from tkinter.test.support import pixels_conv from tkinter.test.widget_tests import AbstractWidgetTest requires('gui') @@ -295,8 +295,7 @@ def test_place_configure_in(self): with self.assertRaisesRegex(TclError, "can't place %s relative to " "itself" % re.escape(str(f2))): f2.place_configure(in_=f2) - if tcl_version >= (8, 5): - self.assertEqual(f2.winfo_manager(), '') + self.assertEqual(f2.winfo_manager(), '') with self.assertRaisesRegex(TclError, 'bad window path name'): f2.place_configure(in_='spam') f2.place_configure(in_=f) @@ -491,8 +490,7 @@ def tearDown(self): for i in range(rows + 1): self.root.grid_rowconfigure(i, weight=0, minsize=0, pad=0, uniform='') self.root.grid_propagate(1) - if tcl_version >= (8, 5): - self.root.grid_anchor('nw') + self.root.grid_anchor('nw') super().tearDown() def test_grid_configure(self): @@ -619,16 +617,14 @@ def test_grid_columnconfigure(self): self.root.grid_columnconfigure((0, 3)) b = tkinter.Button(self.root) b.grid_configure(column=0, row=0) - if tcl_version >= (8, 5): - self.root.grid_columnconfigure('all', weight=3) - with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): - self.root.grid_columnconfigure('all') - self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3) + self.root.grid_columnconfigure('all', weight=3) + with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): + self.root.grid_columnconfigure('all') + self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3) self.assertEqual(self.root.grid_columnconfigure(3, 'weight'), 2) self.assertEqual(self.root.grid_columnconfigure(265, 'weight'), 0) - if tcl_version >= (8, 5): - self.root.grid_columnconfigure(b, weight=4) - self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 4) + self.root.grid_columnconfigure(b, weight=4) + self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 4) def test_grid_columnconfigure_minsize(self): with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): @@ -675,16 +671,14 @@ def test_grid_rowconfigure(self): self.root.grid_rowconfigure((0, 3)) b = tkinter.Button(self.root) b.grid_configure(column=0, row=0) - if tcl_version >= (8, 5): - self.root.grid_rowconfigure('all', weight=3) - with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): - self.root.grid_rowconfigure('all') - self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3) + self.root.grid_rowconfigure('all', weight=3) + with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): + self.root.grid_rowconfigure('all') + self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3) self.assertEqual(self.root.grid_rowconfigure(3, 'weight'), 2) self.assertEqual(self.root.grid_rowconfigure(265, 'weight'), 0) - if tcl_version >= (8, 5): - self.root.grid_rowconfigure(b, weight=4) - self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 4) + self.root.grid_rowconfigure(b, weight=4) + self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 4) def test_grid_rowconfigure_minsize(self): with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): @@ -774,7 +768,6 @@ def test_grid_info(self): self.assertEqual(info['pady'], self._str(4)) self.assertEqual(info['sticky'], 'ns') - @requires_tcl(8, 5) def test_grid_anchor(self): with self.assertRaisesRegex(TclError, 'bad anchor "x"'): self.root.grid_anchor('x') diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py index c0b92bf3b19..fe8ecfeb326 100644 --- a/Lib/tkinter/test/test_tkinter/test_widgets.py +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -4,11 +4,11 @@ import os from test.support import requires -from tkinter.test.support import (tcl_version, requires_tcl, +from tkinter.test.support import (requires_tcl, get_tk_patchlevel, widget_eq, AbstractDefaultRootTest) from tkinter.test.widget_tests import ( - add_standard_options, noconv, pixels_round, + add_standard_options, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, setUpModule) @@ -20,7 +20,7 @@ def float_round(x): class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): - _conv_pad_pixels = noconv + _conv_pad_pixels = False def test_configure_class(self): widget = self.create() @@ -139,7 +139,7 @@ def test_configure_labelwidget(self): class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): - _conv_pixels = noconv + _conv_pixels = False def test_configure_highlightthickness(self): widget = self.create() @@ -249,7 +249,7 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): 'takefocus', 'text', 'textvariable', 'underline', 'width', 'wraplength', ) - _conv_pixels = staticmethod(pixels_round) + _conv_pixels = round def create(self, **kwargs): return tkinter.Menubutton(self.root, **kwargs) @@ -345,10 +345,7 @@ def test_configure_insertwidth(self): self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p') self.checkParam(widget, 'insertwidth', 0.1, expected=2) self.checkParam(widget, 'insertwidth', -2, expected=2) - if pixels_round(0.9) <= 0: - self.checkParam(widget, 'insertwidth', 0.9, expected=2) - else: - self.checkParam(widget, 'insertwidth', 0.9, expected=1) + self.checkParam(widget, 'insertwidth', 0.9, expected=1) def test_configure_invalidcommand(self): widget = self.create() @@ -550,8 +547,6 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): 'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap', 'xscrollcommand', 'yscrollcommand', ) - if tcl_version < (8, 5): - _stringify = True def create(self, **kwargs): return tkinter.Text(self.root, **kwargs) @@ -560,12 +555,10 @@ def test_configure_autoseparators(self): widget = self.create() self.checkBooleanParam(widget, 'autoseparators') - @requires_tcl(8, 5) def test_configure_blockcursor(self): widget = self.create() self.checkBooleanParam(widget, 'blockcursor') - @requires_tcl(8, 5) def test_configure_endline(self): widget = self.create() text = '\n'.join('Line %d' for i in range(100)) @@ -589,7 +582,6 @@ def test_configure_maxundo(self): widget = self.create() self.checkIntegerParam(widget, 'maxundo', 0, 5, -1) - @requires_tcl(8, 5) def test_configure_inactiveselectbackground(self): widget = self.create() self.checkColorParam(widget, 'inactiveselectbackground') @@ -603,8 +595,7 @@ def test_configure_insertunfocussed(self): def test_configure_selectborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'selectborderwidth', - 1.3, 2.6, -2, '10p', conv=noconv, - keep_orig=tcl_version >= (8, 5)) + 1.3, 2.6, -2, '10p', conv=False) def test_configure_spacing1(self): widget = self.create() @@ -621,7 +612,6 @@ def test_configure_spacing3(self): self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c') self.checkParam(widget, 'spacing3', -10, expected=0) - @requires_tcl(8, 5) def test_configure_startline(self): widget = self.create() text = '\n'.join('Line %d' for i in range(100)) @@ -637,27 +627,18 @@ def test_configure_startline(self): def test_configure_state(self): widget = self.create() - if tcl_version < (8, 5): - self.checkParams(widget, 'state', 'disabled', 'normal') - else: - self.checkEnumParam(widget, 'state', 'disabled', 'normal') + self.checkEnumParam(widget, 'state', 'disabled', 'normal') def test_configure_tabs(self): widget = self.create() - if get_tk_patchlevel() < (8, 5, 11): - self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'), - expected=('10.2', '20.7', '1i', '2i')) - else: - self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i')) + self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i')) self.checkParam(widget, 'tabs', '10.2 20.7 1i 2i', expected=('10.2', '20.7', '1i', '2i')) self.checkParam(widget, 'tabs', '2c left 4c 6c center', expected=('2c', 'left', '4c', '6c', 'center')) self.checkInvalidParam(widget, 'tabs', 'spam', - errmsg='bad screen distance "spam"', - keep_orig=tcl_version >= (8, 5)) + errmsg='bad screen distance "spam"') - @requires_tcl(8, 5) def test_configure_tabstyle(self): widget = self.create() self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor') @@ -674,10 +655,7 @@ def test_configure_width(self): def test_configure_wrap(self): widget = self.create() - if tcl_version < (8, 5): - self.checkParams(widget, 'wrap', 'char', 'none', 'word') - else: - self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word') + self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word') def test_bbox(self): widget = self.create() @@ -1055,12 +1033,12 @@ def test_configure_handlepad(self): def test_configure_handlesize(self): widget = self.create() self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m', - conv=noconv) + conv=False) def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i', - conv=noconv) + conv=False) def test_configure_opaqueresize(self): widget = self.create() @@ -1076,7 +1054,7 @@ def test_configure_proxyborderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'proxyborderwidth', 0, 1.3, 2.9, 6, -2, '10p', - conv=noconv) + conv=False) @requires_tcl(8, 6, 5) def test_configure_proxyrelief(self): @@ -1098,7 +1076,7 @@ def test_configure_sashrelief(self): def test_configure_sashwidth(self): widget = self.create() self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m', - conv=noconv) + conv=False) def test_configure_showhandle(self): widget = self.create() @@ -1107,7 +1085,7 @@ def test_configure_showhandle(self): def test_configure_width(self): widget = self.create() self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i', - conv=noconv) + conv=False) def create2(self): p = self.create() @@ -1127,15 +1105,12 @@ def test_paneconfigure(self): self.assertEqual(v, p.paneconfigure(b, k)) self.assertEqual(v[4], p.panecget(b, k)) - def check_paneconfigure(self, p, b, name, value, expected, stringify=False): - conv = lambda x: x - if not self.wantobjects or stringify: + def check_paneconfigure(self, p, b, name, value, expected): + if not self.wantobjects: expected = str(expected) - if self.wantobjects and stringify: - conv = str p.paneconfigure(b, **{name: value}) - self.assertEqual(conv(p.paneconfigure(b, name)[4]), expected) - self.assertEqual(conv(p.panecget(b, name)), expected) + self.assertEqual(p.paneconfigure(b, name)[4], expected) + self.assertEqual(p.panecget(b, name), expected) def check_paneconfigure_bad(self, p, b, name, msg): with self.assertRaisesRegex(TclError, msg): @@ -1155,12 +1130,10 @@ def test_paneconfigure_before(self): def test_paneconfigure_height(self): p, b, c = self.create2() - self.check_paneconfigure(p, b, 'height', 10, 10, - stringify=get_tk_patchlevel() < (8, 5, 11)) + self.check_paneconfigure(p, b, 'height', 10, 10) self.check_paneconfigure_bad(p, b, 'height', 'bad screen distance "badValue"') - @requires_tcl(8, 5) def test_paneconfigure_hide(self): p, b, c = self.create2() self.check_paneconfigure(p, b, 'hide', False, 0) @@ -1193,7 +1166,6 @@ def test_paneconfigure_sticky(self): 'be a string containing zero or more of ' 'n, e, s, and w') - @requires_tcl(8, 5) def test_paneconfigure_stretch(self): p, b, c = self.create2() self.check_paneconfigure(p, b, 'stretch', 'alw', 'always') @@ -1203,8 +1175,7 @@ def test_paneconfigure_stretch(self): def test_paneconfigure_width(self): p, b, c = self.create2() - self.check_paneconfigure(p, b, 'width', 10, 10, - stringify=get_tk_patchlevel() < (8, 5, 11)) + self.check_paneconfigure(p, b, 'width', 10, 10) self.check_paneconfigure_bad(p, b, 'width', 'bad screen distance "badValue"') @@ -1218,7 +1189,7 @@ class MenuTest(AbstractWidgetTest, unittest.TestCase): 'postcommand', 'relief', 'selectcolor', 'takefocus', 'tearoff', 'tearoffcommand', 'title', 'type', ) - _conv_pixels = noconv + _conv_pixels = False def create(self, **kwargs): return tkinter.Menu(self.root, **kwargs) @@ -1290,7 +1261,7 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase): 'justify', 'padx', 'pady', 'relief', 'takefocus', 'text', 'textvariable', 'width', ) - _conv_pad_pixels = noconv + _conv_pad_pixels = False def create(self, **kwargs): return tkinter.Message(self.root, **kwargs) diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py index 1cb7e74c66e..c14c321ca26 100644 --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -7,7 +7,7 @@ from test.test_ttk_textonly import MockTclObj from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel, simulate_mouse_click, AbstractDefaultRootTest) -from tkinter.test.widget_tests import (add_standard_options, noconv, +from tkinter.test.widget_tests import (add_standard_options, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, setUpModule) @@ -110,7 +110,7 @@ def test_cb(arg1, **kw): class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): - _conv_pixels = noconv + _conv_pixels = False @add_standard_options(StandardTtkOptionsTests) @@ -193,7 +193,7 @@ class LabelTest(AbstractLabelTest, unittest.TestCase): 'takefocus', 'text', 'textvariable', 'underline', 'width', 'wraplength', ) - _conv_pixels = noconv + _conv_pixels = False def create(self, **kwargs): return ttk.Label(self.root, **kwargs) @@ -473,8 +473,7 @@ def check_get_current(getval, currval): self.assertEqual(self.combo.get(), getval) self.assertEqual(self.combo.current(), currval) - self.assertEqual(self.combo['values'], - () if tcl_version < (8, 5) else '') + self.assertEqual(self.combo['values'], '') check_get_current('', -1) self.checkParam(self.combo, 'values', 'mon tue wed thur', @@ -741,7 +740,7 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase): 'class', 'command', 'cursor', 'from', 'length', 'orient', 'style', 'takefocus', 'to', 'value', 'variable', ) - _conv_pixels = noconv + _conv_pixels = False default_orient = 'horizontal' def setUp(self): @@ -848,7 +847,7 @@ class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): 'mode', 'maximum', 'phase', 'style', 'takefocus', 'value', 'variable', ) - _conv_pixels = noconv + _conv_pixels = False default_orient = 'horizontal' def create(self, **kwargs): @@ -1231,8 +1230,7 @@ def test_configure_wrap(self): self.assertEqual(self.spin.get(), '1') def test_configure_values(self): - self.assertEqual(self.spin['values'], - () if tcl_version < (8, 5) else '') + self.assertEqual(self.spin['values'], '') self.checkParam(self.spin, 'values', 'mon tue wed thur', expected=('mon', 'tue', 'wed', 'thur')) self.checkParam(self.spin, 'values', ('mon', 'tue', 'wed', 'thur')) @@ -1316,7 +1314,7 @@ def test_configure_displaycolumns(self): def test_configure_height(self): widget = self.create() self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) - self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv) + self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=False) def test_configure_selectmode(self): widget = self.create() diff --git a/Lib/tkinter/test/widget_tests.py b/Lib/tkinter/test/widget_tests.py index 9702ff45300..37d1979c23f 100644 --- a/Lib/tkinter/test/widget_tests.py +++ b/Lib/tkinter/test/widget_tests.py @@ -2,26 +2,15 @@ import unittest import tkinter -from tkinter.test.support import (AbstractTkTest, tcl_version, requires_tcl, - get_tk_patchlevel, pixels_conv, tcl_obj_eq) +from tkinter.test.support import (AbstractTkTest, tcl_version, + pixels_conv, tcl_obj_eq) import test.support -noconv = False -if get_tk_patchlevel() < (8, 5, 11): - noconv = str - -pixels_round = round -if get_tk_patchlevel()[:3] == (8, 5, 11): - # Issue #19085: Workaround a bug in Tk - # http://core.tcl.tk/tk/info/3497848 - pixels_round = int - - _sentinel = object() class AbstractWidgetTest(AbstractTkTest): - _conv_pixels = staticmethod(pixels_round) + _conv_pixels = round _conv_pad_pixels = None _stringify = False @@ -65,8 +54,7 @@ def checkParam(self, widget, name, value, *, expected=_sentinel, self.assertEqual(len(t), 5) self.assertEqual2(t[4], expected, eq=eq) - def checkInvalidParam(self, widget, name, value, errmsg=None, *, - keep_orig=True): + def checkInvalidParam(self, widget, name, value, errmsg=None): orig = widget[name] if errmsg is not None: errmsg = errmsg.format(value) @@ -74,18 +62,12 @@ def checkInvalidParam(self, widget, name, value, errmsg=None, *, widget[name] = value if errmsg is not None: self.assertEqual(str(cm.exception), errmsg) - if keep_orig: - self.assertEqual(widget[name], orig) - else: - widget[name] = orig + self.assertEqual(widget[name], orig) with self.assertRaises(tkinter.TclError) as cm: widget.configure({name: value}) if errmsg is not None: self.assertEqual(str(cm.exception), errmsg) - if keep_orig: - self.assertEqual(widget[name], orig) - else: - widget[name] = orig + self.assertEqual(widget[name], orig) def checkParams(self, widget, name, *values, **kwargs): for value in values: @@ -128,8 +110,7 @@ def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs): def checkCursorParam(self, widget, name, **kwargs): self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**kwargs) - if tcl_version >= (8, 5): - self.checkParam(widget, name, 'none') + self.checkParam(widget, name, 'none') self.checkInvalidParam(widget, name, 'spam', errmsg='bad cursor spec "spam"') @@ -154,7 +135,7 @@ def checkEnumParam(self, widget, name, *values, errmsg=None, **kwargs): self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) def checkPixelsParam(self, widget, name, *values, - conv=None, keep_orig=True, **kwargs): + conv=None, **kwargs): if conv is None: conv = self._conv_pixels for value in values: @@ -167,9 +148,9 @@ def checkPixelsParam(self, widget, name, *values, self.checkParam(widget, name, value, expected=expected, conv=conv1, **kwargs) self.checkInvalidParam(widget, name, '6x', - errmsg='bad screen distance "6x"', keep_orig=keep_orig) + errmsg='bad screen distance "6x"') self.checkInvalidParam(widget, name, 'spam', - errmsg='bad screen distance "spam"', keep_orig=keep_orig) + errmsg='bad screen distance "spam"') def checkReliefParam(self, widget, name): self.checkParams(widget, name, @@ -475,12 +456,10 @@ def test_configure_selectimage(self): widget = self.create() self.checkImageParam(widget, 'selectimage') - @requires_tcl(8, 5) def test_configure_tristateimage(self): widget = self.create() self.checkImageParam(widget, 'tristateimage') - @requires_tcl(8, 5) def test_configure_tristatevalue(self): widget = self.create() self.checkParam(widget, 'tristatevalue', 'unknowable') diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index acdd565ec48..efeabb7a92c 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -28,23 +28,6 @@ import tkinter from tkinter import _flatten, _join, _stringify, _splitdict -# Verify if Tk is new enough to not need the Tile package -_REQUIRE_TILE = True if tkinter.TkVersion < 8.5 else False - -def _load_tile(master): - if _REQUIRE_TILE: - import os - tilelib = os.environ.get('TILE_LIBRARY') - if tilelib: - # append custom tile path to the list of directories that - # Tcl uses when attempting to resolve packages with the package - # command - master.tk.eval( - 'global auto_path; ' - 'lappend auto_path {%s}' % tilelib) - - master.tk.eval('package require tile') # TclError may be raised here - master._tile_loaded = True def _format_optvalue(value, script=False): """Internal function.""" @@ -360,11 +343,6 @@ class Style(object): def __init__(self, master=None): master = setup_master(master) - - if not getattr(master, '_tile_loaded', False): - # Load tile now, if needed - _load_tile(master) - self.master = master self.tk = self.master.tk @@ -546,9 +524,6 @@ def __init__(self, master, widgetname, kw=None): readonly, alternate, invalid """ master = setup_master(master) - if not getattr(master, '_tile_loaded', False): - # Load tile now, if needed - _load_tile(master) tkinter.Widget.__init__(self, master, widgetname, kw=kw) diff --git a/Misc/NEWS.d/next/Build/2022-03-12-18-09-31.bpo-46996.SygzVz.rst b/Misc/NEWS.d/next/Build/2022-03-12-18-09-31.bpo-46996.SygzVz.rst new file mode 100644 index 00000000000..08138f2e3e9 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2022-03-12-18-09-31.bpo-46996.SygzVz.rst @@ -0,0 +1 @@ +The :mod:`tkinter` package now requires Tcl/Tk version 8.5.12 or newer. diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index cd167fd8125..4807ad59f6d 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -9,8 +9,8 @@ Copyright (C) 1994 Steen Lumholt. /* TCL/TK VERSION INFO: - Only Tcl/Tk 8.4 and later are supported. Older versions are not - supported. Use Python 3.4 or older if you cannot upgrade your + Only Tcl/Tk 8.5.12 and later are supported. Older versions are not + supported. Use Python 3.10 or older if you cannot upgrade your Tcl/Tk libraries. */ @@ -54,15 +54,11 @@ Copyright (C) 1994 Steen Lumholt. #include "tkinter.h" -#if TK_HEX_VERSION < 0x08040200 -#error "Tk older than 8.4 not supported" +#if TK_HEX_VERSION < 0x0805020c +#error "Tk older than 8.5.12 not supported" #endif -#if TK_HEX_VERSION >= 0x08050208 && TK_HEX_VERSION < 0x08060000 || \ - TK_HEX_VERSION >= 0x08060200 -#define HAVE_LIBTOMMATH #include -#endif #if !(defined(MS_WINDOWS) || defined(__CYGWIN__)) #define HAVE_CREATEFILEHANDLER @@ -885,7 +881,6 @@ static PyType_Spec PyTclObject_Type_spec = { #define CHECK_STRING_LENGTH(s) #endif -#ifdef HAVE_LIBTOMMATH static Tcl_Obj* asBignumObj(PyObject *value) { @@ -922,7 +917,6 @@ asBignumObj(PyObject *value) } return result; } -#endif static Tcl_Obj* AsObj(PyObject *value) @@ -965,9 +959,7 @@ AsObj(PyObject *value) #endif /* If there is an overflow in the wideInt conversion, fall through to bignum handling. */ -#ifdef HAVE_LIBTOMMATH return asBignumObj(value); -#endif /* If there is no wideInt or bignum support, fall through to default object handling. */ } @@ -1087,7 +1079,6 @@ fromWideIntObj(TkappObject *tkapp, Tcl_Obj *value) return NULL; } -#ifdef HAVE_LIBTOMMATH static PyObject* fromBignumObj(TkappObject *tkapp, Tcl_Obj *value) { @@ -1122,7 +1113,6 @@ fromBignumObj(TkappObject *tkapp, Tcl_Obj *value) mp_clear(&bigValue); return res; } -#endif static PyObject* FromObj(TkappObject *tkapp, Tcl_Obj *value) @@ -1167,13 +1157,11 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) fall through to bignum handling. */ } -#ifdef HAVE_LIBTOMMATH if (value->typePtr == tkapp->IntType || value->typePtr == tkapp->WideIntType || value->typePtr == tkapp->BignumType) { return fromBignumObj(tkapp, value); } -#endif if (value->typePtr == tkapp->ListType) { int size; @@ -1211,23 +1199,19 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value) return unicodeFromTclObj(value); } -#if TK_HEX_VERSION >= 0x08050000 if (tkapp->BooleanType == NULL && strcmp(value->typePtr->name, "booleanString") == 0) { /* booleanString type is not registered in Tcl */ tkapp->BooleanType = value->typePtr; return fromBoolean(tkapp, value); } -#endif -#ifdef HAVE_LIBTOMMATH if (tkapp->BignumType == NULL && strcmp(value->typePtr->name, "bignum") == 0) { /* bignum type is not registered in Tcl */ tkapp->BignumType = value->typePtr; return fromBignumObj(tkapp, value); } -#endif return newPyTclObject(value); } @@ -1921,11 +1905,7 @@ _tkinter_tkapp_getint(TkappObject *self, PyObject *arg) Prefer bignum because Tcl_GetWideIntFromObj returns ambiguous result for value in ranges -2**64..-2**63-1 and 2**63..2**64-1 (on 32-bit platform). */ -#ifdef HAVE_LIBTOMMATH result = fromBignumObj(self, value); -#else - result = fromWideIntObj(self, value); -#endif Tcl_DecrRefCount(value); if (result != NULL || PyErr_Occurred()) return result;