Skip to content

Commit

Permalink
gh-97928: Change the behavior of tkinter.Text.count() (GH-98484)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
serhiy-storchaka authored Oct 24, 2023
1 parent 81eba76 commit b8c20f9
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 55 deletions.
10 changes: 9 additions & 1 deletion Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,15 @@ Porting to Python 3.13
This section lists previously described changes and other bugfixes
that may require changes to your code.

* None yet
Changes in the Python API
-------------------------

* :meth:`!tkinter.Text.count` 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``.
(Contributed by Serhiy Storchaka in :gh:`97928`.)


Build Changes
Expand Down
7 changes: 3 additions & 4 deletions Lib/idlelib/sidebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ def get_end_linenumber(text):

def get_displaylines(text, index):
"""Display height, in lines, of a logical line in a Tk text widget."""
res = text.count(f"{index} linestart",
f"{index} lineend",
"displaylines")
return res[0] if res else 0
return text.count(f"{index} linestart",
f"{index} lineend",
"displaylines")

def get_widget_padding(widget):
"""Get the total padding of a Tk widget, including its border."""
Expand Down
56 changes: 19 additions & 37 deletions Lib/test/test_tkinter/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class TextTest(AbstractTkTest, unittest.TestCase):
def setUp(self):
super().setUp()
self.text = tkinter.Text(self.root)
self.text.pack()

def test_debug(self):
text = self.text
Expand Down Expand Up @@ -41,8 +42,6 @@ def test_search(self):
self.assertEqual(text.search('test', '1.0', 'end'), '1.3')

def test_count(self):
# XXX Some assertions do not check against the intended result,
# but instead check the current result to prevent regression.
text = self.text
text.insert('1.0',
'Lorem ipsum dolor sit amet,\n'
Expand All @@ -53,44 +52,27 @@ def test_count(self):
options = ('chars', 'indices', 'lines',
'displaychars', 'displayindices', 'displaylines',
'xpixels', 'ypixels')
if self.wantobjects:
self.assertEqual(len(text.count('1.0', 'end', *options)), 8)
else:
text.count('1.0', 'end', *options)
self.assertEqual(text.count('1.0', 'end', 'chars', 'lines'), (124, 4)
if self.wantobjects else '124 4')
self.assertEqual(text.count('1.3', '4.5', 'chars', 'lines'), (92, 3)
if self.wantobjects else '92 3')
self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines'), (-92, -3)
if self.wantobjects else '-92 -3')
self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines'), (0, 0)
if self.wantobjects else '0 0')
self.assertEqual(text.count('1.0', 'end', 'lines'), (4,)
if self.wantobjects else ('4',))
self.assertEqual(text.count('end', '1.0', 'lines'), (-4,)
if self.wantobjects else ('-4',))
self.assertEqual(text.count('1.3', '1.5', 'lines'), None
if self.wantobjects else ('0',))
self.assertEqual(text.count('1.3', '1.3', 'lines'), None
if self.wantobjects else ('0',))
self.assertEqual(text.count('1.0', 'end'), (124,) # 'indices' by default
if self.wantobjects else ('124',))
self.assertEqual(len(text.count('1.0', 'end', *options)), 8)
self.assertEqual(text.count('1.0', 'end', 'chars', 'lines'), (124, 4))
self.assertEqual(text.count('1.3', '4.5', 'chars', 'lines'), (92, 3))
self.assertEqual(text.count('4.5', '1.3', 'chars', 'lines'), (-92, -3))
self.assertEqual(text.count('1.3', '1.3', 'chars', 'lines'), (0, 0))
self.assertEqual(text.count('1.0', 'end', 'lines'), 4)
self.assertEqual(text.count('end', '1.0', 'lines'), -4)
self.assertEqual(text.count('1.3', '1.5', 'lines'), 0)
self.assertEqual(text.count('1.3', '1.3', 'lines'), 0)
self.assertEqual(text.count('1.0', 'end'), 124) # 'indices' by default
self.assertEqual(text.count('1.0', 'end', 'indices'), 124)
self.assertRaises(tkinter.TclError, text.count, '1.0', 'end', 'spam')
self.assertRaises(tkinter.TclError, text.count, '1.0', 'end', '-lines')

self.assertIsInstance(text.count('1.3', '1.5', 'ypixels'), tuple)
self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels'), int
if self.wantobjects else str)
self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels'), None
if self.wantobjects else '0')
self.assertEqual(text.count('1.3', '1.5', 'update', 'indices'), 2
if self.wantobjects else '2')
self.assertEqual(text.count('1.3', '1.3', 'update', 'indices'), None
if self.wantobjects else '0')
self.assertEqual(text.count('1.3', '1.5', 'update'), (2,)
if self.wantobjects else ('2',))
self.assertEqual(text.count('1.3', '1.3', 'update'), None
if self.wantobjects else ('0',))
self.assertIsInstance(text.count('1.3', '1.5', 'ypixels'), int)
self.assertIsInstance(text.count('1.3', '1.5', 'update', 'ypixels'), int)
self.assertEqual(text.count('1.3', '1.3', 'update', 'ypixels'), 0)
self.assertEqual(text.count('1.3', '1.5', 'update', 'indices'), 2)
self.assertEqual(text.count('1.3', '1.3', 'update', 'indices'), 0)
self.assertEqual(text.count('1.3', '1.5', 'update'), 2)
self.assertEqual(text.count('1.3', '1.3', 'update'), 0)


if __name__ == "__main__":
Expand Down
29 changes: 16 additions & 13 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3715,25 +3715,28 @@ def compare(self, index1, op, index2):
return self.tk.getboolean(self.tk.call(
self._w, 'compare', index1, op, index2))

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

def debug(self, boolean=None):
"""Turn on the internal consistency checks of the B-Tree inside the text
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Change the behavior of :meth:`tkinter.Text.count`. 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``.

0 comments on commit b8c20f9

Please sign in to comment.