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

Flask与pytest同作为子进程运行时挂起问题排查求助

测试脚本中Flask请求挂起问题排查

问题背景

我花了一个半小时调试测试代码却毫无进展,完全陷入困境。为简化Flask服务器的测试流程,我编写了一个简易脚本:启动服务器、运行pytest、终止服务器、将输出写入文件,最后以pytest的退出码结束。该代码此前运行正常,但今日突然出现问题(除调试操作外未修改过代码)。

问题表现

测试执行到特定环节时会挂起,但其他运行方式均无此问题:

  • 在VS Code中调试服务器,终端运行测试:正常
  • 用测试脚本中的代码启动服务器,手动运行pytest:正常
  • 用测试脚本运行pytest,在另一终端用启动脚本启动服务器:正常

另一个奇怪现象:测试总是在前置fixture的执行过程中挂起。执行数据库清空命令后,向服务器发送echo请求(用于打印当前测试名称),数据库清空成功,服务器也打印出了正确信息,但echo路由始终不返回响应,测试无法继续。此前的50余次测试中,该echo路由均正常工作。若注释掉导致挂起的测试,会在后续测试的其他路由请求时挂起。挂起时,服务器无法通过SIGTERM终止,必须使用SIGKILL。

相关代码片段

Echo路由代码

@debug.get('/echo')
def echo() -> IEcho:
    """
    Echo an input. This returns the given value, but also prints it to stdout
    on the server. Useful for debugging tests.

    ## Params:
    * `value` (`str`): value to echo
    """
    try:
        value = request.args['value']
    except KeyError:
        raise http_errors.BadRequest('echo route requires a `value` argument')

    to_print = f'{Fore.MAGENTA}[ECHO]		{value}{Fore.RESET}'
    # Print it to both stdout and stderr to ensure it is seen across all logs
    # Otherwise it could be more difficult to figure out what's up with server
    # output
    print(to_print)
    print(to_print, file=sys.stderr)
    return {'value': value}

请求发送代码

def get(token: JWT | None, url: str, params: dict) -> dict:
    """
    Returns the response to a GET web request

    This also parses the response to help with error checking

    ### Args:
    * `url` (`str`): URL to request to

    * `params` (`dict`): parameters to send

    ### Returns:
    * `dict`: response data
    """
    return handle_response(requests.get(
        url,
        params=params,
        headers=encode_headers(token),
        timeout=3
    ))

def echo(value: str) -> IEcho:
    """
    Echo an input. This returns the given value, but also prints it to stdout
    on the server. Useful for debugging tests.

    ## Params:
    * `value` (`str`): value to echo
    """
    return cast(IEcho, get(None, f"{URL}/echo", {"value": value}))

@pytest.fixture(autouse=True)
def before_each(request: pytest.FixtureRequest):
    """Clear the database between tests"""
    clear()
    echo(f"{request.module.__name__}.{request.function.__name__}")
    print("After echo")  # This never prints

测试脚本中运行pytest的代码

def pytest():
    pytest = subprocess.Popen(
        [sys.executable, '-u', '-m', 'pytest', '-v', '-s'],
    )

    # Wait for tests to finish
    print("🔨 Running tests...")
    try:
        ret = pytest.wait()
    except KeyboardInterrupt:
        print("❗ Testing cancelled")
        pytest.terminate()
        # write_outputs(pytest, None)
        # write_outputs(pytest, "pytest")
        raise
    # write_outputs(pytest, "pytest")
    if ret == 0:
        print("✅ It works!")
    else:
        print("❌ Tests failed")
    return bool(ret)

测试脚本中启动服务器的代码

def backend(debug=False, live_output=False):
    env = os.environ.copy()
    if debug:
        env.update({"ENSEMBLE_DEBUG": "TRUE"})
        debug_flag = ["--debug"]
    else:
        debug_flag = []
    if live_output is False:
        outputs = subprocess.PIPE
    else:
        outputs = None
    flask = subprocess.Popen(
        [sys.executable, '-u', '-m', 'flask'] + debug_flag + ['run'],
        env=env,
        stderr=outputs,
        stdout=outputs,
    )
    if outputs is not None and (flask.stderr is None or flask.stdout is None):
        print("❗ Can't read flask output", file=sys.stderr)
        flask.kill()
        sys.exit(1)

    # Request until we get a success, but crash if we failed to start in 10
    # seconds
    start_time = time.time()
    started = False
    while time.time() - start_time < 10:
        try:
            requests.get(
                f'http://localhost:{os.getenv("FLASK_RUN_PORT")}/debug/echo',
                params={'value': 'Test script startup...'},
            )
        except requests.ConnectionError:
            continue
        started = True
        break

    if not started:
        print("❗ Server failed to start in time")
        flask.kill()
        if outputs is not None:
            write_outputs(flask, None)
        sys.exit(1)
    else:
        if flask.poll() is not None:
            print("❗ Server crashed during startup")
            if outputs is not None:
                write_outputs(flask, None)
            sys.exit(1)
        print("✅ Server started")
        return flask

疑问与调试请求

这么简单的路由居然会挂起,这让我非常担忧。我怀疑这可能是Flask或requests库的某个罕见bug。

即使不知道具体原因,也恳请提供进一步调试的思路。

内容的提问来源于stack exchange,提问作者Maddy Guthridge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 09:41:00