--- tags: notes, RT_blog, python --- # Python `.py` to `.exe` using `pyinstaller` There are a bunch of tutorials showing how to pack your `.py` code to an executable(such as `.exe` in windows). However, most of them just show one or two command packing the executable in the simplest way. In this note, I will also show you the basic, the simplest way to pack your executable and I will also show you how to deal with the annoying import errors. I believe that [PyPi page of pyinstaller](https://pypi.org/project/pyinstaller/) helps alot. ## Install `pyinstaller` If you have pip installed: ```bash pip install pyinstaller ``` If `pip` is not available in your enviroment, then you can download the archive and unzip it. Excute following with administration access: ```bash python setup.py install ``` To verify installation: ```bash pyinstaller --version ``` Ref: [How to Install PyInstaller](https://pyinstaller.readthedocs.io/en/stable/installation.html) ## Pack your executable `pyinstaller` follows the pattern: ```bash pyinstaller [options] script.py [script2.py …] | specfile ``` For a simplest case: ```bash pyinstaller main.py ``` This command does: - Create `./build/`, where logging and middle biniary files located. - Create `./dist/`, where your executable and bundle apps located. Bulndle apps are imported packages. - Create `main.spec` - Generate a executabl according to `main.spec` If you want your executable to run without external files as dependencies, you can pack everythin into one file: ```bash pyinstaller -F main.py ``` Here are some example with common options: ```bash # Create a one-folder bundle containing an executable (default) pyinstaller -D main.py # Create a one-file bundled executable. pyinstaller -F main.py # Do not provide a console window for standard i/o. # On Mac OS X this also triggers building an OS X .app bundle. # On Windows this option will be set if the first script is a ‘.pyw’ file. # This option is ignored in *NIX systems. pyinstaller --windowed main.py # Open a console window for standard i/o (default). # On Windows this option has no effect if the first script is a ‘.pyw’ file. pyinstaller --console main.py # Display an icon on the executable file, this option is only valid for windows and mac # note that the icon file should be in ico format, you can google "ico online converter" pyinstaller main.py --icon main.ico ``` ## Troubleshooting You have your python script tested ```shell python main.py ``` and you packed it into a executalbe. You doube click the executable but it just crashed. Welcome to toubleshooting. In a GUI os, double clicking the executable(`.exe`) has no help for debugging. You can execute your program in a commandline in order to see the messages printing to standard output. Here are some common errors for a executable packed by pyinstaller. ### Detected as a malware or a virus - Possible reasons and work around - Try pack dependecies to one folder using `-D` flag. - Avoid import whole modules such as `os` or `sys` - Get a digital sign for `.exe`. [Ref](https://stackoverflow.com/questions/252226/signing-a-windows-exe-file) ### `ModuleNotFoundError` Module not found: You are sure that the module is definitely imported in `main.py`, but the module is missed no matter how many times you have repack the script to a executable. ```shell C:/Users/usr1/Documents/python_code>python main.py ... ... ModuleNotFoundError: No module named 'win32timezone' ``` This is because the `pyinstaller` didn't statically import the module, you have to tell it for a force import. Solution: After you have pack your script, you should see a file `main.spec`. Edit that file, find the following patterns and add the module to `hiddenimports`. ```python a = Analysis(['main.py'], pathex=['C:\\Users\\usr1\\Documents\\python_code'], binaries=[], datas=[], hiddenimports=[‘win32timezone’], hookspath=[], runtime_hooks=[], ``` Clean the binraries from last build(pack) by deleting the folder `./build` and `./list` Before `pyinstaller` generates the executable according to the spec file you just edit: ```shell pyinstaller .\main.spec # Can also be combined with options mentioned above ``` Then you should see your module-well-imported executalbe. > Note that every time executing `pyinstaller main.py` generates a `main.spec`, which overwrites the one in the file system. ### `RecursionError` Maximum recursion depth exceeded ```shell C:/Users/usr1/Documents/python_code>python main.py ... ... RecursionError: maximum recursion depth exceeded ``` I don't know what is this error stands for, maybe it's about the call stack layers, but I do have a solution. Edit `main.spec`, insert two lines ```python # -*- mode: python ; coding: utf-8 -*- import sys # inserted sys.setrecursionlimit(9000000) # inserted, set the value that fits your need ... ``` Clean the binraries from last build(pack) by deleting the folder `./build` and `./list`. Before `pyinstaller` generates the executable according to the spec file you just edit: ```shell pyinstaller .\main.spec # Can also be combined with options mentioned above ``` Then you should see your recursion-limit executalbe. > Note that every time executing `pyinstaller main.py` generates a `main.spec`, which overwrites the one in the file system.