pyinstallerpython-3.9dynamic-import

Pyinstaller to bundle code package with dynamic imports


I have a code that has dynamic module import calls using importlib.import_module and uses Python3.9.10.

The code structure is as below

`main.py
.\Main_dependency
      |__ test_calling_script.py
.\Test
      |__ test1.py
      |__ test2.py
      :
      |__ testN.py`

My dynamic import is like this where prm.TestName is a function within the test<n>.py test_module = getattr(import_module(f'Test.{prm.TestFile}'), prm.TestName)

The code works fine when running through terminal/IDE.

I have converted this into a standalone .exe using pyinstaller and am able to run it (which is where my question is). To make the .exe work, I have specified each dynamic import file path in the hidden import variable of pyinstaller .spec file.

I have some other dynamic imports as well which is bound to make my .spec file messy. Is there a more efficient/clean way to implement the same?

My current .spec file

I have tried adding the Test directory in pathex of .spec file since it is supposed to make pyinstaller add it as a path to search for imports based on the official documentation

pathex: a list of paths to search for imports (like using PYTHONPATH), including paths given by the --paths option.

However, this is not behaving as I expected. Only adding the Test path under pathex runs into NoModuleFoundError for the dynamically imported modules.


Solution

  • Answering my own question. I found out a simple solution to this using the collect_modules from PyInstaller

    To include all files within a directory import the collect_modules and store all modules under a variable like so

    from PyInstaller.utils.hooks import collect_submodules
    hiddenimports_DUT_Config = collect_submodules('DUT_Config')
    

    All that is left is to assign the hiddenimports_DUT_Config variable to hidden_imports in Analysis section of the .spec file.

    If there are multiple folders to be included i.e folder_1 and folder_2,

    from PyInstaller.utils.hooks import collect_submodules
    hiddenimports_1 = collect_submodules(<folder_1)
    hiddenimports_2 = collect_submodules(<folder_2)
    all_hidden_imports = hiddenimports_1 + hiddenimports_2
    
    a = Analysis(
        ['main.py'],
        pathex=['folder_1', 'folder_2''],
        binaries=[],
        datas=[],
        hiddenimports=all_hidden_imports,