【问题标题】:PyInstaller ModuleNotFoundError --paths flag seems to not workPyInstaller ModuleNotFoundError --paths 标志似乎不起作用
【发布时间】:2021-12-09 20:58:15
【问题描述】:

我使用 Tkinter 构建了一个简单的 GUI,我想将其冻结为独立的可执行文件。我在 conda 环境中执行此操作。使用 OSX 10.15.7、python 3.7、PyInstaller 4.5.1 和 conda 4.10.0。文件夹结构如下(简化):

 - ImSep_files
  | - ai4eutils
  | - ImSep
  |  | - ImSep_GUI.py
  | - cameratraps
     | - detection
        | - run_tf_detector.py

脚本调用 ai4eutils 和 cameratraps 文件夹中的其他脚本。如果我创建一个 conda 环境,设置PYTHONPATH 以包含ai4eutilscameratraps 的路径,然后运行python ImSep_GUI.py,没有问题。 GUI 打开并完美运行。但是,如果我执行完全相同的操作但运行 pyinstaller 而不是 python,它会创建一个打开 GUI 但在按下按钮时抛出错误的 exe。

  File "/Users/peter/Applications/ImSep_files/cameratraps/detection/run_tf_detector_batch.py", line 56, in <module>
    from detection.run_tf_detector import ImagePathUtils, TFDetector
ModuleNotFoundError: No module named 'detection.run_tf_detector'

这意味着pyinstaller 找不到run_tf_detector.py 文件。我尝试添加 --paths 标志,例如:

pyinstaller --onefile --windowed --name='ImSep' --icon='imgs/logo_small_bg.icns' --paths=/Users/peter/Applications/ImSep_files --paths=/Users/peter/Applications/ImSep_files/ai4eutils --paths=/Users/peter/Applications/ImSep_files/cameratraps --paths=/Users/peter/Applications/ImSep_files/cameratraps/detection ImSep_GUI.py

我知道有很多关于这种类型或错误的主题。我尝试了许多潜在的解决方案,但似乎都没有奏效。我尝试了以下方法:

  • 使用 --hidden-import 标志,正如 HHest 建议的 here。如果尝试不同的版本:--hidden-import detection.run_tf_detector--hidden-import cameratraps.detection.run_tf_detector--hidden-import cameratraps.detection 等。
  • 根据 user1251007 的建议 here,使用上述路径调整 hiddenimports=[], 行。
  • sys.path.append(path/to/run_tf_detector.py) 添加到ImSep_GUI.py 的顶部。
  • 按照 Fivef 的建议 herepyinstaller 降级到 3.1。
  • hooks 文件夹中创建带有 detection.run_tf_detectorhook.py 并将其添加为 --additional-hooks-dir=hooks,正如 Legorooj 建议的 here
  • 按照 Ken4scholars here 的建议,将所需模块作为数据加载到规范文件中。
  • run_tf_detector.py复制到与ImSep.exe同级的文件夹中,按照Wayne Zhang的建议here
  • 从父目录调用pyinstaller,按照here的建议,全部或无。
  • 按照 Habeeb Rahman K T 的建议 here,将 pyinstaller 安装在存在 ImSep_GUI.py 的同一目录中。
  • 安装pyinstaller 使用conda-forge 而不是pip,正如建议的here 通过管道管道。

仅供参考,这就是我创建环境并运行pyinstaller的方式:

conda create --name imsepcondaenv python=3.7 -y
conda activate imsepcondaenv
pip install tensorflow==1.14 pillow==8.4.0 humanfriendly==10.0 matplotlib==3.4.3 tqdm==4.62.3 jsonpickle==2.0.0 statistics==1.0.3.5 requests==2.26.0
conda install -c conda-forge pyinstaller -y
cd ~/Applications/ImSep_files
export PYTHONPATH="$PYTHONPATH:$PWD/ai4eutils:$PWD/cameratraps"
cd ImSep
pyinstaller --onefile --windowed --name='ImSep' --icon='imgs/logo_small_bg.icns' --paths=/Users/peter/Applications/ImSep_files --paths=/Users/peter/Applications/ImSep_files/ai4eutils --paths=/Users/peter/Applications/ImSep_files/cameratraps --paths=/Users/peter/Applications/ImSep_files/cameratraps/detection ImSep_GUI.py

有人知道我做错了什么吗?

PS:对于 OSX 和 UNIX 用户,可以获得可重现的示例:

mkdir ImSep_files
cd ImSep_files
git clone https://github.com/Microsoft/cameratraps -b tf1-compat
git clone https://github.com/Microsoft/ai4eutils
git clone https://github.com/PetervanLunteren/ImSep.git
curl --output md_v4.1.0.pb https://lilablobssc.blob.core.windows.net/models/camera_traps/megadetector/md_v4.1.0/md_v4.1.0.pb

【问题讨论】:

    标签: python-3.x pyinstaller modulenotfounderror


    【解决方案1】:

    PYTHONPATH 几乎总是局部最小值。根据我的经验,从长远来看,它只会使事情复杂化。我建议第 1 步是从您的工作流程中删除 PYTHONPATH 并了解 python 包和可编辑的 intsalls。从长远来看,这将使开发变得更加容易。

    PYTHONPATH 基本上是作为一种让“脚本”在不实际安装包的情况下访问其他模块的方式开始的。这在 virtualenv 和 conda 之前的糟糕时代更有意义,但现在使用包结构更容易,更有条理。

    尝试像典型的可安装 python 库一样构建您的项目。例如

    .
    ├── .git
    ├── ImSep_files
    │  ├── ai4eutils
    │  ├── cameratraps
    │  │  └── detection
    │  │     └── run_tf_detector.py
    │  └── ImSep
    │     └── ImSep_GUI.py
    └── setup.py
    

    确保您可以从根目录pip install .。您应该有一些您从中导入的顶级包名称(在这种情况下,我随意选择 ImgSep_Files 作为您的库名称,但它可以是任何名称)。那么您应该能够始终使用绝对或相对包语法导入,例如

    from .detection.run_tf_detector import ImagePathUtils, TFDetector
    

    最终的测试是你是否可以运行python -m ImSep_files.cameratraps.detection.run_tf_detector没有使用PYTHONPATH。这意味着您的导入结构正确,pyinstaller 应该可以毫无问题地处理您的依赖项。

    更新:这是一个带有setup.py 的简单包示例。我选择了 setup.py,尽管这有点老派,而且事情正朝着pyproject.toml 发展,因为有更多关于这种风格的文档:

    from setuptools import setup, find_packages
    
    setup(
        name="my_package",
        description="An example setup.py",
        license="MIT",
        packages=find_packages(),
        python_requires=">=3.7",
        zip_safe=False,
        install_requires=[
            "tensorflow",
        ],
        classifiers=[
            "Programming Language :: Python :: 3.7",
        ],
        entry_points={
            "console_scripts": [
                "run_tf_detector=my_package.scripts.run_tf_detector:main",
                "imsep_gui=my_package.gui.gui:main",
            ]
        },
    )
    

    然后我有这样的布局:

    .
    └── my_project_name
       ├── .git
       ├── my_package
       │  ├── gui
       │  │  ├── gui.py
       │  │  └── gui_utils.py
       │  ├── scripts
       │  │  └── run_tf_detector.py
       │  └── detection
       │     └── tf_detector.py
       ├── README.md
       ├── setup.py
       └── tests
          └── test_tf_detector.py
    

    my_project_name 是我的“回购根”。 my_package 是我的包裹的名称。我会像from my_package.detection.tf_detector import TFDetector 一样导入。在这种情况下,我会将所有的类和逻辑放在tf_detector.py 中,然后run_tf_detector.py 基本上就是:

    import sys
    from my_package.detection.tf_detector import TFDetector
    
    
    def main(args=None):
        args = args or sys.argv
        detector = TFDetector()
        detector.detect(args)
    
    if __name__ == __main__:
        main()
    
    

    GUI 遵循一个简单的模式,gui.py 包含启动 gui 的入口点。这种组织使您的功能代码与作为脚本运行的具体细节分开。例如,它可以让检测器作为 CLI 脚本、GUI 的一部分或作为可以导入的库运行。

    Entry points 用于告诉安装程序“这是您运行的东西或插件”。一些more 信息。

    【讨论】:

    • 我想我做错了什么。我是否正确理解文件结构保持不变,除了添加setup.py?另外,您确定setup.py 应该在ImSep_files/ 之外吗?我在这个setup.py 中输入什么?我用name="ImSep_files"package_dir={"": "cameratraps"} 运行它。这是正确的吗?我需要为我的项目调整任何字段吗?我已经按照本教程来更好地理解打包 python 项目 Packaging.python.org/tutorials/packaging-projects/。没有必要实际上传到pypi.org,对吧?
    • 顺便说一句,pip install . 来自Imsep_files/ 工作正常。但是,python -m ImSep_files.cameratraps.detection.run_tf_detector 的输出是 /Users/peter/anaconda3/envs/imsepcondaenv/bin/python: Error while finding module specification for 'ImSep_files.cameratraps.detection.run_tf_detector' (ModuleNotFoundError: No module named 'ImSep_files')
    • 你把项目的“根”称为什么?就像如果你把它放在版本控制中,什么是最高级别?也许 ImSep_files 是你的根,在这种情况下,cameratraps 是你的包。但这意味着 ImSep 应该在相机陷阱下。这是构建 python 项目的典型方式:docs.python-guide.org/writing/structure
    • > 没有必要实际上传到pypi.org,对吧?正确的。 Python 包只是 gzip 存档或.whl 文件,它们只是一种二进制存档。 Python 包索引本质上只是一个 HTTP 服务器,具有托管这些包的目录结构。 pypi.org 只是最著名的一个。
    • 哇,好用。感谢您所有详尽的回答!
    猜你喜欢
    • 2011-09-19
    • 2020-12-08
    • 1970-01-01
    • 2021-07-24
    • 2018-10-31
    • 1970-01-01
    • 2011-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多