1

我有 Flask 应用程序,它暴露了可以在后台运行应用程序的 API,并且可以稍后通过指定 PID 来终止它。但是,出于单元测试的目的,在杀死 PID 并检查 PID 是否被杀死后,psutil.pid_exists(pid)它似乎总是返回 true。我手动检查了 PID 不存在并psutil.pid_exists(pid)在不同的 python 控制台上运行,它返回 true。这导致我的测试失败。

views.py中,我有:

@api.route('/cancel/<pid>', methods=['POST'])
def cancel(pid=None):
    try:
        os.kill(int(pid), signal.SIGTERM)

        data = dict(
            message = 'Successfully killed pid ' + pid)

        return jsonify(status='success', data=data), 200
    except:
        data = dict(
            message = 'Fail to kill job with pid ' + pid)
        return jsonify(status='error', data=data), 400

在我的测试中:

def test_cancel_job(self):
    # run_script will run something in the background and return the PID
    jobid, pid, cmd = run_script('fake_db', 'fake_cancel_jobid', 'tests/doubles/child.py')

    if not psutil.pid_exists(pid):
        raise Exception('Process is not running')

    # kill the job and assert it is successful
    resp = self.client.post('/api/cancel/' + str(pid))
    self.assert200(resp)

    # at this point, I have confirmed that the PID has been killed
    # but, the line below still get executed
    # psutil.pid_exists(pid) returns true

    # check pid is really killed
    if psutil.pid_exists(pid):
        raise Exception('Process {0} still exist'.format(pid))

如果这有什么不同,我正在 OSX 上运行。

更新:我尝试在我的构建服务器(Ubuntu 14.04)上运行测试,但测试失败。

这是我的run_script

def run_script(db, jobid, script):
    log = SCRIPTS_LOG + jobid + ".log"

    if not os.path.exists(SCRIPTS_LOG):
        os.makedirs(SCRIPTS_LOG)

    with open(log, "w") as output:
        cmd = ["nohup", "python", script, db]
        p = subprocess.Popen(cmd, stdout=output)

        return jobid, p.pid, " ".join(cmd)

和我的child.py

#!/usr/bin/env python

import time
import os, sys
if 'TEST_ENV' not in os.environ:
    os.environ['TEST_ENV'] = 'Set some env'

    try:
        os.execv(sys.argv[0], sys.argv)
    except Exception, exc:
        print "Failed re-exec:", exc
        sys.exit(1)


def main(argv):
    db = argv[0]

    while True:
        print 'Running child with params ', db
        time.sleep(1)


if __name__ == '__main__':
    main(sys.argv[1:])

我添加了一个简单的脚本来演示这一点。https://github.com/shulhi/kill-pid/tree/master

4

1 回答 1

0

终止正在运行的进程需要时间。在这种情况下,它是一个子进程,因此os.wait()或其变体之一将确切知道要等待多长时间。为了面向未来,我会使用os.waitpid(pid, os.WEXITED).

于 2018-02-07T15:14:05.977 回答