Search code examples
pythonparsingpyinstaller

How to extract an argument value from an existing Pyinstaller .spec file?


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?


Solution

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