This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: tkinter searches for tk.tcl in wrong directory
Type: Stage: resolved
Components: Tkinter Versions: Python 3.8
process
Status: closed Resolution: third party
Dependencies: Superseder:
Assigned To: Nosy List: ajstewart, ned.deily
Priority: normal Keywords:

Created on 2021-05-27 18:40 by ajstewart, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (8)
msg394584 - (view) Author: Adam Stewart (ajstewart) * Date: 2021-05-27 18:40
I'm trying to install Python with tkinter support using the Spack package manager. Spack adds the following flags to configure during install:
```
'--with-tcltk-libs=-L/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib -ltcl8.6 -L/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib -ltk8.6'
```
It also sets the following environment variables:
```
TCLLIBPATH='/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib/tcl8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib/tcl8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib64/tcl8.6'; export TCLLIBPATH
TCL_LIBRARY=/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib; export TCL_LIBRARY
```
The install seems to correctly pick up tk/tcl and builds correctly. However, when I try to use tkinter, I see the following run-time error:
```
$ python
Python 3.8.10 (default, May 27 2021, 13:28:01) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import tkinter
>>> tkinter._test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/lib/python3.8/tkinter/__init__.py", line 4557, in _test
    root = Tk()
  File "/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/lib/python3.8/tkinter/__init__.py", line 2270, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: Can't find a usable tk.tcl in the following directories: 
    /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib/tcl8.6/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib/tcl8.6/tk8.6/Resources/Scripts /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib/tcl8.6/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib/tcl8.6/tk8.6/Resources/Scripts /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib64/tcl8.6/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib64/tcl8.6/tk8.6/Resources/Scripts /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib/tk8.6/Resources/Scripts /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/tk8.6/Resources/Scripts /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/lib/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/lib/tk8.6/Resources/Scripts /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/lib/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/lib/tk8.6 /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/library

This probably means that tk wasn't installed properly.
```
It seems that tkinter searches for tk.tcl in `<prefix>/lib`, but tk.tcl is actually installed in `<prefix>/lib/tk8.6`. I asked the tk developers, but it looks like `<prefix>/lib/tk8.6` is indeed the correct installation location: https://core.tcl-lang.org/tk/tktview/447bd3e4abe17452d19a80e6840dcc8a2603fcbc

Is there a way to tell tkinter where to find tk.tcl? If not, can we modify the default search path to search in `<prefix>/lib/tk8.6`?

Related to https://github.com/spack/spack/issues/23780
msg394590 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2021-05-27 20:05
The message is coming from Tcl/Tk; tkinter is just passing it along and is otherwise not involved AFAIK. You don't need to use framework builds for Tcl or Tk on macOS but you should follow the recommendation of how to do a Unix build of Tcl and Tk as far as configure options and placement of directories. When built properly Tcl should have no trouble finding Tk and the tk.tcl file.
msg394595 - (view) Author: Adam Stewart (ajstewart) * Date: 2021-05-27 20:35
Thanks, in that case it sounds like the problem is that Spack installs tcl and tk to separate directories, but since tk depends on tcl and not the other way around, tcl has no way of knowing where tk is installed. I'll see if I can convince the other Spack devs to combine tcl and tk into a single package.
msg394629 - (view) Author: Adam Stewart (ajstewart) * Date: 2021-05-28 02:31
I think I FINALLY figured out the problem. We were setting `TCLLIBPATH` to `<prefix>/lib/tk8.6` when it should be `<prefix>/lib`. With this change, tkinter seems to work for me. Thanks for all of your help!
msg394675 - (view) Author: Adam Stewart (ajstewart) * Date: 2021-05-28 17:43
And... now it's not working again. Can you clarify exactly how tkinter finds tk/tcl? Does it rely on TCL_LIBRARY or TK_LIBRARY env vars? TCLLIBPATH? If I use all of these env vars, tkinter finds tcl/tk, but commands like:

$ python -m tkinter
$ python -c 'import tkinter; tkinter._test()'

open a window and immediately minimize it. If I try to maximize the window it immediately closes, so something is definitely wrong with my installation.
msg394692 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2021-05-28 21:56
tkinter does not find Tk; the linker at build time (ld) and usually the dynamic linker (dyld) at run time do all the work. Typically on macOS, the linkers prefer to dynamically link to libraries, deferring the decision to dyld at run time exactly which dynamic library will be linked to rather than statically linking in a library at build time. The expected shared library name (or path) is recorded at build time into the executables generated. The --with-tcltk-includes Python ./configure option is passed at build time to the C compiler chain to add the directories(s) for Tcl and Tk header files, if not otherwise found, and the --with-tcltk-libs option gives the locations of the libraries to add directories with Tcl and Tk libraries for ld to link against including generating the shared library file paths in the binaries produced.

In a standard Python build, tkinter has a Python component that calls a private C-language extension _module helper named _tkinter that does all the C-level calls to Tk and Tkinter. You can find the file name of the _tkinter extension module by importing it in the interpreter and looking at its __file__ attribute. With that you can use the Xcode / Command Line Tools "otool" utility to examine the binary to find the shared library names it is linked with and expecting to find at run time.

An example with a current python.org macOS Python:

$ /usr/local/bin/python3.9
Python 3.9.5 (v3.9.5:0a7dcbdb13, May  3 2021, 13:05:53)
[Clang 12.0.5 (clang-1205.0.22.9)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import _tkinter
>>> _tkinter.__file__
'/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/lib-dynload/_tkinter.cpython-39-darwin.so'
>>> ^D
$ otool -L '/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/lib-dynload/_tkinter.cpython-39-darwin.so'
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/lib-dynload/_tkinter.cpython-39-darwin.so:
	/Library/Frameworks/Python.framework/Versions/3.9/lib/libtcl8.6.dylib (compatibility version 8.6.0, current version 8.6.11)
	/Library/Frameworks/Python.framework/Versions/3.9/lib/libtk8.6.dylib (compatibility version 8.6.0, current version 8.6.11)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.100.5)

In this case, the libtcl and libtk shared libraries are included within the python.org distribution installed in /Library/Frameworks.

Another example with a MacPorts python built from source:

$ otool -L $(/opt/macports/bin/python3.9 -c "import _tkinter;print(_tkinter.__file__)")
/opt/macports/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/_tkinter.cpython-39-darwin.so:
	/opt/macports/lib/libtcl8.6.dylib (compatibility version 8.6.0, current version 8.6.11)
	/opt/macports/lib/libtk8.6.dylib (compatibility version 8.6.0, current version 8.6.11)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.100.5)

In this case, the _tkinter module was linked with libtcl and libtk included in the MacPorts distribution.

Unlike many other platforms, the shared library names seen in macOS binaries are usually absolute path names although they can be relative paths.  It is possible to use environment variables to override the default search paths used by dyld at run time and it is also possible to ask dyld to display debug info about exactly what files it ends up using, among other things.  See the dyld man page for lots of useful info.  An example:

$ DYLD_PRINT_LIBRARIES=1 /opt/macports/bin/python3.9 -c "import _tkinter;print(_tkinter.__file__)" 2>&1 | grep 'libt'
dyld: loaded: <FCA34869-A151-383A-AF54-F988070D5977> /opt/macports/lib/libtcl8.6.dylib
dyld: loaded: <403FAC32-CB24-3ED3-B680-02AE4FBB11A7> /opt/macports/lib/libtk8.6.dylib

Hope this helps!
msg394697 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2021-05-28 22:21
Correction: "In a standard Python build, tkinter has a Python component that calls a private C-language extension module helper named _tkinter that does all the C-level calls to Tk and Tcl [not Tkinter!]."
msg394705 - (view) Author: Adam Stewart (ajstewart) * Date: 2021-05-28 23:48
Thanks, that does help. Spack uses both `--with-tcltk-includes` and `--with-tcltk-libs`, and actually RPATHs the libraries in place. According to otool, that is all working fine:

$ otool -L /Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/lib/python3.8/lib-dynload/_tkinter.cpython-38-darwin.so
/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/python-3.8.10-fkj5vkn3tpottyv6yqoj5ucz2emstpvo/lib/python3.8/lib-dynload/_tkinter.cpython-38-darwin.so:
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tcl-8.6.11-n7nea33urrk25rkoqpsc2tdcgai5u4z2/lib/libtcl8.6.dylib (compatibility version 8.6.0, current version 8.6.11)
	/Users/Adam/spack/opt/spack/darwin-catalina-x86_64/apple-clang-12.0.0/tk-8.6.11-ydmhrbboheucxsuhrnyoxqaihgna5dfe/lib/libtk8.6.dylib (compatibility version 8.6.0, current version 8.6.11)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)

So like you initially thought, the problem isn't that tkinter/_tkinter can't find tcl, it's that tcl can't find tk. I'll talk more with the tcl developers and see how tcl is trying to find tk. Thanks for all of your help!
History
Date User Action Args
2022-04-11 14:59:46adminsetgithub: 88419
2021-05-28 23:48:19ajstewartsetmessages: + msg394705
2021-05-28 22:21:15ned.deilysetmessages: + msg394697
2021-05-28 21:56:15ned.deilysetmessages: + msg394692
2021-05-28 17:43:08ajstewartsetmessages: + msg394675
2021-05-28 02:31:58ajstewartsetmessages: + msg394629
2021-05-27 20:35:44ajstewartsetmessages: + msg394595
2021-05-27 20:05:26ned.deilysetstatus: open -> closed

type: crash ->

nosy: + ned.deily
messages: + msg394590
resolution: third party
stage: resolved
2021-05-27 18:40:40ajstewartcreate