Python 子进程从单元测试框架工作,但在应用程序中挂起

时间:2021-07-29 22:26:50

标签: python subprocess python-unittest

我正在围绕 SQL 数据库访问调用编写 Python 包装器,但无法使实际的子进程正常工作,因为从单元测试和实际脚本调用时,它的行为不同。

如果我注释掉 proc.wait() 调用,那么单元测试会引发一堆关于仍在运行的子进程的“资源警告”。如果我取消对 proc.wait() 和 proc.stdout.close() 和 proc.stderr.close() 语句的注释,那么单元测试可以完美运行,但是从命令行运行时脚本偶尔会挂在我身上。

单元测试环境与从命令行实际运行脚本之间的区别是什么会导致它(a)在单元测试中启动资源警告或(b)导致实际脚本从命令行运行时挂起?

有没有办法同时消除上述(a)和(b)问题?

感谢您的建议,

凯瑟琳

子进程调用

def execute_command(command, env=os.environ):

    proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, \
                            stderr=subprocess.PIPE, env=env)
    #proc.wait()
    out_string = proc.stdout.read()
    err_string = proc.stderr.read()

    #proc.stdout.close()
    #proc.stderr.close()

    out = [s.decode("utf-8") for s in out_string.split(b"\n") if (len(s) > 0)]
    err = [s.decode("utf-8") for s in err_string.split(b"\n") if (len(s) > 0)]

    #print("Command %s: out=%s, err=%s" % (command, out, err))

    return (out, err)

单元测试


from filemgr_oco3 import FileMgrOco3
import parameters_oco3 as parameters

import os
import unittest

TESTDIR = "testdata/in"

class FileMgrOco3Test(unittest.TestCase):

    def setUp(self):
        pass

    def tearDown(self):
        pass
            
    def test_lites(self):

        <snip>

        products = ["OCO3_L2DailyFP", "OCO3_L2DailySIF"]
        select = ["Filename", "StartDateTime", "EndDateTime", "CollectionLabel"]
        output = ["Filename", "StartDateTime", "EndDateTime", "CollectionLabel"]
        conditions = {"StartDateTime": (">=", "2020-04-29T00:00:00.000Z"), \
                      "EndDateTime": ("<=", "2020-04-30T00:00:00.000Z")}

        query = FileMgrOco3(products, select, output, conditions, logic="AND")
        result = query.execute_query()

        <snip>

类定义

class FileMgrOco3(object):

    <snip>

    def execute_query(self, print_debug=False):

        self.validate_query()

        if ("L2Daily" in self.products[0]):
            self.command = f"{self.PCSQUERYBIN} --url {self.FILEMGR_LITES} --sql "
        else:
            self.command = f"{self.PCSQUERYBIN} --url {self.FILEMGR_L1L2} --sql "

        self.from_string = f','.join(self.products)
        self.query_string = f"SELECT {','.join(self.select)}"
        self.output_string = ",".join([f"${o}" for o in self.output])
        self.where_string = self.build_conditions()

        self.command += f'-query "{self.query_string} ' \
                     +  f'FROM {self.from_string} WHERE {self.where_string}" ' \
                     +  f"-outputFormat '{self.output_string}' "

        (self.out, self.err) = python_utility.execute_command(self.command, \
                                                              env=self.environ)
        

1 个答案:

答案 0 :(得分:1)

关键是用对 proc.communicate() 的单个调用替换 proc.wait()、proc.stdout.read() 和 proc.stderr.read() 调用。

以下子流程代码非常适合我:

def execute_command(command, env=os.environ):

    proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, \
                            stderr=subprocess.PIPE, env=env)

    (out_string, err_string) = proc.communicate()
    out = [s.decode("utf-8") for s in out_string.split(b"\n") \
          if (len(s) > 0)]
    err = [s.decode("utf-8") for s in err_string.split(b"\n") \
           if (len(s) > 0)]