简体   繁体   English

在使用 PyInstaller 创建的单文件 exe 中导入外部模块

[英]Importing external module in single-file exe created with PyInstaller

I am using PyInstaller to create a single-file executable.我正在使用 PyInstaller 创建一个单文件可执行文件。 Is it possible for my script to perform an import such that i) the imported module is imported from the same directory as the exe (ie it's not packaged into the exe) and ii) that imported module can import other modules that were packaged into the exe?是否有可能为我的脚本执行导入使得i)导入的模块是从同一个目录中的exe(即它不是打包成EXE)和ii)在导入模块可以导入打包到其他模块中引入exe?

The background here is that the imported module contains configuration that the user should be able to modify.这里的背景是导入的模块包含用户应该能够修改的配置。 This may include creation of custom derived classes and use of enums from the packaged modules.这可能包括创建自定义派生类和使用来自打包模块的枚举。

I haven't found any advice on this, though it's a difficult search because there are so many similar topics that use basically the same keywords.我没有找到任何关于此的建议,尽管搜索起来很困难,因为有太多使用基本相同关键字的相似主题。

The following steps allow a Python module (named module_name here) outside of an executable created by PyInstaller to be imported and for that module to import modules that were bundled into the executable.以下步骤允许导入 PyInstaller 创建的可执行文件之外的 Python 模块(此处名为module_name ),并允许该模块导入捆绑到可执行文件中的模块。

  • Add excludes=['module_name'] to the Analysis object used in the PyInstaller spec.excludes=['module_name']添加到 PyInstaller 规范中使用的 Analysis 对象。 This prevents module_name.py being bundled into the executable.这可以防止module_name.py被捆绑到可执行文件中。
  • Add sys.path.append(os.path.dirname(sys.executable)) where module_name is imported in your application.添加sys.path.append(os.path.dirname(sys.executable))其中module_name在您的应用程序中导入。 This allows it to be imported from the directory the executable is in, which is different to the directory that the application will run in (due to being decompressed to a temporary folder).这允许它从可执行文件所在的目录导入,该目录不同于应用程序将在其中运行的目录(由于被解压缩到一个临时文件夹)。 See below for my recommended method of achieving this.请参阅下文,了解我推荐的实现此目的的方法。
  • Make sure any imports performed by the external module are also performed by one of the bundled modules before the external one is imported.在导入外部模块之前,确保由外部模块执行的任何导入也由捆绑模块之一执行。 The interpreter will not resolve the external module's imports against bundled modules, but will use ones that already exist in sys.modules .解释器不会针对捆绑模块解析外部模块的导入,但使用sys.modules中已经存在的模块。

In order to set up the paths correctly you can use the following:为了正确设置路径,您可以使用以下内容:

if getattr(sys, 'frozen', False):
    app_path = os.path.dirname(sys.executable)
    sys.path.append(app_path)
else:
    app_path = os.path.dirname(os.path.abspath(__file__))

frozen is only available in generated executables, not when running as a script directly. frozen仅在生成的可执行文件中可用,在直接作为脚本运行时不可用。 This snippet will add the executable's location to sys.path if required as well as giving you easy access to the executable or script's location for use in code.如果需要,此代码段会将可执行文件的位置添加到sys.path ,并让您轻松访问可执行文件或脚本的位置以在代码中使用。

As an example of the final bullet point, consider the following.作为最后一个要点的示例,请考虑以下内容。

# bundled_module1.py
import external_module
# bundled_module2.py
# module content
# external_module.py
import bundled_module2

This will fail in external_module.py because the imported module can't be found.这将在external_module.py失败,因为无法找到导入的模块。 However, the following will work:但是,以下内容将起作用:

# bundled_module1.py
import bundled_module2
import external_module
# bundled_module2.py
# module content
# external_module.py
import bundled_module2

This will be fine if there are a limited set of bundled modules that the external one should be able to import.如果外部模块应该能够导入一组有限的捆绑模块,这将没问题。 It may get unwieldy for larger sets.对于较大的集合,它可能会变得笨拙。

Given that the documentation states that the interpreter will resolve imports against modules bundled into the executable, this feels like a possible bug.鉴于文档指出解释器针对捆绑到可执行文件中的模块解析导入,这感觉像是一个可能的错误。 Interoperating with modules outside of the executable isn't explicitly called out though.与可执行文件之外的模块互操作虽然没有明确调用。

Type in Pyinstaller -h .输入Pyinstaller -h It will give you info about pyinstaller and tell you about --runtime-hook .它将为您提供有关 pyinstaller 的信息并告诉您有关--runtime-hook I presume adding this to the executable should work.我认为将此添加到可执行文件应该可以工作。 There's actually a whole page of documentation for this.这实际上有一整页文档 Surprised you could not find that.很惊讶你找不到那个。

Anyway,反正,

The docs say put in: pyinstaller --additional-hooks-dir=. myscript.py文档说放入: pyinstaller --additional-hooks-dir=. myscript.py pyinstaller --additional-hooks-dir=. myscript.py . pyinstaller --additional-hooks-dir=. myscript.py

I presume then something like pyinstaller --additional-hooks-dir=C:\\pathtofolder myscript.py should work in theory.我认为像pyinstaller --additional-hooks-dir=C:\\pathtofolder myscript.py这样的东西理论上应该可以工作。 Yet to test it.还是要测试一下。 Tell us how it goes and what made kinks made it work for you.告诉我们它是怎么回事,是什么让扭结让它为你工作。

Lastly, if you want to be hipster try integrating cython for speed and obfuscation.最后,如果您想成为时髦人士,请尝试集成 cython 以提高速度和混淆。 Fair warning, cython is not as user friendly as pyinstaller appears to be.公平警告,cython 不像 pyinstaller 看起来那么用户友好。 I have yet to use it successfully.我还没有成功使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM