1
1
from typing import Callable , NamedTuple , Any , Coroutine , Literal
2
2
from pathlib import Path
3
+ from signal import SIGINT , SIGTERM
4
+ from dataclasses import dataclass
3
5
import socket
4
6
import asyncio
5
7
from .utils .logging import exception , info , warning , event
17
19
from .config import HOST , PORT
18
20
19
21
22
+ @dataclass (slots = True )
23
+ class ServerState :
24
+ isRunning : bool = True
25
+
26
+ def stop (self ) -> None :
27
+ self .isRunning = False
28
+
29
+ def onException (
30
+ self , loop : asyncio .AbstractEventLoop , context : dict [str , Any ]
31
+ ) -> None :
32
+ e = context .get ("exception" )
33
+ if e :
34
+ exception (e )
35
+
36
+
20
37
class ServerOptions (NamedTuple ):
21
38
host : str = "0.0.0.0" # nosec: B104
22
39
port : int = 8000
@@ -336,6 +353,13 @@ async def Serve(
336
353
except RuntimeError :
337
354
loop = asyncio .new_event_loop ()
338
355
356
+ # Manage server state
357
+ state = ServerState ()
358
+ # Registers handlers for signals and exception (so that we log them)
359
+ loop .add_signal_handler (SIGINT , lambda : state .stop ())
360
+ loop .add_signal_handler (SIGTERM , lambda : state .stop ())
361
+ loop .set_exception_handler (state .onException )
362
+
339
363
info (
340
364
"Extra AIO Server listening" ,
341
365
icon = "🚀" ,
@@ -344,7 +368,7 @@ async def Serve(
344
368
)
345
369
346
370
try :
347
- while True :
371
+ while state . isRunning :
348
372
if options .condition and not options .condition ():
349
373
break
350
374
try :
0 commit comments