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:
Code language: Bash (bash)$ python test1.py KdcM[btk8JvW
Excellent!
So now let’s add Nuitka into the mix. Run the following:
Code language: Bash (bash)python -m nuitka test1.py
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:
Code language: Bash (bash)$ 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/
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.
Code language: Bash (bash)$ 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/
How to Build a standalone Python App
Code language: Bash (bash)python -m nuitka --standalone test1.py
This takes a moment or two, but when it’s done we see our distribution created.
Code language: Bash (bash)$ 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/
Let’s examine the build in more depth:
Code language: Bash (bash)$ 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
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.
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?
Hi Ben,
I believe compiling required SCons on Windows.
“SCons will not be able out of the box to compile from the Windows Command Prompt (cmd.exe) because SCons and Visual C++ compiler will not be able to locate environment variables and executables they need for compilation.” [source]
As the Nuitka flow is as follows:
This means that you need to have the relevant C/C++ compiler setup on your local machine.
The Visual Studio Compiler is necessary to make sure these steps can be completed.
You might have to get Scons and VS Compiler on Windows.
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?
[…] Learn how to Package a Python app in this in-depth tutorial on using Nuitka. Build standalone Python applications fast! Read more […]
[…] Learn how to Package a Python app in this in-depth tutorial on using Nuitka. Build standalone Python applications fast! Read more […]