Skip to content

tkinter.Text.count(index1, index2) returns None not (0,) when index1 equals index2 #97928

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
RogerMarsh opened this issue Oct 5, 2022 · 4 comments
Assignees
Labels
3.13 bugs and security fixes topic-tkinter

Comments

@RogerMarsh
Copy link

RogerMarsh commented Oct 5, 2022

Surely the Text.count() method should return (0,) in the title code snippet? Following the example of what happens when more than one option is given.

I suppose the text_count method should do so too, but it was a hack of something that did not exist when written.

Tk8.6 documentation says a list of integers is returned.

Sample code below.

import tkinter


def text_count(widget, index1, index2, *options):
    """Hack Text count command. Return integer, or tuple if len(options) > 1.

    Tkinter does not provide a wrapper for the Tk Text widget count command
    at Python 2.7.1

    widget is a Tkinter Text widget.
    index1 and index2 are Indicies as specified in TkCmd documentation.
    options must be a tuple of zero or more option values.  If no options
    are given the Tk default option is used.  If less than two options are
    given an integer is returned.  Otherwise a tuple of integers is returned
    (in the order specified in TkCmd documentation).

    See text manual page in TkCmd documentation for valid option values and
    index specification.

    Example:
    chars, lines = text_count(widget, start, end, '-chars', '-lines')

    """
    return widget.tk.call((widget._w, 'count') + options + (index1, index2))


text = tkinter.Text()

print(text.count("1.0", tkinter.END))                                       # (1,0)
print(text.count("1.0", "1.0"))                                                 # None
print(text_count(text, "1.0", "1.0"))                                        # 0
print(text_count(text, "1.0", tkinter.END))                              # 1
print(text.count("1.0", tkinter.END, "chars"))                         # (1,)
print(text.count("1.0", "1.0", "chars"))                                   # None
print(text_count(text, "1.0", "1.0", "-chars"))                         # 0
print(text_count(text, "1.0", tkinter.END, "-chars"))               # 1
print(text.count(tkinter.END, "1.0", "chars"))                         # (-1,)
print(text_count(text, tkinter.END, "1.0", "-chars"))               # -1
print(text.count("1.0", tkinter.END, "chars", "lines"))             # (1, 1)
print(text.count("1.0", "1.0", "chars", "lines"))                       # (0, 0)
print(text_count(text, "1.0", "1.0", "-chars", "-lines"))            # (0, 0)
print(text_count(text, "1.0", tkinter.END, "-chars", "-lines"))  # (1, 1)

Linked PRs

@serhiy-storchaka
Copy link
Member

There are other issues with Text.count().

  • Options starting with '-' are silently ignored. It can lead to bugs: text.count(begin, end, '-lines')) returns the the number of "indices" rather than the number of lines. Perhaps the initial intention was to only ignore the '-' prefix, so '-lines' and 'lines' would mean the same, but it never worked. It is better to make it an error.
  • If wantobjects was set to 0, the method returns a string.
  • It tries to always return a tuple (except when the result is zero count), but it fails with the 'update' option (which is undocumented in Tkinter). It returns an integer for 'update', 'ypixels' and 'update', 'indices', but a 1-tuple for 'ypixels' and 'update'.

There were no any tests for this method, so all this was unnoticed.

Other issues could be solved in compatible way, but the originally reported issue does not have backward compatible solution. Returning (0.) instead of None would make the behavior more uniform, but it would break the code if text.count(begin, end) which just checks where there is something in the interval.

I think that the decision of returning 1-tuple for a single count (unless it is zero) was a mistake. The Tk code is different for a single count and multiple code -- a single count is returned as an integer instead of a 1-list. The difference is only disappeared if convert it to string.

It would be more convenient for users to get an integer in Python too. But it is a breaking change. Choosing between two breaking changes, returning (0,) for an empty count or returning a single count as integer, I prefer the latter.

serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 14, 2022
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Oct 19, 2022
(cherry picked from commit 1b684c8)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Oct 19, 2022
(cherry picked from commit 1b684c8)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 19, 2022
…t.count()

Previously they were silently ignored. Now they are errors.
miss-islington added a commit that referenced this issue Oct 19, 2022
(cherry picked from commit 1b684c8)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
miss-islington added a commit that referenced this issue Oct 19, 2022
(cherry picked from commit 1b684c8)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
serhiy-storchaka added a commit that referenced this issue Oct 19, 2022
…t() (GH-98436)

Previously they were silently ignored. Now they are errors.
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Oct 19, 2022
…t.count() (pythonGH-98436)

Previously they were silently ignored. Now they are errors.
(cherry picked from commit e4ec8de)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Oct 19, 2022
…t.count() (pythonGH-98436)

Previously they were silently ignored. Now they are errors.
(cherry picked from commit e4ec8de)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
miss-islington added a commit that referenced this issue Oct 19, 2022
…t() (GH-98436)

Previously they were silently ignored. Now they are errors.
(cherry picked from commit e4ec8de)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
miss-islington added a commit that referenced this issue Oct 19, 2022
…t() (GH-98436)

Previously they were silently ignored. Now they are errors.
(cherry picked from commit e4ec8de)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 20, 2022
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 20, 2022
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.
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 20, 2022
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.
@serhiy-storchaka serhiy-storchaka added the 3.13 bugs and security fixes label Oct 24, 2023
serhiy-storchaka added a commit that referenced this issue Oct 24, 2023
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.
@terryjreedy
Copy link
Member

Thanks for the fix.

@serhiy-storchaka
Copy link
Member

It was suggested to partially restore the old behavior and add an optional argument return_ints to control the returned value.

@terryjreedy
Copy link
Member

I clicked and read most of that discussion.

serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Feb 5, 2024
…) by default

By default, it preserves an inconsistent behavior of older Python
versions: packs the count into a 1-tuple if only one or none
options are specified (including 'update'), returns None instead of 0.
Except that setting wantobjects to 0 no longer affects the result.

Add a new parameter return_ints: specifying return_ints=True makes
Text.count() always returning the single count as an integer
insteaf of a 1-tuple or None.
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Feb 5, 2024
…) by default

By default, it preserves an inconsistent behavior of older Python
versions: packs the count into a 1-tuple if only one or none
options are specified (including 'update'), returns None instead of 0.
Except that setting wantobjects to 0 no longer affects the result.

Add a new parameter return_ints: specifying return_ints=True makes
Text.count() always returning the single count as an integer
insteaf of a 1-tuple or None.
serhiy-storchaka added a commit that referenced this issue Feb 11, 2024
…efault (GH-115031)

By default, it preserves an inconsistent behavior of older Python
versions: packs the count into a 1-tuple if only one or none
options are specified (including 'update'), returns None instead of 0.
Except that setting wantobjects to 0 no longer affects the result.

Add a new parameter return_ints: specifying return_ints=True makes
Text.count() always returning the single count as an integer
instead of a 1-tuple or None.
aisk pushed a commit to aisk/cpython that referenced this issue Feb 11, 2024
…-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.
fsc-eriker pushed a commit to fsc-eriker/cpython that referenced this issue Feb 14, 2024
…) by default (pythonGH-115031)

By default, it preserves an inconsistent behavior of older Python
versions: packs the count into a 1-tuple if only one or none
options are specified (including 'update'), returns None instead of 0.
Except that setting wantobjects to 0 no longer affects the result.

Add a new parameter return_ints: specifying return_ints=True makes
Text.count() always returning the single count as an integer
instead of a 1-tuple or None.
Glyphack pushed a commit to Glyphack/cpython that referenced this issue Sep 2, 2024
…-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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 bugs and security fixes topic-tkinter
Projects
None yet
Development

No branches or pull requests

4 participants