我打包rasterio到可执行文件中的方式有问题。

7

预期行为和实际行为。

我希望使用rasterio编译脚本,并使用pyinstaller将其制作成可执行文件。该脚本可以在我的Python环境中正常运行。但是,我无法使用PyInstaller将其冻结为可执行文件。

问题的复现步骤。

我有一个名为workflow_3.py的脚本,其中包含以下内容:

import rasterio

就这样。我尝试使用以下方式编译并运行它:

(wps_env36) D:\11202750-002_RA2CE\Basis>pyinstaller workflow_3.py (wps_env36) D:\11202750-002_RA2CE\Basis>dist\workflow_3\workflow_3.exe

编译似乎已经完成,但是当我运行可执行文件时,出现以下错误:

(wps_env36) D:\11202750-002_RA2CE\Basis>dist\workflow_3\workflow_3.exe
Traceback (most recent call last):
  File "workflow_3.py", line 1, in <module>
    import rasterio
  File "c:\programdata\anaconda2\envs\wps_env36\lib\site-packages\PyInstaller\loader\pyimod03_i
mporters.py", line 627, in exec_module
    exec(bytecode, module.__dict__)
  File "site-packages\rasterio\__init__.py", line 23, in <module>
  File "rasterio\_base.pyx", line 1, in init rasterio._base
ModuleNotFoundError: No module named 'rasterio._shim'
[17536] Failed to execute script workflow_3

尝试解决问题

我修改了规范文件,通过将'rasterio._shim'显式添加到hidden-imports变量的列表中。然后我运行了pyinstaller workflow_3.spec。这导致其他模块(例如control.py,crs.pyvrt.py)出现ModuleNotFoundError

将它们添加到hidden-imports可以成功消除该特定软件包的ModuleNotFoundError,但它仍会寻找其他软件包,所有这些软件包都包含在C:\ProgramData\Anaconda2\envs\wps_env36\Lib\site-packages\rasterio中。此目录中有大约40个模块。将该目录中的每个文件名都添加到hidden-imports变量中似乎过于繁琐。实际上,我甚至不知道是否有效。

因此,我还尝试将整个目录添加到我的pathex变量中,以便它可以扩展PYTHONPATH。然而,这会引起另一个问题:

File "c:\programdata\anaconda2\envs\wps_env36\lib\traceback.py", line 5, in <module> File "c:\programdata\anaconda2\envs\wps_env36\lib\linecache.py", line 11, in <module> File "c:\programdata\anaconda2\envs\wps_env36\lib\tokenize.py", line 27, in <module> ImportError: cannot import name 'open' pre-safe-import-module hook failed, needs fixing.

操作系统

Windows 7

Rasterio版本和来源

rasterio版本为1.0.8,来自conda-forge。Python版本为3.6.6。

我有两个版本的pyinstaller。

pyinstaller               3.4              py36h7602738_0    conda-forge
PyInstaller               3.5.dev0+b13e6b30b           <pip>

第二个是开发版本,由于这个问题,我不得不获取它。
问题:
如何使用PyInstaller来冻结使用rasterio的应用程序?

有人能帮忙吗?我可以提供哪些其他信息,以便更容易回答这个问题? - user32882
我认为你只能等待有人知道答案。如果他们缺少一些信息,人们会告诉你的。 - YesThatIsMyName
有时候人们不会主动提供帮助,除非你开口请求。无论如何,检查一下也不会有什么损失。 - user32882
我有一个非常类似的问题,目前正在尝试您的解决方案,但运气不太好。如果我找到另一个解决方案,我会发布的。 - morrismc
2个回答

8

我想到的当前解决方案是使用 glob 包强制将 C:\ProgramData\Anaconda2\envs\wps_env36\Lib\sitepackages\rasterio 中包含的所有模块添加到 hidden-imports 变量中。在我的 spec 文件中,我添加了一些 Python 代码来实现这一点:

# -*- mode: python -*-

block_cipher = None
import glob, os
rasterio_imports_paths = glob.glob(r'C:\ProgramData\Anaconda2\envs\wps_env36\Lib\site-packages\rasterio\*.py')
rasterio_imports = ['rasterio._shim']

for item in rasterio_imports_paths:
    current_module_filename = os.path.split(item)[-1]
    current_module_filename = 'rasterio.'+current_module_filename.replace('.py', '')
    rasterio_imports.append(current_module_filename)

a = Analysis(['workflow_3.py'],
             pathex=['D:\\11202750-002_RA2CE\\Basis'],
             binaries=[],
             datas=[],
             hiddenimports=rasterio_imports,
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='workflow_3',
          debug=True,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='workflow_3')

不幸的是,这并没有解释为什么pyinstaller一开始无法识别这些模块。但它暂时解决了这个问题,代码编译正常。


1
你除了安装PyInstaller之外,是否还安装了PyWin32?它可以作为pypi软件包https://pypi.org/project/pypiwin32获得。根据PyInstaller文档,这应该自动安装为依赖项,但我过去遇到了一些问题,手动安装解决了这些问题。 - lhk
1
你为什么认为这与pywin32有关? - user32882
1
只是猜测。你得到的错误信息是:“ImportError: 无法导入名称 'open',预安全导入模块钩子失败,需要修复。” 这与我的非常相似。 - lhk

2

我使用了这个回答来避免像user32882一样硬编码rasterio的安装路径,因此spec文件可以在任何操作系统上工作。方法的其余部分相同:

import pkgutil

import rasterio

# list all rasterio and fiona submodules, to include them in the package
additional_packages = list()
for package in pkgutil.iter_modules(rasterio.__path__, prefix="rasterio."):
    additional_packages.append(package.name)


block_cipher = None

a = Analysis(['src\\main.py'],
             binaries=[],
             datas=[],
             hiddenimports=additional_packages,
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='myProgram',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='myProgram')

我测试发现这种方法也适用于Fiona,它和 PyInstaller 存在相同的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接