2015-03-18 15 views
10

Dies ist ähnlich wie Calling coroutines in asyncio.Protocol.data_received, aber ich denke, es garantiert eine neue Frage.Aufruf einer Coroutine aus asyncio.Protocol.data_received

Ich habe einen einfachen Server wie diese feinen Es funktioniert

loop.create_unix_server(lambda: protocol, path=serverSocket) 

einrichten, wenn ich dies tun

def data_received(self, data): 
    data = b'data reply' 
    self.send(data) 

mein Kunde die Antwort bekommt. Aber ich kann es nicht mit irgendeiner Art von asyncio Aufruf arbeiten. Ich habe alles ausprobiert und keiner von ihnen hat funktioniert.

@asyncio.coroutine 
def go(self): 
    yield from asyncio.sleep(1, result = b'data reply') 

def data_received(self, data): 
    print('Data Received', flush=True) 

    task = asyncio.get_event_loop().create_task(self.go()) 
    data = yield from asyncio.wait_for(task,10) 
    self.send(data) 

, dass man hing und nichts gedruckt (wenn ich data_received mit @asyncio.coroutine verziert ich, dass das nicht von nachgegeben wird) OK, ich bekommen, dass Ausbeute unter Verwendung in data_received nicht richtig.

Wenn ich eine neue Ereignisschleife versuchen, wie weiter unten, das hängt in run_until_complete

loop = asyncio.new_event_loop() 
    task = loop.create_task(self.go()) 
    loop.run_until_complete(task) 
    data = task.result() 
    self.send(data) 

Wenn ich ein Future verwenden, die in run_until_complete

@asyncio.coroutine 
def go(self, future): 
    yield from asyncio.sleep(1) 
    future.set_result(b'data reply') 

def data_received(self, data): 
    print('Data Received', flush=True) 

    loop = asyncio.new_event_loop() 
    future = asyncio.Future(loop=loop) 
    asyncio.async(self.go(future)) 
    loop.run_until_complete(future) 
    data = future.result() 
    self.send(data) 
hängt auch

Die folgenden nahe kommt, aber es gibt sofort zurück und das Ergebnis ist vom Typ asyncio.coroutines.CoroWrapper, was bedeutet, dass die wait_for Zeile sofort mit der nicht abgeschlossenen Aufgabe zurückgegeben wurde?

@asyncio.coroutine 
def go(self): 
    return(yield from asyncio.sleep(3, result = b'data reply')) 

@asyncio.coroutine 
def go2(self): 
    task = asyncio.get_event_loop().create_task(self.go()) 
    res = yield from asyncio.wait_for(task, 10) 
    return result 

def data_received(self, data): 
    print('Data Received', flush=True) 

    data = self.go2() 
    self.send(data) 

Ich bin ein bisschen fest, und würde gerne einige Hinweise darüber, was zu sehen ist.

Antwort

11

Sie benötigen Koroutine zu der Ereignisschleife hinzuzufügen, und dann Future.add_done_callback verwenden um das Ergebnis zu behandeln, wenn die Koroutine vervollständigt:

@asyncio.coroutine 
def go(self): 
    return(yield from asyncio.sleep(3, result = b'data reply')) 

def data_received(self, data): 
    print('Data Received', flush=True) 

    task = asyncio.async(self.go()) # or asyncio.get_event_loop().create_task() 
    task.add_done_callback(self.handle_go_result) 

def handle_go_result(self, task): 
    data = task.result() 
    self.send(data) 

eine Koroutine direkt in data_received Aufruf ist nur einfach nicht erlaubt, da die Aufrufer wird nicht versuchen, yield from es, und Erstellen/Ausführen einer neuen Ereignisschleife innerhalb von data_received wird immer am Ende blockiert die Main Event-Schleife, bis die innere Ereignisschleife seine Arbeit beendet.

Sie wollen einfach nur einige Arbeit mit Ihrer Haupt-Ereignisschleife planen (asyncio.async/loop.create_task()) und einen Rückruf planen zu laufen, wenn die Arbeit getan ist (add_done_callback).

+0

Hallo, danke für diese Antwort - das funktioniert in meinem Code. Ich habe es nur in meinen nicht-trivialen Code integriert (der einen Calling-Stack usw. hat). Ich hatte über Callbacks nachgedacht, aber ich dachte, dass Rendite/Aufgaben/Futures der neuere Ansatz waren und den Vorteil hatten, sich keine Sorgen zu machen über Ihren Aufruf-Stack oder wie Sie Daten in Ihren Callback bekommen. Weißt du, warum die innere Schleife ihre Arbeit nicht beenden würde? Oder warum der zukünftige Ansatz mit der Hauptschleife nicht funktionieren würde? – Dave

+0

Oh, ich schätze, die innere Schleife wartet auf die geblockte äußere, um ein IO zu machen. – Dave

+1

@Dave 'asyncio' bevorzugt definitiv einen Coroutine-ähnlichen Ansatz für die Verwendung auf höherer Ebene, aber Low-Level-Funktionen wie Protokolle verwenden immer noch die Rückrufart Wenn Sie stattdessen Coroutinen verwenden möchten, empfiehlt [docs] (https://docs.python.org/3/library/asyncio-protocol.html#coroutires-and-protocols) Stream-Objekte anstelle von Protokollen. – dano