How to Package a Python App using Nuitka

Learn how to package a Python app in this tutorial.

For the most part, once you’ve written your Python code, you simply deploy it to a server, install the environment, grab the dependencies and you’re done.

However, there are times when you may want to provide your app to someone else and don’t want the hassle of getting them setup with all the training around making sure they have Python on their machine and can run your app.

Perhaps it’s even because you don’t want the other party to have your precious source code. Python is an interpreted language, making this mostly unavoidable.

What if there were another way? … enter Nuitka!

What is Nuitka?

Nuitka can be understood as being a compiler for your python code. No, it technically isn’t a compiler. What it really does is convert your code to C and then compile that down to a binary for distribution.

Show me an example!

If you’re saying “This all sounds too good, don’t tell me.. Show Me!”, then get ready, because I plan to do just that!

Installing Nuitka to Package a Python App

As with most things Python, it’s quick to get straight to the point.

Head over to PyPi and search for Nuitka to make sure we have the latest version.

https://pypi.org/project/Nuitka/

N.B. Before performing the next step, make sure to setup a Python Virtual Environment so that all packages will be installed locally to this tutorial.

Continuing; This gives us an easy way to get going, pip install Nuitka.

mkdir -p ~/src/tutorials/nuitka_testing cd $_ virtualenv -p python3 venv . venv/bin/activate
Code language: Bash (bash)

Now run the pip install Nuitka:

$ pip install nuitka Collecting nuitka Downloading Nuitka-0.6.7.tar.gz (2.3 MB) |████████████████████████████████| 2.3 MB 1.6 MB/s Building wheels for collected packages: nuitka Building wheel for nuitka (setup.py) ... done Created wheel for nuitka: filename=Nuitka-0.6.7-py3-none-any.whl size=2117847 sha256=5ce6d2ef97e7fd72aa8980c8ba7d6cfdecaf6f7b8971fd397241070d8a0f6e2e Stored in directory: /Users/ao/Library/Caches/pip/wheels/60/7f/ef/8c1ef8cf2b509e25ead8f221725a8f95db6d7af0fc67565fde Successfully built nuitka Installing collected packages: nuitka Successfully installed nuitka-0.6.7
Code language: Bash (bash)

If you got stuff for some reason, read more about downloading Nuitka from the project’s website directly.

Testing out Nuitka

Nuitka is a Python module that we run against a project or python script.

This means we need a nice little test script to try it out.

Create a file called test1.py and enter the following code in it:

import string from random import * characters = string.ascii_letters + string.punctuation + string.digits password = "".join(choice(characters) for x in range(randint(12, 16))) print(password)
Code language: Python (python)

This will generate a unique strong password for us, between 12 and 16 characters.

If we run the script using python, we get output similar to this:

$ python test1.py KdcM[btk8JvW
Code language: Bash (bash)

Excellent!

So now let’s add Nuitka into the mix. Run the following:

python -m nuitka test1.py
Code language: Bash (bash)

This will take a moment and will not render any output to the screen.

If we execute a ls -lashp then we will see what has been created:

$ ls -lashp total 496 0 drwxr-xr-x 6 ao staff 192B ... ./ 0 drwxr-xr-x 4 ao staff 128B ... ../ 488 -rwxr-xr-x 1 ao staff 243K ... test1.bin 0 drwxr-xr-x 18 ao staff 576B ... test1.build/ 8 -rw-r--r-- 1 ao staff 195B ... test1.py 0 drwxr-xr-x 6 ao staff 192B ... venv/
Code language: Bash (bash)

We can now execute ./test1.bin directly and see the application run.

$ ./test1.bin 7'4^5`YNux5Z
Code language: Bash (bash)

Additional CLI arguments

While the default arguments work pretty well, if we want to add debug symbols, or package our application as a standalone app, there are a ton of additional arguments we can pass in.

Issue a python -m nuitka --help to see all the options.

$ python -m nuitka --help Usage: __main__.py [--module] [--run] [options] main_module.py Options: --version -h, --help --module --standalone --python-debug --python-flag=PYTHON_FLAGS --python-for-scons=PYTHON_SCONS --warn-implicit-exceptions --warn-unusual-code --assume-yes-for-downloads Control the inclusion of modules and packages: --include-package=PACKAGE --include-module=MODULE --include-plugin-directory=MODULE/PACKAGE --include-plugin-files=PATTERN Control the recursion into imported modules: --follow-stdlib, --recurse-stdlib --nofollow-imports, --recurse-none --follow-imports, --recurse-all --follow-import-to=MODULE/PACKAGE, --recurse-to=MODULE/PACKAGE --nofollow-import-to=MODULE/PACKAGE, --recurse-not-to=MODULE/PACKAGE Immediate execution after compilation: --run --debugger, --gdb --execute-with-pythonpath Dump options for internal tree: --xml Code generation choices: --full-compat --file-reference-choice=FILE_REFERENCE_MODE Output choices: -o FILENAME --output-dir=DIRECTORY --remove-output --no-pyi-file Debug features: --debug --unstripped --profile --graph --trace-execution --recompile-c-only --generate-c-only --experimental=EXPERIMENTAL Backend C compiler choice: --clang --mingw64 --msvc=MSVC -j N, --jobs=N --lto Tracing features: --show-scons --show-progress --show-memory --show-modules --verbose Windows specific controls: --windows-dependency-tool=DEPENDENCY_TOOL --windows-disable-console --windows-icon=ICON_PATH Plugin control: --plugin-enable=PLUGINS_ENABLED, --enable-plugin=PLUGINS_ENABLED --plugin-disable=PLUGINS_DISABLED, --disable-plugin=PLUGINS_DISABLED --plugin-no-detection --plugin-list --user-plugin=USER_PLUGINS
Code language: Bash (bash)

First let’s remove all the old stuff so that we can see what happens when a standalone build occurs.

$ rm -rf test1.bin test1.build $ ls -lashp total 8 0 drwxr-xr-x 4 ao staff 128B ... ./ 0 drwxr-xr-x 4 ao staff 128B ... ../ 8 -rw-r--r-- 1 ao staff 195B ... test1.py 0 drwxr-xr-x 6 ao staff 192B ... venv/
Code language: Bash (bash)

How to Build a standalone Python App

python -m nuitka --standalone test1.py
Code language: Bash (bash)

This takes a moment or two, but when it’s done we see our distribution created.

$ ls -lashp total 8 0 drwxr-xr-x 6 ao staff 192B ... ./ 0 drwxr-xr-x 4 ao staff 128B ... ../ 0 drwxr-xr-x 20 ao staff 640B ... test1.build/ 0 drwxr-xr-x 65 ao staff 2.0K ... test1.dist/ 8 -rw-r--r-- 1 ao staff 195B ... test1.py 0 drwxr-xr-x 6 ao staff 192B ... venv/
Code language: Bash (bash)

Let’s examine the build in more depth:

$ tree -L 2 . ├── test1.build │   ├── @sources.tmp │   ├── __constants.bin │   ├── __constants.c │   ├── __constants.o │   ├── __constants_data.c │   ├── __constants_data.o │   ├── __frozen.c │   ├── __frozen.o │   ├── __helpers.c │   ├── __helpers.h │   ├── __helpers.o │   ├── build_definitions.h │   ├── module.__main__.c │   ├── module.__main__.o │   ├── scons-report.txt │   └── static_src ├── test1.dist │   ├── Python │   ├── _asyncio.so │   ├── _bisect.so │   ├── _blake2.so │   ├── _bz2.so │   ├── _codecs_cn.so │   ├── _codecs_hk.so │   ├── _codecs_iso2022.so │   ├── _codecs_jp.so │   ├── _codecs_kr.so │   ├── _codecs_tw.so │   ├── _contextvars.so │   ├── _crypt.so │   ├── _csv.so │   ├── _ctypes.so │   ├── _curses.so │   ├── _curses_panel.so │   ├── _datetime.so │   ├── _dbm.so │   ├── _decimal.so │   ├── _elementtree.so │   ├── _gdbm.so │   ├── _hashlib.so │   ├── _heapq.so │   ├── _json.so │   ├── _lsprof.so │   ├── _lzma.so │   ├── _multibytecodec.so │   ├── _multiprocessing.so │   ├── _opcode.so │   ├── _pickle.so │   ├── _posixsubprocess.so │   ├── _queue.so │   ├── _random.so │   ├── _scproxy.so │   ├── _sha3.so │   ├── _socket.so │   ├── _sqlite3.so │   ├── _ssl.so │   ├── _struct.so │   ├── _tkinter.so │   ├── _uuid.so │   ├── array.so │   ├── audioop.so │   ├── binascii.so │   ├── fcntl.so │   ├── grp.so │   ├── libcrypto.1.1.dylib │   ├── libgdbm.6.dylib │   ├── liblzma.5.dylib │   ├── libreadline.8.dylib │   ├── libsqlite3.0.dylib │   ├── libssl.1.1.dylib │   ├── math.so │   ├── mmap.so │   ├── pyexpat.so │   ├── readline.so │   ├── select.so │   ├── site │   ├── termios.so │   ├── test1 │   ├── unicodedata.so │   └── zlib.so ├── test1.py └── venv ├── bin ├── include └── lib 8 directories, 78 files
Code language: Bash (bash)

From the above output, we see the build directory contains C language code, while the dist directory contains a self executable test1 application.

Closing remarks

I really like the idea of Nuitka and the potential it brings to the table.

Being able to compile Python code would be a fantastic edge to the Python community. Albeit if only ever used to package a Python app and to distribute it.

Tell me what you think.

Subscribe
Notify of
guest
5 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
ben
ben
1 year ago

I’m trying to build a standalone Windows exe from an old python program, CanFestival3/objdictgen/objdictedit.py, that uses python 2.x and wx:
“python27 -m nuitka –show-progress –show-modules –standalone –recurse-all objdictedit.py

I have mingw_64 installed, with the PATH pointing to it.. but during compilation (SingleExe.scons) it says ‘No version of Visual Studio compiler found”?!

Nuitka seems to compile MANY files before it gets to this stage, so not sure why Visual Studio compiler is suddenly necessary.. can it can be forced to use mingw_64 throughout? Is it possible it’s a 32bit vs 64bit issue?

ben
ben
1 year ago
Reply to  Andrew

I think I got a little further, however there’s still something fishy..

I reinstalled mingw64, python27_x64 on a new computer, and python correctly reports 64-bit, however gcc reports
“gcc (i686-win32-dwarf-rev0, Built by MinGW-W64 project) 8.1.0”

despite executing out of the path pointed to in “mingw-w64.bat”.

Perhaps no surprise, Nuitka fails to compile:
“Error, mismatch between Python binary (‘C:\Python\Python27_x64\python.exe’ -> ‘pei-x86-64’) and C compiler (‘C:\MinGW64\bin\gcc.exe’ -> ‘pei-i386’) arches”

I installed mingw-w64-install.exe , so not sure why the mingw install looks bad. Any ideas?

trackback

[…] Learn how to Package a Python app in this in-depth tutorial on using Nuitka. Build standalone Python applications fast! Read more […]

trackback

[…] Learn how to Package a Python app in this in-depth tutorial on using Nuitka. Build standalone Python applications fast! Read more […]