Asynchroniczny dekorator w pythonie

Pytania i problemy
ivallpl
ivallpl

Hej, mam napisany dekorator w pythonie, który cały czas pobiera dane z websocketa i wykonuje funkcję. Używam go w innym pliku, działa on dobrze, ale niestety blokuje resztę kodu pod funkcją z tym dekoratem. Co mogę zrobić? Potrzebuję asyncio. Próbowałem z threadami + asyncio, ale nic to nie dało.

    def event(self, func):
        async def wrapper():
            while True:
                event = await self.ws.recv()
                event = json.loads(event)
                if event['t'] == func.__name__:
                    func(event)

        loop = asyncio.get_event_loop()
        loop.create_task(wrapper())
        loop.run_forever()

Przykład jego użycia: image|302x136 (nie działało wklejanie kodu). Wszystko co jest pod funkcją z dekoratorem już się nie wykonuje, nawet inna funkcja z tym dekoratorem. Co mogę zrobić?

Nieznajomy11
Nieznajomy11 Moderator forum.lvlup.pro

ivallpl:

Co mogę zrobić?

Generalnie dwie opcje:

  1. Nie używaj pythona jak normalnego języka z prawdziwymi wątkami.
  2. Oszczędź sobie bólu i użyj biblioteki do eventów (np. PyEventEmitter) lub zrób własną (koncept na wiki).

To rozwiązuje pierwszy problem, czyli sam handling eventów. Teraz zostaje coś, co może uda ci się wydzielić, czyli pobieranie z websocketu i emitowanie eventów. Twoje obecne rozwiązanie wygląda jakbyś chciał spawnować jeden wątek na każdy dekorator, tj. jeden handler eventów, super niewydajne i przerażające jeszcze bardziej, bo python. :thinking:

Asyncio to nie rozwiązanie, jeśli wszystko, czego nie robisz nie jest na asyncio. Tam nie ma wątków, to wszystko złudzenie. Funkcja run_forever jest blokująca i wykonuje taski, które wrzuciłeś do loopu.

Najbliższe do prawdziwych wątków w pythonie są procesy (multiprocessing.Process), lecz napotkasz tam dodatkowe problemy z dzieleniem pamięci, które też trzeba rozważyć (Pipe, Queue).

https://docs.python.org/2/library/multiprocessing.html

ivallpl
ivallpl

Właśnie problem jest taki, że asyncio musi być przez websockety. Aktualnie dla każdego dekoratora robię instancje klasy, która ma metodę odpowiedzialną za pobranie danych z websocketa i uruchamiam cały ten dekorat w nowym wątku. Niestety to i tak nie działa do końca i pewnie jest baardzo nie wydajne. Chciałem uniknąć używanie innych bibliotek, niż threading/asyncio :/ Spróbuję z pyeventemitter, dzięki

Nieznajomy11
Nieznajomy11 Moderator forum.lvlup.pro

PyEventEmiter jest tylko do samego zarządzania systemem eventów. Odbieranie danych z websocketa i tak musisz sobie zaimplementować, zwyczajnie gdzieś w innym miejscu. W dekoratorach wydaje się to okropny pomysł, z powodów wcześniej wymienionych. Trochę jak startowanie na każdego gracza nowego wątku na serwerze.

system
system

Ten temat został automatycznie zamknięty 32 dni po ostatnim wpisie. Tworzenie nowych odpowiedzi nie jest już możliwe.