Building a packaged python executable in Windows with py2exe

After spending considerable time on getting a working Windows setup for Pronterface a random person on IRC convinced me to document it for others who might have the same problem. This is a guide to creating a self-contained windows executable from a python script. This is useful for distributing a script to users who do not have python and/or all necessary libraries installed. An executable made this way also enables 32-bit only Python libraries to run correctly on 64-bit Windows installations if built on a 32-bit Windows machine. This guide assumes you already have a python install and a functional program you intend to distribute. The user will not need administrator priviledges on their machine to use the resulting executable.

Prepare your program

Make sure your program runs correctly. It is much more difficult to debug an already exe-ified program.

Install py2exe.

You can get py2exe from the py2exe sourceforge page

Create a setup.py file for your project.

This file describes the structure of your project to py2exe so it knows which files to include. Put it in the same directory as your script. Here is an example setup.py:

from distutils.core import setup
import py2exe

setup(
    windows=[{'script':'filename.py'}],
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

Let’s go through it line by line:

from distutils.core import setup
import py2exe

This imports the basic setup function from distutils, which checks the dependencies of your script and determines which other modules mush be included for your script to function.

setup(
    windows=[{'script':'filename.py'}],
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

Here, we call the setup function and give it a number of options. The “windows” parameter defines which script file corresponds to a windowed application. In this case, it’s set to “filename.py”. Substitute the name of the script you are building. Windows makes a distinction between console and windowed applications. If your application needs a console (even if it does GUI stuff as well) you might want to define it as a console application rather than a windowed one. To do this, replace windows with console:

setup(
    console=[{'script':'filename.py'}],
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

You can also have more than one script, and thus generate more than one executable:

setup(
    windows=[{'script':'filename1.py'},{'script':'filename2.py'}],
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

You can also combine any number of windowed and console applications:

setup(
    windows=[{'script':'w1.py'},{'script':'w2.py'}],
    console=[{'script':'c1.py'},{'script':'c2.py'}],
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

You might have noticed the entries in windows and console are dictionaries. You can add extra options in there that affect each individual file. One particularly useful one is adding an icon to the executable.

setup(
    windows=[{'script':'filename.py','icon_resources':[(1,'iconname.ico')] } ],
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

You can add any number of icons, but only the first one will be displayed by default. The “options” parameter allows us to pass options directly to py2exe. Let’s look at the options there in more detail:

    options={'py2exe':{'bundle_files':1,'compressed':1}}

bundle_files tells py2exe to collect all the files that the script uses together into a bundle. That way you have to distribute fewer files. All the python libraries and modules that your script uses will be collected into a single file called library.zip by default. Compressed tells py2exe to compress the file so it takes up less space. This is nice when you have downloadable files. When you run the executable, it will automagically access the files it needs from within the compressed bundle without having to unpack them anywhere. You can specify the name of the file where all the libraries and modules are stored, like this:

setup(
    windows=[{'script':'filename.py'}],
    zipfile='blob.zip',
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

Now instead of library.zip all the libraries and modules will be stored in blob.zip. If you only have one executable, you can do one better. You can include all the libraries and modules directly in the executable itself. You can do this with multiple executables also, but then each one will have its own copy of all the libraries, and will be much larger as a result. To do this, use:

setup(
    windows=[{'script':'filename.py'}],
    zipfile=None,
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

This way, you will only have a single executable with no other files.

Add supporting files

Suppose you have some supporting files you want to include with your script. These can include images, libraries python does not know about, sounds, other content files, or anything else that is not part of the code your program runs. You can tell py2exe to copy those over to the same directory where your exe gets generated. Unfortunately, additional files cannot be easily included in the bundle file, because they must be unpacked somewhere before the script can access them. Here is how you copy them over:

dfiles = [(".",["f1.dat"]),("logs",[]),('img', ["i1.png", "i2.png"]), ("snd",["s1.wav","s2.wav"])]
    
setup(
    windows=[{'script':'filename.py'}],
    data_files=dfiles,
    options={'py2exe':{'bundle_files':1,'compressed':1}}
)

This will copy the file “f1.dat” to the base directory of the executable, create an empty folder called “logs”, create a folder called “img” and copy i1.png and i2.png into it, and then create a folder called “snd” and copy s1.wav and s2.wav into it.

Create a batch file for convenience

In the folder where your setup.py script is, create a text file called setup.bat with the following content:

C:\\python27\python setup.py py2exe -v
pause

Substitute the path to your Python version. When you double-click this file, it will run py2exe and show you the output. The -v makes py2exe spit out extra information about what it’s doing. The pause command makes sure that Windows does not hide the output window as soon as it’s done, giving you time to read the output.

Build your executable

Double-click the setup.bat file. It will run py2exe, which will then create a build directory. The files needed by your script will be copied into that directory. Once they are all there, they will then be bundled and the bundle and executable files, as well as all the supporting files you specified, will be copied into a directory called “dist”. The contents of this directory are what you have to distribute.

Add necessary DLL files

When you run the setup.bat file, it will show you a list of DLL files needed for your program to run. If any of those include a file that starts with msvcp or msvcr then the users of your program will need to have the same one installed. It is a good idea to copy these files into your dist directory before you distribute it. Most of the other files are included with Windows by default and do not need to be copied.

Test your program

Go into the dist directory where your executable now lives, and try to run it. See whether everything works correctly. Copy the dist directory to the target machine and test it there.

Additional resources

The py2exe page has a lot of additional information about more advanced use cases.



blog comments powered by Disqus

Published

19 December 2012

Tags