1

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:

  1. 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?
  2. 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() 
+0

Sieht aus wie das gleiche Problem, http://StackOverflow.com/Questions/24941359/ctx-Parameter-in-Multiprocessing-Queue –

+0

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. –

+0

Ich konnte eine Unterklasse in Python 3.5.2 erstellen, kann aber die Warteschlange noch nicht beenden. –

Antwort

3
>>> import multiprocessing 
>>> type(multiprocessing.Queue) 
<class 'method'> 
AttributeError: module 'multiprocessing' has no attribute 'queues' 
>>> import multiprocessing.queues 
>>> type(multiprocessing.queues.Queue) 
<class 'type'> 

So wie Sie multiprocessing.Queue sehen kann, ist nur Konstruktor für multiprocessing.queues.Queue Klasse. Gut: Wenn Sie ein Kind Klasse machen wollen einfach nicht class MyQueue(multiprocessing.queues.Queue)

Sie können Quelle dieser Methode here

EDIT sehen. Ich habe jetzt dein Problem. Wie Sie auf einem Link oben sehen können, übergibt multiprocessing.Queuectx Argument Warteschlange.Also habe ich es geschafft, es selbst in __init__ Methode zu arbeiten. Ich verstehe nicht vollständig, wo BaseContext Objekt sollte _name Attribut erhalten, also habe ich es manuell übergeben.

def __init__(self,*args,**kwargs): 
    from multiprocessing.context import BaseContext 
    ctx = BaseContext() 
    ctx._name = "Name" 
    super(StdoutQueue,self).__init__(*args,**kwargs, ctx=ctx) 

EDIT2: Es stellte sich heraus docs haben einige Informationen über Kontext here. Anstatt also die manuelle Erstellen es wie ich Sie

import multiprocessing 
ctx = multiprocessing.get_context() 

tun können Es richtigen Kontext mit _name Satz erstellen wird (zu ‚Gabel‘ in Ihrem speziellen Fall), und Sie können es auf Ihre Warteschlange übergeben.

+0

Ich habe beide Möglichkeiten versucht (multiprocessing.Queue und multiprocessing.queue.Queue), aber keine funktioniert. –

+0

Hallo @Darth Kotik, ich habe eine Bearbeitung zu meiner Frage hinzugefügt. Dort zeige ich das komplette Beispiel und die detaillierte Fehlermeldung. Arbeitest du an Python 3.5? Es wäre großartig, wenn Sie den Fehler reproduzieren könnten :-) –

+0

Vielen Dank! Ich werde es ausprobieren und Sie wissen lassen, ob es funktioniert :-) –

Verwandte Themen