Suppose, for some obscure reason, we need to determine the values of some input arguments that are hardcoded into an existing PyInstaller .spec
file, without actually running PyInstaller.
For example, we would like to extract the values of the name
arguments in the simplified example below.
How could that be done?
...
a = Analysis(['path/to/my/script.py'], pathex=[], ...)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(pyz,a.scripts, [], exclude_binaries=True, name='my_exe_name', ...)
coll = COLLECT(exe, a.binaries, a.zipfiles, ..., name='my_output_dir_name')
...
My initial thought would be to read the file content as text, then parse it, e.g. looking for "name="
. However, that feels fragile.
Is there a better way?
Here's one approach that appears to work well:
This uses unittest.mock.Mock to replace the PyInstaller classes, such as Analysis, and then uses runpy.run_path to run the spec file.
The Mock
instances remember the arguments they were called with.
from runpy import run_path
from unittest.mock import Mock
# run the .spec file with mocked PyInstaller classes
mocks = dict(Analysis=Mock(), EXE=Mock(), PYZ=Mock(), COLLECT=Mock())
run_path(path_name="path/to/my.spec", init_globals=mocks)
# extract any desired input arg or kwarg from the Mock instances
exe_name = mocks["EXE"].call_args.kwargs["name"] # 'my_exe_name'
output_dir_name = mocks["COLLECT"].call_args.kwargs["name"] # 'my_output_dir_name'
...