Friday, May 5, 2017

How to package and distribute standalone python project on Windows

UPDATE: thanks to lots of contributed hooks, I would choose pyinstaller these days

 I recently had a need to distribute an app written in python as standalone on windows machines. Usual way to do it is to use py2exe(or similar) to generate standalone exe from your python script and then use Inno Setup or NSIS to write an installer for it. As it turned out, py2exe, nuitka and pyinstall could not track all the dependencies of the project automatically(i..e. some conditional imports, etc) and even after I explicitly supplied hooks and helpers to fix that, still resulting exe was exiting silently. So to solve that, I approached it from the other end and started looking into ways to distribute the app with everything needed included. And, luckily, there are plenty of projects that supply portable python for windows:
From this list, Anaconda and WinPython looked like the most promising ones. And both of them supplied mini versions called Miniconda and WinPythonZero, that provide the most essential packages and tools for your project. For no particular reason, I chose Miniconda.

Preparing your project

Installing Miniconda couldn't be easier. You just download the installer and install it in your project directory. After that, you just go there and simply call

python -m pip install yourproject

If your project is not available on pypi, you might want to build sdist from it and then call pip to install it

python -m pip install -r requirements.txt
python setup.py sdist
python -m pip install dist\myproject-0.1.tar.gz

And that's all! Now, you have a fully functional portable python which you can use for your project.

Creating installer

I chose Inno Setup for my installer. There are plenty of tutorials on how to write an installer with it, so i'm not going to talk about it here. Inno Setup also provides a GUI wizard which you can use to generate an installer for your project, so it really couldn't be easier. Just make sure to specify the directory with your project and proper flags:

Source: "{#MyAppDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

You might also want your installer to modify PATH, so you might find modpath.iss useful:

[Tasks]
Name: modifypath
Description: Adds dvc's application directory to environmental path
Another feature that you might want to have your installer handled is adding CreateSymLink permissions and to do that, take a look at this brilliant post Adding Permission for creating Symlink using PowerShell. Just copy that script into script.iss and call it in [Task] section of your installer:
[Tasks]
Name: modifypath; Description: Adds dvc's application directory to environmental path;
Name: addsymlinkpermissions; Description: Add permission for creating symbolic links;
Now just call:
iscc setup.iss
and here you have yourself yourproject.exe installer that you can distribute to users that don't even know what Python is.