Как прокормить асинхронный генератор событиями из python.net - PullRequest
4 голосов
/ 22 марта 2019

Я использую стороннюю библиотеку .Net в Python 3.6 через Python для .Net, которая использует EventHandlers для предоставления асинхронных данных моему приложению, как в примере с игрушкой ниже:

import clr
from System import Timers

def tock(__, args):
    print(args.SignalTime)

timer = Timers.Timer()
timer.Interval = 1000
timer.Elapsed += tock
timer.Enabled = True
while True:
    pass

Я бы хотелчтобы получить эти данные в асинхронный генератор, что-то вроде:

import clr
from System import Timers

async def tock(__, args):
    yield args.SignalTime

async def main():
    result = await tock
    print(result)

timer = Timers.Timer()
timer.Interval = 1000
timer.Elapsed += tock
timer.Enabled = True
while true:
    result = await timer
    print result

Явно просто бросив asyc и yield на функцию-обработчик событий и await с таймером, этого не будет, ноЕсть ли простой способ сделать это?

1 Ответ

3 голосов
/ 22 марта 2019

Поскольку "асинхронные" данные поступают из другого потока, вам потребуется мост между asyncio и потоком, который вызывает tock.Со стороны asyncio вам необходимо реализовать асинхронный генератор и выполнить итерацию по нему с помощью цикла async for.Например (не проверено):

import clr, asyncio
from System import Timers

def adapt_sync_to_async():
    # Adapt a series of sync callback invocations to an async
    # iterator. Returns an async iterator and a feed callback
    # such that the async iterator will produce a new item
    # whenever the feed callback is fed one.
    loop = asyncio.get_event_loop()
    queue = asyncio.Queue()
    def feed(item):
        loop.call_soon_threadsafe(queue.put_nowait, item)
    async def drain():
        while True:
            yield await queue.get()
    return drain, feed

tock, feed_tock = adapt_sync_to_async()

async def main():
    async for result in tock():
        print(result)

timer = Timers.Timer()
timer.Interval = 1000
timer.Elapsed += lambda _, args: feed_tock(args.SignalTime)
timer.Enabled = True

asyncio.get_event_loop().run_until_complete(main())
...