pyinstaller相关错误

本文详细介绍如何使用PyInstaller将Python项目转换成exe文件,并解决转换过程中常见的数据文件丢失问题。提供了三种解决方案,包括调整代码逻辑、修改spec配置文件及利用pakutil模块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/pipisorry/article/details/50620495

目录结构

 

将主文件testMain.py转换成exe可执行文件

主文件调用了自定义包:nlptest,其中nlptest1.py读取文件stopwordsFile.txt。

错误1:[Errno 2] No such file or directory: 'C:\Users\...\AppData\Local\Temp\_MEI***'

问题:

这个问题实质上是python代码转换成exe时怎么添加数据文件[http://pythonhosted.org/PyInstaller/#adding-data-files]

python文件通过命令$pyinstaller -F E:/mine/python_workspace/NLP/TargetOpinion/TargetOpinionMain.py转换为exe文件时数据文件找不到[pyinstaller使用-python项目转换成exe可执行文件]
原来python文件中读取路径为stopwordsFile的文件with open(stopwordsFile, encoding='utf-8')as f

转换为exe后,只有一个exe文件了,exe文件执行时,会将资源释放到temp文件夹下再执行,但是原来的数据文件stopwordsFile并没打包和释放,这样就会找不到文件。

原因详解:

单独生成一个exe文件(-F参数)后,exe文件是怎么运行的:

How the One-File Program Works
The bootloader is the heart of the one-file bundle also. When started it creates a temporary folder in the appropriate temp-folder location for this OS. The folder is named _MEIxxxxxx, where xxxxxx is a random number.
The one executable file contains an embedded archive of all the Python modules used by your script, as well as compressed copies of any non-Python support files (e.g. .so files). The bootloader uncompresses the support files and writes copies into the the temporary folder. This can take a little time. That is why a one-file app is a little slower to start than a one-folder app.
After creating the temporary folder, the bootloader proceeds exactly as for the one-folder bundle, in the context of the temporary folder. When the bundled code terminates, the bootloader deletes the temporary folder.

[http://pythonhosted.org/PyInstaller/#how-the-one-file-program-works]

解决方案:

方案1:

最后发现比较简单的解决方案只要将python代码中的数据文件拷贝到dist文件夹下,如dist/data/stopwordsFile.txt,在python代码中读取文件时文件路径为stopwordsFile=r"data/stopwordsFile.txt"就可以了。

为了python代码直接执行和exe执行都不会出错,可以这样写代码

添加一个判断,如果是pyinstaller生成的exe文件执行的,会在sys中(之前版本是这个os.environ?)添加一个_MEIPASS(即exe释放路径C:\Users\...\AppData\Local\Temp\_MEI***)和一个frozen [It also adds the attributes frozen and _MEIPASS to the sys built-in module],直接从python代码执行则不会

analysis.py

    if getattr(sys, 'frozen', False):
        pathname = r""
    else:
        pathname = os.path.split(os.path.realpath(__file__))[0]
    print("pathname: " + pathname)

    stopwordsFile = GlobalOptions.stopwordsFile
    stopwordsFile = os.path.join(pathname, stopwordsFile)


class GlobalOptions():
    '''
    全局变量设置
    '''
    # 如果用pyinstaller转换后,对应于exe文件的dist目录,下面的文件要要放在exe同目录下
    stopwordsFile = r'data/English_stopwords.txt'

要保证在dist/data目录下存在数据文件,并且python执行时analysis.py同目录下/data目录中也有数据文件,这样不管exe执行还是python代码执行都找得到数据文件。

[pyinstaller打包selenium找不到webdriver_prefs.json]


方案2:(目前推荐这个,有更好的方案lz会更新)

修改中间文件,再去生成exe文件,这样生成exe文件时将python项目中的数据文件加入到生成的exe bundle的文件中,exe文件执行时,会释放txt文件到temp目录下。

为了python代码直接执行和exe执行都不会出错,可以这样写代码:

添加一个判断,如果是pyinstaller生成的exe文件执行的,会在sys中(之前版本是这个os.environ?)添加一个_MEIPASS(即exe释放路径C:\Users\...\AppData\Local\Temp\_MEI***),数据文件也会在这个目录下。和一个frozen [It also adds the attributes frozen and _MEIPASS to the sys built-in module],直接从python代码执行则不会。

analysis.py:

if getattr(sys, 'frozen', False):
    pathname = sys._MEIPASS
else:
    pathname = os.path.split(os.path.realpath(__file__))[0]
# print("pathname: " + pathname)

patternFile = GlobalOptions.patternFile
patternFile = os.path.join(pathname, patternFile)

with open(stopwordsFile, encoding='utf-8')as f: ...

Note: lz要提醒的是:os.path.split(os.path.realpath(__file__))[0]这个路径在exe文件中实际就是C:\Users\...\AppData\Local\Temp\_MEI***\test0\nlptest也就是路径sys._MEIPASS\python项目顶层目录\...\analysis.py 

[http://pythonhosted.org/PyInstaller/#using-file-and-sys-meipass]

[PyInstaller: IOError: [Errno 2] No such file or directory]

[PyInstaller executable issue]

1. 同方案1在原来的python项目读取数据文件的pythony文件中添加一个判断,如果是pyinstaller生成的exe文件执行的,会在sys中添加一个_MEIPASS(即exe释放路径C:\Users\...\AppData\Local\Temp\_MEI***)和一个frozen [It also adds the attributes frozen and _MEIPASS to the sys built-in module],直接从python代码执行则不会

2. 修改spec文件中analysis.datas如下:

a = Analysis(...
     datas= [ ('nlptest/data/stopwordsFile.txt', 'data/stopwordsFile.txt' ) ],

     ...
     )

datas内部元组参数解释:

  • The first string specifies the file or files as they are in this system now.
  • The second specifies the name of the folder to contain the files at run-time.

或者在a = Analysis()下面加上代码,告诉pyinstaller打包python文件时也将数据文件加入进去:
这个数据结构是TOC结构,类似数组列表a TOC object contains a list of tuples of the form (name,path,typecode)

a.datas+= [('stopwordsFile.txt', r'nlptest\stopwordsFile.txt', 'DATA'),]

a.datas += [('data/patterns.txt', 'TargetOpinion/Algorithm/data/patterns.txt',  'DATA'),
('data/English_stopwords.txt', 'TargetOpinion/Algorithm/data/English_stopwords.txt',  'DATA'),]

Note:第一个参数代码exe文件释放时候数据文件在temp/_mei***目录下的目录位置(也就是python代码中读取文件时候的位置(python代码中(python项目顶层目录为e/mine/py/nlp/时)e/mine/py/nlp/target/a.txt在exe释放时就成了C:\Users\...\AppData\Local\Temp\_MEI***\nlp/target/a.txt)),第二个参数代表要读取的数据文件的真实目录(没打包前的目录,spec文件相对路径?或者绝对路径),最后一个代表文件类型为数据文件。

typecode description name path
'DATA' Arbitrary files. Run-time name. Full path name in build.
'BINARY' A shared library. Run-time name. Full path name in build.
'EXTENSION' A binary extension to Python. Run-time name. Full path name in build.
'OPTION' A Python run-time option. Option code ignored.

[http://pythonhosted.org/PyInstaller/#the-toc-and-tree-classes]

当然也可以用tree数据结构读取目录下的所有文件,再将tree结构转换成toc结构:

a.datas += Tree('TargetOpinion/Algorithm/data/', prefix='data', excludes=[''], typecode='DATA')

这个等价于a.datas += [('data/patterns.txt', 'TargetOpinion/Algorithm/data/patterns.txt',  'DATA'), ('data/English_stopwords.txt', 'TargetOpinion/Algorithm/data/English_stopwords.txt',  'DATA'),]

Note: Tree(root, prefix=run-time-folder, excludes=string_list, typecode=code | 'DATA' )

参数root代表python代码中读取文件位置(没打包前的目录,spec文件相对路径?或者绝对路径),参数prefix代表运行时exe文件释放目录temp/_mei***下的相对子目录,参数excludes代表目录下要排除的文件如"*.pyc",typecode代表文件类型为数据文件。

[ http://pythonhosted.org/PyInstaller/#the-tree-class]

3. 再通过spec文件生成单文件exe文件:

pyinstaller -F E:/mine/python_workspace/test0/testMain.spec

或者

from PyInstaller.__main__ import run

if __name__ == '__main__':
    opts = ['TargetOpinionMain.spec', '-F']
    run(opts)

[Bundling data files with PyInstaller (--onefile)]

[http://pythonhosted.org/PyInstaller/#adding-data-files]


方案3:

使用python自带包pakutil

testMain.py

import nlptest

nlptest1.py

pkgutil.get_data('nlptest', patternFile)

但是这个和open打开是一样的,转换成exe后还是会找不到文件,并且如果不在testMain.py中添加import nlptest,get_data返回值为None。

另外要注意的问题

with open("***.txt", encoding='utf-8')as f

txt文件所在目录对应的是这个代码执行的目录(可能被另一个目录中的文件调用,则目录就是那个文件所在目录),而不是当前所在的文件a.py所在的目录。

[Python文件输入输出]

皮皮blog



错误2:pag_resources.distributionnotfound: pyinstaller testMain.py returned -1

在文件nlptest1.py中加入这一行出错

from PyInstaller.building.build_main import Analysis

就是引入PyInstaller出错,也不知道为什么,不加入就可以了,我的加入只是测试一个东西。

from:http://blog.youkuaiyun.com/pipisorry/article/details/50620495

ref:


<think>好的,我现在需要帮助用户解决PyInstaller打包过程中遇到的错误。根据用户提供的引用内容,我需要先整理可能的问题和解决方案。首先,用户提到了递归错误、绝对路径问题、numpy包版本问题以及Crypto模块缺失的错误。 首先,用户提到递归错误,引用[1]指出在打包时可能会遇到递归深度超过限制的问题。解决方案是调整Python的递归深度限制。这通常可以通过在代码中添加sys.setrecursionlimit(5000)来实现,或者在spec文件中修改递归限制参数。另外,用户可能需要在打包命令中添加--recursion-limit参数。 接下来是绝对路径的问题,引用[2]说明如果系统找不到pyinstaller,可能需要使用绝对路径来指定pyinstaller.exe的位置。用户需要进入项目目录,复制pyinstaller的安装路径,并在打包命令中使用该绝对路径。同时,生成spec文件时也要用绝对路径的pyi-makespec。 然后是关于numpy包的版本问题,引用[3]提到虽然打包前程序运行正常,但打包后可能因为numpy版本导致错误。建议先升级numpy包,但需要注意这可能是打包过程中的依赖处理问题,而非numpy本身的问题。可以尝试升级后重新打包,或者使用pyinstaller的钩子文件来正确处理numpy的依赖。 最后是Crypto.Math模块找不到的错误,引用[4]指出可能是由于PyCrypto和PyCryptodome的兼容性问题。解决方法包括安装pycryptodome包,并确保在代码中正确导入模块,或者在打包时添加隐藏导入参数--hidden-import。 综合这些信息,我需要将解决方案分步骤列出,确保用户能够逐步排查和解决问题。同时需要生成相关问题,帮助用户进一步了解可能遇到的其它问题。需要注意使用中文,正确应用LaTeX格式,以及引用标识。现在整理回答结构,确保清晰易懂,并符合用户的要求。</think>### PyInstaller打包错误解决方案 以下是针对PyInstaller常见打包问题的分步解决方案,结合引用内容及实践总结: --- #### **1. 递归深度报错(RecursionError)** **现象**:打包时出现`RecursionError: maximum recursion depth exceeded`。 **原因**:Python默认递归深度限制较低,复杂项目可能超出限制。 **解决方案**: - **方法1**:在代码开头添加递归深度设置(临时调整) ```python import sys sys.setrecursionlimit(5000) # 将限制提高到5000 ``` - **方法2**:修改PyInstaller的`.spec`文件 在`exe`配置中添加`runtime_tmpdir`参数或调整递归限制参数[^1]。 - **方法3**:打包命令中直接指定递归限制 ```bash pyinstaller --recursion-limit 5000 your_script.py ``` --- #### **2. PyInstaller未找到或路径错误** **现象**:提示`未找到pyinstaller`或`pyi-makespec`无法识别。 **原因**:环境变量未正确配置或未使用绝对路径。 **解决方案**: - **步骤1**:确认PyInstaller安装路径,例如`C:\Python38\Scripts\pyinstaller.exe`。 - **步骤2**:使用绝对路径执行打包命令: ```bash "C:\Python38\Scripts\pyinstaller.exe" your_script.py ``` 生成spec文件同理: ```bash "C:\Python38\Scripts\pyi-makespec.exe" your_script.py ``` [^2] --- #### **3. 依赖库版本冲突(如numpy)** **现象**:打包后运行报错,但打包前代码正常。 **原因**:PyInstaller可能未正确捕获动态加载的库。 **解决方案**: - **步骤1**:升级numpy至最新版本: ```bash pip install --upgrade numpy ``` - **步骤2**:若仍报错,手动添加依赖路径到`.spec`文件: ```python # your_script.spec a = Analysis( ..., pathex=['C:\\your_project_path'], hiddenimports=['numpy.core._dtype_ctypes'], ) ``` [^3] --- #### **4. 模块缺失错误(如Crypto.Math)** **现象**:打包后提示`ModuleNotFoundError: No module named 'Crypto.Math'`。 **原因**:PyCryptodome与PyCrypto库命名冲突。 **解决方案**: - **步骤1**:安装`pycryptodome`并确保代码中导入正确模块: ```bash pip uninstall pycrypto pip install pycryptodome ``` - **步骤2**:在代码中替换导入语句: ```python from Crypto.Math import ... # 原语句 from Cryptodome.Math import ... # 新语句 ``` - **步骤3**:打包时添加隐藏导入参数: ```bash pyinstaller --hidden-import=Crypto --hidden-import=Crypto.Math your_script.py ``` [^4] --- #### **5. 通用排查流程** 1. **生成调试日志**: ```bash pyinstaller --debug=all your_script.py ``` 2. **检查`.spec`文件**:确认所有依赖路径和隐藏导入已正确配置。 3. **清理缓存**:删除`build/`和`__pycache__/`目录后重新打包。 ---
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值