Package a Python application with its dependencies
Web agency » Digital news » Package a Python application with its dependencies

Package a Python application with its dependencies

Some time ago I developed an application in Python. To set the context, it was about creating an executable able to deploy a docker-compose.yml by applying business rules. In order not to rewrite everything, I created a project in Python to be able to use the library docker-compose which is also in Python. Once functional, my application had to be built in a single file. My goal was to generate a binary file including all its dependencies (a bit like Golang).

Attention ! Generating a binary file does not exempt you from installing Python on your machine. The binary file is not a compiled application but just a package.

Architecture

First you will create a folder foobar in your project. This will contain all your work.

1
2
3
4
5
6
project
|_ __main__.py
|_ foobar
|_ __init__.py
|_ __main__.py
|_cli.py

If you have come across this article, it means that you know Python at least as well as I do and therefore know how to install dependencies globally or in a virtualenv.

Personally, I carry out my developments in a Docker-container so I install my dependencies as globals.

Here's how to install a dependency.

1
$ pip install docopt

Then you can work on your file cli.py. Here is an example with the use of the library docpt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# project/foobar/cli.py
from docpt import docpt
version= "1.0.0"
help= « » »Foobar
Usage:
foobar version
Options:
-h --help Display help
Foobar is a fake open-source project developed by Baptiste Donaux.
"" "
def (the eye and the hand)():
arguments = docopt(help)
if arguments[" version "]:
print (“foobar version”, version)

A file to launch the developing application will be needed (in my case __init__.py is an empty but required file).

1
2
3
4
5
6
# project/foobar/__main__.py
from . import Cli
if __name__ == " __hand__ ":
cli.main()

To build a binary package, you will need an entry point (project/__main__.py).

1
2
3
4
5
6
# project/__main__.py
from foobar import Cli
if __name__ == " __hand__ ":
cli.main()

Now you can run your application easily.

1
2
$ python ./foobar/version
('foobar version', '1.0.0')

Build a static binary

Workflow

From a clean project (without dependency…), here are the steps that will be carried out.

  • To create a virtualenv and activate it
  • Install dependencies and exit virtualenv
  • Delete files and folders that are present in the virtualenv/lib/python2.7/sites-packages that correspond to a folder pip, a cache folder, to compiled files or information files.
  • Create a folder to build the final file
  • Copy dependencies, sources and input file to the newly created folder.
  • Create a compressed folder (.zip) of the contents of the build folder.
  • Create the “binary” file with a header to specify the environment and append to this file the contents of the compressed folder.

Hardcore

Here are the technical steps to follow.

1
2
3
4
5
6
7
8
9
10
11
12
$ virtualenv dependencies
$. ./dependencies/bin/activate
$ pip install docopt
$ deactivate
$ rm -rf $(find ./dependencies/lib/python2.7/site-packages -print | egrep '(/pip/)|(__pycache__)|(.(pyc|dist-info|so)$)')
$ mkdir build
$ cp -R ./dependencies/lib/python2.7/site-packages/* ./foobar ./__main__.py ./build/
$ cd ./build
$ zip -r ../release.zip *
$ threw out '#!/usr/bin/env python' > ../release
$ cat ../release.zip >> ../release
$ chmod +x ../release

Conclusion

Now you can easily run your binary. It embeds all its context.

1
2
$ ./release version
('foobar version', '1.0.0')

This solution was very convenient for me to deploy an application with a single file. You can download the sample files archive to reproduce my demonstration.

★ ★ ★ ★ ★