How to use PyInstaller to create Python executables – InfoWorld

Senior Writer, InfoWorld |
Python, powerful and versatile as it is, lacks a few key capabilities out of the box. For one, Python provides no native mechanism for compiling a Python program into a standalone executable package.
To be fair, the original use case for Python never called for standalone packages. Python programs have, by and large, been run in-place on systems where a copy of the Python interpreter lived. But the surging popularity of Python has created greater demand for running Python apps on systems with no installed Python runtime.
Several third parties have engineered solutions for deploying standalone Python apps. The most popular solution of the bunch, and the most mature, is PyInstaller. PyInstaller doesn’t make the process of packaging a Python app to go totally painless, but it goes a long way there.
In this article we’ll explore the basics of using PyInstaller including how PyInstaller works, how to use PyInstaller to create a standalone Python executable, how to fine-tune the Python executables you create, and how to avoid some of the common pitfalls that go with using PyInstaller.
PyInstaller is a Python package, installed with pip (pip install pyinstaller). PyInstaller can be installed in your default Python installation, but it’s best to create a virtual environment for the project you want to package and install PyInstaller there.
PyInstaller works by reading your Python program, analyzing all of the imports it makes, and bundling copies of those imports with your program. PyInstaller reads in your program from its entry point. For instance, if your program’s entry point is, you would run pyinstaller to perform the analysis. PyInstaller can detect and automatically package many common Python packages, like NumPy, but you might need to provide hints in some cases. (More on this later.)
After analyzing your code and discovering all of the libraries and modules it uses, PyInstaller then generates a “spec file.” A Python script with the extension .spec, this file includes details about how your Python app needs to be packed up. The first time you run PyInstaller on your app, PyInstaller will generate a spec file from scratch and populate it with some sane defaults. Don’t discard this file; it’s the key to refining a PyInstaller deployment!
Finally, PyInstaller attempts to produce an executable from the app, bundled with all its dependencies. When it’s finished, a subfolder named dist (by default; you are free to specify a different name) will appear in the project directory. This in turn contains a directory that is your bundled app — it has an .exe file to run, along with all of the libraries and other supplemental files required.
All you need to do to distribute your program, then, is package up this directory as a .zip file or some other bundle. The bundle will typically need to be extracted in a directory where the user has write permissions in order to run.
A .spec file is used to create an executable with PyInstaller. Note the namespaces listed in excludes. These will be left out of the created bundle to save space.
There’s a fair chance your first attempt to use PyInstaller to package an app won’t be completely successful.
To check whether your PyInstaller package works, navigate to the directory containing the bundled executable and run the .exe file there from the command line. If it fails to run, the errors you’ll see printed to the command line should provide a hint as to what’s wrong.
The most common reason a PyInstaller package fails is that PyInstaller failed to bundle a required file. Such missing files fall into a few categories:
The good news is that PyInstaller provides an easy way to deal with the above problems. The .spec file created by PyInstaller includes fields we can fill in to provide the details that PyInstaller missed.
Open the .spec file in a text editor and look for the definition of the Analysis object. Several of the parameters passed to Analysis are blank lists, but they can be edited to specify the missing details:
Keep in mind that any of the lists passed to Analysis can be programmatically generated earlier in the .spec file. After all, the .spec file is just a Python script by another name.
After you make changes to the .spec file, rerun PyInstaller to rebuild the package. However, from now on, be sure to pass the modified .spec file as the parameter (e.g. pyinstaller myapp.spec). Test the executable as before. If something is still broken, you can re-edit the .spec file and repeat the process until everything works.
Finally, when you’re satisfied everything works as intended, you might want to edit the .spec file to prevent your packaged app from presenting a command-line window when launched. In the EXE object settings in the .spec file, set console=False. Suppressing the console is useful if your app has a GUI and you don’t want a spurious command-line window leading users astray. Of course, don’t change this setting if your app requires a command line.
Once you have your app packaged with PyInstaller and running properly, the next thing you’ll likely want to do is slim it down a little. PyInstaller packages are not known for being svelte.
Because Python is a dynamic language, it’s difficult to predict just what will be needed at runtime by a given program. For that reason, when PyInstaller detects a package import, it includes everything in that package, whether or not it’s actually used at runtime by your program. 
Here’s the good news. PyInstaller includes a mechanism for selectively excluding entire packages, or individual namespaces within packages. For instance, let’s say your program imports package foo, which includes and foo.bip. If you know for a fact that your program only uses logic in, you can safely exclude foo.bip and save some space.
To do this, you use the excludes parameter passed to the Analysis object in the .spec file. You can pass a list of names — top-level modules, or dotted namespaces — to exclude from your package. For example, to exclude foo.bip, you would simply specify ['foo.bip'].
The deliverable directory created by PyInstaller. Because of the large number of files in the directory, a third-party installer project like NSIS may be used to pack up the directory into a self-extracting archive, and automatically create a shortcut to the executable.
One common exclusion you can make is tkinter, the Python library for creating simple cross-platform graphical user interfaces. By default, tkinter and all of its support files are packed with a PyInstaller project. If you’re not using tkinter in your project, you can exclude it by adding 'tkinter' to the excludes list. Omitting tkinter will reduce the size of the package by around 7 MB.
Another common exclusion is test suites. If a package your program imports has a test suite, the test suite could end up being included in your PyInstaller package. Unless you actually run the test suite in your deployed program, you can safely exclude it.
Bear in mind that packages created using exclusions should be thoroughly tested before being used. If you end up excluding functionality that is used in some future scenario you didn’t anticipate, your app will break.
Serdar Yegulalp is a senior writer at InfoWorld, focused on machine learning, containerization, devops, the Python ecosystem, and periodic reviews.
Copyright © 2020 IDG Communications, Inc.
Copyright © 2022 IDG Communications, Inc.


Leave a Comment