When you execute PyInstaller, the first thing it does is build a .spec
file. This file is stored in the --specpath
directory, which defaults to the current directory. The spec
file is actually executable Python code - PyInstaller builds applications by executing this file.
Consider this example command:
pyinstaller -F myscript.py
PyInstaller first creates a myscript.spec
file that encodes most options passed to the pyinstaller
command. The generated myscript.spec
file contents:
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['myscript.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='myscript',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
Typically, you won't need to inspect or modify the spec file contents.
pyi-makespec Usage
You can use the pyi-makespec
command to create spec files:
This command shares identical syntax with pyinstaller
, but only generates spec files without building executables.
Reference:
pyi-makespec [options] name.py [other scripts ...]
After creating and customizing a spec file, build your application by passing it to pyinstaller
:
pyinstaller [options] name.spec
Most command options are encoded in the spec file during creation. If specified during pyinstaller
execution, they'll be ignored.
When building from spec files, only these options remain effective:
--upx-dir
--distpath
--workpath
--noconfirm
--clean
--log-level
SPEC File Deep Dive
The spec file primarily uses four classes: Analysis
, PYZ
, EXE
, and COLLECT
.
Analysis
: Analyzes Python script dependencies and identifies files needing bundling.
a = Analysis(
['myscript.py'], # Main script (equivalent to `pyinstaller myscript.py`)
pathex=[], # Additional module search paths (equivalent to `--paths=DIR`)
binaries=[], # Additional binaries (DLLs, .so files) - no CLI equivalent
datas=[], # Additional data files (equivalent to `--add-data`)
hiddenimports=[], # Implicit imports (equivalent to `--hidden-import`)
hookspath=[], # Custom hook paths (equivalent to `--additional-hooks-dir`)
hooksconfig={}, # Hook behavior configuration - no CLI equivalent
runtime_hooks=[], # Pre-execution hook scripts (equivalent to `--runtime-hook`)
excludes=[], # Modules to exclude (equivalent to `--exclude-module`)
noarchive=False, # Disable .pyz packaging (equivalent to `--noarchive`)
optimize=0, # Python optimization level: 0=default, 1=`-O`, 2=`-OO` (equivalent to `--optimize`)
)
PYZ
: Creates a zlib-based PYZ archive containing byte-compiled pure Python modules (packages .pyc files into .pyz).
Rarely needs manual modification. If noarchive=True
, no .pyz file will be generated.
pyz = PYZ(a.pure)
EXE
: Generates the final executable.
exe = EXE(
pyz, # PYZ archive
a.scripts, # Main script wrapper
[], # Other zip files (usually empty)
exclude_binaries=True, # Equivalent to --onedir (defers binary packaging)
name='myscript', # Output executable name (equivalent to `--name`)
debug=False, # Enable debug info (equivalent to `--debug`)
bootloader_ignore_signals=False, # Ignore signals (for containerized environments)
strip=False, # Reduce size via strip (equivalent to `--strip`)
upx=True, # Enable UPX compression (automatically enabled via `--upx-dir`)
console=True, # Show console (equivalent to `--console` for CLI or `--windowed` for GUI)
disable_windowed_traceback=False, # Disable GUI error popups (equivalent to `--disable-windowed-traceback`)
argv_emulation=False, # macOS only (equivalent to `--argv-emulation`)
target_arch=None, # Target architecture (e.g. 'x86_64', equivalent to `--target-arch`)
codesign_identity=None, # macOS code signing identity
entitlements_file=None, # macOS entitlements configuration
)
COLLECT
: Used only in one-dir mode to package executables and dependencies into output directory.
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False, # Strip resource files (size reduction)
upx=True, # Apply UPX to resources
upx_exclude=[], # Files to exclude from compression (supports wildcards)
name='myscript', # Output directory name (equivalent to `--name`)
)