sys.excepthook 在 multiprocessing.Process 被忽略了?
sys.excepthook in multiprocessing.Process ignored?
假设我们有两个文件,即 mymanger.py
和 mysub.py
。
mymanager.py
import time
from multiprocessing import Process
import mysub # the process file
def main():
xprocess = Process(
target=mysub.main,
)
xprocess.start()
xprocess.join()
time.sleep(1)
print(f"== Done, errorcode is {xprocess.exitcode} ==")
if __name__ == '__main__':
main()
mysub.py
import sys
def myexception(exc_type, exc_value, exc_traceback):
print("I want this to be printed!")
print("Uncaught exception", exc_type, exc_value, exc_traceback)
def main():
sys.excepthook = myexception # !!!
raise ValueError()
if __name__ == "__main__":
sys.exit()
执行mymanager.py
时,结果输出为:
Process Process-1:
Traceback (most recent call last):
File "c:\program files\python.9\lib\multiprocessing\process.py", line 315, in _bootstrap
self.run()
File "c:\program files\python.9\lib\multiprocessing\process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\lx\mysub.py", line 11, in main
raise ValueError()
ValueError
== Done, errorcode is 1 ==
当我预期的输出类似于:
I want this to be printed!
Uncaught exception <class 'ValueError'> <traceback object at 0x0000027B6F952780>
如果我在没有 multiprocessing.Process
.
的情况下从 mysub.py
执行 main
,我会得到什么
我检查了底层的 cpython (reference),问题似乎是 _boostrap
函数中的 try-except
优先于我的子进程 sys.excepthook
但根据我的理解,不应该先触发子进程的例外钩子,然后触发 _boostrap
的 except
?
我需要子进程使用sys.excepthook
函数来处理异常。
我怎样才能做到这一点?
sys.excepthook
在异常未被捕获时调用(从 运行ning 程序中一直冒出)。但是 Process
objects 运行 它们的目标函数在一个特殊的 bootstrap 函数中(BaseProcess._bootstrap
如果它对你很重要)它有意捕获所有异常,打印有关失败进程的信息以及回溯,然后 returns 调用者的退出代码(启动器因启动方法而异)。
当使用 fork
start 方法时,_bootstrap
的调用者然后使用 os._exit(code)
退出 worker(一个绕过正常异常处理系统的“硬退出”命令,尽管因为您的异常已经被捕获并处理,所以这无关紧要)。当使用 'spawn'
时,它使用 sys.exit
而不是 os._exit
,但是据我所知 sys.exit
实现的 SystemExit
异常在解释器中是特殊情况所以它在未被捕获时不会通过 sys.excepthook
(大概是因为它通过异常实现被认为是一个实现细节;当你 要求 退出程序时它与死亡不同出现意外异常)。
总结:无论启动方法如何,您的代码引发的异常都不可能“未处理”(达到 sys.excepthook
的目的),因为 multiprocessing
处理 所有 函数可以自行抛出的异常 。 理论上 可能 excepthook
您在工作程序中设置的异常引发 在 之后 target
完成,如果multiprocessing
包装器代码本身会引发异常,但前提是你做了病态的事情,比如替换 os._exit
或 sys.exit
的定义(它只会报告因为你替换了它们而发生的可怕事情,你自己的异常已经被那个点吞没了,所以不要那样做)。
如果您真的想这样做,最接近的方法是显式捕获异常并手动调用您的处理程序。例如,一个简单的包装函数将允许这样做:
def handle_exceptions_with(excepthook, target, /, *args, **kwargs)
try:
target(*args, **kwargs)
except:
excepthook(*sys.exc_info())
raise # Or maybe convert to sys.exit(1) if you don't want multiprocessing to print it again
将您的 Process
启动更改为:
xprocess = Process(
target=handle_exceptions_with,
args=(mysub.myexception, mysub.main)
)
或者为了one-off使用,偷懒只把mysub.main
重写为:
def main():
try:
raise ValueError()
except:
myexception(*sys.exc_info())
raise # Or maybe convert to sys.exit(1) if you don't want multiprocessing to print it again
并保持其他一切不变。您仍然可以在 sys.excepthook
and/or threading.excepthook()
中设置您的处理程序(以处理在工作进程中启动的线程可能因未处理的异常而终止的情况),但它不适用于工作进程的主线程(或者更准确地说,异常无法到达它)。
假设我们有两个文件,即 mymanger.py
和 mysub.py
。
mymanager.py
import time
from multiprocessing import Process
import mysub # the process file
def main():
xprocess = Process(
target=mysub.main,
)
xprocess.start()
xprocess.join()
time.sleep(1)
print(f"== Done, errorcode is {xprocess.exitcode} ==")
if __name__ == '__main__':
main()
mysub.py
import sys
def myexception(exc_type, exc_value, exc_traceback):
print("I want this to be printed!")
print("Uncaught exception", exc_type, exc_value, exc_traceback)
def main():
sys.excepthook = myexception # !!!
raise ValueError()
if __name__ == "__main__":
sys.exit()
执行mymanager.py
时,结果输出为:
Process Process-1:
Traceback (most recent call last):
File "c:\program files\python.9\lib\multiprocessing\process.py", line 315, in _bootstrap
self.run()
File "c:\program files\python.9\lib\multiprocessing\process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\lx\mysub.py", line 11, in main
raise ValueError()
ValueError
== Done, errorcode is 1 ==
当我预期的输出类似于:
I want this to be printed!
Uncaught exception <class 'ValueError'> <traceback object at 0x0000027B6F952780>
如果我在没有 multiprocessing.Process
.
mysub.py
执行 main
,我会得到什么
我检查了底层的 cpython (reference),问题似乎是 _boostrap
函数中的 try-except
优先于我的子进程 sys.excepthook
但根据我的理解,不应该先触发子进程的例外钩子,然后触发 _boostrap
的 except
?
我需要子进程使用sys.excepthook
函数来处理异常。
我怎样才能做到这一点?
sys.excepthook
在异常未被捕获时调用(从 运行ning 程序中一直冒出)。但是 Process
objects 运行 它们的目标函数在一个特殊的 bootstrap 函数中(BaseProcess._bootstrap
如果它对你很重要)它有意捕获所有异常,打印有关失败进程的信息以及回溯,然后 returns 调用者的退出代码(启动器因启动方法而异)。
当使用 fork
start 方法时,_bootstrap
的调用者然后使用 os._exit(code)
退出 worker(一个绕过正常异常处理系统的“硬退出”命令,尽管因为您的异常已经被捕获并处理,所以这无关紧要)。当使用 'spawn'
时,它使用 sys.exit
而不是 os._exit
,但是据我所知 sys.exit
实现的 SystemExit
异常在解释器中是特殊情况所以它在未被捕获时不会通过 sys.excepthook
(大概是因为它通过异常实现被认为是一个实现细节;当你 要求 退出程序时它与死亡不同出现意外异常)。
总结:无论启动方法如何,您的代码引发的异常都不可能“未处理”(达到 sys.excepthook
的目的),因为 multiprocessing
处理 所有 函数可以自行抛出的异常 。 理论上 可能 excepthook
您在工作程序中设置的异常引发 在 之后 target
完成,如果multiprocessing
包装器代码本身会引发异常,但前提是你做了病态的事情,比如替换 os._exit
或 sys.exit
的定义(它只会报告因为你替换了它们而发生的可怕事情,你自己的异常已经被那个点吞没了,所以不要那样做)。
如果您真的想这样做,最接近的方法是显式捕获异常并手动调用您的处理程序。例如,一个简单的包装函数将允许这样做:
def handle_exceptions_with(excepthook, target, /, *args, **kwargs)
try:
target(*args, **kwargs)
except:
excepthook(*sys.exc_info())
raise # Or maybe convert to sys.exit(1) if you don't want multiprocessing to print it again
将您的 Process
启动更改为:
xprocess = Process(
target=handle_exceptions_with,
args=(mysub.myexception, mysub.main)
)
或者为了one-off使用,偷懒只把mysub.main
重写为:
def main():
try:
raise ValueError()
except:
myexception(*sys.exc_info())
raise # Or maybe convert to sys.exit(1) if you don't want multiprocessing to print it again
并保持其他一切不变。您仍然可以在 sys.excepthook
and/or threading.excepthook()
中设置您的处理程序(以处理在工作进程中启动的线程可能因未处理的异常而终止的情况),但它不适用于工作进程的主线程(或者更准确地说,异常无法到达它)。