Моя конечная цель - перенаправить stdout
от нескольких подпроцессов к некоторым очередям и распечатать их где-нибудь (возможно, в небольшом графическом интерфейсе).Невозможно подпроцесс многопроцессорной очереди в Python 3.5
Первым шагом является подкласс Queue
в объект, который ведет себя так же, как stdout
. Но вот где я застрял. Подкласс многопроцессорности Queue
кажется невозможным в Python v3.5.
# This is a Queue that behaves like stdout
# Unfortunately, doesn't work in Python 3.5 :-(
class StdoutQueue(Queue):
def __init__(self,*args,**kwargs):
Queue.__init__(self,*args,**kwargs, ctx='')
def write(self,msg):
self.put(msg)
def flush(self):
sys.__stdout__.flush()
Я нашел этот фрагмент в следующем посте (возможно, Python 3.5 еще не существует в данный момент): Python multiprocessing redirect stdout of a child process to a Tkinter Text
В Python v3.5 вы наткнуться на странных сообщениях об ошибках, когда подклассы многопроцессорных Queue
класса , Я нашел два сообщения об ошибках, описывающие проблему:
https://bugs.python.org/issue21367
https://bugs.python.org/issue19895
У меня 2 вопроса:
- Предположим, я хочу, чтобы придерживаться Python v3.5 - идти к предыдущей версии на самом деле не вариант. Какое обходное решение можно использовать для подкласса очереди многопроцессорности?
- Является ли ошибка еще при обновлении до Python v3.6?
EDIT:
Существует известная проблема, когда вы пытаетесь подкласс Queue
класс найден здесь:
from multiprocessing import Queue # <- known issue: you cannot subclass
# this Queue class, because it is
# not a genuine python class.
Но должно работать:
from multiprocessing.queues import Queue # <- from this Queue class, you
# should be able to make a
# subclass. But Python 3.5
# refuses :-(
К сожалению, даже это не работает в Python v3.5. Вы получите следующее сообщение об ошибке:
C:\Users\..\myFolder > python myTest.py
Traceback (most recent call last):
File "myTest.py", line 49, in <module>
q = StdoutQueue()
File "myTest.py", line 22, in __init__
super(StdoutQueue,self).__init__(*args,**kwargs)
TypeError: __init__() missing 1 required keyword-only argument: 'ctx'
EDIT:
Спасибо Дарт Котик для решения проблемы! Вот полный код, обновленный его решением. Теперь это работает.
import sys
import time
import multiprocessing as mp
import multiprocessing.queues as mpq
from threading import Thread
from tkinter import *
'''-------------------------------------------------------------------'''
''' SUBCLASSING THE MULTIPROCESSING QUEUE '''
''' '''
''' ..and make it behave as a general stdout io '''
'''-------------------------------------------------------------------'''
# The StdoutQueue is a Queue that behaves like stdout.
# We will subclass the Queue class from the multiprocessing package
# and give it the typical stdout functions.
#
# (1) First issue
# Subclassing multiprocessing.Queue or multiprocessing.SimpleQueue
# will not work, because these classes are not genuine
# python classes.
# Therefore, you need to subclass multiprocessing.queues.Queue or
# multiprocessing.queues.SimpleQueue . This issue is known, and is not
# the reason for asking this question. But I mention it here, for
# completeness.
#
# (2) Second issue
# There is another problem that arises only in Python V5 (and beyond).
# When subclassing multiprocessing.queues.Queue, you have to provide
# a 'multiprocessing context'. Not doing that, leads to an obscure error
# message, which is in fact the main topic of this question. Darth Kotik
# solved it.
# His solution is visible in this code:
class StdoutQueue(mpq.Queue):
def __init__(self,*args,**kwargs):
ctx = mp.get_context()
super(StdoutQueue, self).__init__(*args, **kwargs, ctx=ctx)
def write(self,msg):
self.put(msg)
def flush(self):
sys.__stdout__.flush()
'''-------------------------------------------------------------------'''
''' TEST SETUP '''
'''-------------------------------------------------------------------'''
# This function takes the text widget and a queue as inputs.
# It functions by waiting on new data entering the queue, when it
# finds new data it will insert it into the text widget.
def text_catcher(text_widget,queue):
while True:
text_widget.insert(END, queue.get())
def test_child(q):
# This line only redirects stdout inside the current process
sys.stdout = q
# or sys.stdout = sys.__stdout__ if you want to print the child to the terminal
print('child running')
def test_parent(q):
# Again this only redirects inside the current (main) process
# commenting this like out will cause only the child to write to the widget
sys.stdout = q
print('parent running')
time.sleep(0.5)
mp.Process(target=test_child,args=(q,)).start()
if __name__ == '__main__':
gui_root = Tk()
gui_txt = Text(gui_root)
gui_txt.pack()
q = StdoutQueue()
gui_btn = Button(gui_root, text='Test', command=lambda:test_parent(q),)
gui_btn.pack()
# Instantiate and start the text monitor
monitor = Thread(target=text_catcher,args=(gui_txt,q))
monitor.daemon = True
monitor.start()
gui_root.mainloop()
Похож на ту же проблему, http://stackoverflow.com/questions/24941359/ctx-parameter-in-multiprocessing-queue –
Привет @BiRico, большое вам спасибо. К сожалению, это не та же проблема. Проблема, описанная в этой статье, заключается в создании экземпляра объекта Queue. Проблема очереди подклассов не упоминается и не объясняется. –
Мне удалось создать подкласс в Python 3.5.2, но пока не могу завершить очередь. –