How to Distribute Commercial Python Applications

by Ville Laurikari on Monday, August 24, 2009

Most of us in the software business are not in a position to release our source code to the public. As much as I love free and open source software, I also understand it’s not part of all business models. Python is enjoying commercial success, but perhaps less so in cases where end-users actually install the software.

Distributing applications written in Python can be tricky, especially if you don’t want to distribute your source code. I set out to find out how exactly do you distribute a Python program, sans source code, on various operating systems. It turns out there are quite a few alternatives to consider.

In each alternative, the first step is to compile your code to bytecode (.pyc or .pyo). That’s what you’ll be shipping instead of the source code.

1. Using a preinstalled Python

Nowadays, many operating systems actually come with a sane Python interpreter preinstalled. Most Linux distributions ship with Python, and Mac OS X comes with Python, for example. Windows does not come with Python, nor do most commercial Unix systems.

Even if a preinstalled Python is available, it may not work for you. Maybe it’s an older version, and you want to use the latest and greatest. Another potential hurdle is that the Python version will be different on different operating systems. Mac OS X 10.5 (Leopard) comes with Python 2.5.1, Mac OS X 10.4 (Tiger) came with Python 2.3.5. Ubuntu 8.04 came with Python 2.5.2, Ubuntu 9.04 comes with Python 2.6.2… You get the point.

Sometimes modules are deprecated between versions and you’ll need to write stuff like this to suppress warnings:

Varying Python versions may or may not be a problem for you application. The example above is actually a fairly bening problem, and it’s easily solved. If you ever need to support both Python 3 and Python 2, you will have bigger problems than just deprecated modules.

2. Freezing tools

Freezing tools compile Python programs to executables. If you’re distributing a small number of executables, a freeze tool might be just what you need. There are quite a few of these around. Disclaimer: I haven’t used most of these tools. Caveat emptor.

py2app

cx_Freeze

cx_Freeze is similar to py2exe, but claims to be portable to both Windows and Unix systems.

bbfreeze

bbfreeze works on Windows and Unix, but not on OS X. Originally based on cx_Freeze.

Freeze

The Freeze tool ships with Python. It compiles Python programs to executables on Unix systems.

In my simple tests, it was able to handle a trivial hello world application pretty well, if we ignore the size of the resulting executable (several megabytes). Throwing anything more complex at it tended to fail miserably for various reasons. It seems to me that Freeze does not work very well out of the box.

PyInstaller

3. Bundling CPython with your application

CPython is the default, most widely used implementation of Python. It is written in C. When people talk about the Python interpreter, they mean CPython unless they explicitly mention IronPython (for .NET and SilverLight), Jython (for JVM), Stackless Python (for concurrent programming), or something like that.

Sometimes the freezing tools aren’t a good choice. They might lack platform coverage or could have other problems. If you’re distributing a large collection of separate programs the overhead of bundling the full Python interpreter to each program will be prohibitive. A good alternative is to bundle the Python interpreter with your application yourself. You gotta love some DIY in the software business, right?

The first step is to bundle the CPython interpreter with your application. That’s fairly straightforward if you know what you’re doing. You simply include the Python interpreter with your code.

The second step is to cherry-pick what you need from the Python standard libraries. Tools like snakefood or modulegraph can help you do that. From what I can tell, these tools aren’t perfect, so be prepared to manually fix the results or patch the tools.

A third and optional step is to customize your Python distribution. For example, you might inline all bytecode directly into your executables instead of separate files. Then you can override the loading functions to pass the code directly from memory instead of loading from disk.

4. My recommendations

My first choice is to use the Python interpreter which comes with your target platform, if it has one. Use distutils to install custom modules or extensions. If your application only supports Windows, use app2exe py2exe.

For complex scenarios, my recommendation is to go with bundling CPython yourself and steer clear of the freezers. If a freezing tool works for you, awesome. You just saved yourself from a lot of trouble. But if you need to cover a large number of platforms, or your application has special needs from the interpreter, then packaging CPython yourself seems to be the best way to go.

The added benefit of bundling CPython yourself is that you can customize the interpreter to your heart’s content. Not that you’ll really need to, but it sounds like fun. There’s nothing like making your own bubble wrap, complete with a custom hand-painted smiley face on each bubble.

Wow, I never know there are so many freezing tools. I’ve only used py2exe. I don’t think dealing with multiple 2.x Python versions would be a problem. Support both Python 3 and 2 is probably impossible in practice?

Thanks for this interesting article and for advising on your findings. Its much appreciated as I am currently looking to follow your advice, bundling CPython with some code I am creating for a client. Any further advice is welcome

Beautiful post! Exactly what I was looking for and I wholeheartedly agree with the whole ‘just bundle the interpreter’ solution. Any chance you wrote that article on how to do it? I haven’t managed to find any decent documentation for it