Location>code7788 >text

An elegant way to control package size in PyInstaller

Popularity:855 ℃/2024-08-27 11:54:15

PyInstaller will automatically collect some dependencies for us when packaging, especially when we package PyQt/PySide related applications, PyInstaller will automatically include files that our programs usually don't need, such as 'tanslations' folder, 'plugins/imageformats', etc., and usually these files will make our final exe file bigger. I didn't find any good way to exclude these files on the web from this Issue/pyinstaller/pyinstaller/issues/5503 One way to do this is to remove unneeded files from the PyQt installation directory before packaging, which serves the purpose, but in my opinion is not elegant enough.

PyInstaller actually ultimately relies on the spec file for dependencies and other configuration items, and generating the contents of the spec file is done by a template (/pyinstaller/pyinstaller/blob/develop/PyInstaller/building/) control, so we can modify (patch) the template before generating, we can achieve the purpose of controlling dependencies.

The following assumes that we are calling Pyinstaller by code and using onefile mode (the onedir module can be modified by reference).

Note the followingAnalysisThe code following the statement is the code that handles datas and binaries, where any filtering logic can be added.

import as pyi_spec_templates
from PyInstaller.__main__ import run as pyi_build

# copy from
# add datas and binaries filter after Analysis
onefiletmplate = """# -*- mode: python ; coding: utf-8 -*-
%(preamble)s

a = Analysis(
    %(scripts)s,
    pathex=%(pathex)s,
    binaries=%(binaries)s,
    datas=%(datas)s,
    hiddenimports=%(hiddenimports)s,
    hookspath=%(hookspath)r,
    hooksconfig={},
    runtime_hooks=%(runtime_hooks)r,
    excludes=%(excludes)s,
    noarchive=%(noarchive)s,
    optimize=%(optimize)r,
)

# begin filter any datas you want
datas = []
for dest_name, src_name, res_type in :
    # Add filtering logic here
    ((dest_name, src_name, res_type))
 = datas
# end filter datas

# begin filter any binaries you want
binaries = []
for dest_name, src_name, res_type in :
    # Add filtering logic here
    ((dest_name, src_name, res_type))
 = binaries
# end filter datas

pyz = PYZ()
%(splash_init)s
exe = EXE(
    pyz,
    ,
    ,
    ,%(splash_target)s%(splash_binaries)s
    %(options)s,
    name='%(name)s',
    debug=%(debug_bootloader)s,
    bootloader_ignore_signals=%(bootloader_ignore_signals)s,
    strip=%(strip)s,
    upx=%(upx)s,
    upx_exclude=%(upx_exclude)s,
    runtime_tmpdir=%(runtime_tmpdir)r,
    console=%(console)s,
    disable_windowed_traceback=%(disable_windowed_traceback)s,
    argv_emulation=%(argv_emulation)r,
    target_arch=%(target_arch)r,
    codesign_identity=%(codesign_identity)r,
    entitlements_file=%(entitlements_file)r,%(exe_options)s
)
"""

pyi_spec_templates.onefiletmplt = onefiletmplate # ==> patch the template string here

# build exe
pyi_build(['', '--onefile', ...])