Repository: pyinstaller/pyinstaller

Issue:
_tkinter.TclError  Possible regression from ticket 540
**Original date**: 2012/10/26
**Original reporter**: _Lynn Oliver <raycores AND gmail DOT  COOM >_

When I build with pyinstaller-2.0 on OSX 10.8.2, I can run the resulting binary on the build machine without any issues.  When I run the binary on a fresh install of OSX 10.8.2, I get a _tkinter.TclError (traceback below).  I believe this is similar to the error that I reported on ticket 540.

I'm able to build successfully with a pre-2.0 dev version of pyinstaller.  I don't know of any way to identify the exact build I am using, but the files are dated 5/4/2012 at 10:20 AM (PDT).

```
Traceback (most recent call last):
  File "<string>", line 149, in <module>
  File "../axeomatic2/normal/build/pyi.darwin/aomDSP/out00-PYZ.pyz/Tkinter", line 1685, in __init__
_tkinter.TclError: Can't find a usable init.tcl in the following directories: 
    /var/folders/wr/ngmkb4s56cj4d2n1gxll358r0000gn/T/_MEIzgaAGB/_MEI/tcl /var/folders/wr/ngmkb4s56cj4d2n1gxll358r0000gn/T/_MEIzgaAGB/_MEI/tcl8.5 /Library/Frameworks/Tcl.framework/Versions/8.5/Resources/Scripts /Applications/aomDSP.app/Contents/lib/tcl8.5 /Applications/aomDSP.app/lib/tcl8.5 /Applications/aomDSP.app/Contents/library /Applications/aomDSP.app/library /Applications/aomDSP.app/tcl8.5.11/library /Applications/tcl8.5.11/library

/var/folders/wr/ngmkb4s56cj4d2n1gxll358r0000gn/T/_MEIzgaAGB/_MEI/tcl/init.tcl: can't find package activestate::teapot::link
can't find package activestate::teapot::link
    while executing
"package require activestate::teapot::link"
    invoked from within
"if {![interp issafe] && ![catch {package require platform}]} {
        package require activestate::teapot::link
        ::activestate::teapot::link::setup
..."
    (file "/var/folders/wr/ngmkb4s56cj4d2n1gxll358r0000gn/T/_MEIzgaAGB/_MEI/tcl/init.tcl" line 873)
    invoked from within
"source /var/folders/wr/ngmkb4s56cj4d2n1gxll358r0000gn/T/_MEIzgaAGB/_MEI/tcl/init.tcl"
    ("uplevel" body line 1)
    invoked from within
"uplevel #0 [list source $tclfile]"


This probably means that Tcl wasn't installed properly.
```



Discussion:
**Original date**: 2012/10/26

Do you use activestate python distribution? 

```
can't find package activestate::teapot::link
```

It seems so and probably activestate uses a modified version of tcl/tk.

I have just come across this problem. Here's what I did:
- Installed ActiveTcl as per the recommendations at https://www.python.org/download/mac/tcltk/ (that is, I downloaded ActiveTcl 8.5.17.0)
- Installed Python from Python.org (and from hombrew - I tried both)
- Built my Tkinter project using Pyinstaller
- Copied my .app file to another computer (that doesn't have ActiveTcl 8.5.17 installed on it)

Just as an update: I found that the problematic code was in `/Library/Frameworks/Tcl.framework/Versions/8.5/Resources/Scripts/init.tcl`

At the end of the file there are the following lines:

```
        # TEAPOT LINK BOOT BEGIN -*- tcl -*-
        # Copyright (C) 2006-2007 ActiveState Software Inc.
        if {![interp issafe] && ![catch {package require platform}]} {
            package require activestate::teapot::link
            ::activestate::teapot::link::setup
        }
        # TEAPOT LINK BOOT END
```

I commented these out, and re-built with PyInstaller and everything worked fine. Perhaps PyInstaller should check the Tkinter `init.tcl` file for references to 'teapot' and either print a warning or remove the lines completely.
I can confirm that Apple's Tkinter doesn't refer to `teapot` except in one Documentation file at `/System/Library/Frameworks/Tcl.framework/Versions/8.5/Resources/Documentation/Reference/Tcl/TclCmd/fconfigure.htm`

@pjrobertson Could you please try to implement this warning?

OK, I will do. I'll submit a patch this evening/tomorrow.
Do you have a 'standard' way of finding if a path is in a system library?
At the moment I'm using:

```
if path.startswith("/System/Library"):
    ... 
```

which probably isn't ideal

look at file ./PyInstaller/depend/dylib.py, there is code like this:

``` python
157 
158 if is_darwin:
159     # On Mac use macholib to decide if a binary is a system one.
160     from PyInstaller.lib.macholib import util
161 
162     class MacExcludeList(object):
163         def search(self, libname):
164             return util.in_system_path(libname)
165 
166     exclude_list = MacExcludeList()

```

Cool, I was assuming there was something like that. Thanks

On 29 January 2015 at 09:08, Martin Zibricky notifications@github.com
wrote:

> look at file ./PyInstaller/depend/dylib.py, there is code like this:
> 
> 157 158 if is_darwin:159     # On Mac use macholib to decide if a binary is a system one.160     from PyInstaller.lib.macholib import util161 162     class MacExcludeList(object):163         def search(self, libname):164             return util.in_system_path(libname)165 166     exclude_list = MacExcludeList()
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/pyinstaller/pyinstaller/issues/621#issuecomment-71991588
> .


Resolving patch:
diff --git a/PyInstaller/hooks/hook-_tkinter.py b/PyInstaller/hooks/hook-_tkinter.py
--- a/PyInstaller/hooks/hook-_tkinter.py
+++ b/PyInstaller/hooks/hook-_tkinter.py
@@ -45,6 +45,58 @@ def _handle_broken_tk():
                 v['TIX_LIBRARY'] = abs_path
 
 
+def _warn_if_actvivetcl_or_teapot_install(tcl_root, tcltree):
+    """
+    Workaround ActiveTcl on OS X
+
+    PyInstaller does not package all requirements of ActiveTcl
+    (most notably teapot, which is not typically required). This
+    means packages built against ActiveTcl usually won't run on
+    non-host systems.
+
+    This method checks if ActiveTcl is being used, and if so logs
+    a warning if the problematic code is not commented out.
+
+    https://github.com/pyinstaller/pyinstaller/issues/621
+    """
+
+    from PyInstaller.lib.macholib import util
+    if util.in_system_path(tcl_root):
+        # system libraries do not experience this problem
+        return
+
+    # get the path to the 'init.tcl' script
+    try:
+        init_resource = [r[1] for r in tcltree if r[1].endswith('init.tcl')][0]
+    except IndexError:
+        # couldn't find the init script, return
+        return
+
+    mentions_activetcl = False
+    mentions_teapot = False
+    with open(init_resource, 'r') as init_file:
+        for line in init_file.readlines():
+            line = line.strip().lower()
+            if line.startswith('#'):
+                continue
+            if 'activetcl' in line:
+                mentions_activetcl = True
+            if 'teapot' in line:
+                mentions_teapot = True
+            if mentions_activetcl and mentions_teapot:
+                break
+
+    if mentions_activetcl and mentions_teapot:
+        logger.warning("""It seems you are using an ActiveTcl build of Tcl/Tk.\
+ This may not package correctly with PyInstaller.
+To fix the problem, please try commenting out all mentions of 'teapot' in:
+
+     %s
+
+See https://github.com/pyinstaller/pyinstaller/issues/621 for more information"""
+                       % init_resource)
+
+
 def _find_tk_darwin_frameworks(binaries):
     """
     Tcl and Tk are installed as Mac OS X Frameworks.
@@ -137,6 +189,11 @@ def _collect_tkfiles(mod):
 
     tcltree = Tree(tcl_root, os.path.join('_MEI', tcldir),
                    excludes=['demos', '*.lib', 'tclConfig.sh'])
+
+    if is_darwin:
+        # handle workaround for ActiveTcl on OS X
+        _warn_if_actvivetcl_or_teapot_install(tcl_root, tcltree)
+
     tktree = Tree(tk_root, os.path.join('_MEI', tkdir),
                   excludes=['demos', '*.lib', 'tkConfig.sh'])
     return (tcltree + tktree)
@@ -146,7 +203,7 @@ def hook(mod):
     # If not supported platform, skip TCL/TK detection.
     if not (is_win or is_darwin or is_unix):
         logger.info("... skipping TCL/TK detection on this platform (%s)",
-                sys.platform)
+                    sys.platform)
         return mod
 
     # Get the Tcl/Tk data files for bundling with executable.


Test patch: