Sitemap

Running Installer
Python Archives
Standalones
The MEInc.Dist package
Frequently Asked Questions
Extensions

It doesn't work!

The most likely cause is that some Python code is doing an import in a manner that slips right by Modulefinder, so despite using lots of CPU and memory, we never included that dependency. Sigh. A waste is a terrible thing to mind.

Check Builder.log. Is the dependency mentioned? If so, you have a very advanced or bizarre error! Check with your doctor, your clergyman, or me (in that order ).

If not, you need to force Builder to include the module (or dll). This is usually just a matter of sticking it on the misc = option for the appropriate section, or doing explicit imports in your code before the guilty party does his imports.

For example, if you use shelve you will hit 2 problems. First, shelve uses anydbm which uses __import__ to get to one of the db implementations. You can force dbhash into the config, or put import dbhash at the top of your code.

The second problem is that cPickle imports a couple modules, and those imports are done from C, so they won't go through our import hooks. In this case, we have to do explicit imports in the code, because we need to get them (via our hooks) into sys.modules before cPickle starts. (Once they're in sys.modules, any method of importing will just grab a reference to the thing in sys.modules.) The required modules are string and copy_reg.

The slow, plodding, painful way through this kind of problem is to:

  1. Make sure userunw = 0.
  2. Set debug = 1.
  3. Build.
  4. Try it with -v on the command line, in a DOS box with a big buffer.
  5. Between the details that spit out from the debug version of Run and the verbose imports from Python, you should be able to figure out who is calling for what and where.
  6. Fix the config file to force what's inclusion, or explicitly import the thing in your top level script.
  7. Go back to step 1.

It's usually faster to fix it by looking at the source, but sometimes you can't figure it out!

Note: With 3g, builder.log now includes modulefinder's report of missing imported names. When you write from A import B, modulefinder doesn't know if B is a module or an object. If it can't find B as a module, it will list it in this report. Most of the stuff listed there will be objects (classes, functions, etc.), but sometimes there's an import hack going on, and this will help you track it down.

The script I'm working with does run time __import__ (or uses execfile or some other perversion), so there *no*way* I can keep trying things, and adding modules one by one!

Oh, freezing IDLE, huh?

OK. Punt. We know IDLE depends on code in Python/Tools/idle. And on the Tk stuff. Oh yeah, and on _tkinter. So we force including Tk, _tkinter, lib-tk and the whole idle directory:

[MYINSTALL]
type= INSTALL
name= Install_spython.exe
bindepends= spythontk.py
zlib = INSTALLZLIB
misc= TK, MYSTANDALONE, _tkinter
debug = 0
includes = tcl80.dll, tk80.dll
excludes = PyWinTypes15.dll, win32api

[MYSTANDALONE]
type= STANDALONE
name= spythontk.exe
script= RunTK.py, spythontk.py
zlib = APPZLIB, IDLEPYZ
userunw = 0
support = 0
debug = 0

[APPZLIB]
name= spythontk.pyz
dependencies= spythontk.py
directories = D:/Programs/Python/Lib
packages = lib-tk
excludes= dospath, posixpath, macpath

[INSTALLZLIB]
name = installzlib.pyz
dependencies = installutils
includes = installutils
excludes = dospath, posixpath, macpath

[IDLEPYZ]
name = idle.pyz
directories = D:/Programs/Python/Tools/idle

Then we build with:
..\..\Builder.py spythontk.cfg ../BuildTk/tk.cfg

Huh? That doesn't answer my question!

Sure it does! OK. Too much going on. IDLE is a very extreme example, because Modulefinder fails completely on idle.py. It doesn't even detect that IDLE uses Tk! So I'll break it down.

I force _tkinter and the tcl and tk dlls into the INSTALL section, because they're binary (_tkinter.pyd). Any pure Python is going to go into a .pyz, and the .pyz's will be packed into the STANDALONE.

We need everything from Tools/idle. We could just tell our APPZLIB to include everything from /idle, by adding a directory= option. But I chose instead to make Tools/idle it's own .pyz, and have 2 .pyz's in the standalone. You can go either way. The only "hard" reason for doing it my way is that you can avoid nameclashes within packages if you give each it's own .pyz.

I chose the opposite approach for lib-tk, only because lib-tk is a part of the standard lib.

I also have to include the whole TK machinery. For that, I'm using the extensions mechanism. That is, the BuildTK project creates a CArchive with the basics of Tcl/Tk packed up in it. When you invoke Builder with more than one .cfg file on the command line, it sees them all as one big config. So the misc= TK, in the INSTALL section ties to a section named TK in BuildTK/tk.cfg.

The install script generated when I build will ensure that the TK CArchive tucked into the INSTALL exe will get unpacked, (this happens because Builder "asks" the tk.cfg file if it has instructions on how to install itself). So when the user installs, your app will get a private little Tcl/Tk installation.

There's one more trick, though. To run with this private installation of Tcl/Tk, we need to trick _tkinter into finding it. The BuildTk directory has a tiny script that does that, by manipulating the environment. We stick that into the script= option of the STANDALONE, before the main script.

As I said, IDLE is an extreme case; you shouldn't ever have to go this far!

I get a traceback when building, saying that "archmodules.modules" doesn't exist!

This problem could manifest in a number of different ways, but it would usually show up at Build time, not runtime.

The problem occurs when Builder goes looking for something of his and finds something of yours (with the same name) instead.

In beta 3e, the window has been tightened down a lot. But there's still the possibility of a name clash, (most likely something of mine would hide something of yours). I moved to a package structure in beta 3 to avoid most of these problems. But the things in the support directory could still be a problem. Inside a self-extracting exe, these pretty much need to be top-level modules, or the embedding tricks get really nasty.

The best solution for me is for you to acknowledge my right of eminent domain over your namespaces, and send me shopping bags full of $20 bills on a monthly basis. If for some reason, this doesn't appeal to you, the best I can advise is to rename anything that conflicts with something in the support directory.

I get a syntax error when I run my standalone!

I haven't figured this one out (and I haven't heard about it in quite awhile, so maybe it's fixed!). It happens on some scripts when everything happens at the top level:


            import whatsit
            whatsit.do_this()
            whatsit.do_that()
            
            

The solution seems to be to restructure your script:


            import whatsit
            def main()
            
              whatsit.do_this()
              whatsit.do_that()
            
            main()
            

In normal Python, recursive imports can cause this type of problem, but I'm not sure why this happens with a top level script.

It works fine on my machine, but fails with some message about "ordinal number " on the user's machine.

Your user probably has out-of-date .dlls. The most likely problem would be MFC42.DLL. Installer is not in the business of updating MicroSoft .dlls. The easiest solution is probably to have your user install a recent version of IE, (they can de-install later). This updates all the system dlls that Python uses (and then some).

I used Standalone with the -tk option. After installing on the user's machine, the exe doesn't work - nothing happens at all!

When you use -tk, you get a file named tk.pkg in the collect directory. Once that file is on the user's machine, it needs to get unpacked into a tree below the user's installation directory. Then at runtime, you need to set some environment variables so Tkinter can find this private TCL/TK installation.

If you run Simple, Builder will generate all the code to do this. So you could run Simple, and then cut and paste from the gen_install script. Also notice the runTK.py script that gets stuffed (before your main script) into the exe.

Also see the section on TK and other extensions.

My Python script is a COM client. How do I get it to work?

First, by default all of the top level scripts (Simple, Standalone and Freeze) will exclude PyWinTypes15.dll and win32api. (This is because any use of os will find these as dependencies, when they're not normally needed.) Remove the exclude of PyWinTypes15.dll.

Second, if you have an import pywintypes or a import pythoncom, you'll need to fool the import hacks. These hacks use the registry to translate the release neutral names (pywintypes and pythoncom) into the release specific names (pywintypes15 and pythcom15). Here, courtesy of the guilty party (Mark Hammond), is some hack-around code you should use at the top of your main script. Once it has imported the modules, subsequent imports in other modules will succeed.

Late breaking new: with the latest build of the Python 2.0 installer, the following is no longer needed! Thanks to code from Barry Scott, archive_rt.py will (when an import fails) try again after tacking sys.version onto the end of the module name. So import pywintypes will find PyWinTypes20.dll! (The notes about gencache still apply, however.)


            import win32api, imp, sys
             
            def magic_import(modulename, filename):
              # win32 can find the DLL name.
              h = win32api.LoadLibrary(filename)
              found = win32api.GetModuleFileName(h)
              # Python can load the module
              mod = imp.load_module(modulename, None, found, ('.dll', 'rb', imp.C_EXTENSION))
              # inject it into the global module list.
              sys.modules[modulename] = mod
              # And finally inject it into the namespace.
              globals()[modulename] = mod
              win32api.FreeLibrary(h)
             
            try:
              import pywintypes
            except ImportError:
              magic_import("pywintypes", "pywintypes15.dll") 
            try:
              import pythoncom
            except ImportError:
              magic_import("pythoncom", "pythoncom15.dll") 
            

Finally, there is no way to use gencache from a "frozen" script. However, you should be able to determine which generated module gencache is using, and import that directly. Installer should then pick it up without problem.

As for COM servers written in Python - forget it (or be prepared to do a lot of research)! COM server support depends heavily on the registry and requires not only installing your script, but also on installing a bunch of plumbing to find and run your script (as an in-process dll, or out-of-process exe, etc.).

copyright 1999-2001
McMillan Enterprises, Inc.