Azure Pipelines运行matplotlib subplots测试随机报Tcl错误求助
Azure Pipelines中plt.subplots随机触发TclError排查与修复
问题表现
在Azure Pipelines托管Windows Python 3.9 CI环境运行Python单元测试时,调用plt.subplots会随机抛出_tkinter.TclError: invalid command name "tcl_findLibrary"错误,本地开发环境无法稳定复现。
报错位置前4行存在完全相同的plt.subplots调用可正常执行,错误触发点标记如下:
def test_plot_epoch_segmentation(): """Test segmentation plots for epochs.""" n_clusters = 4 labels = np.random.choice( [-1, 0, 1, 2, 3], (len(epochs), epochs.times.size) ) plot_epoch_segmentation(labels, epochs, n_clusters) plt.close("all") # provide ax f, ax = plt.subplots(1, 1) plot_epoch_segmentation(labels, epochs, n_clusters, axes=ax) plt.close("all") # provide cbar_ax >> f, cbar_ax = plt.subplots(1, 1)
错误栈信息
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\site-packages\matplotlib\pyplot.py:1455: in subplots fig = figure(**fig_kw) C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\site-packages\matplotlib\pyplot.py:808: in figure C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\site-packages\matplotlib\backends\_backend_tk.py:949: in new_figure_manager_given_figure window = tk.Tk(className="matplotlib") _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <tkinter.Tk object .>, screenName = None, baseName = 'pytest' className = 'matplotlib', useTk = True, sync = False, use = None def __init__(self, screenName=None, baseName=None, className='Tk', useTk=True, sync=False, use=None): """Return a new Toplevel widget on screen SCREENNAME. A new Tcl interpreter will be created. BASENAME will be used for the identification of the profile file (see readprofile). It is constructed from sys.argv[0] without extensions if None is given. CLASSNAME is the name of the widget class.""" self.master = None self.children = {} self._tkloaded = False # to avoid recursions in the getattr code in case of failure, we # ensure that self.tk is always _something_. self.tk = None if baseName is None: import os baseName = os.path.basename(sys.argv[0]) baseName, ext = os.path.splitext(baseName) if ext not in ('.py', '.pyc'): baseName = baseName + ext interactive = False > self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use) E _tkinter.TclError: invalid command name "tcl_findLibrary" C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\tkinter\__init__.py:2270: TclError
根因
这个错误的核心是无GUI(headless)CI环境使用了需要桌面会话的TkAgg绘图后端导致的:
- Azure托管Windows runner没有常驻的交互桌面会话,Tkinter依赖的Tcl/Tk运行环境初始化本身就存在不稳定问题
- 第一次
plt.subplots调用时Tk解释器首次加载可以正常工作,执行plt.close("all")后Tk实例被销毁,第二次重新初始化Tk解释器时,受CI环境资源回收时序、会话权限影响,会随机出现找不到Tcl库入口的报错,这也是本地有桌面的开发环境无法复现的核心原因
修复方案
- 优先方案:全局强制matplotlib使用无GUI的Agg后端,这是CI环境跑绘图类测试的标准配置
可以在pytest配置文件pytest.ini中添加全局配置,无需修改测试代码:[pytest] matplotlib_backend = Agg - 如果不想修改pytest配置,也可以在所有测试文件的最开头(导入matplotlib/pyplot之前)添加如下代码手动指定后端:
import matplotlib matplotlib.use("Agg") - 不要在CI测试流程中使用TkAgg、QtAgg这类依赖本地GUI组件的后端,这类后端在无桌面环境下的实例销毁、重建逻辑本身就不稳定,容易出现随机报错。
内容的提问来源于stack exchange,提问作者Mathieu
相关产品推荐
相关产品推荐

