2016-02-21 34 views
10

Rozważmy ten krótki fragment:Jak korzystać z asynchronizmu w stylu Python 3.5 i czekać w Tornado na strony internetowe?

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 korzysta z pre-3.5 sposób robienia funkcje asynchroniczne w Tornado, i to działa dobrze. Jednakże, as the documentation states, korzystne jest użycie PEP 0492 dla czytelności i szybkości.

Dokumentacja mówi:

Wystarczy użyć async def foo() zamiast definicji funkcji z @gen.coroutine dekoratora i await zamiast yield.

Tak więc napisałem NewWsHandler. Jednak podczas wysyłania komunikatu websocket wywołuje on ostrzeżenie:

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

Naprawdę nie wiem jak (prawidłowo) to naprawić. Próbowałem go udekorować w tornado.web.asynchronous, ale to zakłada, że ​​jest to HTTP verb method. Więc po przesłonić finish() (WebSockets nie wolno tego robić), wydaje się być rodzajem pracy:

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

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

Ale to wciąż wygląda hackerish, i wydaje się być sprzeczne z dokumentacją. Jaki jest właściwy sposób na zrobienie tego?

Uwaga: Używam Python 3.5.1 i Tornado 4.3.

Odpowiedz

4

Corutines są nazywane inaczej niż zwykłe funkcje; dlatego w przypadku podklasowania i nadpisywania metod nie można zmienić zwykłej metody w klasie bazowej na coroutine w podklasie (chyba że klasa bazowa mówi, że to jest w porządku). WebSocketHandler.on_message może nie być kontentem (jak w Tornado 4.3, może się to zmienić w przyszłości). Zamiast tego, jeśli potrzebujesz czegoś asynchronicznego w odpowiedzi na wiadomość, umieść asynchroniczne części w oddzielnej funkcji i wywołaj ją za pomocą IOLoop.current().spawn_callback. (lub jeśli write_message jest jedyną rzeczą asynchroniczną, którą właśnie robisz, po prostu wywołaj ją synchronicznie)

+0

Czy istnieje sposób, w jaki mógłbym wykonać zapytanie do bazy danych (asynchronicznie) w odpowiedzi na komunikat websocket? Coś jak 'result = czekaj na zapytanie (...)'? Nie mogę znaleźć właściwego sposobu na zrobienie tego. (Oprócz "hacków" w moim pytaniu). – evertheylen

+1

Po utworzeniu oddzielnego wywołania zwrotnego możesz zrobić, co chcesz. Trzeba tylko zachować ostrożność przy obsłudze wyjątków, ponieważ każdy zgłoszony tutaj wyjątek nie przejdzie przez 'on_message' i nie wpłynie na połączenie websocket. Należy również pamiętać, że inna wiadomość może nadejść przed zakończeniem poprzedniej; Aby tego uniknąć, możesz odrodzić wywołanie zwrotne w 'on_open()' i komunikować się z nim za pomocą kolejki. –

+0

Dzięki! Teraz wszystko jest jasne. – evertheylen