Distributing python programs ala go, as a single file

I am glad to see discussions about the problem of distributing python programs in the wild. A recent post by Glyph articulates the main issues better than I could. The developers vs end-users focus is indeed critical, as is making the platform an implementation detail.

There is one solution that Glyph did not mention, the freeze tool in python itself. While not for the faint of the heart, it allows building a single, self-contained executable. Since the process is not really documented, I thought I would do it here.

Setting up a statically linked python

The freeze tool is not installed by default, so you need to get it from the sources, e.g. one of the source tarball. You also need to build python statically, which is itself a bit of an adventure.

I prepared a special build of static python on OS X which statically link sqlite (3.8.11) and ssl (1.0.2d), both from homebrew.

Building a single-file, hello world binary

Let’s say you have a script hello.py with the following content:

print("hello world")

To freeze it, simply do as follows:

<static-python>/bin/python <static-python>/lib/python2.7/freeze/freeze.py hello.py
make

You should now have an executable called hello of approximately 7-8 MB. This binary should be relatively portable across machines, although in this case I built the binary on Yosemite, so I am not sure whether it would work on older OS X versions.

How does it work ?

The freeze tool works by bytecompiling every dependent module, and creating a corresponding .c file containing the bytecode as a string. Every module is then statically linked into a new executable.

Limitations

I have used this process successfully to build non trivial applications that depend on dozens of libraries. If you want a single executable, the main limitation is no C extension requirement.

More generally, the main limitations are:

  1. you need to statically build python
  2. you have to use unix
  3. you are not depending on C extensions
  4. none of your dependency uses shenanigans for package data or import

1 and 2 are linked. There is no reason why it should not work on windows, but statically linking python on windows is even less supported than doing it on unix. It would be nice for python itself to support static builds better.

3 is one of the feature that has been solved over and over by the multiple freezer tools. It would be nice to get a minimal, well-written library solving this problem. Alternatively, a way to load C extensions from within a file would be even better, but not every platform can do this.

4 is actually the main issue in practice, it would be nice for good solution here. Something like pkg_resources, but more hackable/tested.

I would argue that the pieces for a better deployment story in python are there: what is needed is taking the existing pieces to build a cohesive solution.