Ok, I have achieved it :D.
#!/usr/bin/env python
import sys
from select import select
def main(argv):
timeout = 3
prompt = '> '
max_chars = 3
# set raw input mode if relevant
# it is necessary to make stdin not wait for enter
try:
import tty, termios
prev_flags = termios.tcgetattr(sys.stdin.fileno())
tty.setraw(sys.stdin.fileno())
except ImportError:
prev_flags = None
buf = ''
sys.stderr.write(prompt)
while True: # main loop
rl, wl, xl = select([sys.stdin], [], [], timeout)
if rl: # some input
c = sys.stdin.read(1)
# you will probably want to add some special key support
# for example stop on enter:
if c == '\n':
break
buf += c
# auto-output is disabled as well, so you need to print it
sys.stderr.write(c)
# stop if N characters
if len(buf) >= max_chars:
break
else:
# timeout
break
# restore non-raw input
if prev_flags is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, prev_flags)
# and print newline
sys.stderr.write('\n')
# now buf contains your input
# ...
if __name__ == "__main__":
main(sys.argv[1:])
It's fairly incomplete; I just put a few values to test it. A few words of explanation:
- You need to switch the tty to 'raw' mode — otherwise you wouldn't be able to get input without it being confirmed by enter key,
- in raw mode the typed in characters are no longer output by default — you need to output them yourself if you want user to see what he is typing,
- you probably want to handle special keys like enter and backspace — I've added enter handling here. Maybe you could reuse parts of
curses for that,
- I've assumed the timeout is '3 seconds after last key'. If you want timeout for whole process, I think the easiest way would be to get current time, increase it by timeout (i.e. get
end_time), and then pass end_time - current_time in seconds as timeout to select(),
- I've made unix-specific imports optional. I don't know whether it will work on Windows correctly, though.
select()can account for the 3rd item. Assuming it will return (continue loop execution) every time there's input, you can count characters and either stop or run anotherselect()call.