bpo-46996: Remove support of Tcl/Tk < 8.5.12 (GH-31839)

This commit is contained in:
Serhiy Storchaka 2022-03-17 13:05:52 +02:00 committed by GitHub
parent 7aeb06f78e
commit c2e3c06139
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 90 additions and 216 deletions

View file

@ -13,9 +13,7 @@
-------------- --------------
The :mod:`tkinter.ttk` module provides access to the Tk themed widget set, 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 introduced in Tk 8.5. It provides additional benefits including anti-aliased font
module can still be accessed if *Tile* has been installed. The former
method using Tk 8.5 provides additional benefits including anti-aliased font
rendering under X11 and window transparency (requiring a composition rendering under X11 and window transparency (requiring a composition
window manager on X11). window manager on X11).

View file

@ -712,6 +712,9 @@ Build Changes
be removed at some point in the future. (Contributed by Mark Dickinson in be removed at some point in the future. (Contributed by Mark Dickinson in
:issue:`45569`.) :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 C API Changes
============= =============

View file

@ -143,27 +143,18 @@ def testUnsetVarException(self):
self.assertRaises(TclError,tcl.unsetvar,'a') self.assertRaises(TclError,tcl.unsetvar,'a')
def get_integers(self): def get_integers(self):
integers = (0, 1, -1, 2**31-1, -2**31, 2**31, -2**31-1, 2**63-1, -2**63) return (0, 1, -1,
# bignum was added in Tcl 8.5, but its support is able only since 8.5.8. 2**31-1, -2**31, 2**31, -2**31-1,
# Actually it is determined at compile time, so using get_tk_patchlevel() 2**63-1, -2**63, 2**63, -2**63-1,
# is not reliable. 2**1000, -2**1000)
# 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
def test_getint(self): def test_getint(self):
tcl = self.interp.tk tcl = self.interp.tk
for i in self.get_integers(): for i in self.get_integers():
self.assertEqual(tcl.getint(' %d ' % i), i) 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((' %#o ' % i).replace('o', '')), i)
self.assertEqual(tcl.getint(' %#x ' % i), 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.assertEqual(tcl.getint(42), 42)
self.assertRaises(TypeError, tcl.getint) self.assertRaises(TypeError, tcl.getint)
self.assertRaises(TypeError, tcl.getint, '42', '10') self.assertRaises(TypeError, tcl.getint, '42', '10')
@ -317,8 +308,7 @@ def check(expr, expected):
check('"a\xbd\u20ac"', 'a\xbd\u20ac') check('"a\xbd\u20ac"', 'a\xbd\u20ac')
check(r'"a\xbd\u20ac"', 'a\xbd\u20ac') check(r'"a\xbd\u20ac"', 'a\xbd\u20ac')
check(r'"a\0b"', 'a\x00b') 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): def test_exprdouble(self):
tcl = self.interp tcl = self.interp
@ -349,8 +339,7 @@ def check(expr, expected):
check('[string length "a\xbd\u20ac"]', 3.0) check('[string length "a\xbd\u20ac"]', 3.0)
check(r'[string length "a\xbd\u20ac"]', 3.0) check(r'[string length "a\xbd\u20ac"]', 3.0)
self.assertRaises(TclError, tcl.exprdouble, '"abc"') 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): def test_exprlong(self):
tcl = self.interp tcl = self.interp
@ -381,8 +370,7 @@ def check(expr, expected):
check('[string length "a\xbd\u20ac"]', 3) check('[string length "a\xbd\u20ac"]', 3)
check(r'[string length "a\xbd\u20ac"]', 3) check(r'[string length "a\xbd\u20ac"]', 3)
self.assertRaises(TclError, tcl.exprlong, '"abc"') 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): def test_exprboolean(self):
tcl = self.interp tcl = self.interp
@ -422,10 +410,8 @@ def check(expr, expected):
check('[string length "a\xbd\u20ac"]', True) check('[string length "a\xbd\u20ac"]', True)
check(r'[string length "a\xbd\u20ac"]', True) check(r'[string length "a\xbd\u20ac"]', True)
self.assertRaises(TclError, tcl.exprboolean, '"abc"') 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): def test_booleans(self):
tcl = self.interp tcl = self.interp
def check(expr, expected): def check(expr, expected):
@ -455,8 +441,6 @@ def test_expr_bignum(self):
else: else:
self.assertEqual(result, str(i)) self.assertEqual(result, str(i))
self.assertIsInstance(result, str) 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 test_passing_values(self):
def passValue(value): def passValue(value):
@ -485,8 +469,6 @@ def passValue(value):
b'str\xbding' if self.wantobjects else 'str\xbding') b'str\xbding' if self.wantobjects else 'str\xbding')
for i in self.get_integers(): for i in self.get_integers():
self.assertEqual(passValue(i), i if self.wantobjects else str(i)) 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, 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,
-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') check(b'str\xc0\x80ing\xe2\x82\xac', 'str\xc0\x80ing\xe2\x82\xac')
for i in self.get_integers(): for i in self.get_integers():
check(i, str(i)) 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): for f in (0.0, 1.0, -1.0):
check(f, repr(f)) check(f, repr(f))
for f in (1/3.0, sys.float_info.min, sys.float_info.max, 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')), ('1', '2', '3.4')),
] ]
tk_patchlevel = get_tk_patchlevel() tk_patchlevel = get_tk_patchlevel()
if tcl_version >= (8, 5): if not self.wantobjects:
if not self.wantobjects or tk_patchlevel < (8, 5, 5): expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4')
# Before 8.5.5 dicts were converted to lists through string else:
expected = ('12', '\u20ac', '\xe2\x82\xac', '3.4') expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,))
else: testcases += [
expected = (12, '\u20ac', b'\xe2\x82\xac', (3.4,)) (call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)),
testcases += [ expected),
(call('dict', 'create', 12, '\u20ac', b'\xe2\x82\xac', (3.4,)), ]
expected),
]
dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s' dbg_info = ('want objects? %s, Tcl version: %s, Tk patchlevel: %s'
% (self.wantobjects, tcl_version, tk_patchlevel)) % (self.wantobjects, tcl_version, tk_patchlevel))
for arg, res in testcases: for arg, res in testcases:
@ -642,15 +620,13 @@ def test_splitdict(self):
{'a': (1, 2, 3) if self.wantobjects else '1 2 3', {'a': (1, 2, 3) if self.wantobjects else '1 2 3',
'something': 'foo', 'status': ''}) 'something': 'foo', 'status': ''})
if tcl_version >= (8, 5): arg = tcl.call('dict', 'create',
arg = tcl.call('dict', 'create', '-a', (1, 2, 3), '-something', 'foo', 'status', ())
'-a', (1, 2, 3), '-something', 'foo', 'status', ()) if not self.wantobjects:
if not self.wantobjects or get_tk_patchlevel() < (8, 5, 5): expected = {'a': '1 2 3', 'something': 'foo', 'status': ''}
# Before 8.5.5 dicts were converted to lists through string else:
expected = {'a': '1 2 3', 'something': 'foo', 'status': ''} expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''}
else: self.assertEqual(splitdict(tcl, arg), expected)
expected = {'a': (1, 2, 3), 'something': 'foo', 'status': ''}
self.assertEqual(splitdict(tcl, arg), expected)
def test_join(self): def test_join(self):
join = tkinter._join join = tkinter._join

View file

@ -4,7 +4,7 @@
from tkinter import TclError from tkinter import TclError
from test.support import requires 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 from tkinter.test.widget_tests import AbstractWidgetTest
requires('gui') requires('gui')
@ -295,8 +295,7 @@ def test_place_configure_in(self):
with self.assertRaisesRegex(TclError, "can't place %s relative to " with self.assertRaisesRegex(TclError, "can't place %s relative to "
"itself" % re.escape(str(f2))): "itself" % re.escape(str(f2))):
f2.place_configure(in_=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'): with self.assertRaisesRegex(TclError, 'bad window path name'):
f2.place_configure(in_='spam') f2.place_configure(in_='spam')
f2.place_configure(in_=f) f2.place_configure(in_=f)
@ -491,8 +490,7 @@ def tearDown(self):
for i in range(rows + 1): for i in range(rows + 1):
self.root.grid_rowconfigure(i, weight=0, minsize=0, pad=0, uniform='') self.root.grid_rowconfigure(i, weight=0, minsize=0, pad=0, uniform='')
self.root.grid_propagate(1) self.root.grid_propagate(1)
if tcl_version >= (8, 5): self.root.grid_anchor('nw')
self.root.grid_anchor('nw')
super().tearDown() super().tearDown()
def test_grid_configure(self): def test_grid_configure(self):
@ -619,16 +617,14 @@ def test_grid_columnconfigure(self):
self.root.grid_columnconfigure((0, 3)) self.root.grid_columnconfigure((0, 3))
b = tkinter.Button(self.root) b = tkinter.Button(self.root)
b.grid_configure(column=0, row=0) b.grid_configure(column=0, row=0)
if tcl_version >= (8, 5): self.root.grid_columnconfigure('all', weight=3)
self.root.grid_columnconfigure('all', weight=3) with self.assertRaisesRegex(TclError, 'expected integer but got "all"'):
with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): self.root.grid_columnconfigure('all')
self.root.grid_columnconfigure('all') self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3)
self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 3)
self.assertEqual(self.root.grid_columnconfigure(3, 'weight'), 2) self.assertEqual(self.root.grid_columnconfigure(3, 'weight'), 2)
self.assertEqual(self.root.grid_columnconfigure(265, 'weight'), 0) self.assertEqual(self.root.grid_columnconfigure(265, 'weight'), 0)
if tcl_version >= (8, 5): self.root.grid_columnconfigure(b, weight=4)
self.root.grid_columnconfigure(b, weight=4) self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 4)
self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 4)
def test_grid_columnconfigure_minsize(self): def test_grid_columnconfigure_minsize(self):
with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'):
@ -675,16 +671,14 @@ def test_grid_rowconfigure(self):
self.root.grid_rowconfigure((0, 3)) self.root.grid_rowconfigure((0, 3))
b = tkinter.Button(self.root) b = tkinter.Button(self.root)
b.grid_configure(column=0, row=0) b.grid_configure(column=0, row=0)
if tcl_version >= (8, 5): self.root.grid_rowconfigure('all', weight=3)
self.root.grid_rowconfigure('all', weight=3) with self.assertRaisesRegex(TclError, 'expected integer but got "all"'):
with self.assertRaisesRegex(TclError, 'expected integer but got "all"'): self.root.grid_rowconfigure('all')
self.root.grid_rowconfigure('all') self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3)
self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 3)
self.assertEqual(self.root.grid_rowconfigure(3, 'weight'), 2) self.assertEqual(self.root.grid_rowconfigure(3, 'weight'), 2)
self.assertEqual(self.root.grid_rowconfigure(265, 'weight'), 0) self.assertEqual(self.root.grid_rowconfigure(265, 'weight'), 0)
if tcl_version >= (8, 5): self.root.grid_rowconfigure(b, weight=4)
self.root.grid_rowconfigure(b, weight=4) self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 4)
self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 4)
def test_grid_rowconfigure_minsize(self): def test_grid_rowconfigure_minsize(self):
with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): 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['pady'], self._str(4))
self.assertEqual(info['sticky'], 'ns') self.assertEqual(info['sticky'], 'ns')
@requires_tcl(8, 5)
def test_grid_anchor(self): def test_grid_anchor(self):
with self.assertRaisesRegex(TclError, 'bad anchor "x"'): with self.assertRaisesRegex(TclError, 'bad anchor "x"'):
self.root.grid_anchor('x') self.root.grid_anchor('x')

View file

@ -4,11 +4,11 @@
import os import os
from test.support import requires 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, get_tk_patchlevel, widget_eq,
AbstractDefaultRootTest) AbstractDefaultRootTest)
from tkinter.test.widget_tests import ( from tkinter.test.widget_tests import (
add_standard_options, noconv, pixels_round, add_standard_options,
AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
setUpModule) setUpModule)
@ -20,7 +20,7 @@ def float_round(x):
class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
_conv_pad_pixels = noconv _conv_pad_pixels = False
def test_configure_class(self): def test_configure_class(self):
widget = self.create() widget = self.create()
@ -139,7 +139,7 @@ def test_configure_labelwidget(self):
class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests):
_conv_pixels = noconv _conv_pixels = False
def test_configure_highlightthickness(self): def test_configure_highlightthickness(self):
widget = self.create() widget = self.create()
@ -249,7 +249,7 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
'takefocus', 'text', 'textvariable', 'takefocus', 'text', 'textvariable',
'underline', 'width', 'wraplength', 'underline', 'width', 'wraplength',
) )
_conv_pixels = staticmethod(pixels_round) _conv_pixels = round
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Menubutton(self.root, **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.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p')
self.checkParam(widget, 'insertwidth', 0.1, expected=2) self.checkParam(widget, 'insertwidth', 0.1, expected=2)
self.checkParam(widget, 'insertwidth', -2, expected=2) self.checkParam(widget, 'insertwidth', -2, expected=2)
if pixels_round(0.9) <= 0: self.checkParam(widget, 'insertwidth', 0.9, expected=1)
self.checkParam(widget, 'insertwidth', 0.9, expected=2)
else:
self.checkParam(widget, 'insertwidth', 0.9, expected=1)
def test_configure_invalidcommand(self): def test_configure_invalidcommand(self):
widget = self.create() widget = self.create()
@ -550,8 +547,6 @@ class TextTest(AbstractWidgetTest, unittest.TestCase):
'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap', 'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap',
'xscrollcommand', 'yscrollcommand', 'xscrollcommand', 'yscrollcommand',
) )
if tcl_version < (8, 5):
_stringify = True
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Text(self.root, **kwargs) return tkinter.Text(self.root, **kwargs)
@ -560,12 +555,10 @@ def test_configure_autoseparators(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'autoseparators') self.checkBooleanParam(widget, 'autoseparators')
@requires_tcl(8, 5)
def test_configure_blockcursor(self): def test_configure_blockcursor(self):
widget = self.create() widget = self.create()
self.checkBooleanParam(widget, 'blockcursor') self.checkBooleanParam(widget, 'blockcursor')
@requires_tcl(8, 5)
def test_configure_endline(self): def test_configure_endline(self):
widget = self.create() widget = self.create()
text = '\n'.join('Line %d' for i in range(100)) text = '\n'.join('Line %d' for i in range(100))
@ -589,7 +582,6 @@ def test_configure_maxundo(self):
widget = self.create() widget = self.create()
self.checkIntegerParam(widget, 'maxundo', 0, 5, -1) self.checkIntegerParam(widget, 'maxundo', 0, 5, -1)
@requires_tcl(8, 5)
def test_configure_inactiveselectbackground(self): def test_configure_inactiveselectbackground(self):
widget = self.create() widget = self.create()
self.checkColorParam(widget, 'inactiveselectbackground') self.checkColorParam(widget, 'inactiveselectbackground')
@ -603,8 +595,7 @@ def test_configure_insertunfocussed(self):
def test_configure_selectborderwidth(self): def test_configure_selectborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'selectborderwidth', self.checkPixelsParam(widget, 'selectborderwidth',
1.3, 2.6, -2, '10p', conv=noconv, 1.3, 2.6, -2, '10p', conv=False)
keep_orig=tcl_version >= (8, 5))
def test_configure_spacing1(self): def test_configure_spacing1(self):
widget = self.create() widget = self.create()
@ -621,7 +612,6 @@ def test_configure_spacing3(self):
self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c') self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c')
self.checkParam(widget, 'spacing3', -10, expected=0) self.checkParam(widget, 'spacing3', -10, expected=0)
@requires_tcl(8, 5)
def test_configure_startline(self): def test_configure_startline(self):
widget = self.create() widget = self.create()
text = '\n'.join('Line %d' for i in range(100)) text = '\n'.join('Line %d' for i in range(100))
@ -637,27 +627,18 @@ def test_configure_startline(self):
def test_configure_state(self): def test_configure_state(self):
widget = self.create() widget = self.create()
if tcl_version < (8, 5): self.checkEnumParam(widget, 'state', 'disabled', 'normal')
self.checkParams(widget, 'state', 'disabled', 'normal')
else:
self.checkEnumParam(widget, 'state', 'disabled', 'normal')
def test_configure_tabs(self): def test_configure_tabs(self):
widget = self.create() widget = self.create()
if get_tk_patchlevel() < (8, 5, 11): 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'))
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')) expected=('10.2', '20.7', '1i', '2i'))
self.checkParam(widget, 'tabs', '2c left 4c 6c center', self.checkParam(widget, 'tabs', '2c left 4c 6c center',
expected=('2c', 'left', '4c', '6c', 'center')) expected=('2c', 'left', '4c', '6c', 'center'))
self.checkInvalidParam(widget, 'tabs', 'spam', self.checkInvalidParam(widget, 'tabs', 'spam',
errmsg='bad screen distance "spam"', errmsg='bad screen distance "spam"')
keep_orig=tcl_version >= (8, 5))
@requires_tcl(8, 5)
def test_configure_tabstyle(self): def test_configure_tabstyle(self):
widget = self.create() widget = self.create()
self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor') self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor')
@ -674,10 +655,7 @@ def test_configure_width(self):
def test_configure_wrap(self): def test_configure_wrap(self):
widget = self.create() widget = self.create()
if tcl_version < (8, 5): self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word')
self.checkParams(widget, 'wrap', 'char', 'none', 'word')
else:
self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word')
def test_bbox(self): def test_bbox(self):
widget = self.create() widget = self.create()
@ -1055,12 +1033,12 @@ def test_configure_handlepad(self):
def test_configure_handlesize(self): def test_configure_handlesize(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m', self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m',
conv=noconv) conv=False)
def test_configure_height(self): def test_configure_height(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i', self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i',
conv=noconv) conv=False)
def test_configure_opaqueresize(self): def test_configure_opaqueresize(self):
widget = self.create() widget = self.create()
@ -1076,7 +1054,7 @@ def test_configure_proxyborderwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'proxyborderwidth', self.checkPixelsParam(widget, 'proxyborderwidth',
0, 1.3, 2.9, 6, -2, '10p', 0, 1.3, 2.9, 6, -2, '10p',
conv=noconv) conv=False)
@requires_tcl(8, 6, 5) @requires_tcl(8, 6, 5)
def test_configure_proxyrelief(self): def test_configure_proxyrelief(self):
@ -1098,7 +1076,7 @@ def test_configure_sashrelief(self):
def test_configure_sashwidth(self): def test_configure_sashwidth(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m', self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m',
conv=noconv) conv=False)
def test_configure_showhandle(self): def test_configure_showhandle(self):
widget = self.create() widget = self.create()
@ -1107,7 +1085,7 @@ def test_configure_showhandle(self):
def test_configure_width(self): def test_configure_width(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i', self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i',
conv=noconv) conv=False)
def create2(self): def create2(self):
p = self.create() p = self.create()
@ -1127,15 +1105,12 @@ def test_paneconfigure(self):
self.assertEqual(v, p.paneconfigure(b, k)) self.assertEqual(v, p.paneconfigure(b, k))
self.assertEqual(v[4], p.panecget(b, k)) self.assertEqual(v[4], p.panecget(b, k))
def check_paneconfigure(self, p, b, name, value, expected, stringify=False): def check_paneconfigure(self, p, b, name, value, expected):
conv = lambda x: x if not self.wantobjects:
if not self.wantobjects or stringify:
expected = str(expected) expected = str(expected)
if self.wantobjects and stringify:
conv = str
p.paneconfigure(b, **{name: value}) p.paneconfigure(b, **{name: value})
self.assertEqual(conv(p.paneconfigure(b, name)[4]), expected) self.assertEqual(p.paneconfigure(b, name)[4], expected)
self.assertEqual(conv(p.panecget(b, name)), expected) self.assertEqual(p.panecget(b, name), expected)
def check_paneconfigure_bad(self, p, b, name, msg): def check_paneconfigure_bad(self, p, b, name, msg):
with self.assertRaisesRegex(TclError, msg): with self.assertRaisesRegex(TclError, msg):
@ -1155,12 +1130,10 @@ def test_paneconfigure_before(self):
def test_paneconfigure_height(self): def test_paneconfigure_height(self):
p, b, c = self.create2() p, b, c = self.create2()
self.check_paneconfigure(p, b, 'height', 10, 10, self.check_paneconfigure(p, b, 'height', 10, 10)
stringify=get_tk_patchlevel() < (8, 5, 11))
self.check_paneconfigure_bad(p, b, 'height', self.check_paneconfigure_bad(p, b, 'height',
'bad screen distance "badValue"') 'bad screen distance "badValue"')
@requires_tcl(8, 5)
def test_paneconfigure_hide(self): def test_paneconfigure_hide(self):
p, b, c = self.create2() p, b, c = self.create2()
self.check_paneconfigure(p, b, 'hide', False, 0) 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 ' 'be a string containing zero or more of '
'n, e, s, and w') 'n, e, s, and w')
@requires_tcl(8, 5)
def test_paneconfigure_stretch(self): def test_paneconfigure_stretch(self):
p, b, c = self.create2() p, b, c = self.create2()
self.check_paneconfigure(p, b, 'stretch', 'alw', 'always') self.check_paneconfigure(p, b, 'stretch', 'alw', 'always')
@ -1203,8 +1175,7 @@ def test_paneconfigure_stretch(self):
def test_paneconfigure_width(self): def test_paneconfigure_width(self):
p, b, c = self.create2() p, b, c = self.create2()
self.check_paneconfigure(p, b, 'width', 10, 10, self.check_paneconfigure(p, b, 'width', 10, 10)
stringify=get_tk_patchlevel() < (8, 5, 11))
self.check_paneconfigure_bad(p, b, 'width', self.check_paneconfigure_bad(p, b, 'width',
'bad screen distance "badValue"') 'bad screen distance "badValue"')
@ -1218,7 +1189,7 @@ class MenuTest(AbstractWidgetTest, unittest.TestCase):
'postcommand', 'relief', 'selectcolor', 'takefocus', 'postcommand', 'relief', 'selectcolor', 'takefocus',
'tearoff', 'tearoffcommand', 'title', 'type', 'tearoff', 'tearoffcommand', 'title', 'type',
) )
_conv_pixels = noconv _conv_pixels = False
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Menu(self.root, **kwargs) return tkinter.Menu(self.root, **kwargs)
@ -1290,7 +1261,7 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase):
'justify', 'padx', 'pady', 'relief', 'justify', 'padx', 'pady', 'relief',
'takefocus', 'text', 'textvariable', 'width', 'takefocus', 'text', 'textvariable', 'width',
) )
_conv_pad_pixels = noconv _conv_pad_pixels = False
def create(self, **kwargs): def create(self, **kwargs):
return tkinter.Message(self.root, **kwargs) return tkinter.Message(self.root, **kwargs)

View file

@ -7,7 +7,7 @@
from test.test_ttk_textonly import MockTclObj from test.test_ttk_textonly import MockTclObj
from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel, from tkinter.test.support import (AbstractTkTest, tcl_version, get_tk_patchlevel,
simulate_mouse_click, AbstractDefaultRootTest) 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, AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
setUpModule) setUpModule)
@ -110,7 +110,7 @@ def test_cb(arg1, **kw):
class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
_conv_pixels = noconv _conv_pixels = False
@add_standard_options(StandardTtkOptionsTests) @add_standard_options(StandardTtkOptionsTests)
@ -193,7 +193,7 @@ class LabelTest(AbstractLabelTest, unittest.TestCase):
'takefocus', 'text', 'textvariable', 'takefocus', 'text', 'textvariable',
'underline', 'width', 'wraplength', 'underline', 'width', 'wraplength',
) )
_conv_pixels = noconv _conv_pixels = False
def create(self, **kwargs): def create(self, **kwargs):
return ttk.Label(self.root, **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.get(), getval)
self.assertEqual(self.combo.current(), currval) self.assertEqual(self.combo.current(), currval)
self.assertEqual(self.combo['values'], self.assertEqual(self.combo['values'], '')
() if tcl_version < (8, 5) else '')
check_get_current('', -1) check_get_current('', -1)
self.checkParam(self.combo, 'values', 'mon tue wed thur', self.checkParam(self.combo, 'values', 'mon tue wed thur',
@ -741,7 +740,7 @@ class ScaleTest(AbstractWidgetTest, unittest.TestCase):
'class', 'command', 'cursor', 'from', 'length', 'class', 'command', 'cursor', 'from', 'length',
'orient', 'style', 'takefocus', 'to', 'value', 'variable', 'orient', 'style', 'takefocus', 'to', 'value', 'variable',
) )
_conv_pixels = noconv _conv_pixels = False
default_orient = 'horizontal' default_orient = 'horizontal'
def setUp(self): def setUp(self):
@ -848,7 +847,7 @@ class ProgressbarTest(AbstractWidgetTest, unittest.TestCase):
'mode', 'maximum', 'phase', 'mode', 'maximum', 'phase',
'style', 'takefocus', 'value', 'variable', 'style', 'takefocus', 'value', 'variable',
) )
_conv_pixels = noconv _conv_pixels = False
default_orient = 'horizontal' default_orient = 'horizontal'
def create(self, **kwargs): def create(self, **kwargs):
@ -1231,8 +1230,7 @@ def test_configure_wrap(self):
self.assertEqual(self.spin.get(), '1') self.assertEqual(self.spin.get(), '1')
def test_configure_values(self): def test_configure_values(self):
self.assertEqual(self.spin['values'], self.assertEqual(self.spin['values'], '')
() if tcl_version < (8, 5) else '')
self.checkParam(self.spin, 'values', 'mon tue wed thur', self.checkParam(self.spin, 'values', 'mon tue wed thur',
expected=('mon', 'tue', 'wed', 'thur')) expected=('mon', 'tue', 'wed', 'thur'))
self.checkParam(self.spin, 'values', ('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): def test_configure_height(self):
widget = self.create() widget = self.create()
self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) 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): def test_configure_selectmode(self):
widget = self.create() widget = self.create()

View file

@ -2,26 +2,15 @@
import unittest import unittest
import tkinter import tkinter
from tkinter.test.support import (AbstractTkTest, tcl_version, requires_tcl, from tkinter.test.support import (AbstractTkTest, tcl_version,
get_tk_patchlevel, pixels_conv, tcl_obj_eq) pixels_conv, tcl_obj_eq)
import test.support 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() _sentinel = object()
class AbstractWidgetTest(AbstractTkTest): class AbstractWidgetTest(AbstractTkTest):
_conv_pixels = staticmethod(pixels_round) _conv_pixels = round
_conv_pad_pixels = None _conv_pad_pixels = None
_stringify = False _stringify = False
@ -65,8 +54,7 @@ def checkParam(self, widget, name, value, *, expected=_sentinel,
self.assertEqual(len(t), 5) self.assertEqual(len(t), 5)
self.assertEqual2(t[4], expected, eq=eq) self.assertEqual2(t[4], expected, eq=eq)
def checkInvalidParam(self, widget, name, value, errmsg=None, *, def checkInvalidParam(self, widget, name, value, errmsg=None):
keep_orig=True):
orig = widget[name] orig = widget[name]
if errmsg is not None: if errmsg is not None:
errmsg = errmsg.format(value) errmsg = errmsg.format(value)
@ -74,18 +62,12 @@ def checkInvalidParam(self, widget, name, value, errmsg=None, *,
widget[name] = value widget[name] = value
if errmsg is not None: if errmsg is not None:
self.assertEqual(str(cm.exception), errmsg) self.assertEqual(str(cm.exception), errmsg)
if keep_orig: self.assertEqual(widget[name], orig)
self.assertEqual(widget[name], orig)
else:
widget[name] = orig
with self.assertRaises(tkinter.TclError) as cm: with self.assertRaises(tkinter.TclError) as cm:
widget.configure({name: value}) widget.configure({name: value})
if errmsg is not None: if errmsg is not None:
self.assertEqual(str(cm.exception), errmsg) self.assertEqual(str(cm.exception), errmsg)
if keep_orig: self.assertEqual(widget[name], orig)
self.assertEqual(widget[name], orig)
else:
widget[name] = orig
def checkParams(self, widget, name, *values, **kwargs): def checkParams(self, widget, name, *values, **kwargs):
for value in values: for value in values:
@ -128,8 +110,7 @@ def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs):
def checkCursorParam(self, widget, name, **kwargs): def checkCursorParam(self, widget, name, **kwargs):
self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**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', self.checkInvalidParam(widget, name, 'spam',
errmsg='bad cursor spec "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) self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg)
def checkPixelsParam(self, widget, name, *values, def checkPixelsParam(self, widget, name, *values,
conv=None, keep_orig=True, **kwargs): conv=None, **kwargs):
if conv is None: if conv is None:
conv = self._conv_pixels conv = self._conv_pixels
for value in values: for value in values:
@ -167,9 +148,9 @@ def checkPixelsParam(self, widget, name, *values,
self.checkParam(widget, name, value, expected=expected, self.checkParam(widget, name, value, expected=expected,
conv=conv1, **kwargs) conv=conv1, **kwargs)
self.checkInvalidParam(widget, name, '6x', self.checkInvalidParam(widget, name, '6x',
errmsg='bad screen distance "6x"', keep_orig=keep_orig) errmsg='bad screen distance "6x"')
self.checkInvalidParam(widget, name, 'spam', self.checkInvalidParam(widget, name, 'spam',
errmsg='bad screen distance "spam"', keep_orig=keep_orig) errmsg='bad screen distance "spam"')
def checkReliefParam(self, widget, name): def checkReliefParam(self, widget, name):
self.checkParams(widget, name, self.checkParams(widget, name,
@ -475,12 +456,10 @@ def test_configure_selectimage(self):
widget = self.create() widget = self.create()
self.checkImageParam(widget, 'selectimage') self.checkImageParam(widget, 'selectimage')
@requires_tcl(8, 5)
def test_configure_tristateimage(self): def test_configure_tristateimage(self):
widget = self.create() widget = self.create()
self.checkImageParam(widget, 'tristateimage') self.checkImageParam(widget, 'tristateimage')
@requires_tcl(8, 5)
def test_configure_tristatevalue(self): def test_configure_tristatevalue(self):
widget = self.create() widget = self.create()
self.checkParam(widget, 'tristatevalue', 'unknowable') self.checkParam(widget, 'tristatevalue', 'unknowable')

View file

@ -28,23 +28,6 @@
import tkinter import tkinter
from tkinter import _flatten, _join, _stringify, _splitdict 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): def _format_optvalue(value, script=False):
"""Internal function.""" """Internal function."""
@ -360,11 +343,6 @@ class Style(object):
def __init__(self, master=None): def __init__(self, master=None):
master = setup_master(master) master = setup_master(master)
if not getattr(master, '_tile_loaded', False):
# Load tile now, if needed
_load_tile(master)
self.master = master self.master = master
self.tk = self.master.tk self.tk = self.master.tk
@ -546,9 +524,6 @@ def __init__(self, master, widgetname, kw=None):
readonly, alternate, invalid readonly, alternate, invalid
""" """
master = setup_master(master) 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) tkinter.Widget.__init__(self, master, widgetname, kw=kw)

View file

@ -0,0 +1 @@
The :mod:`tkinter` package now requires Tcl/Tk version 8.5.12 or newer.

View file

@ -9,8 +9,8 @@ Copyright (C) 1994 Steen Lumholt.
/* TCL/TK VERSION INFO: /* TCL/TK VERSION INFO:
Only Tcl/Tk 8.4 and later are supported. Older versions are not Only Tcl/Tk 8.5.12 and later are supported. Older versions are not
supported. Use Python 3.4 or older if you cannot upgrade your supported. Use Python 3.10 or older if you cannot upgrade your
Tcl/Tk libraries. Tcl/Tk libraries.
*/ */
@ -54,15 +54,11 @@ Copyright (C) 1994 Steen Lumholt.
#include "tkinter.h" #include "tkinter.h"
#if TK_HEX_VERSION < 0x08040200 #if TK_HEX_VERSION < 0x0805020c
#error "Tk older than 8.4 not supported" #error "Tk older than 8.5.12 not supported"
#endif #endif
#if TK_HEX_VERSION >= 0x08050208 && TK_HEX_VERSION < 0x08060000 || \
TK_HEX_VERSION >= 0x08060200
#define HAVE_LIBTOMMATH
#include <tclTomMath.h> #include <tclTomMath.h>
#endif
#if !(defined(MS_WINDOWS) || defined(__CYGWIN__)) #if !(defined(MS_WINDOWS) || defined(__CYGWIN__))
#define HAVE_CREATEFILEHANDLER #define HAVE_CREATEFILEHANDLER
@ -885,7 +881,6 @@ static PyType_Spec PyTclObject_Type_spec = {
#define CHECK_STRING_LENGTH(s) #define CHECK_STRING_LENGTH(s)
#endif #endif
#ifdef HAVE_LIBTOMMATH
static Tcl_Obj* static Tcl_Obj*
asBignumObj(PyObject *value) asBignumObj(PyObject *value)
{ {
@ -922,7 +917,6 @@ asBignumObj(PyObject *value)
} }
return result; return result;
} }
#endif
static Tcl_Obj* static Tcl_Obj*
AsObj(PyObject *value) AsObj(PyObject *value)
@ -965,9 +959,7 @@ AsObj(PyObject *value)
#endif #endif
/* If there is an overflow in the wideInt conversion, /* If there is an overflow in the wideInt conversion,
fall through to bignum handling. */ fall through to bignum handling. */
#ifdef HAVE_LIBTOMMATH
return asBignumObj(value); return asBignumObj(value);
#endif
/* If there is no wideInt or bignum support, /* If there is no wideInt or bignum support,
fall through to default object handling. */ fall through to default object handling. */
} }
@ -1087,7 +1079,6 @@ fromWideIntObj(TkappObject *tkapp, Tcl_Obj *value)
return NULL; return NULL;
} }
#ifdef HAVE_LIBTOMMATH
static PyObject* static PyObject*
fromBignumObj(TkappObject *tkapp, Tcl_Obj *value) fromBignumObj(TkappObject *tkapp, Tcl_Obj *value)
{ {
@ -1122,7 +1113,6 @@ fromBignumObj(TkappObject *tkapp, Tcl_Obj *value)
mp_clear(&bigValue); mp_clear(&bigValue);
return res; return res;
} }
#endif
static PyObject* static PyObject*
FromObj(TkappObject *tkapp, Tcl_Obj *value) FromObj(TkappObject *tkapp, Tcl_Obj *value)
@ -1167,13 +1157,11 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value)
fall through to bignum handling. */ fall through to bignum handling. */
} }
#ifdef HAVE_LIBTOMMATH
if (value->typePtr == tkapp->IntType || if (value->typePtr == tkapp->IntType ||
value->typePtr == tkapp->WideIntType || value->typePtr == tkapp->WideIntType ||
value->typePtr == tkapp->BignumType) { value->typePtr == tkapp->BignumType) {
return fromBignumObj(tkapp, value); return fromBignumObj(tkapp, value);
} }
#endif
if (value->typePtr == tkapp->ListType) { if (value->typePtr == tkapp->ListType) {
int size; int size;
@ -1211,23 +1199,19 @@ FromObj(TkappObject *tkapp, Tcl_Obj *value)
return unicodeFromTclObj(value); return unicodeFromTclObj(value);
} }
#if TK_HEX_VERSION >= 0x08050000
if (tkapp->BooleanType == NULL && if (tkapp->BooleanType == NULL &&
strcmp(value->typePtr->name, "booleanString") == 0) { strcmp(value->typePtr->name, "booleanString") == 0) {
/* booleanString type is not registered in Tcl */ /* booleanString type is not registered in Tcl */
tkapp->BooleanType = value->typePtr; tkapp->BooleanType = value->typePtr;
return fromBoolean(tkapp, value); return fromBoolean(tkapp, value);
} }
#endif
#ifdef HAVE_LIBTOMMATH
if (tkapp->BignumType == NULL && if (tkapp->BignumType == NULL &&
strcmp(value->typePtr->name, "bignum") == 0) { strcmp(value->typePtr->name, "bignum") == 0) {
/* bignum type is not registered in Tcl */ /* bignum type is not registered in Tcl */
tkapp->BignumType = value->typePtr; tkapp->BignumType = value->typePtr;
return fromBignumObj(tkapp, value); return fromBignumObj(tkapp, value);
} }
#endif
return newPyTclObject(value); return newPyTclObject(value);
} }
@ -1921,11 +1905,7 @@ _tkinter_tkapp_getint(TkappObject *self, PyObject *arg)
Prefer bignum because Tcl_GetWideIntFromObj returns ambiguous result for 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). 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); result = fromBignumObj(self, value);
#else
result = fromWideIntObj(self, value);
#endif
Tcl_DecrRefCount(value); Tcl_DecrRefCount(value);
if (result != NULL || PyErr_Occurred()) if (result != NULL || PyErr_Occurred())
return result; return result;