从子进程重新引发异常

7

我有一段在子进程中执行的代码,应该会引发一个异常。

当子进程返回异常时,我希望在主进程中抛出相同的异常(最好保留堆栈跟踪),但我不确定如何做到这一点。

我可以很好地捕获子进程的stderr,但我找不到如何解析它以获取异常类型。我该如何实现这一点?

我正在使用Python 2.7。

主方法

import subprocess

example=subprocess.Popen(["python","example.py"],
                                    stdout = subprocess.PIPE,
                                    stderr = subprocess.PIPE)
return_obj, return_error = example.communicate()

if return_error:
# replace with error from subprocess
  print "Should raise ",NameError('HiThere')
  raise TypeError('Wrong type')

子进程

raise NameError('HiThere')

我认为你无法获取信息,除非重新解析异常并抛出新的异常。 - wim
你应该考虑使用 import 代替运行另一个解释器。请注意,如果这样做,您仍然可以在单独的进程中运行 example.py 中的任何函数。 - loopbackbee
if return_error: raise Exception, return_error - Padraic Cunningham
你可能想要使用execfile()在同一进程中运行example.py脚本,以便轻松地将异常作为对象获取。 - jfs
3个回答

4
如果你只是想让你的Python代码在一个独立的进程中运行,那么你可能不应该使用subprocess。像Serge Ballesta所说的,subprocess的目的是在操作系统级别上运行一个不同的程序,它并不特别关心它是什么——没有任何东西可以帮助你正确处理Python解释器进程的复杂性。
为了实现这种目的,最好的方法可能是简单地import你的代码并使用multiprocessing,它提供了一个高级接口来帮助你在多个进程中运行Python代码。
假设你有一个在example.py上定义的良好的main函数:
from examply import main as example_main
import multiprocessing
pool= multiprocessing.Pool(1)
pool.apply( example_main )

使用此代码,异常和返回值都将透明地传递给您的主进程。
如果您不想阻塞等待结果,还可以使用Pool.apply_async。

我遇到的情况是,我的程序在标准输入上接受输入,并在某个特定的输入上崩溃。我正在使用子进程来测试这种行为。是否可以使用多进程来实现? - Bomaz
@Bomaz 这是可行的,但有些古怪 - 请参阅编程指南中有关替换stdin的部分。最简单的方法可能是重写example.py以接受一个带有文件对象的可选参数,并从测试程序传递它。 - loopbackbee
@Bomaz 或者使用 multiprocessing内置通信机制,例如管道。 - loopbackbee

1

子进程执行不同的本地应用程序。它可以是Java、C++、Lisp,甚至Fortran或Cobol。因此,您只有两种方法可以从Python子进程中获取异常:

  • 完全忘记子进程,并直接在同一Python程序中调用Python代码,简单的try except就可以完成工作。
  • 在主进程和子进程之间定义接口协议,例如要求如果没有致命异常发生,则错误输出必须为空,否则包含异常和堆栈跟踪的pickle。

0
如果两个进程都是Python进程,并且必须使用管道,则可以通过管道序列化异常对象。
调用者:
parent_conn.send(event)
result = parent_conn.recv()
if isinstance(result, Exception):
    raise result
else:
    return result

接收器:

def subprocess_loop(parent_conn, child_conn):
    
    parent_conn.close()

    while True:
        event = child_conn.recv()
        try:
            result = event_handler(event)
        except Exception as exc:
            child_conn.send(exc)
            continue
        child_conn.send(result)

接收器进程创建:

parent_conn, child_conn = Pipe()
process = Process(target=subprocess_loop, args=(parent_conn, child_conn))
process.start()
child_conn.close()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接