Skip to content

Commit 12303b1

Browse files
gh-140481: Improve error message when trying to iterate a Tk widget, image or font
1 parent aa9d0a6 commit 12303b1

File tree

6 files changed

+59
-22
lines changed

6 files changed

+59
-22
lines changed

Lib/test/test_tkinter/test_font.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import collections.abc
12
import unittest
23
import tkinter
34
from tkinter import font
@@ -118,6 +119,16 @@ def test_repr(self):
118119
repr(self.font), f'<tkinter.font.Font object {fontname!r}>'
119120
)
120121

122+
def test_iterable_protocol(self):
123+
self.assertNotIsSubclass(font.Font, collections.abc.Iterable)
124+
self.assertNotIsSubclass(font.Font, collections.abc.Container)
125+
self.assertNotIsInstance(self.font, collections.abc.Iterable)
126+
self.assertNotIsInstance(self.font, collections.abc.Container)
127+
with self.assertRaisesRegex(TypeError, 'is not iterable'):
128+
iter(self.font)
129+
with self.assertRaisesRegex(TypeError, 'is not a container or iterable'):
130+
self.font in self.font
131+
121132

122133
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
123134

Lib/test/test_tkinter/test_images.py

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import collections.abc
12
import unittest
23
import tkinter
34
from test import support
@@ -61,7 +62,33 @@ def test_image_create_photo(self):
6162
self.assertRaises(RuntimeError, tkinter.PhotoImage)
6263

6364

64-
class BitmapImageTest(AbstractTkTest, unittest.TestCase):
65+
class BaseImageTest:
66+
def create(self):
67+
return self.image_class('::img::test', master=self.root,
68+
file=self.testfile)
69+
70+
def test_bug_100814(self):
71+
# gh-100814: Passing a callable option value causes AttributeError.
72+
with self.assertRaises(tkinter.TclError):
73+
self.image_class('::img::test', master=self.root, spam=print)
74+
image = self.image_class('::img::test', master=self.root)
75+
with self.assertRaises(tkinter.TclError):
76+
image.configure(spam=print)
77+
78+
def test_iterable_protocol(self):
79+
image = self.create()
80+
self.assertNotIsSubclass(self.image_class, collections.abc.Iterable)
81+
self.assertNotIsSubclass(self.image_class, collections.abc.Container)
82+
self.assertNotIsInstance(image, collections.abc.Iterable)
83+
self.assertNotIsInstance(image, collections.abc.Container)
84+
with self.assertRaisesRegex(TypeError, 'is not iterable'):
85+
iter(image)
86+
with self.assertRaisesRegex(TypeError, 'is not a container or iterable'):
87+
image in image
88+
89+
90+
class BitmapImageTest(BaseImageTest, AbstractTkTest, unittest.TestCase):
91+
image_class = tkinter.BitmapImage
6592

6693
@classmethod
6794
def setUpClass(cls):
@@ -144,26 +171,15 @@ def test_configure_foreground(self):
144171
self.assertEqual(image['foreground'],
145172
'-foreground {} {} #000000 yellow')
146173

147-
def test_bug_100814(self):
148-
# gh-100814: Passing a callable option value causes AttributeError.
149-
with self.assertRaises(tkinter.TclError):
150-
tkinter.BitmapImage('::img::test', master=self.root, spam=print)
151-
image = tkinter.BitmapImage('::img::test', master=self.root)
152-
with self.assertRaises(tkinter.TclError):
153-
image.configure(spam=print)
154-
155174

156-
class PhotoImageTest(AbstractTkTest, unittest.TestCase):
175+
class PhotoImageTest(BaseImageTest, AbstractTkTest, unittest.TestCase):
176+
image_class = tkinter.PhotoImage
157177

158178
@classmethod
159179
def setUpClass(cls):
160180
AbstractTkTest.setUpClass.__func__(cls)
161181
cls.testfile = support.findfile('python.gif', subdir='tkinterdata')
162182

163-
def create(self):
164-
return tkinter.PhotoImage('::img::test', master=self.root,
165-
file=self.testfile)
166-
167183
def colorlist(self, *args):
168184
if tkinter.TkVersion >= 8.6 and self.wantobjects:
169185
return args
@@ -282,14 +298,6 @@ def test_configure_palette(self):
282298
image.configure(palette='3/4/2')
283299
self.assertEqual(image['palette'], '3/4/2')
284300

285-
def test_bug_100814(self):
286-
# gh-100814: Passing a callable option value causes AttributeError.
287-
with self.assertRaises(tkinter.TclError):
288-
tkinter.PhotoImage('::img::test', master=self.root, spam=print)
289-
image = tkinter.PhotoImage('::img::test', master=self.root)
290-
with self.assertRaises(tkinter.TclError):
291-
image.configure(spam=print)
292-
293301
def test_blank(self):
294302
image = self.create()
295303
image.blank()

Lib/test/test_tkinter/test_misc.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import collections.abc
12
import functools
23
import unittest
34
import tkinter
@@ -508,6 +509,17 @@ def test_embedded_null(self):
508509
widget.selection_range(0, 'end')
509510
self.assertEqual(widget.selection_get(), '\u20ac\0abc\x00def')
510511

512+
def test_iterable_protocol(self):
513+
widget = tkinter.Entry(self.root)
514+
self.assertNotIsSubclass(tkinter.Entry, collections.abc.Iterable)
515+
self.assertNotIsSubclass(tkinter.Entry, collections.abc.Container)
516+
self.assertNotIsInstance(widget, collections.abc.Iterable)
517+
self.assertNotIsInstance(widget, collections.abc.Container)
518+
with self.assertRaisesRegex(TypeError, 'is not iterable'):
519+
iter(widget)
520+
with self.assertRaisesRegex(TypeError, 'is not a container or iterable'):
521+
widget in widget
522+
511523

512524
class WmTest(AbstractTkTest, unittest.TestCase):
513525

Lib/tkinter/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,7 @@ def cget(self, key):
18481848
return self.tk.call(self._w, 'cget', '-' + key)
18491849

18501850
__getitem__ = cget
1851+
__iter__ = None # prevent using __getitem__ for iteration
18511852

18521853
def __setitem__(self, key, value):
18531854
self.configure({key: value})
@@ -4280,6 +4281,8 @@ def __setitem__(self, key, value):
42804281
def __getitem__(self, key):
42814282
return self.tk.call(self.name, 'configure', '-'+key)
42824283

4284+
__iter__ = None # prevent using __getitem__ for iteration
4285+
42834286
def configure(self, **kw):
42844287
"""Configure the image."""
42854288
res = ()

Lib/tkinter/font.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ def __getitem__(self, key):
114114
def __setitem__(self, key, value):
115115
self.configure(**{key: value})
116116

117+
__iter__ = None # prevent using __getitem__ for iteration
118+
117119
def __del__(self):
118120
try:
119121
if self.delete_font:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve error message when trying to iterate a Tk widget, image or font.

0 commit comments

Comments
 (0)