You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 17:48:13