Я пытаюсь создать декоратор python, который принимает функцию с аргументами arg и kwargs, выполняет ее в новом процессе, выключает ее и возвращает любую возвращаемую функцию, в том числе вызывает то же исключение, если оно есть.
Пока что мой декоратор нормально обрабатывает функции, если они не вызывают исключений, но не могут обеспечить трассировку. Как передать его в родительский процесс?
from functools import wraps
from multiprocessing import Process, Queue
import sys
def process_wrapper(func):
@wraps(func)
def wrapper(*args, **kwargs):
# queue for communicating between parent and child processes
q = Queue()
def func_to_q(_q: Queue, *_args, **_kwargs):
# do the same as func, but put result into the queue. Also put
# there an exception if any.
try:
_res = func(*_args, **_kwargs)
_q.put(_res)
except:
_q.put(sys.exc_info())
# start another process and wait for it to join
p = Process(target=func_to_q, args=(q, )+args, kwargs=kwargs)
p.start()
p.join()
# get result from the queue and return it, or raise if it's an exception
res = q.get(False)
if isinstance(res, tuple) and isinstance(res[0], Exception):
raise res[1].with_traceback(res[2])
else:
return res
return wrapper
if __name__ == '__main__':
@process_wrapper
def ok():
return 'ok'
@process_wrapper
def trouble():
def inside():
raise UserWarning
inside()
print(ok())
print(trouble())
Я ожидаю, что результат будет примерно таким:
ok
Traceback (most recent call last):
File "/temp.py", line 47, in <module>
print(trouble())
File "/temp.py", line 44, in trouble
inside()
File "/temp.py", line 43, in inside
raise UserWarning
UserWarning
Process finished with exit code 1
Но похоже, что дочерний процесс не может поместить трассировку стека в очередь, и я получаю следующее:
ok
Traceback (most recent call last):
File "/temp.py", line 47, in <module>
print(trouble())
File "/temp.py", line 26, in wrapper
res = q.get(False)
File "/usr/lib/python3.6/multiprocessing/queues.py", line 107, in get
raise Empty
queue.Empty
Process finished with exit code 1
Кроме того, если дочерний элемент помещает в очередь только само исключение _q.put(sys.exc_info()[1])
, родитель получает его оттуда и вызывает, но с новой трассировкой стека (обратите внимание, отсутствует вызов inside()
):
ok
Traceback (most recent call last):
File "/temp.py", line 47, in <module>
print(trouble())
File "/temp.py", line 28, in wrapper
raise res
UserWarning
Process finished with exit code 1