gh-140481: Improve error message when trying to iterate a Tk widget, image or font (GH-140501)

This commit is contained in:
Serhiy Storchaka
2025-10-30 13:11:56 +02:00
committed by GitHub
parent ad0a3f733b
commit 09b1f10ef7
6 changed files with 59 additions and 22 deletions

View File

@@ -1,3 +1,4 @@
import collections.abc
import unittest
import tkinter
from tkinter import font
@@ -118,6 +119,16 @@ class FontTest(AbstractTkTest, unittest.TestCase):
repr(self.font), f'<tkinter.font.Font object {fontname!r}>'
)
def test_iterable_protocol(self):
self.assertNotIsSubclass(font.Font, collections.abc.Iterable)
self.assertNotIsSubclass(font.Font, collections.abc.Container)
self.assertNotIsInstance(self.font, collections.abc.Iterable)
self.assertNotIsInstance(self.font, collections.abc.Container)
with self.assertRaisesRegex(TypeError, 'is not iterable'):
iter(self.font)
with self.assertRaisesRegex(TypeError, 'is not a container or iterable'):
self.font in self.font
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):

View File

@@ -1,3 +1,4 @@
import collections.abc
import unittest
import tkinter
from test import support
@@ -61,7 +62,33 @@ class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
self.assertRaises(RuntimeError, tkinter.PhotoImage)
class BitmapImageTest(AbstractTkTest, unittest.TestCase):
class BaseImageTest:
def create(self):
return self.image_class('::img::test', master=self.root,
file=self.testfile)
def test_bug_100814(self):
# gh-100814: Passing a callable option value causes AttributeError.
with self.assertRaises(tkinter.TclError):
self.image_class('::img::test', master=self.root, spam=print)
image = self.image_class('::img::test', master=self.root)
with self.assertRaises(tkinter.TclError):
image.configure(spam=print)
def test_iterable_protocol(self):
image = self.create()
self.assertNotIsSubclass(self.image_class, collections.abc.Iterable)
self.assertNotIsSubclass(self.image_class, collections.abc.Container)
self.assertNotIsInstance(image, collections.abc.Iterable)
self.assertNotIsInstance(image, collections.abc.Container)
with self.assertRaisesRegex(TypeError, 'is not iterable'):
iter(image)
with self.assertRaisesRegex(TypeError, 'is not a container or iterable'):
image in image
class BitmapImageTest(BaseImageTest, AbstractTkTest, unittest.TestCase):
image_class = tkinter.BitmapImage
@classmethod
def setUpClass(cls):
@@ -144,26 +171,15 @@ class BitmapImageTest(AbstractTkTest, unittest.TestCase):
self.assertEqual(image['foreground'],
'-foreground {} {} #000000 yellow')
def test_bug_100814(self):
# gh-100814: Passing a callable option value causes AttributeError.
with self.assertRaises(tkinter.TclError):
tkinter.BitmapImage('::img::test', master=self.root, spam=print)
image = tkinter.BitmapImage('::img::test', master=self.root)
with self.assertRaises(tkinter.TclError):
image.configure(spam=print)
class PhotoImageTest(AbstractTkTest, unittest.TestCase):
class PhotoImageTest(BaseImageTest, AbstractTkTest, unittest.TestCase):
image_class = tkinter.PhotoImage
@classmethod
def setUpClass(cls):
AbstractTkTest.setUpClass.__func__(cls)
cls.testfile = support.findfile('python.gif', subdir='tkinterdata')
def create(self):
return tkinter.PhotoImage('::img::test', master=self.root,
file=self.testfile)
def colorlist(self, *args):
if tkinter.TkVersion >= 8.6 and self.wantobjects:
return args
@@ -282,14 +298,6 @@ class PhotoImageTest(AbstractTkTest, unittest.TestCase):
image.configure(palette='3/4/2')
self.assertEqual(image['palette'], '3/4/2')
def test_bug_100814(self):
# gh-100814: Passing a callable option value causes AttributeError.
with self.assertRaises(tkinter.TclError):
tkinter.PhotoImage('::img::test', master=self.root, spam=print)
image = tkinter.PhotoImage('::img::test', master=self.root)
with self.assertRaises(tkinter.TclError):
image.configure(spam=print)
def test_blank(self):
image = self.create()
image.blank()

View File

@@ -1,3 +1,4 @@
import collections.abc
import functools
import unittest
import tkinter
@@ -508,6 +509,17 @@ class MiscTest(AbstractTkTest, unittest.TestCase):
widget.selection_range(0, 'end')
self.assertEqual(widget.selection_get(), '\u20ac\0abc\x00def')
def test_iterable_protocol(self):
widget = tkinter.Entry(self.root)
self.assertNotIsSubclass(tkinter.Entry, collections.abc.Iterable)
self.assertNotIsSubclass(tkinter.Entry, collections.abc.Container)
self.assertNotIsInstance(widget, collections.abc.Iterable)
self.assertNotIsInstance(widget, collections.abc.Container)
with self.assertRaisesRegex(TypeError, 'is not iterable'):
iter(widget)
with self.assertRaisesRegex(TypeError, 'is not a container or iterable'):
widget in widget
class WmTest(AbstractTkTest, unittest.TestCase):

View File

@@ -1848,6 +1848,7 @@ class Misc:
return self.tk.call(self._w, 'cget', '-' + key)
__getitem__ = cget
__iter__ = None # prevent using __getitem__ for iteration
def __setitem__(self, key, value):
self.configure({key: value})
@@ -4280,6 +4281,8 @@ class Image:
def __getitem__(self, key):
return self.tk.call(self.name, 'configure', '-'+key)
__iter__ = None # prevent using __getitem__ for iteration
def configure(self, **kw):
"""Configure the image."""
res = ()

View File

@@ -114,6 +114,8 @@ class Font:
def __setitem__(self, key, value):
self.configure(**{key: value})
__iter__ = None # prevent using __getitem__ for iteration
def __del__(self):
try:
if self.delete_font:

View File

@@ -0,0 +1 @@
Improve error message when trying to iterate a Tk widget, image or font.