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
base: main
Are you sure you want to change the base?
Conversation
Lib/tkinter/__init__.py
Outdated
@@ -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,) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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/__init__.py
Outdated
@@ -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 |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
Lib/tkinter/__init__.py
Outdated
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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 pushed a rebase and got the Appveyor error. I'm working on the other concerns about the |
@@ -0,0 +1 @@ | |||
Add after_info to tkinter. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concur.
Cheryl, can you explain the closing? I still think adding after_info would be good. |
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. |
FWIW, I also think adding |
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 |
There was a problem hiding this 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)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concur.
This PR is stale because it has been open for 30 days with no activity. |
https://bugs.python.org/issue32839