https://github.com/python/cpython/commit/09b1f10ef7b1183d40fe08e56d42dc6152d31f9a
commit: 09b1f10ef7b1183d40fe08e56d42dc6152d31f9a
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-10-30T13:11:56+02:00
summary:
gh-140481: Improve error message when trying to iterate a Tk widget, image or
font (GH-140501)
files:
A Misc/NEWS.d/next/Library/2025-10-23-13-42-15.gh-issue-140481.XKxWpq.rst
M Lib/test/test_tkinter/test_font.py
M Lib/test/test_tkinter/test_images.py
M Lib/test/test_tkinter/test_misc.py
M Lib/tkinter/__init__.py
M Lib/tkinter/font.py
diff --git a/Lib/test/test_tkinter/test_font.py
b/Lib/test/test_tkinter/test_font.py
index 3616da54cf7075..fc50f9fdbb588c 100644
--- a/Lib/test/test_tkinter/test_font.py
+++ b/Lib/test/test_tkinter/test_font.py
@@ -1,3 +1,4 @@
+import collections.abc
import unittest
import tkinter
from tkinter import font
@@ -118,6 +119,16 @@ def test_repr(self):
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):
diff --git a/Lib/test/test_tkinter/test_images.py
b/Lib/test/test_tkinter/test_images.py
index 38371fe00d6eb5..358a18beee2571 100644
--- a/Lib/test/test_tkinter/test_images.py
+++ b/Lib/test/test_tkinter/test_images.py
@@ -1,3 +1,4 @@
+import collections.abc
import unittest
import tkinter
from test import support
@@ -61,7 +62,33 @@ def test_image_create_photo(self):
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 @@ def test_configure_foreground(self):
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 @@ def test_configure_palette(self):
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()
diff --git a/Lib/test/test_tkinter/test_misc.py
b/Lib/test/test_tkinter/test_misc.py
index 0c76e07066f8a8..32e2329506e7ff 100644
--- a/Lib/test/test_tkinter/test_misc.py
+++ b/Lib/test/test_tkinter/test_misc.py
@@ -1,3 +1,4 @@
+import collections.abc
import functools
import unittest
import tkinter
@@ -508,6 +509,17 @@ def test_embedded_null(self):
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):
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index 9526d8b949fa3b..c54530740395f7 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -1848,6 +1848,7 @@ def cget(self, key):
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 @@ def __setitem__(self, key, value):
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 = ()
diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py
index 7aed523cce3784..896e910d69f6f3 100644
--- a/Lib/tkinter/font.py
+++ b/Lib/tkinter/font.py
@@ -114,6 +114,8 @@ def __getitem__(self, key):
def __setitem__(self, key, value):
self.configure(**{key: value})
+ __iter__ = None # prevent using __getitem__ for iteration
+
def __del__(self):
try:
if self.delete_font:
diff --git
a/Misc/NEWS.d/next/Library/2025-10-23-13-42-15.gh-issue-140481.XKxWpq.rst
b/Misc/NEWS.d/next/Library/2025-10-23-13-42-15.gh-issue-140481.XKxWpq.rst
new file mode 100644
index 00000000000000..1f511c3b9d0583
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-10-23-13-42-15.gh-issue-140481.XKxWpq.rst
@@ -0,0 +1 @@
+Improve error message when trying to iterate a Tk widget, image or font.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]