---
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.