PyInstaller#

Quick guide to build a standalone application from a python script with pyinstaller.

Install pyinstaller:

pip install pyinstaller

Run pyinstaller:

pyinstaller yourprogram.py

That’s it!

The bundled application will be generated in a subdirectory called dist.

Warning

It is not recommanded to use the flag --onefile to bundle the application in a single .exe file. In fact, when exectued, the application will be first extracted in a temporary directoy somewhere in the user’s profile and that takes time.


During the process, pyinstaller create a directory called build which can be deleted thereafter.

A .spec file is equally generated. This file contain all information to build the application and can be tweaked for more grained results (files to include, exclude, …).

Normally, all parameters can be passed as arguments when calling pyinstaller. Thoses parameter will be refleced in the .spec file. Therefore, the second time, one can call pyinstaller directly with the .spec file as follow:

pyinstaller yourprogram.spec

Note

One may think that the Spec file is a temporary file and should not be versionned and provided along the application code. But it is most easier to store the build information in this file and to provide this latter with the application code for subsequent builds.

This file is treated as a python script and one can import python module and write more complex code within this file.

For example, with shutil module we can copy folders and files in the dist folder when the build finish:

import shutil
shutil.copyfile('filetocopy.ini', '{0}/{1}'.format(DISTPATH, 'filetocopy.ini'))

DISTPATH is a global variable available in the Spec file. All global variables available are listed in the official documentation

Access the application folder#

When accessing files located within the application folder, one must use the code bellow to find the current execution folder of the application that works both in bundled or live code:

if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
    # running as bundle (aka frozen)
    app_dir = sys._MEIPASS
else:
    # running live
    app_dir = os.path.dirname(os.path.abspath(__file__))

or more simply:

app_dir = getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__)))

Then, to read a particular file within the application folder, use app_dir as follow for example:

config_file = os.path.abspath(os.path.join(app_dir, 'app_config.ini'))

Official doc (Using __file__)

Complete example of Spec file#

In this example, pyinstaller couldn’t find and copy the DLL files of the pyzbar module. We added them manually.

# -*- mode: python ; coding: utf-8 -*-
from os import path, environ
import shutil

block_cipher = None

project_name = 'QRCodeScanner'
project_folder = path.dirname(path.abspath('.'))
venv_folder = environ['VIRTUAL_ENV']
pyzbar_folder = path.join(venv_folder, 'Lib\site-packages\pyzbar')

# For some reason pyzbar binaries files aren't find automatically.
# We add them manually here.

a = Analysis(['main.py'],
            pathex=[project_folder],
            binaries=[(path.join(pyzbar_folder, 'libzbar-64.dll'), '.'),
                    (path.join(pyzbar_folder, 'libiconv.dll'), '.')],
            datas=[],
            hiddenimports=[],
            hookspath=[],
            runtime_hooks=[],
            excludes=[],
            win_no_prefer_redirects=False,
            win_private_assemblies=False,
            cipher=block_cipher,
            noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
        cipher=block_cipher)
exe = EXE(pyz,
        a.scripts,
        [],
        exclude_binaries=True,
        name=project_name,
        debug=False,
        bootloader_ignore_signals=False,
        strip=False,
        upx=True,
        console=True)
coll = COLLECT(exe,
            a.binaries,
            a.zipfiles,
            a.datas,
            strip=False,
            upx=True,
            upx_exclude=[],
            name=project_name)

shutil.copyfile('app_config.ini', '{0}/{1}/app_config.ini'.format(DISTPATH, project_name))

Python Pyinstaller