@@ -283,10 +283,11 @@ def __init__(
283
283
self .encoding = encoding
284
284
self .style = style
285
285
self .enable_cpr = enable_cpr
286
+
287
+ self ._run_task : asyncio .Task [None ] | None = None
286
288
self ._application_tasks : list [asyncio .Task [None ]] = []
287
289
288
290
self .connections : set [TelnetConnection ] = set ()
289
- self ._listen_socket : socket .socket | None = None
290
291
291
292
@classmethod
292
293
def _create_socket (cls , host : str , port : int ) -> socket .socket :
@@ -298,44 +299,74 @@ def _create_socket(cls, host: str, port: int) -> socket.socket:
298
299
s .listen (4 )
299
300
return s
300
301
301
- def start (self ) -> None :
302
+ async def run (self , ready_cb : Callable [[], None ] | None = None ) -> None :
302
303
"""
303
- Start the telnet server.
304
- Don't forget to call `loop.run_forever()` after doing this.
304
+ Run the telnet server, until this gets cancelled.
305
+
306
+ :param ready_cb: Callback that will be called at the point that we're
307
+ actually listening.
305
308
"""
306
- self . _listen_socket = self ._create_socket (self .host , self .port )
309
+ socket = self ._create_socket (self .host , self .port )
307
310
logger .info (
308
311
"Listening for telnet connections on %s port %r" , self .host , self .port
309
312
)
310
313
311
- get_running_loop ().add_reader (self ._listen_socket , self ._accept )
314
+ get_running_loop ().add_reader (socket , lambda : self ._accept (socket ))
315
+
316
+ if ready_cb :
317
+ ready_cb ()
318
+
319
+ try :
320
+ # Run forever, until cancelled.
321
+ await asyncio .Future ()
322
+ finally :
323
+ get_running_loop ().remove_reader (socket )
324
+ socket .close ()
325
+
326
+ # Wait for all applications to finish.
327
+ for t in self ._application_tasks :
328
+ t .cancel ()
329
+
330
+ # (This is similar to
331
+ # `Application.cancel_and_wait_for_background_tasks`. We wait for the
332
+ # background tasks to complete, but don't propagate exceptions, because
333
+ # we can't use `ExceptionGroup` yet.)
334
+ if len (self ._application_tasks ) > 0 :
335
+ await asyncio .wait (
336
+ self ._application_tasks ,
337
+ timeout = None ,
338
+ return_when = asyncio .ALL_COMPLETED ,
339
+ )
340
+
341
+ def start (self ) -> None :
342
+ """
343
+ Start the telnet server (stop by calling and awaiting `stop()`).
344
+
345
+ Note: When possible, it's better to call `.run()` instead.
346
+ """
347
+ if self ._run_task is not None :
348
+ # Already running.
349
+ return
350
+
351
+ self ._run_task = get_running_loop ().create_task (self .run ())
312
352
313
353
async def stop (self ) -> None :
314
- if self ._listen_socket :
315
- get_running_loop ().remove_reader (self ._listen_socket )
316
- self ._listen_socket .close ()
317
-
318
- # Wait for all applications to finish.
319
- for t in self ._application_tasks :
320
- t .cancel ()
321
-
322
- # (This is similar to
323
- # `Application.cancel_and_wait_for_background_tasks`. We wait for the
324
- # background tasks to complete, but don't propagate exceptions, because
325
- # we can't use `ExceptionGroup` yet.)
326
- if len (self ._application_tasks ) > 0 :
327
- await asyncio .wait (
328
- self ._application_tasks , timeout = None , return_when = asyncio .ALL_COMPLETED
329
- )
354
+ """
355
+ Stop a telnet server that was started using `.start()` and wait for the
356
+ cancellation to complete.
357
+ """
358
+ if self ._run_task is not None :
359
+ self ._run_task .cancel ()
360
+ try :
361
+ await self ._run_task
362
+ except asyncio .CancelledError :
363
+ pass
330
364
331
- def _accept (self ) -> None :
365
+ def _accept (self , listen_socket : socket . socket ) -> None :
332
366
"""
333
367
Accept new incoming connection.
334
368
"""
335
- if self ._listen_socket is None :
336
- return # Should not happen. `_accept` is called after `start`.
337
-
338
- conn , addr = self ._listen_socket .accept ()
369
+ conn , addr = listen_socket .accept ()
339
370
logger .info ("New connection %r %r" , * addr )
340
371
341
372
# Run application for this connection.
0 commit comments