adang1345 / delvewheel

Self-contained Python wheels for Windows

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support pyinstaller locations for dll files

rerobins opened this issue · comments

When using pyinstaller, an executable is created containing all of the required binaries/datafiles/source code. When the executable is run, the contents are extracted into a temporary directory and then executed.

This prevents the current logic in _delvewheel_init_patch_0_0_14() from working because the patch assumes that the files are in a sibling directory of the directory of the file. This is not the case when using a file generated by pyinstaller.

I propose two solutions. One is to not add the libs_dir file to the dll directory path if it doesn't exist. This would allow the dlls that are in the current directory to be loaded (probably not preferred logic).

Or...

pyinstaller sets a variable on the sys module called _MEIPASS that points to the temporary directory. If that value is defined, then patch method should use that value as the base directory for finding the dll files, and then not add os.pardir to the path.

For example: (using rdkit as an example).

def _delvewheel_init_patch_0_0_14():
    import os
    import sys
    
    if hasattr(sys, '_MEIPASS'):
        libs_dir = os.path.abspath(os.path.join(sys._MEIPASS, 'rdkit_pypi.libs'))
    else:
        libs_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'rdkit_pypi.libs'))
        
    if sys.version_info[:2] >= (3, 8):
        if os.path.exists(os.path.join(sys.base_prefix, 'conda-meta')):
            os.environ['CONDA_DLL_SEARCH_MODIFICATION_ENABLE']='1'
        if os.path.exists(libs_dir):    
            os.add_dll_directory(libs_dir)
    else:
        from ctypes import WinDLL
        with open(os.path.join(libs_dir, '.load-order-rdkit_pypi-2021.3.5.1')) as file:
            load_order = file.read().split()
        for lib in load_order:
            WinDLL(os.path.join(libs_dir, lib))

And then the pyinstaller command can be run using:

pyinstaller --onefile --clean --add-binary "env\Lib\site-packages\rdkit_pypi.libs\**;rdkit_pypi.libs" test.py

I can provide an example project using rdkit and pyinstaller if necessary.

It is not clear to me whether this is something that's best handled in delvewheel, pyinstaller, or a collaboration between the two. I found a report of this issue at pyinstaller/pyinstaller-hooks-contrib#388, which was fixed at the pyinstaller side.

I created pyinstaller/pyinstaller-hooks-contrib#421 to ask for some ideas.

Recent changes in PyInstaller have greatly improved the support for delvewheel-repaired wheels. The discussion at pyinstaller/pyinstaller-hooks-contrib#421 indicates that the PyInstaller developers prefer to handle this primarily at the PyInstaller side.