4

Im trying to make non-blocking console for network client with asyncio.

import sys
import select
import tty
import termios
import asyncio

def isData():
    return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])

@asyncio.coroutine
def load():
    n = 0
    while True:
        print(n)
        n += 1
        yield from asyncio.sleep(1)

@asyncio.coroutine
def get_input():
    old_settings = termios.tcgetattr(sys.stdin)
    try:
        tty.setcbreak(sys.stdin.fileno())        
        while True:
            if isData():
                c = sys.stdin.read(1)
                print(c)
                if c == '\x1b':         
                    break

                if c == '\x0a':
                    print('Enter pressed')


    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)


loop =  asyncio.get_event_loop()
asyncio.ensure_future(load())
asyncio.ensure_future(get_input())
loop.run_forever()

load() is simulates some coroutine. It will be replaced with communication with server. So, non-blocking input was made with get_input(). It works well alone, but for some reason it blocks execution of load(). Looks like its because of c = sys.stdin.read(1) Can I make it like

data = yield from asyncio.wait_for(client_reader.readline(), timeout=TIMEOUT)

I use to read data from server.

Or maybe where is straightforward way to make non-blocking console with asyncio?

UPDATE: possible solution:

@asyncio.coroutine
def get_input():
    old_settings = termios.tcgetattr(sys.stdin)
    try:
        tty.setcbreak(sys.stdin.fileno())        
        while True:
            check = yield from loop.run_in_executor(None, isData)
            if check:
                c = sys.stdin.read(1)
                print(c)
                if c == '\x1b':         
                    break    
                if c == '\x0a':
                    print('Enter pressed')     
    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
  • I wrote something similar and published it in a project called [apython](https://github.com/vxgmichel/apython). It includes a [non-blocking python console](https://github.com/vxgmichel/apython#asynchronous-console) and the possibility to write [command line interfaces](https://github.com/vxgmichel/apython#command-line-interfaces). – Vincent Jun 23 '16 at 15:45
  • @Vincent Very interesting... It will take some time to autopsy your code. But I still need understanding why my code doesn't work? – Alexander Zot Jun 24 '16 at 05:44
  • 2
    Asyncio already uses `select` behind the scenes, so you're supposed to use [loop.add_reader](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.BaseEventLoop.add_reader) instead, e.g: `loop.add_reader(sys.stdin.fileno(), on_stdin)` – Vincent Jun 24 '16 at 08:09
  • @Vincent Looks like callback is called only after Enter is pressed, but not when key is pressed. Thats not as I expected... – Alexander Zot Jul 04 '16 at 08:56
  • 2
    That's because `sys.stdin` is [buffered](http://stackoverflow.com/questions/3670323/setting-smaller-buffer-size-for-sys-stdin#comment19834065_3670470). You can use [`tty.setraw(sys.stdin.fileno())`](http://stackoverflow.com/questions/510357/python-read-a-single-character-from-the-user#510364) but things will get messy. – Vincent Jul 04 '16 at 12:30
  • @Vincent Please check out my update. It looks like crutch but it works, despite stdin is buffered. – Alexander Zot Jul 06 '16 at 03:56

0 Answers0