Python subprocess调用Genymotion重启命令超时,手动执行正常
问题描述
我编写了以下Python函数用于重启Genymotion设备:
def restart_device(d, stop_timeout=40, start_timeout=30): """Restarts a specific Genymotion device with timeouts and logs the process.""" try: device_name = get_device_name_by_ip(d.serial) logging.info(f"Stopping device: {device_name}") subprocess.run([gmtoolPath, "admin", "stop", device_name], check=True, timeout=stop_timeout) time.sleep(5) # Wait a bit before restarting logging.info(f"Starting device: {device_name}") subprocess.run([gmtoolPath, "admin", "start", device_name], check=True, timeout=start_timeout) # Wait for the device to boot up logging.info(f"Waiting for device {device_name} to boot...") time.sleep(30) # Adjust the wait time as needed logging.info(f"Device {device_name} restarted successfully.") except subprocess.CalledProcessError as e: logging.error(f"Error restarting device {device_name}: {e.stderr}") except subprocess.TimeoutExpired as e: logging.error(f"Timeout expired while restarting device {device_name}: {e}")
执行该函数时会触发超时,但手动在终端运行对应的gmtool admin stop <device_name>和gmtool admin start <device_name>命令完全正常,且另一台电脑上运行相同代码无异常。
排查方向与解决方案
1. 环境变量差异
手动执行终端的环境变量和Python进程的环境变量可能不一致,尤其是gmtoolPath的路径是否正确,或是gmtool依赖的系统路径未在Python进程中配置。可在代码中打印对比:
import os print(os.environ["PATH"]) print(gmtoolPath)
确认gmtool的完整路径是否正确,也可以在subprocess调用中直接指定gmtool的绝对路径,避免依赖环境变量。
2. 子进程输出阻塞
subprocess.run默认会捕获stdout和stderr,若gmtool执行时产生大量输出,可能导致缓冲区阻塞进而触发超时。可修改调用方式重定向输出:
# 丢弃输出 subprocess.run( [gmtoolPath, "admin", "stop", device_name], check=True, timeout=stop_timeout, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT )
或写入日志文件:
with open("gmtool_exec_log.txt", "a") as f: subprocess.run( [gmtoolPath, "admin", "stop", device_name], check=True, timeout=stop_timeout, stdout=f, stderr=f )
3. 运行权限差异
Python进程的运行权限与手动执行终端的权限可能不同,比如是否有访问Genymotion设备目录、虚拟设备文件的权限,或是是否以管理员身份运行。尝试以相同权限运行Python脚本(比如手动终端用普通用户,脚本也用该用户运行;或都用管理员权限)。
4. 超时时间调整
不同机器性能有差异,当前设置的超时时间可能不足以支撑本地设备的启停操作。可临时调大stop_timeout和start_timeout的值(比如设为60秒),测试是否还会超时。若调大后正常,说明本地机器需要更长的执行时间。
5. 设备状态预检查
执行stop命令前,先调用gmtool admin list获取设备状态,确认设备处于可停止状态再执行操作,避免因设备异常导致stop命令耗时过长。
内容的提问来源于stack exchange,提问作者Achiya Ben Natan

