Mein Ziel ist es, die stdout
aus mehreren Teilprozesse in einige Warteschlangen umzuleiten und diese irgendwo auszudrucken (vielleicht in einer kleinen GUI).Multiprozessor-Warteschlange in Python 3.5 kann nicht abgeleitet werden
Der erste Schritt ist die Unterklasse Queue
in ein Objekt, das sich sehr ähnlich verhält wie stdout
. Aber da bin ich stecken geblieben. Das Subclassing des Multiprocessing Queue
scheint in Python v3.5 unmöglich zu sein.
# 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()
fand ich diese Schnipsel in dem folgenden Beitrag (wahrscheinlich Python 3.5 hat noch nicht in diesem Augenblick vorhanden sind): Python multiprocessing redirect stdout of a child process to a Tkinter Text
In Python v3.5 Sie auf seltsame Fehlermeldungen stolpern, wenn die Multiprocessing Queue
Klasse Subklassen . Ich fand zwei Bugreports beschreibt das Problem:
https://bugs.python.org/issue21367
https://bugs.python.org/issue19895
Ich habe 2 Fragen:
- Angenommen, ich möchte Python v3.5 bleiben - zu einer früheren Version gehen ist nicht wirklich eine Option. Welche Problemumgehung kann ich verwenden, um die Multiprocessing Queue irgendwie zu unterklassifizieren?
- Ist der Fehler immer noch vorhanden, wenn ich auf Python v3.6 aufrüste?
EDIT:
Es ist ein bekanntes Problem, wenn Sie versuchen, die Queue
Klasse in hier zu Unterklasse:
from multiprocessing import Queue # <- known issue: you cannot subclass
# this Queue class, because it is
# not a genuine python class.
Aber die folgenden sollte funktionieren:
from multiprocessing.queues import Queue # <- from this Queue class, you
# should be able to make a
# subclass. But Python 3.5
# refuses :-(
Leider funktioniert das auch nicht in Python v3.5. Sie erhalten die folgende Fehlermeldung:
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:
Danke Darth Kotik das Problem zu lösen! Hier ist der vollständige Code, der mit seiner Lösung aktualisiert wurde. Jetzt funktioniert es.
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()
Sieht aus wie das gleiche Problem, http://StackOverflow.com/Questions/24941359/ctx-Parameter-in-Multiprocessing-Queue –
Hi @BiRico, vielen Dank. Leider ist es nicht das gleiche Problem. Bei dem in diesem Post beschriebenen Problem handelt es sich um das Instanziieren eines Warteschlangenobjekts. Das Problem der Unterklassen-Warteschlange wird weder erwähnt noch erklärt. –
Ich konnte eine Unterklasse in Python 3.5.2 erstellen, kann aber die Warteschlange noch nicht beenden. –