Skip to content

Commit b8c20f9

Browse files
gh-97928: Change the behavior of tkinter.Text.count() (GH-98484)
It now always returns an integer if one or less counting options are specified. Previously it could return a single count as a 1-tuple, an integer (only if option "update" was specified) or None if no items found. The result is now the same if wantobjects is set to 0.
1 parent 81eba76 commit b8c20f9

File tree

5 files changed

+53
-55
lines changed

5 files changed

+53
-55
lines changed

Doc/whatsnew/3.13.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,15 @@ Porting to Python 3.13
940940
This section lists previously described changes and other bugfixes
941941
that may require changes to your code.
942942

943-
* None yet
943+
Changes in the Python API
944+
-------------------------
945+
946+
* :meth:`!tkinter.Text.count` now always returns an integer if one or less
947+
counting options are specified.
948+
Previously it could return a single count as a 1-tuple, an integer (only if
949+
option ``"update"`` was specified) or ``None`` if no items found.
950+
The result is now the same if ``wantobjects`` is set to ``0``.
951+
(Contributed by Serhiy Storchaka in :gh:`97928`.)
944952

945953

946954
Build Changes

Lib/idlelib/sidebar.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ def get_end_linenumber(text):
2525

2626
def get_displaylines(text, index):
2727
"""Display height, in lines, of a logical line in a Tk text widget."""
28-
res = text.count(f"{index} linestart",
29-
f"{index} lineend",
30-
"displaylines")
31-
return res[0] if res else 0
28+
return text.count(f"{index} linestart",
29+
f"{index} lineend",
30+
"displaylines")
3231

3332
def get_widget_padding(widget):
3433
"""Get the total padding of a Tk widget, including its border."""

Lib/test/test_tkinter/test_text.py

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class TextTest(AbstractTkTest, unittest.TestCase):
1010
def setUp(self):
1111
super().setUp()
1212
self.text = tkinter.Text(self.root)
13+
self.text.pack()
1314

1415
def test_debug(self):
1516
text = self.text
@@ -41,8 +42,6 @@ def test_search(self):
4142
self.assertEqual(text.search('test', '1.0', 'end'), '1.3')
4243

4344
def test_count(self):
44-
# XXX Some assertions do not check against the intended result,
45-
# but instead check the current result to prevent regression.
4645
text = self.text
4746
text.insert('1.0',
4847
'Lorem ipsum dolor sit amet,\n'
@@ -53,44 +52,27 @@ def test_count(self):
5352
options = ('chars', 'indices', 'lines',
5453
'displaychars', 'displayindices', 'displaylines',
5554
'xpixels', 'ypixels')
56-
if self.wantobjects:
57-
self.assertEqual(len(text.count('1.0', 'end', *options)), 8)
58-
else:
59-
text.count('1.0', 'end', *options)
60-
self.assertEqual(text.count('1.0', 'end', 'chars', 'lines'), (124, 4)
61-
if self.wantobjects else '124 4')
62-
self.assertEqual(text.count('1.3', '4.5', 'chars', 'lines'), (92, 3)
63-
if self.wantobjects else '92 3')
64-
self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines'), (-92, -3)
65-
if self.wantobjects else '-92 -3')
66-
self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines'), (0, 0)
67-
if self.wantobjects else '0 0')
68-
self.assertEqual(text.count('1.0', 'end', 'lines'), (4,)
69-
if self.wantobjects else ('4',))
70-
self.assertEqual(text.count('end', '1.0', 'lines'), (-4,)
71-
if self.wantobjects else ('-4',))
72-
self.assertEqual(text.count('1.3', '1.5', 'lines'), None
73-
if self.wantobjects else ('0',))
74-
self.assertEqual(text.count('1.3', '1.3', 'lines'), None
75-
if self.wantobjects else ('0',))
76-
self.assertEqual(text.count('1.0', 'end'), (124,) # 'indices' by default
77-
if self.wantobjects else ('124',))
55+
self.assertEqual(len(text.count('1.0', 'end', *options)), 8)
56+
self.assertEqual(text.count('1.0', 'end', 'chars', 'lines'), (124, 4))
57+
self.assertEqual(text.count('1.3', '4.5', 'chars', 'lines'), (92, 3))
58+
self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines'), (-92, -3))
59+
self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines'), (0, 0))
60+
self.assertEqual(text.count('1.0', 'end', 'lines'), 4)
61+
self.assertEqual(text.count('end', '1.0', 'lines'), -4)
62+
self.assertEqual(text.count('1.3', '1.5', 'lines'), 0)
63+
self.assertEqual(text.count('1.3', '1.3', 'lines'), 0)
64+
self.assertEqual(text.count('1.0', 'end'), 124) # 'indices' by default
65+
self.assertEqual(text.count('1.0', 'end', 'indices'), 124)
7866
self.assertRaises(tkinter.TclError, text.count, '1.0', 'end', 'spam')
7967
self.assertRaises(tkinter.TclError, text.count, '1.0', 'end', '-lines')
8068

81-
self.assertIsInstance(text.count('1.3', '1.5', 'ypixels'), tuple)
82-
self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels'), int
83-
if self.wantobjects else str)
84-
self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels'), None
85-
if self.wantobjects else '0')
86-
self.assertEqual(text.count('1.3', '1.5', 'update', 'indices'), 2
87-
if self.wantobjects else '2')
88-
self.assertEqual(text.count('1.3', '1.3', 'update', 'indices'), None
89-
if self.wantobjects else '0')
90-
self.assertEqual(text.count('1.3', '1.5', 'update'), (2,)
91-
if self.wantobjects else ('2',))
92-
self.assertEqual(text.count('1.3', '1.3', 'update'), None
93-
if self.wantobjects else ('0',))
69+
self.assertIsInstance(text.count('1.3', '1.5', 'ypixels'), int)
70+
self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels'), int)
71+
self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels'), 0)
72+
self.assertEqual(text.count('1.3', '1.5', 'update', 'indices'), 2)
73+
self.assertEqual(text.count('1.3', '1.3', 'update', 'indices'), 0)
74+
self.assertEqual(text.count('1.3', '1.5', 'update'), 2)
75+
self.assertEqual(text.count('1.3', '1.3', 'update'), 0)
9476

9577

9678
if __name__ == "__main__":

Lib/tkinter/__init__.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3715,25 +3715,28 @@ def compare(self, index1, op, index2):
37153715
return self.tk.getboolean(self.tk.call(
37163716
self._w, 'compare', index1, op, index2))
37173717

3718-
def count(self, index1, index2, *args): # new in Tk 8.5
3718+
def count(self, index1, index2, *options): # new in Tk 8.5
37193719
"""Counts the number of relevant things between the two indices.
3720-
If index1 is after index2, the result will be a negative number
3720+
3721+
If INDEX1 is after INDEX2, the result will be a negative number
37213722
(and this holds for each of the possible options).
37223723
3723-
The actual items which are counted depends on the options given by
3724-
args. The result is a list of integers, one for the result of each
3725-
counting option given. Valid counting options are "chars",
3724+
The actual items which are counted depends on the options given.
3725+
The result is a tuple of integers, one for the result of each
3726+
counting option given, if more than one option is specified,
3727+
otherwise it is an integer. Valid counting options are "chars",
37263728
"displaychars", "displayindices", "displaylines", "indices",
3727-
"lines", "xpixels" and "ypixels". There is an additional possible
3729+
"lines", "xpixels" and "ypixels". The default value, if no
3730+
option is specified, is "indices". There is an additional possible
37283731
option "update", which if given then all subsequent options ensure
37293732
that any possible out of date information is recalculated."""
3730-
args = ['-%s' % arg for arg in args]
3731-
args += [index1, index2]
3732-
res = self.tk.call(self._w, 'count', *args) or None
3733-
if res is not None and len(args) <= 3:
3734-
return (res, )
3735-
else:
3736-
return res
3733+
options = ['-%s' % arg for arg in options]
3734+
res = self.tk.call(self._w, 'count', *options, index1, index2)
3735+
if not isinstance(res, int):
3736+
res = self._getints(res)
3737+
if len(res) == 1:
3738+
res, = res
3739+
return res
37373740

37383741
def debug(self, boolean=None):
37393742
"""Turn on the internal consistency checks of the B-Tree inside the text
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Change the behavior of :meth:`tkinter.Text.count`. It now always returns an
2+
integer if one or less counting options are specified. Previously it could
3+
return a single count as a 1-tuple, an integer (only if option ``"update"``
4+
was specified) or ``None`` if no items found. The result is now the same if
5+
``wantobjects`` is set to ``0``.
6+

0 commit comments

Comments
 (0)