StreamReader.read is an awaitable method that waits for a specified number of bytes to arrive, then returns them. Unlike readline, it does not wait for a line ending. section 8.3
Use the method according to the shape of the input:
- Call read(1) when an application must process input one character at a time. section 8.4.1
- Use readline when input is naturally line-oriented and waiting for a complete line is appropriate. section 8.3
- Remember that the result is bytes, so decode it before treating it as text. section 8.4.1
Reading an interactive line
A cbreak-mode terminal can repeatedly await stdin_reader.read(1), append each byte to a buffer, echo it, and stop when the returned byte is b'\n'. This enables application-controlled editing and output placement. section 8.4.1
Listing 8.8 Reading input one character at a time
import sys
from asyncio import StreamReader
from collections import deque
from chapter_08.listing_8_7 import move_back_one_char, clear_line
async def read_line(stdin_reader: StreamReader) -> str:
def erase_last_char(): #1
move_back_one_char()
sys.stdout.write(' ')
move_back_one_char()
delete_char = b'\x7f'
input_buffer = deque()
while (input_char := await stdin_reader.read(1)) != b'\n':
if input_char == delete_char: #2
if len(input_buffer) > 0:
input_buffer.pop()
erase_last_char()
sys.stdout.flush()
else:
input_buffer.append(input_char) #3
sys.stdout.write(input_char.decode())
sys.stdout.flush()
clear_line()
return b''.join(input_buffer).decode()
Inspect the complete read_line coroutine, including read(1), delete-key handling, buffering, decoding, and terminal echo. section 8.4.1
