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

Ability to trace Tcl commands executed by Tkinter #71592

Closed
serhiy-storchaka opened this issue Jun 28, 2016 · 19 comments
Closed

Ability to trace Tcl commands executed by Tkinter #71592

serhiy-storchaka opened this issue Jun 28, 2016 · 19 comments
Assignees
Labels
3.7 (EOL) end of life topic-tkinter type-feature A feature request or enhancement

Comments

@serhiy-storchaka
Copy link
Member

serhiy-storchaka commented Jun 28, 2016

BPO 27405
Nosy @terryjreedy, @ned-deily, @serhiy-storchaka, @csabella
Files
  • tkinter_trace_tcl.patch
  • tkinter_trace_tcl2.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/serhiy-storchaka'
    closed_at = None
    created_at = <Date 2016-06-28.08:06:00.853>
    labels = ['3.7', 'type-feature', 'expert-tkinter']
    title = 'Ability to trace Tcl commands executed by Tkinter'
    updated_at = <Date 2018-10-17.22:53:22.982>
    user = 'https://github.com/serhiy-storchaka'

    bugs.python.org fields:

    activity = <Date 2018-10-17.22:53:22.982>
    actor = 'cheryl.sabella'
    assignee = 'serhiy.storchaka'
    closed = False
    closed_date = None
    closer = None
    components = ['Tkinter']
    creation = <Date 2016-06-28.08:06:00.853>
    creator = 'serhiy.storchaka'
    dependencies = []
    files = ['43592', '44560']
    hgrepos = []
    issue_num = 27405
    keywords = ['patch']
    message_count = 16.0
    messages = ['269425', '269447', '269626', '269631', '269697', '269699', '269702', '269745', '275813', '275983', '275988', '275991', '275992', '276000', '276007', '327915']
    nosy_count = 4.0
    nosy_names = ['terry.reedy', 'ned.deily', 'serhiy.storchaka', 'cheryl.sabella']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = 'test needed'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue27405'
    versions = ['Python 3.7']

    Linked PRs

    @serhiy-storchaka
    Copy link
    Member Author

    For debugging purpose it would be helpful to have an ability to show all Tcl commands executed by Tkinter. In particular it would be helpful for testing wherever some issue is Tkinter issue or Tcl/Tk issue.

    I'm working on a patch, but there is a design question about an interface.

    In simplest case you set the debug property of Tk instance to True and all Tcl commands are printed to stdout or stderr in the form ready for executing with Tcl/Tk interpreter. You also can set the module global tkinter.debug to True, and this will affect all new Tk instances (as with tkinter.wantobjects).

    But maybe there is a need in larger customization? For example specifying the command that accepts every Tcl command as a string or a tuple?

    @serhiy-storchaka serhiy-storchaka added topic-tkinter type-feature A feature request or enhancement labels Jun 28, 2016
    @terryjreedy
    Copy link
    Member

    +1 for at least a simple interface: tkinter/root.debug = True. Let's do at least that much.

    I don't understand what you propose in the last sentence, do you mean 'add a new function that accepts any tcl command, as string or tuple'? Or add better access to the existing call function? Or does the existing call function have limitations I do not not know about?

    @serhiy-storchaka
    Copy link
    Member Author

    I meant adding API similar to sys.settrace() for specifying a function that will be called before executing every Tcl command.

    @serhiy-storchaka
    Copy link
    Member Author

    Here is preliminary patch. It contains leaks, don't worry.

    @terryjreedy
    Copy link
    Member

    In "debug = False+1', I presume the '+1' is just temporary.

    The debug property and the clinic file refer to _tkinter.tkapp.get/settrace, which do not exist. Did you just forget to include that part? Or does 'preliminary' mean 'do not test yet'?

    >>> import tkinter
    >>> tkinter.debug
    1
    >>> import idlelib.idle
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "F:\Python\dev\36\lib\idlelib\idle.py", line 11, in <module>
        idlelib.pyshell.main()
      File "F:\Python\dev\36\lib\idlelib\pyshell.py", line 1553, in main
        root = Tk(className="Idle")
      File "F:\Python\dev\36\lib\tkinter\__init__.py", line 2018, in __init__
        self.debug = debug
      File "F:\Python\dev\36\lib\tkinter\__init__.py", line 2110, in debug
        self.tk.settrace(trace)
    AttributeError: '_tkinter.tkapp' object has no attribute 'settrace'

    At least the error shows that the code works as far as written ;-).

    I now understand 'larger customization ...' to mean supplying a trace function other than the default, to do something with 'cmd' other than just print it. For instance, one could record tcl commands in a file and later count the tcl function calls, much like python's trace does. To do that, I believe you could just replace 'def trace ... with

    if iscallable(value):
        trace = value
    else:
        def trace ...

    +1 on adding this. The default trace would be useful for simple interaction, but less so for what I tried to do above, which is to trace a complete IDLE session.

    @serhiy-storchaka
    Copy link
    Member Author

    You need to recompile the _tkinter module.

    @terryjreedy
    Copy link
    Member

    I just noticed that the patch includes a patch to _tkinter.c, which however, does not show up on the Rietveld dashboad, where I first reviewed it. Sorry for the mistaken comment.

    @terryjreedy
    Copy link
    Member

    After re-compiling, import tkinter crashed 3.6. Reversion did not help. After trying to update VS2015 (not successfully), and doing a binary search, it was your patch for bpo-26765.

    @serhiy-storchaka
    Copy link
    Member Author

    Here is more mature patch. Now you can set tracing function with the tkinter.Tk.settrace() method. Tracing function takes the single argument -- a tuple containing the command and arguments. If set global tkinter.debug to True, the tracing function of newly created Tk object will be set to tkinter.print_command that prints the command to stdout.

    @terryjreedy
    Copy link
    Member

    The patch changes 4 non-tkinter clinic files. Accident? All 4 are rejected. Will compile and test tkinter changes.

    @terryjreedy
    Copy link
    Member

    Much better.  I can more or less follow IDLE startup.
    >>> import tkinter
    >>> tkinter.debug
    False
    >>> tkinter.debug = True
    >>> import idlelib.idle
    proc tkerror {} {<function _tkerror at 0x038479F8>}
    proc exit {} {<function _exit at 0x03847A58>}
    proc 27305592destroy {} {<bound method CallWrapper.__call__ of <tkinter.CallWrapper object at 0x04033658>>}
    wm protocol . WM_DELETE_WINDOW 27305592destroy
    wm withdraw .
    wm iconbitmap . -default F:\\Python\\dev\\36\\lib\\idlelib\\Icons\\idle.ico
    tcl_wordBreakAfter {a b} 0
    set tcl_wordchars [a-zA-Z0-9_]
    set tcl_nonwordchars [^a-zA-Z0-9_]
    tk windowingsystem  
    menu .`menu
    toplevel .`listedtoplevel -menu .`menu
    wm iconname . None
    wm iconname .`listedtoplevel {}
    wm title . None
    wm title .`listedtoplevel idle
    ...
    proc 67104888handler {} {<bound method CallWrapper.__call__ of <tkinter.CallWrapper object at 0x05727690>>}
    bind .`listedtoplevel.`frame.text <Control-KeyPress> if\ \{"[67104888handler\ %#\ %b\ %f\ %h\ %k\ %s\ %t\ %w\ %x\ %y\ %A\ %E\ %K\ %N\ %W\ %T\ %X\ %Y\ %D]"\ ==\ "break"\}\ break\
    ... # lots of above, binding for Shell window. Default print is noisy.
        # I presume a custom handler that recognizes 'proc' could be better.

    menu .menu.file -tearoff 0 .menu add cascade -label File -menu .menu.file -underline 0 ... # menu for Shell .menu.file add command -label {New File} -underline 0 -command 62029624command -accelerator Ctrl+N
    ... # more menu - new names really help!!!

    Will ...settrace(None)? Aside from that, I would like to see this in 3.6. (ie, applied now, upgraded later, if definitely safe). (Note: 4AM here, so this is best I can review now ;-).

    @terryjreedy
    Copy link
    Member

    I ran idle and tcl/tk/ttk tests. all pass with and without patch. text_tcl before and with patch ends with warning: Windows fatal exception: access violation. I have seen this for perhaps a couple of weeks (check windows buildbot), but not forever. You should probably try to fix.

    @terryjreedy
    Copy link
    Member

    If not in 3.6, I can still apply patch from shelf, recompile, use for awhile, re-shelve, recompile. So not tragedy if miss deadline.

    @serhiy-storchaka
    Copy link
    Member Author

    This feature is useful first at all for us, core developers. I think we could add it with provisional status in the beta stage or wait to 3.7. In any case the documentation and tests are not ready yet.

    @ned-deily
    Copy link
    Member

    Let's target it for 3.7 and when it is ready we can discuss whether to backport it to 3.6 as well.

    @ned-deily ned-deily added the 3.7 (EOL) end of life label Sep 12, 2016
    @csabella
    Copy link
    Contributor

    Seems like this didn't make 3.7. Would it be good to make a PR targeting 3.8?

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Apr 25, 2024
    It is an experimental feature, for internal use.
    
    Setting tkinter._debug = True before creating the root window enables
    printing every executed Tcl command (or a Tcl command equivalent to the
    used Tcl C API).
    
    It will help to convert a Tkinter example into Tcl script to check
    whether the issue is caused by Tkinter or exists in the underlying Tcl/Tk
    library.
    @serhiy-storchaka
    Copy link
    Member Author

    It stuck for a long time because I was hesitant about the user interface. But every time I investigate a problem, I regret that this feature was not added. So I decided to add it as a private feature for our internal use. When we have some experience, we can add a public interface.

    For the same reason, it is better to backport it.

    serhiy-storchaka added a commit that referenced this issue May 6, 2024
    …18291)
    
    This is an experimental feature, for internal use.
    
    Setting tkinter._debug = True before creating the root window enables
    printing every executed Tcl command (or a Tcl command equivalent to the
    used Tcl C API).
    
    This will help to convert a Tkinter example into Tcl script to check
    whether the issue is caused by Tkinter or exists in the underlying Tcl/Tk
    library.
    serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue May 6, 2024
    …Tkinter (pythonGH-118291)
    
    This is an experimental feature, for internal use.
    
    Setting tkinter._debug = True before creating the root window enables
    printing every executed Tcl command (or a Tcl command equivalent to the
    used Tcl C API).
    
    This will help to convert a Tkinter example into Tcl script to check
    whether the issue is caused by Tkinter or exists in the underlying Tcl/Tk
    library.
    (cherry picked from commit 1ff626e)
    
    Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
    serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue May 6, 2024
    serhiy-storchaka added a commit that referenced this issue May 6, 2024
    GH-118291) (GH-118662)
    
    This is an experimental feature, for internal use.
    
    Setting tkinter._debug = True before creating the root window enables
    printing every executed Tcl command (or a Tcl command equivalent to the
    used Tcl C API).
    
    This will help to convert a Tkinter example into Tcl script to check
    whether the issue is caused by Tkinter or exists in the underlying Tcl/Tk
    library.
    (cherry picked from commit 1ff626e)
    
    Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
    @terryjreedy
    Copy link
    Member

    terryjreedy commented May 6, 2024

    The merged interface: set tkinter._debug = True before creating the root window sets tracing (l.2454) with tkinter._print_command (l.5=2543), which prints every executed Tcl command .

    Creating a branch and setting _debug at the top of pyshell.py and running python -m idlelib (in main repository) prints 100s of lines to Command Prompt for the creation of Shell: lots of menu, bind, and handler calls. It eventually settles into repeated Shell 50 ms socket polling loop calls, with 3 calls per poll:

    proc 2199050013168poll_subprocess {} {<bound method CallWrapper.__call__ of <tkinter.CallWrapper object at 0x0000020001BDFCA0>>}
    after 50 2199050013168poll_subprocess  # last action in poll_subprocess
    rename 2199050013936poll_subprocess {}  # internal part of after call?
    

    poll_subprocess defined about l.582; loop started l.486; 50 ms poll_interval set on l.950

    I successfully stopped the loop with ctrl-C. To use this with IDLE, to see for instance the calls resulting from a particular post-startup action, a custom _print_command would be needed that skips most of the editor window setup commands and all of the polling commands. Looks quite doable. The last non-poll call may have be "rename 2199051334160recolorize {}". A separate colorizer loop might be triggered by first code input.

    Sorry I did not review earlier.

    @serhiy-storchaka
    Copy link
    Member Author

    I made sure that all Tkinter and IDLE tests are passed when run with _debug = True. This is why _print_command() prints to the initial sys.stderr instead of the current value, otherwise it created a loop in IDLE.

    SonicField pushed a commit to SonicField/cpython that referenced this issue May 8, 2024
    …pythonGH-118291)
    
    This is an experimental feature, for internal use.
    
    Setting tkinter._debug = True before creating the root window enables
    printing every executed Tcl command (or a Tcl command equivalent to the
    used Tcl C API).
    
    This will help to convert a Tkinter example into Tcl script to check
    whether the issue is caused by Tkinter or exists in the underlying Tcl/Tk
    library.
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.7 (EOL) end of life topic-tkinter type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants