55import warnings # isort:skip
66from concurrent .futures import ThreadPoolExecutor # isort:skip
77from twisted .internet import asyncioreactor # isort:skip
8+ from monkay .asgi import Lifespan # isort:skip
89
910
1011twisted_loop = asyncio .new_event_loop ()
@@ -66,6 +67,7 @@ def __init__(
6667 application_close_timeout = 10 ,
6768 ready_callable = None ,
6869 server_name = "daphne" ,
70+ enable_lifespan = False ,
6971 ):
7072 self .application = application
7173 self .endpoints = endpoints or []
@@ -93,6 +95,9 @@ def __init__(
9395 if not self .endpoints :
9496 logger .error ("No endpoints. This server will not listen on anything." )
9597 sys .exit (1 )
98+ self .lifespan_context = None
99+ if enable_lifespan :
100+ self .lifespan_context = Lifespan (self .application )
96101
97102 def run (self ):
98103 # A dict of protocol: {"application_instance":, "connected":, "disconnected":} dicts
@@ -120,6 +125,12 @@ def run(self):
120125 logger .info (
121126 "HTTP/2 support not enabled (install the http2 and tls Twisted extras)"
122127 )
128+ # Set the asyncio reactor's event loop as global
129+ # TODO: Should we instead pass the global one into the reactor?
130+ evloop = reactor ._asyncioEventloop
131+ asyncio .set_event_loop (evloop )
132+ if self .lifespan_context is not None :
133+ evloop .run_until (self .lifespan_context .__aenter__ ())
123134
124135 # Kick off the timeout loop
125136 reactor .callLater (1 , self .application_checker )
@@ -133,13 +144,9 @@ def run(self):
133144 listener .addErrback (self .listen_error )
134145 self .listeners .append (listener )
135146
136- # Set the asyncio reactor's event loop as global
137- # TODO: Should we instead pass the global one into the reactor?
138- asyncio .set_event_loop (reactor ._asyncioEventloop )
139-
140147 # Verbosity 3 turns on asyncio debug to find those blocking yields
141148 if self .verbosity >= 3 :
142- asyncio . get_event_loop () .set_debug (True )
149+ evloop .set_debug (True )
143150
144151 reactor .addSystemEventTrigger ("before" , "shutdown" , self .kill_all_applications )
145152 if not self .abort_start :
@@ -323,6 +330,11 @@ def kill_all_applications(self):
323330 # Make Twisted wait until they're all dead
324331 wait_deferred = defer .Deferred .fromFuture (asyncio .gather (* wait_for ))
325332 wait_deferred .addErrback (lambda x : None )
333+ # at last execute lifespan cleanup
334+ if self .lifespan_context is not None :
335+ wait_deferred .chainDeferred (
336+ defer .Deferred .fromFuture (self .lifespan_context .__aexit__ ())
337+ )
326338 return wait_deferred
327339
328340 def timeout_checker (self ):
0 commit comments