Conditional in coroutine based on whether it was called again?

I am trying to translate this "debouncing" key from Javascript to Python.

function handle_key(key) {
    if (this.state == null) {
        this.state = ''
    }
    this.state += key
    clearTimeout(this.timeout)
    this.timeout = setTimeout(() => {
        console.log(this.state)
    }, 500)
}

handle_key('a')
handle_key('b')

The idea is that subsequent keystrokes extend the timeout. Javascript version prints:

ab 

I do not want to translate JS timeout functions, I would prefer to use idiomatic Python using asyncio. My attempt in Python (3.5) is lower, but it does not work, as global_stateit is not really updated when I expect.

import asyncio

global_state = ''

@asyncio.coroutine
def handle_key(key):
    global global_state
    global_state += key
    local_state = global_state
    yield from asyncio.sleep(0.5)
    #if another call hasn't modified global_state we print it
    if local_state == global_state:
        print(global_state)

@asyncio.coroutine
def main():
    yield from handle_key('a')
    yield from handle_key('b')

ioloop = asyncio.get_event_loop()
ioloop.run_until_complete(main())

He prints:

a
ab

I reviewed asyncio , and , but it’s not clear to me how to use this. How to implement desired behavior using Python asyncio? EventQueueCondition

EDIT

, handle_keys. , .

@asyncio.coroutine
def check_keys():
    keys = driver.get_keys()
    for key in keys:
        yield from handle_key(key)

, ,

@asyncio.coroutine
def main():
    while True:
        yield from check_keys()
        yield from do_other_stuff()

ioloop = asyncio.get_event_loop()
ioloop.run_until_complete(main())

Qek asyncio.create_task asyncio.gather . ? , handle_keys "" ?

GitHub, .

+4
2

yield from xy() . yield from , . yield from coroutine , , .

, :

  • main .
  • .
  • main , .
  • yield from handle_key('a').
  • handle_key('a') .
  • main handle_key('a'), , handle_key('a').
  • , handle_key('a').
  • , yield from asyncio.sleep(0.5).
  • main(), handle_key('a') sleep(0.5).
    • main() handle_key('a').
    • handle_key('a') sleep(0.5).
    • , .
  • asyncio.sleep(0.5) None 0,5 .
  • None handle_key('a').
  • , .
  • handle_key('a') ( )
  • handle_key coroutine None ( return).
  • None .
  • .
  • yield from handle_key('b') .
  • 5 ( b).

main coroutinr :

@asyncio.coroutine
def main(loop=asyncio.get_event_loop()):
    a_task = loop.create_task(handle_key('a'))
    b_task = loop.create_task(handle_key('b'))
    yield from asyncio.gather(a_task, b_task)

loop.create_task handle_key('a') handle_key('b'), yield from asyncio.gather(a_task, b_task) . handle_key('a'), handle_key('b'), gather(...) main().

  • main() gather()
  • gather() , , ,
  • handle_key('a') handle_key('b') , .

2 , , ? ... , . :

@asyncio.coroutine
def main(loop=asyncio.get_event_loop()):
    a_task = loop.create_task(handle_key('a'))
    yield from asyncio.sleep(0.1)
    b_task = loop.create_task(handle_key('b'))
    yield from asyncio.gather(a_task, b_task)

Python 3.5

:

, asyncio, async def.

Python 3.5 async def, , Python.

, :

@asyncio.coroutine
def main():

async def main():

, yield from await.

+1

?

handle_key javascript . . .

Coroutines -: yield from await coroutine , , :

async def a():
    await asyncio.sleep(1)

async def main():
    await a()
    await b()  # this line would be reached only after a() done - after 1 second delay

asyncio.sleep(0.5) - , , handle_key finsihed.

task, " ". ( , clearTimeout(this.timeout)), , .

Python, javascript:

import asyncio
from contextlib import suppress

global_state = ''
timeout = None

async def handle_key(key):
    global global_state, timeout

    global_state += key

    # cancel previous callback (clearTimeout(this.timeout))
    if timeout:
        timeout.cancel()
        with suppress(asyncio.CancelledError):
            await timeout

    # set new callback (this.timeout = setTimeout ...)
    async def callback():
        await asyncio.sleep(0.5)
        print(global_state)
    timeout = asyncio.ensure_future(callback())


async def main():
    await handle_key('a')
    await handle_key('b')

    # both handle_key functions done, but task isn't finished yet
    # you need to await for task before exit main() coroutine and close loop
    if timeout:
        await timeout

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    loop.close()

?

, asyncio. javascript , asyncio .

, , ( - ) . , async.

async/await javascript ( Python async/await) , callbacks/promises. .

, Python.

Upd:

  • buttons.check driver.get_buttons(), . .

    - button_handler(callback) ( , ), , asyncio.Future .

  • GUI- asyncio . , , .

  • , asyncio / :

.

import asyncio
from contextlib import suppress


# GUI logic:
async def main():
    while True:
        print('We at main window, popup closed')

        key = await key_pressed
        if key == 'Enter':
            print('Enter - open some popup')

            await popup()
            # this place wouldn't be reached until popup is not closed

            print('Popup was closed')

        elif key == 'Esc':
            print('Esc - exit program')
            return


async def popup():
    while True:
        key = await key_pressed
        if key == 'Esc':
            print('Esc inside popup, let us close it')
            return
        else:
            print('Non escape key inside popup, play sound')


# Event loop logic:
async def button_check():
    # Where 'key_pressed' is some global asyncio.Future
    # that can be used by your coroutines to know some key is pressed
    while True:
        global key_pressed
        for key in get_buttons():
            key_pressed.set_result(key)
            key_pressed = asyncio.Future()
        await asyncio.sleep(0.01)


def run_my_loop(coro):
    loop = asyncio.get_event_loop()

    # Run background task to process input
    buttons_task = asyncio.ensure_future(button_check())

    try:
        loop.run_until_complete(main())
    finally:

        # Shutdown task
        buttons_task.cancel()
        with suppress(asyncio.CancelledError):
            loop.run_until_complete(buttons_task)

        loop.close()


if __name__ == '__main__':
    run_my_loop(main())
+1

Source: https://habr.com/ru/post/1683301/


All Articles