2016-02-21 17 views
10

considerare questo breve frammento:Come usare Python 3.5 in stile async e attendere in Tornado per websockets?

import tornado 
import tornado.websocket 
import tornado.ioloop 
import tornado.gen 
import tornado.web 

class NewWsHandler(tornado.websocket.WebSocketHandler): 
    async def on_message(self, message): 
     await self.write_message("echo " + message) 

class OldWsHandler(tornado.websocket.WebSocketHandler): 
    @tornado.gen.coroutine 
    def on_message(self, message): 
     yield self.write_message("echo " + message) 

app = tornado.web.Application([(r'/', OldWsHandler)]) 
app.listen(8080) 
tornado.ioloop.IOLoop.current().start() 

OldWsHandler utilizza il modo di pre-3.5 di fare funzioni asincrone in Tornado, e funziona benissimo. Tuttavia, as the documentation states, è preferibile utilizzare PEP 0492 per la leggibilità e la velocità.

La documentazione dice:

sufficiente utilizzare async def foo() al posto di una definizione di funzione con la @gen.coroutine decoratrice, e await al posto di yield.

Così ho scritto NewWsHandler. Tuttavia, quando si invia un messaggio websocket, solleva un avvertimento:

 
/usr/lib/python3.5/site-packages/tornado/websocket.py:417: RuntimeWarning: coroutine 'on_message' was never awaited 
    callback(*args, **kwargs) 

Io in realtà non so come (correttamente) risolvere il problema. Ho provato a decorarlo in tornado.web.asynchronous, ma questo presuppone un HTTP verb method. Così, dopo sovrascrivo finish() (WebSockets non sono autorizzati a farlo), sembra essere tipo di lavorazione:

class NewWsHandler(tornado.websocket.WebSocketHandler): 
    def finish(self): 
     pass 

    @tornado.web.asynchronous 
    async def on_message(self, message): 
     await self.write_message("echo " + message) 

Ma questo sembra ancora hackerish, e sembra essere in contraddizione con la documentazione. Qual è il modo giusto per farlo?

Nota: sto usando Python 3.5.1 e Tornado 4.3.

risposta

4

Le coroutine sono denominate in modo diverso rispetto alle funzioni regolari; pertanto, quando si sottoclassi e si sovrascrivono i metodi non è possibile cambiare un metodo regolare nella classe base in una coroutine nella sottoclasse (a meno che la classe base non dica esplicitamente che questo è OK). WebSocketHandler.on_message potrebbe non essere una coroutine (a partire da Tornado 4.3, questo potrebbe cambiare in futuro). Invece, se devi fare qualcosa di asincrono in risposta a un messaggio, metti le parti asincrone in una funzione separata e chiamalo con IOLoop.current().spawn_callback. (o se write_message è l'unica cosa asincrona che stai facendo, chiamala in modo sincrono)

+0

C'è un modo per interrogare un database (in modo asincrono) in risposta a un messaggio websocket? Qualcosa come 'result = await query (...)'? Non riesco a trovare un buon modo per farlo. (A parte gli "hack" nella mia domanda). – evertheylen

+1

Una volta generato un callback separato puoi fare quello che vuoi. Dovrai solo fare attenzione con la gestione delle eccezioni poiché qualsiasi eccezione qui sollevata non passerà attraverso 'on_message' e influenzerà la connessione websocket. Ricorda inoltre che potrebbe entrare un altro messaggio prima che sia terminato il precedente; per evitare che tu possa voler generare una richiamata in 'on_open()' e comunicare con essa usando una coda. –

+0

Grazie! Adesso è tutto chiaro. – evertheylen

Problemi correlati