1

Тема: asyncio - як тепер (в 3.10) з ним працювати? Старі приклади - всьо.

З версії 3.10 по викидали депрікейти, які давно хотіли.
run_until_complete, на приклад - вже нормально не працює... Старі приклади, що є у цих наших інтернетах - більшою частиною вже не актуальні.

Є бажання для тестів створити вебсокетний сервер. Намагаюсь щось зробити по прикладах - валиться куча помилок. Гуглю - це через перехід на 3.10

Відкатити назад - не хочу.
тому питання:
Э старий приклад




import asyncio
import websockets
import logging, sys #... і ще там що треба
hostport = "12345"
cip=""#потім з'ясовується реальний IP, який підставляється актуальний. Десь там, воно працює.

class Serverws:
    clients=set()

    async def register(self, ws :WebSocketServerProtocol) -> None:
        self.clients.add(ws)

    async def unregister(self, ws: WebSocketServerProtocol) -> None:
        self.clients.remove(ws)

    async def send_to_clients(self, message: str) -> None:
        if self.clients:
            await asyncio.wait([client.send(message) for client in self.clients])

    async def ws_handler(self, ws: WebSocketServerProtocol, uri: str) -> None:
        await self.register(ws)
        try:
            await self.distribute(ws)
        finally:
            await self.unregister(ws)

    async def distribute(self, ws: WebSocketServerProtocol) -> None:
        async for message in ws:
            await self.send_to_clients(message)

# і от раніше там було таке:
serverr = Serverws()
start_server = websockets.serve(serverr.ws_handler,cip,hostport)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

По перше, run_until_complete - зараз робити нетреба, а треба робити .run

Так от, в такому вигляді - воно як було - вже нормально не працює:

webs_srv.py:88: DeprecationWarning: There is no current event loop
  asyncio.get_event_loop().run_forever()

і потім при спробі конекту - конектить клієнта. але якщо клієнт відішле повідомлення - ніфіга не трапляється взагалі. а повинно роздати месседж усім клієнтам, що наявні.

Як зараз коректно запускати асинхронно отаке на базі класу?
На скільки я розумію - якщо я розвалю це на окремі функції - проблем буде менше. Але, не хотілося б.

P.S. чи, може, там в коді помилка? бо приклад не мій, я на його базі намагаюсь зрозуміти як це робити.

2

Re: asyncio - як тепер (в 3.10) з ним працювати? Старі приклади - всьо.

Ще спостерігається дісконнект при відправці повідомлення

AppData\Roaming\Python\Python311\site-packages\websockets\legacy\server.py:-1: RuntimeWarning: coroutine 'WebSocketCommonProtocol.send' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
INFO:websockets.server:connection closed

P.S. в коді, що я навів - нема логів, реально вони там є (якщо хтось запитає хто пише "INFO:websockets.server:connection closed")

3

Re: asyncio - як тепер (в 3.10) з ним працювати? Старі приклади - всьо.

Ось таку хрінь я писав, хотів передавати інформацію про звук на веб-інтерфейс, щоб там за допомогою pixi.js малювати графік...

#!/usr/bin/python3

import asyncio
from asyncio import exceptions
import datetime
import json
import numpy as np
import queue
import random
import secrets
import sounddevice as sd
import sys
import websockets

#config
device = None
channels = 1
mapping = [0]
device_info = sd.query_devices(device, 'input')
samplerate = device_info['default_samplerate']
downsample = 50


q = queue.Queue()
connections = {}

def audio_callback(indata, frames, time, status):
    """This is called (from a separate thread) for each audio block."""
    if status:
       print(status, file=sys.stderr)
    # Fancy indexing with mapping creates a (necessary!) copy:
    q.put(indata[::downsample, mapping])

def socketId(remoteAddr: tuple):
    return remoteAddr[0] + ":" + str(remoteAddr[1])

async def handler(websocket):
    id = socketId(websocket.remote_address)
    if id not in connections:
        connections[id] = [websocket, False]
    try:
        async for message in websocket:
            event = json.loads(message)
            if (event["cmd"] == "start"):
                connections[id][1] = True
            elif (event["cmd"] == "stop"):
                connections[id][1] = False
    finally:
        del connections[id]


async def show():
    while True:
        try:
            data = q.get_nowait()
        except queue.Empty:
            break
        message = {
           "data": data.tolist()
        }
        for i in connections:
            if connections[i][1]:
                await connections[i][0].send(json.dumps(message))
        await asyncio.sleep(0.005)


async def main():
    stream = sd.InputStream(
        device=device, channels=channels,
        samplerate=samplerate, callback=audio_callback)
    with stream:
        async with websockets.serve(handler, "", 8004): #, stream:
            await show()

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass

По факту, воно дуже сильно лагає, тому від ідеї відмовився. Та й взагалі, те, для чого це робилося, поки що вмерло через апаратну частину.

Базується на на ось цьому: https://python-sounddevice.readthedocs. … -real-time

4

Re: asyncio - як тепер (в 3.10) з ним працювати? Старі приклади - всьо.

Корочє, доведеться самому відповісти собі :)
Треба явний run
тобто, требя через явний main. І await продовбано, звісно

Переписати так:

...
serverr = Serverws()


async def main():
    start_server = await websockets.serve(serverr.ws_handler,cip,hostport)
    await start_server.wait_closed()
    
asyncio.run(main())