Skip to content
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

bpo-32839: Add after_info to tkinter #5664

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

csabella
Copy link
Contributor

@csabella csabella commented Feb 13, 2018

Lib/tkinter/__init__.py Outdated Show resolved Hide resolved
Lib/tkinter/__init__.py Outdated Show resolved Hide resolved
Lib/tkinter/__init__.py Outdated Show resolved Hide resolved
Lib/tkinter/__init__.py Outdated Show resolved Hide resolved
Lib/tkinter/test/test_tkinter/test_misc.py Outdated Show resolved Hide resolved
Lib/tkinter/test/test_tkinter/test_misc.py Outdated Show resolved Hide resolved
Lib/tkinter/test/test_tkinter/test_misc.py Show resolved Hide resolved
@@ -764,14 +764,32 @@ def after_cancel(self, id):
Identifier returned by after or after_idle must be
given as first parameter."""
try:
data = self.tk.call('after', 'info', id)
data = self.after_info(id)
# In Tk 8.3, splitlist returns: (script, type)
# In Tk 8.4, splitlist may return (script, type) or (script,)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that these comments still are related.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find any Tk/Tcl documentation that referenced this. Here is the commit that added the comment:
3c0f2c9

Maybe after_cancel had been called with None, which wouldn't have returned the 2-tuple?

Lib/tkinter/test/test_tkinter/test_misc.py Show resolved Hide resolved
@@ -763,15 +763,31 @@ def after_cancel(self, id):

Identifier returned by after or after_idle must be
given as first parameter."""
if id is None:
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes no sense to me. id is required and AFAKI should not be passed as None. Do after or after_idle ever return None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my following comment with the Tcl code. I added this because I'm not sure if the original comments about the tuple changing were accurate. I think it was a result of id being None vs it having a value.

data = self.tk.call('after', 'info', id)
# In Tk 8.3, splitlist returns: (script, type)
# In Tk 8.4, splitlist may return (script, type) or (script,)
data = self.after_info(id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change requires that tkinter after_info return a reference to the tcl wrapper, (which I think is wrong), so that the wrapper can be deleted (which it should be). I think after_cancel should continue using the tk.call here. The only change here might deleting the 8.3 note, which I believe is obsolete, but this is up to Serhiy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks to me, that after info $id returned a list of 2 elements not in all versions. We can now ignore old unsupported versions, but we should check sources of supported versions. We shouldn't believe the documentation, it contains errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at the 8.3 Tcl code (from 2001) and the current (8.6) code. I'm not sure if the comment about 8.3 and 8.4 was accurate. My thought is that somehow, id was None on a call, thus causing info to return the list of event_handlers instead of a tuple, as the previous code was expecting.

Here is the 8.3 Tcl code:

	case AFTER_INFO: {
	    Tcl_Obj *resultListPtr;

	    if (objc == 2) {
		for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
		     afterPtr = afterPtr->nextPtr) {
		    if (assocPtr->interp == interp) {
			sprintf(buf, "after#%d", afterPtr->id);
			Tcl_AppendElement(interp, buf);
		    }
		}
		return TCL_OK;
	    }
	    if (objc != 3) {
		Tcl_WrongNumArgs(interp, 2, objv, "?id?");
		return TCL_ERROR;
	    }
	    afterPtr = GetAfterEvent(assocPtr, objv[2]);
	    if (afterPtr == NULL) {
		Tcl_AppendResult(interp, "event \"", Tcl_GetString(objv[2]),
			"\" doesn't exist", (char *) NULL);
		return TCL_ERROR;
	    }
	    resultListPtr = Tcl_GetObjResult(interp);
 	    Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr);
 	    Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
 		(afterPtr->token == NULL) ? "idle" : "timer", -1));
	    Tcl_SetObjResult(interp, resultListPtr);
	    break;
	}

Here is the current Tcl code:

    case AFTER_INFO:
	if (objc == 2) {
            Tcl_Obj *resultObj = Tcl_NewObj();

	    for (afterPtr = assocPtr->firstAfterPtr; afterPtr != NULL;
		    afterPtr = afterPtr->nextPtr) {
		if (assocPtr->interp == interp) {
                    Tcl_ListObjAppendElement(NULL, resultObj, Tcl_ObjPrintf(
                            "after#%d", afterPtr->id));
		}
	    }
            Tcl_SetObjResult(interp, resultObj);
	    return TCL_OK;
	}
	if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 2, objv, "?id?");
	    return TCL_ERROR;
	}
	afterPtr = GetAfterEvent(assocPtr, objv[2]);
	if (afterPtr == NULL) {
            const char *eventStr = TclGetString(objv[2]);

	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
                    "event \"%s\" doesn't exist", eventStr));
            Tcl_SetErrorCode(interp, "TCL","LOOKUP","EVENT", eventStr, NULL);
	    return TCL_ERROR;
	} else {
            Tcl_Obj *resultListPtr = Tcl_NewObj();

            Tcl_ListObjAppendElement(interp, resultListPtr,
                    afterPtr->commandPtr);
            Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj(
		    (afterPtr->token == NULL) ? "idle" : "timer", -1));
            Tcl_SetObjResult(interp, resultListPtr);
        }
	break;

objc == 2 is the difference between calling with an id and not calling with an id.

Here's the commit where the comment was added: 3c0f2c9 which changed the code:

-            (script, type) = self.tk.splitlist(
-                self.tk.call('after', 'info', id))
+            data = self.tk.call('after', 'info', id)
+            # In Tk 8.3, splitlist returns: (script, type)
+            # In Tk 8.4, splitlist may return (script, type) or (script,)
+            script = self.tk.splitlist(data)[0]

So, my thought is that somehow self.tk.call('after', 'info', id)) was called with an id = None, which caused the info call to not return a 2-tuple. I don't know how to look at the original issue report, so I don't know if it was able to be recreated or someone just patched the unpacking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I remember, tkinter now requires at least 8.4, (while IDLE requires 8.5), so 8.3 is not relevant.

Lib/tkinter/__init__.py Show resolved Hide resolved
@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@csabella
Copy link
Contributor Author

csabella commented Mar 4, 2018

I pushed a rebase and got the Appveyor error. I'm working on the other concerns about the after_info functionality, so I'll push a fix once those are worked out. Although, this issue on Appveyor does answer the previous question about the after_info() event list containing other events than the ones added in this test. It seems that the other new after* tests are leaving something behind.

Lib/tkinter/__init__.py Outdated Show resolved Hide resolved
@@ -0,0 +1 @@
Add after_info to tkinter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the news entry could/should be a bit more descriptive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this were reopened, I would be willing to expand this (after the intial test rerun finishes).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concur.

Lib/tkinter/__init__.py Outdated Show resolved Hide resolved
@csabella csabella closed this Dec 31, 2019
@terryjreedy
Copy link
Member

Cheryl, can you explain the closing? I still think adding after_info would be good.

@terryjreedy
Copy link
Member

terryjreedy commented Dec 31, 2019

The only reported error, only on Travis, on line 191 is an extra item, 'after#8' (the 'timer' of line 180, I presume), in the tuple returned from .after_info. (The Travis job page somehow disables copying with current Firefox.) On this system, with the tcl/tk used, the preceding root.update() did not remove the 'timer' callback from the list.

I restarted the failing job to see if the failure is repeatable on Travis.

Answer: yes.

@ZackerySpytz
Copy link
Contributor

FWIW, I also think adding after_info() would be good.

@csabella
Copy link
Contributor Author

Sorry about closing this prematurely. I had thought there hadn't been much interest in adding the functionality when I originally proposed it, so I was just doing some housekeeping. Good to know there is interest. Having said that, the tests pass locally, so it may be tricky to get them to pass. I have a root.update() to clear the timer event from a previous test, so it seems to work locally but not on Travis. I'll look into it more.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please expand the NEWS entry, add a What's New entry apply and test the code suggestion.

The PR looks mostly good to me and I want to land it in 3.9.

function to be called by the event handler and type is either 'idle'
or 'timer' to indicate what kind of event handler it is.
"""
return self.tk.splitlist(self.tk.call('after', 'info', id))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return self.tk.splitlist(self.tk.call('after', 'info', id))
result = self.tk.splitlist(self.tk.call('after', 'info', id))
if id is not None:
result = tuple(map(self.tk.splitlist, result))
return result

We need to split nested level tuples. Otherwise we sould get a tuple of strings when wantobject=0.

Please test your changes manually by temporary setting wantobject=0 in the tkinter module (we need to add a CLI test option for this, but it is a different issue).

@@ -0,0 +1 @@
Add after_info to tkinter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concur.

@github-actions
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Aug 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting changes stale Stale PR or inactive for long period of time.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants