基于Stanza的auto-py-to-exe GUI程序在Windows10下抛出异常
问题分析
打包无控制台的GUI程序时,Python的sys.stderr会被设置为None(无终端环境下无标准错误流),而Stanza的get_tqdm()函数在捕获get_ipython未定义的异常后,直接调用sys.stderr.isatty(),未先判断sys.stderr是否为None,因此触发AttributeError: 'NoneType' object has no attribute 'isatty'。
解决方案
方案1:修改Stanza源码修复逻辑
找到Python环境中stanza/models/common/utils.py文件,修改get_tqdm()函数的异常处理部分,增加sys.stderr的非空判断:
def get_tqdm(): """ Return a tqdm appropriate for the situation imports tqdm depending on if we're at a console, redir to a file, notebook, etc from @tcrimi at https://github.com/tqdm/tqdm/issues/506 This replaces `import tqdm`, so for example, you do this: tqdm = utils.get_tqdm() then do this when you want a scroll bar or regular iterator depending on context: tqdm(list) If there is no tty, the returned tqdm will always be disabled unless disable=False is specifically set. """ try: ipy_str = str(type(get_ipython())) if 'zmqshell' in ipy_str: from tqdm import tqdm_notebook as tqdm return tqdm if 'terminal' in ipy_str: from tqdm import tqdm return tqdm except: # 先检查sys.stderr是否存在,再调用isatty() if sys.stderr is not None and sys.stderr.isatty(): from tqdm import tqdm return tqdm from tqdm import tqdm def hidden_tqdm(*args, **kwargs): if "disable" in kwargs: return tqdm(*args, **kwargs) kwargs["disable"] = True return tqdm(*args, **kwargs) return hidden_tqdm
修改完成后重新用auto-py-to-exe打包即可。
方案2:在脚本开头预处理标准流
在你的GUI脚本最顶部(导入Stanza之前)添加以下代码,用模拟流替换为空的标准输入/输出/错误流:
import sys class DummyStream: def isatty(self): return False def write(self, msg): pass def flush(self): pass # 处理无控制台时标准流为None的情况 if sys.stdin is None: sys.stdin = DummyStream() if sys.stdout is None: sys.stdout = DummyStream() if sys.stderr is None: sys.stderr = DummyStream() # 之后再导入Stanza相关模块 from MFTE import tag_MD, tag_MD_parallel, tag_stanford, tag_stanford_stanza, do_counts # ... 其余代码
这样get_tqdm()调用sys.stderr.isatty()时会返回False,直接进入禁用tqdm的分支,避免报错。
方案3:用PyInstaller钩子全局处理
创建hook-stanza.py文件,内容如下:
import sys def pre_safe_import_module(api): # 预处理标准流,避免None class DummyStream: def isatty(self): return False def write(self, msg): pass def flush(self): pass if sys.stderr is None: sys.stderr = DummyStream() if sys.stdout is None: sys.stdout = DummyStream() if sys.stdin is None: sys.stdin = DummyStream() # 确保Stanza的模型数据被打包 from PyInstaller.utils.hooks import collect_data_files datas = collect_data_files('stanza')
在auto-py-to-exe的「Additional Files」中添加该钩子文件,或打包时通过--additional-hooks-dir=.参数指定钩子目录。
内容的提问来源于stack exchange,提问作者Shakir
相关产品推荐
相关产品推荐

