当我的 Python 应用程序被 PyInstaller 卡住时尝​​试导入 Geopandas,它停止工作。

  • Windows 10
  • PyInstaller 3.3.1
  • Geopandas 0.4

  • 这是源代码:
    print("Hello, StackOverflow")
    import geopandas as gpd
    

    这是编译后的 EXE 的控制台输出结果:
    Hello, StackOverflow
    Traceback (most recent call last):
      File "application.py", line 3, in <module>
      File "<frozen importlib._bootstrap>", line 971, in _find_and_load
      File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
      File "d:\documents\projecttwo\publish\harv_venv1\env\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 631, in exec_module
        exec(bytecode, module.__dict__)
      File "site-packages\geopandas\__init__.py", line 9, in <module>
      File "<frozen importlib._bootstrap>", line 971, in _find_and_load
      File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
      File "d:\documents\projecttwo\publish\harv_venv1\env\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 631, in exec_module
        exec(bytecode, module.__dict__)
      File "site-packages\geopandas\datasets\__init__.py", line 7, in <module>
    StopIteration
    [6764] Failed to execute script application
    

    当我尝试导入 Geopandas 是更复杂的应用程序时,这种行为是一致的,并且控制台输出是恒定的。

    Geopandas 已正确安装在 Python 3.6.3 虚拟环境中(通过 PIP,我也尝试过 0.4 和 0.3 版)并且在编译之前运行良好(即 python application.py 成功运行)。

    我尝试从不同来源(例如 Gohlke 的轮子)安装 geopandas 和 pyinstaller,结果相同。我还尝试从头开始创建一个全新的虚拟环境,从 Gohlke 安装 Fiona,从 pip 安装 geopandas。

    我怀疑可能需要进行一些隐藏的导入。我对 PyInstaller 还很陌生,所以任何帮助将不胜感激。

    最佳答案

    看起来 geopandas 正在积极地在 init 上加载他们的数据目录。它包含在你的包中被 pyinstaller 忽略的非 python 文件,所以为了让 geopandas 在你加载它时找到它们,它们必须被显式打包。

    “手动”过程花了我一段时间才弄明白,我使用 conda 作为我的包管理器(如果你不是,这些编辑仍然可以帮助你)。为了让它工作,我们需要修改第一次运行 .spec 时构建的 pyinstaller 文件:

    # -*- mode: python -*-
    
    import os
    from PyInstaller.utils.hooks import collect_data_files # this is very helpful
    env_path = os.environ['CONDA_PREFIX']
    dlls = os.path.join(env_path, 'DLLs')
    bins = os.path.join(env_path, 'Library', 'bin')
    
    paths = [
        os.getcwd(),
        env_path,
        dlls,
        bins,
    ]
    
    # these binary paths might be different on your installation.
    # modify as needed.
    # caveat emptor
    binaries = [
        (os.path.join(bins,'geos.dll'), ''),
        (os.path.join(bins,'geos_c.dll'), ''),
        (os.path.join(bins,'spatialindex_c-64.dll'), ''),
        (os.path.join(bins,'spatialindex-64.dll'),''),
    ]
    
    hidden_imports = [
        'ctypes',
        'ctypes.util',
        'fiona',
        'gdal',
        'geos',
        'shapely',
        'shapely.geometry',
        'pyproj',
        'rtree',
        'geopandas.datasets',
        'pytest',
        'pandas._libs.tslibs.timedeltas',
    ]
    
    # other fancy pyinstaller stuff...
    
    a = Analysis(['run_it.py'],
             pathex=paths, # add all your paths
             binaries=binaries, # add the dlls you may need
             datas=collect_data_files('geopandas', subdir='datasets'), #this is the important bit for your particular error message
             hiddenimports=hidden_imports, # double tap
             hookspath=[],
             runtime_hooks=[],
             excludes=excludes,
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
    # remaining fancy pyinstaller stuff...
    

    那应该收集您丢失的数据目录并将其放在您的可执行文件可以找到它的地方。

    “自动”方法是构建一个 hook-geopandas.py 文件来为您执行此操作。 pyinstaller 在您构建时加载这些钩子(Hook),作为保存和分享这些技巧的一种方式。事实上,已经有一个非常好的 shapely Hook 文件,geopandas 依赖项之一,您可以查看 here

    - - - 编辑 - - - -

    我目前也在构建一个依赖于 geopandas 的项目,我意识到由于这个 issue ,上面的修复在这个日期(2018-08-23)是不完整的。

    在我的 run_it.py 中,我包含了以下测试以确保 fionagdal 都正确打包到包中:
    from osgeo import gdal, ogr, osr
    from fiona.ogrext import Iterator, ItemsIterator, KeysIterator
    from geopandas import GeoDataFrame
    

    除非您是巫师,否则此测试可能会失败。这个垫片在我的 .spec 文件中对我有用:
    _osgeo_pyds = collect_data_files('osgeo', include_py_files=True)
    
    osgeo_pyds = []
    for p, lib in _osgeo_pyds:
        if '.pyd' in p:
            osgeo_pyds.append((p, ''))
    
    binaries = osgeo_pyds + [
        # your other binaries
    ]
    
    a = Analysis(
        # include your kwargs
    )
    

    我希望这有助于使这个答案更加完整,并且您的捆绑应用程序按预期执行地理空间操作。

    关于python - 无法使用 PyInstaller 可执行文件导入 Geopandas - 尽管在虚拟环境中运行良好,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51662773/

    10-12 21:08