使用 pyinstaller 捆绑使用 cython 编译的 python 应用程序

问题描述 投票:0回答:1

问题

我有一个与 pyinstaller 捆绑在一起的应用程序。现在一个新的功能请求是,该部件使用 cyphon 编译为 c 库。

在激活的虚拟环境(诗歌)内编译后,应用程序按预期运行。

但是,当我将它与 pyinstaller 捆绑在一起时,可执行文件无法找到未在 main.py 文件中导入的包。 据我了解,这完全没问题,因为 pyinstaller 的分析阶段无法读取已编译的 c 代码的内容(在下面的示例中

modules/test/test.py
可供 pyinstaller 使用
modules/test/test.cpython-311-x86_64-linux-gnu.so
)。

文件夹概览:

├── compile_with_cython.py
├── main.py
├── main.spec
├── main_window.py
├── poetry.lock
└── pyproject.toml

主.py

import sys
from PySide6.QtWidgets import QApplication
from main_window import MainWindow

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())

主窗口.py

MVP PySide6 应用程序使用 tomllib 加载一些 toml 文件

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QDialog, QVBoxLayout, QTextEdit
from PySide6.QtCore import Slot

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        ... 

错误代码

./main
Traceback (most recent call last):
  File "main.py", line 12, in <module>
  File "modules/test/test.py", line 3, in init modules.test.test
ModuleNotFoundError: No module named 'tomllib'
[174092] Failed to execute script 'main' due to unhandled exception!
python python-3.x pyinstaller cython
1个回答
0
投票

问题

pyinstaller 面临的主要问题是它无法跟踪 cython 编译的文件/模块的导入。因此,它只能解析和打包

main.py
中命名的文件和库,而不能解析和打包
main_window.py
中的文件和库。为了使其工作,我们需要指定 pyinstaller 隐藏的所有导入。

我找到了两种合适的解决方案,可以将 pyinstaller 与 cython 编译的二进制文件一起使用。

解决方案1:

将任何脚本所需的任何导入添加到主 python 文件中,例如:

# imports needed by the main.py file
import argparse
import logging
import sys
import time

# dummy imports (needed by the main_window.py file)

import tomllib
import pydantic

这可行,但仅适用于小型项目。此外,指定的导入将被各种 linter 删除,因为该文件并未真正使用导入...

解决方案2

我在 pyinstaller 文档中找到了以下内容,为了使其正常工作,我更改了我的“.spec”文件,如下所示:

a = Analysis(
    ['main.py'],
    pathex=[],
    binaries=[],
    datas=[],
    hiddenimports=['tomllib', 'pydantic'], 

奖金

由于上面的代码显然只是一个示例,并且我有一个包含数百个 Python 文件和库的项目,因此我想出了以下代码,以便在每次管道构建包时自动生成“hiddenimports”变量的内容:

def find_all_hidden_imports(directory_path: Path) -> set:
    imports_set = set()
    for file_path in directory_path.rglob('*.py'):
        if ".venv" not in str(file_path):
            imports_set.update(get_imports_of_file(file_path))
    return imports_set


def get_imports_of_file(file_path: Path) -> set:
    imports_set = set()

    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()
        try:
            tree = ast.parse(content)
            for node in ast.walk(tree):
                if isinstance(node, ast.Import):
                    for name in node.names:
                        imports_set.add(name.name)
                elif isinstance(node, ast.ImportFrom):
                    if node.module is not None:
                        imports_set.add(node.module)
        except SyntaxError:
            print(f"Syntax error in file: {file_path}")

    return imports_set

然后将此集合转换为正确的列表格式字符串,然后在当前

.spec
文件中替换...

© www.soinside.com 2019 - 2024. All rights reserved.