Python and pandas are a great team for data science. But what if you need to deliver a script to a Windows client who does not have Python installed?

This is a quick tutorial for converting Python scripts (.py) to standalone, one-file Windows executables (.exe).

1
pyinstaller -F my_script.py

Get a 20Mb exe file that is fully functional on every Windows machine!

tl;dr: If you need one standalone file that works on machines without a Python distrubution go for Pyinstaller. Just be aware of some overhead when importing certain libraries.

Python to exe

My aim was to build an .exe-file of a Python script involving pandas with the following specs:

  • standalone
  • one file, i.e. one .exe-file with everything included
  • no installer for client setup
  • should work “plug-’n’-play”
  • as small as possible

In the end I managed to get exactly what I wanted but it was a long and troublesome way to go.

There are plenty of python-to-exe scripts/programs/compilers out there but after testing the most used ones only Pyinstaller satisfied my needs. None of the other fulfilled all the above-listed criteria!

Pyinstaller & Pandas

I tried Pyinstaller some years ago and remembered, that somehow I got it working. The big issue with Pyinstaller is that some Python packages do not work out-of-the-box and require some overhead, particular installations, dependencies or settings which can be quite troublesome to figure out.

Out there are tons of outdated docs, stackoverflow questions, guidelines and medium articles that simply didn’t help. Only a few pointed to the right direction.

Some banana skins

Let’s get to the point. Some Donts first:

  1. Don’t use conda for virtual env
  2. Don’t use conda for installing libraries

Workflow

1. Set up a virtual environment

You simply cannot skip this step as otherwise all your global Python libraries you ever installed will end up in the .exe which can lead to a couple of GB.

Also, do not use conda! I tried with conda envs but simply failed. The trick here is to go for virtualenv and only use pip to install packages.

Install virtualenv and create the environment in your project folder.

1
2
pip install virtualenv
virtualenv venv 

Activate it!

For Linux

1
source ./venv/bin/activate

For Windows

1
"venv/scripts/activate"

The cool thing with venv is, that you could just provide the venv folder to anyone and Python will all it’s dependencies simply works. The folder has around 170Mb in my case.

2. Install your packages

Just install all you need but do not use conda! Particularly not to install pandas! Just stick to pip.

1
2
3
pip install pandas
pip install openpyxl 
pip install pyinstaller

Note that openpyxl is needed for some pandas functions to work.

3. Create the spec-file

Pyinstaller uses a spec-file to define the settings. You need to create it first and change the values.

1
pyi-makespec --onefile my_script.py

This will create a my_script.spec file.

Note that the command pyi-makespec is installed with pyinstaller.

4. Download upx for compression

Download the latest upx version to get file compression. In my case it helped to save 15%-20% file size. Save upx.exe to your working directory.

5. Get the settings right

Open the spec-file and use this boilerplate settings. Just alter the directory and the name of the script.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

a = Analysis(['my_script.py'],
             pathex=['C:\\Users\\your_directory'],
             binaries=[],
             datas=[],
             hiddenimports=['pandas'],
             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,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='my_script',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=True,
          icon='favicon.ico') # if you need it, provide it in the dir

6. Compile!

The last part is the easiest. Just tell pyinstaller to create the standalone exe.

1
pyinstaller -F my_script.spec

Done! This shouldn’t take longer than 15 secs and outputs a <20Mb exe file in the dist folder. It works on every Windows machine without Python installation!

7. Changing the GUI icon

If you want to change the custom icon in the GUI you must declare it with an absolute path in your .py-file. A relative path causes an execution error after compiling.

1
2
3
4
root= tk.Tk()
root.title("Your program's title")
root.geometry('')
root.iconbitmap("C:/Users/.../.../favicon.ico")

Include the icon in the binary with

1
pyinstaller -F my_script.spec --add-binary "favicon.ico;."  

Now everything should look neat and perfect!

Troubleshooting

1. “All packages are already installed!”

A virtual env error. Maybe you used powershell or an outdated shell. On Windows, go with normal cmd (WinKey + R, type cmd and hit enter). If you did everything right, after

1
"venv/scripts/activate"

you should see

1
(venv) C:\Users\yourname\yourdir> 

2. “It compiles for eternity!”

Most likely you did not install pyinstaller in your virtual environment causing your pyinstaller in your global environment (if installed before) to compile all of your global packages. If you tend to install new packages for testing purposes in your global env this can easily lead to few gb and hence takes a lot of time. Just make sure to activate your virtual env first, install pyinstaller and retry.

3. “It just doesn’t work!”

Delete your env and start from scratch. Make sure your virtual env is set up correctly and activated, you did use pip (and not conda!) for installation and installed pyinstaller.