1212 bytes2str , str2bytes , bytes2hex , bytes2utf8 , \
1313 hexdump , AnsiParser , AnsiInstruction
1414from ptrlib .console .color import Color
15+ from ptrlib .types import PtrlibIntLikeT
1516
1617_is_windows = os .name == 'nt'
1718
@@ -51,6 +52,8 @@ def decorator(self, *args, **kwargs):
5152
5253
5354TubeType = TypeVar ('TubeType' , bound = 'Tube' )
55+ AtomicSendT = Union [PtrlibIntLikeT , float , str , bytes ]
56+ AtomicRecvT = Union [str , bytes ]
5457
5558class Tube (metaclass = abc .ABCMeta ):
5659 """Abstract class for streaming data
@@ -169,7 +172,7 @@ def newline(self, line: bytes):
169172 #
170173 # Methods
171174 #
172- def settimeout (self , timeout : Optional [Union [int , float ]]= None ):
175+ def settimeout (self , timeout : Optional [Union [float , PtrlibIntLikeT ]]= None ):
173176 """Set timeout
174177
175178 Args:
@@ -187,17 +190,16 @@ def settimeout(self, timeout: Optional[Union[int, float]]=None):
187190 p.settimeout() # Timeout is set to 3
188191 ```
189192 """
190- assert timeout is None or (isinstance (timeout , (int , float )) and timeout >= 0 ), \
191- "`timeout` must be positive and either int or float"
192-
193193 if timeout is None :
194194 self ._settimeout_impl (self ._default_timeout )
195+ elif not isinstance (timeout , float ):
196+ self ._settimeout_impl (int (timeout ))
195197 else :
196198 self ._settimeout_impl (timeout )
197199
198200 def recv (self ,
199- size : int = 4096 ,
200- timeout : Optional [Union [int , float ]]= None ) -> bytes :
201+ size : PtrlibIntLikeT = 4096 ,
202+ timeout : Optional [Union [PtrlibIntLikeT , float ]]= None ) -> bytes :
201203 """Receive data with buffering
202204
203205 Receive raw data of at most `size` bytes.
@@ -224,8 +226,9 @@ def recv(self,
224226 pass
225227 ```
226228 """
227- assert size is None or (isinstance (size , int ) and size >= 0 ), \
228- "`size` must be a positive integer"
229+ if size is not None :
230+ size = int (size )
231+ assert size >= 0 , "`size` must be a positive integer"
229232
230233 # NOTE: We always return buffer if it's not empty
231234 # This is because we do not know how many bytes we can read.
@@ -300,7 +303,7 @@ def recvonce(self,
300303 return data [:size ]
301304
302305 def recvuntil (self ,
303- delim : Union [str , bytes , List [Union [ str , bytes ] ]],
306+ delim : Union [AtomicRecvT , List [AtomicRecvT ]],
304307 size : int = 4096 ,
305308 timeout : Optional [Union [int , float ]]= None ,
306309 drop : bool = False ,
@@ -336,14 +339,14 @@ def recvuntil(self,
336339 echo.recvonce(6) # 123def
337340 ```
338341 """
339- assert isinstance (delim , (str , bytes , list )), \
342+ assert isinstance (delim , (str , bytes , bytearray , list )), \
340343 "`delim` must be either str, bytes, or list"
341344
342345 # Preprocess
343346 delim_list : List [bytes ] = []
344347 if isinstance (delim , list ):
345348 for i , d in enumerate (delim ):
346- assert isinstance (d , (str , bytes )), \
349+ assert isinstance (d , (str , bytes , bytearray )), \
347350 f"`delim[{ i } ]` must be either str or bytes"
348351 delim_list .append (str2bytes (delim [i ]))
349352 else :
@@ -410,7 +413,7 @@ def recvline(self,
410413 return line .rstrip () if drop else line
411414
412415 def recvlineafter (self ,
413- delim : Union [str , bytes ],
416+ delim : Union [AtomicRecvT , List [ AtomicRecvT ] ],
414417 size : int = 4096 ,
415418 timeout : Optional [Union [int , float ]]= None ,
416419 drop : bool = True ,
@@ -519,21 +522,50 @@ def _ansi_stream(self):
519522 self .unget (ansi .buffer )
520523 return scr
521524
522- def before (self : TubeType , data : Union [str , bytes ]) -> TubeType :
523- raise NotImplementedError ("Method not implemented yet" )
525+ def before (self : TubeType ,
526+ delim : Union [AtomicRecvT , List [AtomicRecvT ]],
527+ size : int = 4096 ,
528+ timeout : Optional [Union [int , float ]] = None ) -> TubeType :
529+ """Wait until data arrives.
530+
531+ This method works in the same as `recvuntil` except that this method returns `self`
532+ and received data is buffered.
533+
534+ Args:
535+ delim : The delimiter bytes
536+ size : The data size to receive at once
537+ timeout : Timeout in second
538+
539+ Returns:
540+ Tube: Self.
541+
542+ Raises:
543+ ConnectionAbortedError: Connection is aborted by process
544+ ConnectionResetError: Connection is closed by peer
545+ TimeoutError: Timeout exceeded
546+ OSError: System error
547+
548+ Examples:
549+ ```
550+ msg = tube.after("Message: ").recvline()
551+ tube.after("[42] ").recvregex(r"ID: (\\ d+)")
552+ ```
553+ """
554+ raise NotImplementedError ("Not implemented yet" )
524555
525556 def after (self : TubeType ,
526- delim : Union [str , bytes ],
557+ delim : Union [AtomicRecvT , List [ AtomicRecvT ] ],
527558 size : int = 4096 ,
528559 timeout : Optional [Union [int , float ]] = None ,
529560 lookahead : bool = False ) -> TubeType :
530561 """Wait until data arrives.
531562
563+ This method works in the same as `recvuntil` except that this method returns `self`.
564+
532565 Args:
533566 delim : The delimiter bytes
534567 size : The data size to receive at once
535568 timeout : Timeout in second
536- drop : Discard delimiter or not
537569 lookahead: Unget delimiter to buffer or not
538570
539571 Returns:
@@ -575,7 +607,7 @@ def send(self, data: Union[str, bytes]) -> int:
575607 tube.send(b"\xde \xad \xbe \xef ")
576608 ```
577609 """
578- assert isinstance (data , (str , bytes )), "`data` must be either str or bytes"
610+ assert isinstance (data , (str , bytes , bytearray )), "`data` must be either str or bytes"
579611 data = str2bytes (data )
580612
581613 size = self ._send_impl (data )
@@ -619,32 +651,34 @@ def sendonce(self, data: Union[str, bytes]):
619651 to_send -= sent
620652
621653 def sendline (self ,
622- data : Union [int , float , str , bytes , List [Union [ int , float , str , bytes ] ]]):
654+ data : Union [AtomicSendT , List [AtomicSendT ]]):
623655 """Send a line
624656
625657 Send a line of data.
626658
627659 Args:
628660 data (bytes) : Data to send
629661 """
630- assert isinstance (data , (int , float , str , bytes , list )), \
631- "`data` must be int, float, str, bytes, or list"
632-
633662 if isinstance (data , list ):
634663 for d in data :
635664 self .sendline (d )
636665 return
666+
667+ if isinstance (data , (str , bytes , bytearray )):
668+ data = str2bytes (data )
637669
638- if isinstance (data , ( int , float ) ):
670+ elif isinstance (data , float ):
639671 data = str (data ).encode ()
672+
640673 else :
641- data = str2bytes (data )
674+ # Explicitly call "__int__"
675+ data = str (int (data )).encode ()
642676
643677 self .send (data + self ._newline )
644678
645679 def sendafter (self ,
646- delim : Union [str , bytes , List [Union [ str , bytes ] ]],
647- data : Union [ int , float , str , bytes ] ,
680+ delim : Union [AtomicRecvT , List [AtomicRecvT ]],
681+ data : AtomicSendT ,
648682 size : int = 4096 ,
649683 timeout : Optional [Union [int , float ]]= None ,
650684 drop : bool = False ,
@@ -670,20 +704,24 @@ def sendafter(self,
670704 tube.sendafter("command: ", 1) # b"1" is sent
671705 ```
672706 """
673- assert isinstance (data , (int , float , str , bytes )), \
674- "`data` must be int, float, str, or bytes"
675-
676707 recv_data = self .recvuntil (delim , size , timeout , drop , lookahead )
677708
678- if isinstance (data , (int , float )):
679- data = str (data )
680- self .send (data )
709+ if isinstance (data , (str , bytes , bytearray )):
710+ data = str2bytes (data )
681711
712+ elif isinstance (data , float ):
713+ data = str (data ).encode ()
714+
715+ else :
716+ # Explicitly call "__int__"
717+ data = str (int (data )).encode ()
718+
719+ self .send (data )
682720 return recv_data
683721
684722 def sendlineafter (self ,
685- delim : Union [str , bytes ],
686- data : Union [ str , bytes , int ] ,
723+ delim : Union [AtomicRecvT , List [ AtomicRecvT ] ],
724+ data : AtomicSendT ,
687725 size : int = 4096 ,
688726 timeout : Optional [Union [int , float ]]= None ,
689727 drop : bool = False ,
@@ -786,8 +824,6 @@ def pretty_print(data: bytes, prev: bytes=b''):
786824 def thread_recv ():
787825 """Receive data from tube and print to stdout
788826 """
789- import signal
790- signal .pthread_sigmask (signal .SIG_BLOCK , {signal .SIGINT })
791827 nonlocal stop_event
792828 leftover = b''
793829 while self .is_alive () and not stop_event .is_set ():
@@ -810,6 +846,7 @@ def thread_recv():
810846 def thread_send ():
811847 """Read user input and send it to tube
812848 """
849+ import time
813850 nonlocal stop_event
814851 while self .is_alive () and not stop_event .is_set ():
815852 sys .stdout .write (prompt )
0 commit comments