一、背景

我们日常测试中存在大量重复的造数操作,且流程较长,为了提升测试效率,我们搭建了数据构造平台。平台采用了前端 脚本分离的形式,数据构造脚本独立存在,页面和脚本的关联关系通过页面配置进行绑定。

页面配置中,包含了脚本的路径以及启动命令,因此,运行脚本的时候需要在服务器上启动子进程中去执行脚本命令。为了能够了解脚本的执行情况,还需要获取脚本的执行状态以及执行日志。

平台后端语言是 Python,因此,选择了 Python 中的 subprocess 模块,本文重点阐述 subprocess 模块在项目实战中遇到的问题以及解决方案。

本文涉及的程序执行环境如下:

Python 版本:3.8.3

操作系统:windows server

二、subprocess 模块基础

subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。subprocess 模块首先推荐使用的是它的 run 方法,更高级的用法可以直接使用 Popen 接口。

1. subprocess.run 方法

subprocess.run() 方法是 3.5 版本新增的,用于可以接受等待进程执行结束后获取返回值的场景,如果可以满足使用需求,官方推荐使用 run() 方法。
subprocess.run() 的执行过程是同步的,脚本执行结束之前是阻塞的,只有脚本结束之后才会返回 subprocess.CompletedProcess 对象。

2. subprocess.Popen 方法

subprocess.Popen() 是 subprocess 的核心,子进程的创建和管理都靠它处理。Popen() 相当于 run() 的高级版本,更加灵活,使开发人员能够处理 run() 方法未涵盖的更丰富的场景。subprocess.Popen() 是异步的,进程启动以后,我们可以通过预先指定好的 stdout 和 stderr 来实时读取到子进程的输出。

subprocess.Popen()常用参数介绍:

args:shell命令,可以是字符串或者序列类型(如:list,元组)

stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄

shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令,args只能是String类型的参数;该参数为False,args可以是序列类型。

Popen 对象常用方法:

poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None,项目中通过该方法返回判断进程是否执行结束。

wait(timeout): 等待子进程终止,如果进程执行时间较长,可以使用该方法来保证进程执行完整。

communicate(input,timeout): 和子进程交互,发送和读取数据。

send_signal(singnal): 发送信号到子进程 。

terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。

kill(): 杀死子进程。发送 SIGKILL 信号到子进程。

3. run 与 Popen 的同步/异步对比实验

Run() 和 Popen() 同步/异步的简单对比如下:

从执行结果可以看出,Popen 在子进程执行过程中就可以获取到日志,run 需要等待进程执行完成才能获取到日志。如果需要执行的命令耗时很短,可以选择 run 方法。因为我们的数据构造流程通常比较长,需要实时获取日志,所以选择了 Popen。

三、遇到的问题与解决方案

在使用 Popen 的过程中也遇到了一些问题,下面将具体介绍一下遇到的问题以及解决方案。

如何保证获取到完整的进程执行日志

subprocess.Popen() 可以获取到执行过程中的日志了,那我们如何保证进程日志获取的完整性呢?我们来看下具体方案:

方案一:这是我们最开始采用的方案。通过获取方法 poll() 返回的状态码来检查进程是否终止。如果终止,返回 returncode,否则返回 None,代码如下:

该方案在使用的过程中存在问题。当子程序已经执行完毕,日志还没有获取完整,会出现日志接收不全的情况。为了解决这种问题,保证日志的完整性,我们选择通过判断日志是否读取完毕作为判断依据,详细参见方案二。

方案二:通过判断日志是否读取完毕保证日志完整性。代码如下:

这种方法看似解决了日志不全的问题,但是存在着一定的风险。日志为 None 无法有效保证子进程执行结束(虽然经过多方实践,暂时没有发现日志为 None 但脚本未执行结束的情况)。为了安全起见,我们还是需要兼顾一下进程的执行状态,具体参见方案三。

方案三:通过判断 poll() 返回状态和日志返回值,也就是说,程序状态结束且返回对象为空,才表示子进程已经执行结束,并且获取到了完整的日志,代码如下:

该方案已经比较完善了,通过子进程执行结束并且执行日志为 None,保证执行日志的完整性。美中不足的是,日志信息可能会比实际的多一些,当输出先读取完毕,子进程还没有结束,我们会获取到一部分空行,为了日志的美观度,我们可以进一步优化,获取日志的时候,过滤掉空行,代码如下:

通过判断输出流和进程的执行状态,完美的解决了上面的问题,保证了日志的完整性与正确性。

如何保证脚本进程正常终止 当脚本执行以后,我们可能会因为某些原因想终止脚本的运行,如参数错误等。 在我们项目代码中,使用 Popen.terminate() 去终止进程的时候,发现命令只终止了父进程,唤起的子进程仍然在执行。

为了找到原因,先看一下项目中创建 Popen 的代码:

参数介绍的时候提到过,shell 为 True 或 False 时,command 的类型是有要求的。因为我们 command 传值是 String 类型,参数 shell 只能设置为 True。当 shell=True 的时,程序会创建一个 shell 进程,command 是 shell 进程的子进程。

我们再来看下 Popen.terminate() 做了什么?官方的说明如下:

Stop the child. On POSIX OSs the method sends SIGTERM to the child. On Windows the Win32 API function TerminateProcess()is called to stop the child

也就是说,在 POSIX 系统中,该方法会发送 SIGTERM 信号给子进程;

在 Windows 系统中,该方法会调用 Win32 提供的 API TerminateProcess() 方法。

原因很清晰了,当 shell=True 的时候,发送 SIGTERM 能够杀死 shell 进程,但是无法杀死它的子进程(command);windows 系统中同理,TerminateProcess() 杀死了 shell 进程,却没有杀死它的子进程(command)。

解决方案如下:

方案一:比较优雅的方式,创建 Popen 对象时,将参数 shell 设为 False。实践发现,当 shell=False 的时候,Popen.terminate() 方法的执行结果是符合预期的;

subprocess.Popen(command, shell=False)

前面提到过,因为 command 格式问题,在我们项目中,shell 只能设置为 True,所以我们又探索了新的解决方案。

方案二:手动终止进程。使用第三方工具包 psutil,获取全部的子进程并逐一杀掉,该方法在 Linux 和 windows 平台通用。代码见下图。

在 windows 服务器下,还可以用以下命令:

taskkill /t /f /pid {pid},强制杀掉指定进程以及它的子进程。

windows 平台的方案无需第三方依赖,所以我们项目中选择了该方案,项目代码如下:

以上就是 Python 中的 subprocess 模块在我们项目实践中遇到的问题以及解决方案,希望可以给大家提供一些使用思路以及规避掉一系列问题,更多关于Python测试开发subprocess的资料请关注Devmax其它相关文章!

Python移动测试开发subprocess模块项目实战的更多相关文章

  1. XCode 3.2 Ruby和Python模板

    在xcode3.2下,我的ObjectiveCPython/Ruby项目仍然可以打开更新和编译,但是你无法创建新项目.鉴于xcode3.2中缺少ruby和python的所有痕迹(即创建项目并添加新的ruby/python文件),是否有一种简单的方法可以再次安装模板?我发现了一些关于将它们复制到某个文件夹的信息,但我似乎无法让它工作,我怀疑文件夹的位置已经改变为3.2.解决方法3.2中的应用程序模板

  2. Swift基本使用-函数和闭包(三)

    声明函数和其他脚本语言有相似的地方,比较明显的地方是声明函数的关键字swift也出现了Python中的组元,可以通过一个组元返回多个值。传递可变参数,函数以数组的形式获取参数swift中函数可以嵌套,被嵌套的函数可以访问外部函数的变量。可以通过函数的潜逃来重构过长或者太复杂的函数。

  3. 10 个Python中Pip的使用技巧分享

    众所周知,pip 可以安装、更新、卸载 Python 的第三方库,非常方便。本文小编为大家总结了Python中Pip的使用技巧,需要的可以参考一下

  4. Swift、Go、Julia与R能否挑战 Python 的王者地位

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 红薯因 Swift 重写开源中国失败,貌似欲改用 Python

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  6. 你没看错:Swift可以直接调用Python函数库

    上周Perfect又推出了新一轮服务器端Swift增强函数库:Perfect-Python。对,你没看错,在服务器端Swift其实可以轻松从其他语种的函数库中直接拿来调用,不需要修改任何内容。以如下python脚本为例:Perfect-Python可以用下列方法封装并调用以上函数,您所需要注意的仅仅是其函数名称以及参数。

  7. Swift中的列表解析

    在Swift中完成这个的最简单的方法是什么?我在寻找类似的东西:从Swift2.x开始,有一些与你的Python样式列表解析相当的东西。(在这个意义上,它更像是Python的xrange。如果你想保持集合懒惰一路通过,只是这样说:与Python中的列表解析语法不同,Swift中的这些操作遵循与其他操作相同的语法。

  8. swift抛出终端的python错误

    每当我尝试启动与python相关的swift时,我都会收到错误.我该如何解决?

  9. 在Android上用Java嵌入Python

    解决方法看看this,它适用于J2SE,你可以尝试在Android上运行.

  10. 在android studio中使用python代码构建android应用程序

    我有一些python代码和它的机器人,我正在寻找一种方法来使用android项目中的那些python代码.有没有办法做到这一点!?解决方法有两种主要工具可供使用,它们彼此不同:>QPython>Kivy使用Kivy,大致相同的代码也可以部署到IOS.

随机推荐

  1. 10 个Python中Pip的使用技巧分享

    众所周知,pip 可以安装、更新、卸载 Python 的第三方库,非常方便。本文小编为大家总结了Python中Pip的使用技巧,需要的可以参考一下

  2. python数学建模之三大模型与十大常用算法详情

    这篇文章主要介绍了python数学建模之三大模型与十大常用算法详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感想取得小伙伴可以参考一下

  3. Python爬取奶茶店数据分析哪家最好喝以及性价比

    这篇文章主要介绍了用Python告诉你奶茶哪家最好喝性价比最高,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

  4. 使用pyinstaller打包.exe文件的详细教程

    PyInstaller是一个跨平台的Python应用打包工具,能够把 Python 脚本及其所在的 Python 解释器打包成可执行文件,下面这篇文章主要给大家介绍了关于使用pyinstaller打包.exe文件的相关资料,需要的朋友可以参考下

  5. 基于Python实现射击小游戏的制作

    这篇文章主要介绍了如何利用Python制作一个自己专属的第一人称射击小游戏,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起动手试一试

  6. Python list append方法之给列表追加元素

    这篇文章主要介绍了Python list append方法如何给列表追加元素,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  7. Pytest+Request+Allure+Jenkins实现接口自动化

    这篇文章介绍了Pytest+Request+Allure+Jenkins实现接口自动化的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  8. 利用python实现简单的情感分析实例教程

    商品评论挖掘、电影推荐、股市预测……情感分析大有用武之地,下面这篇文章主要给大家介绍了关于利用python实现简单的情感分析的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

  9. 利用Python上传日志并监控告警的方法详解

    这篇文章将详细为大家介绍如何通过阿里云日志服务搭建一套通过Python上传日志、配置日志告警的监控服务,感兴趣的小伙伴可以了解一下

  10. Pycharm中运行程序在Python console中执行,不是直接Run问题

    这篇文章主要介绍了Pycharm中运行程序在Python console中执行,不是直接Run问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

返回
顶部