1818import re
1919import asyncio
2020import logging
21- from typing import Tuple , Union , Optional , Generator , cast
21+ from typing import Tuple , Union , Optional , Generator
2222from contextlib import contextmanager
2323
2424# local
@@ -64,6 +64,14 @@ def _latin1_reading(
6464 reader ._decoder = None
6565
6666
67+ def _writer_write (writer : Union [TelnetWriter , TelnetWriterUnicode ], data : str ) -> None :
68+ """Write string data to writer in the appropriate type (str or bytes)."""
69+ if writer .is_binary_writer :
70+ writer .write (data .encode ("latin-1" ))
71+ else :
72+ writer .write (data ) # type: ignore[arg-type]
73+
74+
6775class ConnectionCounter :
6876 """Simple shared counter for limiting concurrent connections."""
6977
@@ -100,12 +108,13 @@ def count(self) -> int:
100108
101109async def _read_line_inner (reader : Union [TelnetReader , TelnetReaderUnicode ], max_len : int ) -> str :
102110 """Inner loop for _read_line, separated for wait_for compatibility."""
103- _reader = cast (TelnetReaderUnicode , reader )
104111 buf = ""
105112 while len (buf ) < max_len :
106- char = await _reader .read (1 )
113+ char = await reader .read (1 )
107114 if not char :
108115 break
116+ if isinstance (char , bytes ):
117+ char = char .decode ("latin-1" )
109118 if char in ("\r " , "\n " ):
110119 break
111120 buf += char
@@ -166,8 +175,7 @@ async def _get_cursor_position(
166175 :returns: (row, col) tuple or (None, None) on timeout/failure.
167176 """
168177 # Send Device Status Report request
169- _writer = cast (TelnetWriterUnicode , writer )
170- _writer .write ("\x1b [6n" )
178+ _writer_write (writer , "\x1b [6n" )
171179 await writer .drain ()
172180
173181 # Read response: ESC [ row ; col R
@@ -189,21 +197,20 @@ async def _measure_width(
189197
190198 :returns: Width in columns, or None on failure.
191199 """
192- _writer = cast (TelnetWriterUnicode , writer )
193200 _ , x1 = await _get_cursor_position (reader , writer , timeout )
194201 if x1 is None :
195202 return None
196203
197- _writer . write ( text )
198- await _writer .drain ()
204+ _writer_write ( writer , text )
205+ await writer .drain ()
199206
200207 _ , x2 = await _get_cursor_position (reader , writer , timeout )
201208 if x2 is None :
202209 return None
203210
204211 # Clear the test character
205- _writer . write ( f"\x1b [{ x1 } G" + " " * (x2 - x1 ) + f"\x1b [{ x1 } G" )
206- await _writer .drain ()
212+ _writer_write ( writer , f"\x1b [{ x1 } G" + " " * (x2 - x1 ) + f"\x1b [{ x1 } G" )
213+ await writer .drain ()
207214
208215 return x2 - x1
209216
@@ -230,10 +237,9 @@ async def _ask_question(
230237 timeout : float = 10.0 ,
231238) -> Optional [str ]:
232239 """Ask a question, echoing input and repeating prompt on blank input."""
233- _writer = cast (TelnetWriterUnicode , writer )
234240 while True :
235- _writer . write ( prompt )
236- await _writer .drain ()
241+ _writer_write ( writer , prompt )
242+ await writer .drain ()
237243
238244 line = await _readline_with_echo (reader , writer , timeout )
239245 if line is None :
@@ -242,7 +248,7 @@ async def _ask_question(
242248 if line .strip ():
243249 return line
244250 # Blank input - repeat prompt
245- _writer . write ( "\r \n " )
251+ _writer_write ( writer , "\r \n " )
246252
247253
248254async def robot_shell (
@@ -254,7 +260,6 @@ async def robot_shell(
254260
255261 Asks philosophical questions, logs responses, and disconnects.
256262 """
257- writer = cast (TelnetWriterUnicode , writer )
258263 peername = writer .get_extra_info ("peername" )
259264 logger .info ("robot_shell: connection from %s" , peername )
260265
@@ -275,7 +280,7 @@ async def robot_shell(
275280 return
276281 answers .append (line2 )
277282
278- writer . write ( "\r \n " )
283+ _writer_write ( writer , "\r \n " )
279284 await writer .drain ()
280285 finally :
281286 if answers :
@@ -291,23 +296,22 @@ async def busy_shell(
291296
292297 Displays busy message, logs any input, and disconnects.
293298 """
294- writer = cast (TelnetWriterUnicode , writer )
295299 logger .info ("busy_shell: connection from %s (limit reached)" , writer .get_extra_info ("peername" ))
296300
297- writer . write ( "Machine is busy, do not touch! " )
301+ _writer_write ( writer , "Machine is busy, do not touch! " )
298302 await writer .drain ()
299303
300304 with _latin1_reading (reader ):
301305 line1 = await _read_line (reader , timeout = 30.0 )
302306 if line1 is not None :
303307 logger .info ("busy_shell: input1=%r" , line1 )
304308
305- writer . write ( "\r \n You hear a distant explosion... " )
309+ _writer_write ( writer , "\r \n You hear a distant explosion... " )
306310 await writer .drain ()
307311
308312 line2 = await _read_line (reader , timeout = 30.0 )
309313 if line2 is not None :
310314 logger .info ("busy_shell: input2=%r" , line2 )
311315
312- writer . write ( "\r \n " )
316+ _writer_write ( writer , "\r \n " )
313317 await writer .drain ()
0 commit comments