[英]How to return stdout from long running process with subprocess and Popen?
[英]How to return a dictionary as a function's return value running as a subprocess to its parent process?
我有两个脚本 parent.py 和 child.py parent.py 调用 child.py 作为子进程。 Child.py 有一个函数可以在字典中收集某些结果,我希望将该字典返回给父进程。 我曾尝试将该字典从 child.py 打印到其 STDOUT 上,以便父进程可以读取它,但这对我没有帮助,因为字典的内容被父进程读取为单独行上的字符串。
此外,正如评论中所建议的,我尝试使用 JSON 序列化字典,同时在标准输出上打印它,并使用 JSON 从父级读取它,这工作正常,但我也将许多其他信息从子级打印到其标准输出最终也被父母阅读并且正在混淆。
提出的另一个建议是将子进程的结果写入目录中的文件,并使父进程从该文件中读取。 这也可以,但是我会在 Celery 中运行 100 个此代码的实例,因此它会导致其他子实例覆盖同一文件。
我的问题是,由于我们有一个连接这两个进程的 PIPE,我如何才能将我的字典直接从 child.py 写入 PIPE 并从 parent.py 中读取它
# parent.py
import subprocess
proc = subprocess.Popen(['python3', 'child.py'],
stdin=subprocess.PIPE,
stdout = subprocess.PIPE
)
proc.comunicate()
result = proc.stdout
#child.py
def child_function():
result = {}
result[1] = "one"
result[2] = "two"
print(result)
#return result
if __name__ == "__main__":
child_function()
运行 Python 的子进程与运行其他东西的子进程没有任何区别。 Python 不知道或不关心其他程序是否也是 Python 程序; 他们无法访问彼此的变量、内存、运行状态或其他内部信息。 简单地想象一下子进程是一个整体的二进制文件。 您可以与之通信的唯一方法是发送和接收字节(可以是字符串,如果您同意字符编码)和信号(这样您就可以终止子进程,或引发其他一些可以捕获和处理的信号 - - 就像一个计时器;当计时器到期时,您只会获得一位信息,而您对该位的处理取决于信号的接收者)。
“序列化”信息意味着以允许接收者反序列化的方式对其进行编码。 JSON 就是一个很好的例子; 您可以将包含(可能是嵌套结构)字典或列表的结构作为文本传输,并且接收者将知道如何将该字节流映射到相同的结构中。
当发送方和接收方运行相同的 Python 版本时,您也可以使用泡菜; pickle 是一种原生 Python 格式,它允许您传输更丰富的结构。 但如果您的需求不大,我会简单地使用 JSON。
parent.py
:
import subprocess
import json
# Prefer subprocess.run() over bare-bones Popen()
proc = subprocess.run(['python3', 'child.py'],
check=True, capture_output=True, text=True)
result = json.loads(proc.stdout)
child.py
:
import json
import logging
def child_function():
result = {}
result[1] = "one"
result[2] = "two"
loggging.info('Some unrelated output which should not go into the JSON')
print(json.dumps(result))
#return result
if __name__ == "__main__":
logging.basicConfig(level=logging.WARNING)
child_function()
为避免将 JSON 与其他输出混合,请将其他输出打印到标准错误而不是标准输出(或者毕竟想办法将其嵌入到 JSON 中)。 logging
模块是一种方便的方法,您可以轻松地、部分地或完全地将其关闭(上面的示例演示了通过logging.basicConfig
关闭的日志记录,因为它只选择打印优先级为WARNING
的消息)或更高,不包括INFO
)。 父proc.stderr
将在proc.stderr
获取这些消息。
让父级为子级创建一个 FIFO(命名管道):
with os.mkfifo(mypipe) as pipe:
proc = subprocess.Popen(['python3', 'child.py', 'mypipe'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
print(pipe.read())
现在孩子可以这样做:
pipe_path = # get from argv
with open(pipe_path, 'w') as pipe:
pipe.write(str(result))
这使您的通信与 stdin/stdout/stderr 分开。
您可以通过文件获取结果。
父母.py:
import tempfile
import os
import subprocess
import json
fd, temp_file_name = tempfile.mkstemp() # create temporary file
os.close(fd) # close the file
proc = subprocess.Popen(['python3', 'child.py', temp_file_name]) # pass file_name
proc.communicate()
with open(temp_file_name) as fp:
result = json.load(fp) # get dictionary from here
os.unlink(temp_file_name) # no longer need this file
孩子.py:
import sys
import json
def child_function(temp_file_name):
result = {}
result[1] = "one"
result[2] = "two"
with open(temp_file_name, 'w') as fp:
json.dump(result, fp)
if __name__ == "__main__":
child_function(sys.argv[1]) # pass the file name argument
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.