PySimpleGUI调用测试脚本时venv环境模块加载异常求助
问题描述
我用PySimpleGUI开发了一个测试运行界面,功能包括:
- 设置测试参数
- 浏览选择测试脚本所在文件夹
- 启动带参数的测试脚本运行命令
目前界面能正常完成参数设置、文件夹选择和命令发送,但执行测试脚本时,系统会在GUI所属的venv环境中查找依赖模块,而非测试脚本所在文件夹的venv环境,导致模块加载失败。相关代码如下:
import sys import PySimpleGUI as sg import infraLib import glob from serial.tools.list_ports import comports import os import subprocess import pytest infraData = infraLib.infraCommands() def window_layout(ttTTY, vlbTTY): layout_01 = [ [sg.Text('Choose TT port ', size=(30), font=('Arial', 10), justification='left')], [sg.Combo(ttTTY, default_value='None', key='_tt_')], [sg.Text('Choose VLB port ', size=(30), font=('Arial', 10), justification='left')], [sg.Combo(vlbTTY, default_value='None', key='_vlb_')], ] layout_02 = [[sg.Text('Browse to Test folder'), sg.In(size=(25, 1), enable_events=True, key='_folder_'), sg.FolderBrowse()]] layout_03 = [ [sg.Checkbox('Default Tests', key=('_0_'), default=False)], [sg.Checkbox('User Management Tests', key=('_1_'), default=False)] ] layout_04 = [ [sg.Button("Run"), sg.Button('Exit'), sg.Button('Cancel')] ] layout = [ [sg.T('Run tests on-demand', font='_ 14', justification='c', expand_x=True)], [sg.T(" ")], [sg.Col(layout_01, p=0, justification="left", size=(300,150))], [sg.Col(layout_02, p=0, justification='left')], [sg.Col(layout_03, p=0, justification='left')], [sg.Output(size=(80, 20))], [sg.Col(layout_04, p=0, justification='c')] ] return layout def run_cmd(folderPath, test_cmd, tty_tt_vlb): os.chdir(os.path.normpath(folderPath)) print(os.getcwd()) tPath = os.path.normpath(folderPath) if sys.platform == "win32": tty_tt = tty_tt_vlb[0].strip('COM') tty_vlb = tty_tt_vlb[1].strip('COM') pCall = "python" if sys.platform == "linux": tty_tt = tty_tt_vlb[0] tty_vlb = tty_tt_vlb[1] pCall = "python3" match (test_cmd): case "default": mFlag = '"default"' cmdStr = pCall + ' -m pytest -vvv --tty ' + tty_tt + ',' + tty_vlb + ' -m=' + mFlag + ' ' + tPath + ' ' + optArg # case "factory reset": # mFlag = "factory" case "userMgmt": optArg = "UserManagement" cmdStr = pCall + ' -m pytest -vvv --tty ' + tty_tt + ',' + tty_vlb + ' ' + tPath + ' ' + optArg #cmdStr = pCall + ' -m pytest -vvv --tty ' + tty_tt + ',' + tty_vlb + ' -m=' + mFlag + ' ' + tPath + ' ' + optArg print(cmdStr) sp = subprocess.Popen(cmdStr, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=None) out, err = sp.communicate() if out: print(out.decode("utf-8")) if err: print(err.decode("utf-8")) ports_list = list(comports()) TTYs = [] for port in ports_list: TTYs.append(port.name) temp_vlb_ports = infraData.get_vlb_ports(TTYs) temp_tt_ports = infraData.get_tt_ports(TTYs, temp_vlb_ports) tty_vlb_ports = [] tty_tt_ports = [] for tport in TTYs: for vKey, vValue in temp_vlb_ports.items(): if vKey == tport: tty_vlb_ports.append(tport) for tKey, tValue in temp_tt_ports.items(): if tKey == tport: tty_tt_ports.append(tport) wlayout = window_layout(tty_tt_ports, tty_vlb_ports) window = sg.Window("Run tests on-demand", wlayout, default_element_size=(25, 1), auto_size_text=False, finalize=True) while True: event, values = window.read() # End program if user closes window or presses the Exit or Cancel button if event == 'Exit' or event == sg.WIN_CLOSED or event == 'Cancel': break elif values is None: break elif event == '_folder_': folder = values['_folder_'] # os.chdir(values[folder]) elif event == 'Run': justChecked = [element for element in values if values[element] == True] tt_vlb_port = values['_tt_'], values['_vlb_'] if ('_0_') in justChecked: run_cmd(folder, "default", tt_vlb_port) if ('_1_') in justChecked: # run_cmd(folder, "factory", tt_vlb_port) run_cmd(folder, "userMgmt", tt_vlb_port) window.close()
解决方案
方法1:直接调用目标venv的Python解释器
核心是定位测试文件夹下venv的Python可执行文件,用它来执行命令,避免使用GUI环境的Python。修改run_cmd函数中pCall的获取逻辑:
def run_cmd(folderPath, test_cmd, tty_tt_vlb): tPath = os.path.normpath(folderPath) # 定位目标venv的Python解释器 if sys.platform == "win32": tty_tt = tty_tt_vlb[0].strip('COM') tty_vlb = tty_tt_vlb[1].strip('COM') # Windows下venv的Python路径 pCall = os.path.join(tPath, "venv", "Scripts", "python.exe") if sys.platform == "linux": tty_tt = tty_tt_vlb[0] tty_vlb = tty_tt_vlb[1] # Linux下venv的Python路径 pCall = os.path.join(tPath, "venv", "bin", "python3") # 原有match逻辑不变... # 执行命令时指定cwd为目标文件夹 sp = subprocess.Popen(cmdStr, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=tPath)
注意:如果目标venv不在
venv目录下,需调整路径拼接规则。
方法2:激活目标venv后执行命令
通过执行venv的激活脚本,切换环境后再运行测试命令:
Windows系统修改
# 在run_cmd的match分支中替换cmdStr生成逻辑 cmdStr = f'cmd /c "{os.path.join(tPath, "venv", "Scripts", "activate.bat")} && python -m pytest -vvv --tty {tty_tt},{tty_vlb} {tPath} UserManagement"'
Linux系统修改
cmdStr = f'bash -c "source {os.path.join(tPath, "venv", "bin", "activate")} && python3 -m pytest -vvv --tty {tty_tt},{tty_vlb} {tPath} UserManagement"'
方法3:通过环境变量强制切换venv
修改subprocess的env参数,替换关键环境变量,让Python加载目标venv的依赖:
def run_cmd(folderPath, test_cmd, tty_tt_vlb): tPath = os.path.normpath(folderPath) # 复制当前环境变量并修改 env = os.environ.copy() if sys.platform == "win32": tty_tt = tty_tt_vlb[0].strip('COM') tty_vlb = tty_tt_vlb[1].strip('COM') venv_bin = os.path.join(tPath, "venv", "Scripts") # 优先使用目标venv的可执行文件 env['PATH'] = f"{venv_bin};{env['PATH']}" env['VIRTUAL_ENV'] = os.path.join(tPath, "venv") pCall = "python.exe" else: tty_tt = tty_tt_vlb[0] tty_vlb = tty_tt_vlb[1] venv_bin = os.path.join(tPath, "venv", "bin") env['PATH'] = f"{venv_bin}:{env['PATH']}" env['VIRTUAL_ENV'] = os.path.join(tPath, "venv") pCall = "python3" # 原有match逻辑不变... # 执行时传入修改后的环境变量 sp = subprocess.Popen(cmdStr, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=tPath, env=env)
额外修复点
- 原代码中
default分支的optArg未定义,会引发报错,需补充定义或移除该变量 - 执行命令时建议将
cwd=None改为cwd=tPath,确保工作目录为测试文件夹
内容的提问来源于stack exchange,提问作者Archie
相关产品推荐
相关产品推荐

