@@ -194,11 +194,66 @@ def delayed_exit():
194194 threading .Thread (target = delayed_exit , daemon = True ).start ()
195195
196196
197+ class NoopLifespan :
198+ """A no-operation lifespan implementation that provides the required methods but doesn't do anything."""
199+
200+ def __init__ (self , app ):
201+ self .app = app
202+
203+ async def startup (self ):
204+ """No-op startup method."""
205+ logger .warning ("Using NoopLifespan - server may not handle startup/shutdown events properly" )
206+ pass
207+
208+ async def shutdown (self ):
209+ """No-op shutdown method."""
210+ pass
211+
212+
197213class ServerWithCallback (uvicorn .Server ):
198214 def install_signal_handlers (self ):
199215 # Override to prevent uvicorn from installing its own handlers
200216 pass
201217
218+ async def startup (self , sockets = None ):
219+ """Override the startup method to add error handling for lifespan startup."""
220+ if self .should_exit :
221+ return
222+
223+ if sockets is not None :
224+ self .servers = []
225+ for socket in sockets :
226+ server = await self .config .server_class (config = self .config , server = self )
227+ await server .start ()
228+ self .servers .append (server )
229+ else :
230+ self .servers = [await self .config .server_class (config = self .config , server = self )]
231+ await self .servers [0 ].start ()
232+
233+ if self .lifespan is not None :
234+ try :
235+ await self .lifespan .startup ()
236+ except Exception as e :
237+ logger .error (f"Error during lifespan startup: { str (e )} " )
238+ logger .debug (f"Lifespan startup error details: { traceback .format_exc ()} " )
239+ # Replace with NoopLifespan if startup fails
240+ self .lifespan = NoopLifespan (self .config .app )
241+ logger .warning ("Replaced failed lifespan with NoopLifespan" )
242+
243+ async def shutdown (self , sockets = None ):
244+ """Override the shutdown method to add error handling for lifespan shutdown."""
245+ if self .servers :
246+ for server in self .servers :
247+ await server .shutdown ()
248+
249+ if self .lifespan is not None :
250+ try :
251+ await self .lifespan .shutdown ()
252+ except Exception as e :
253+ logger .error (f"Error during lifespan shutdown: { str (e )} " )
254+ logger .debug (f"Lifespan shutdown error details: { traceback .format_exc ()} " )
255+ logger .warning ("Continuing shutdown despite lifespan error" )
256+
202257 async def serve (self , sockets = None ):
203258 self .config .setup_event_loop ()
204259
@@ -296,19 +351,25 @@ async def serve(self, sockets=None):
296351 logger .info ("Using LifespanState from uvicorn.lifespan.state" )
297352 except (ImportError , AttributeError , TypeError ) as e :
298353 logger .debug (f"Failed to import or initialize LifespanState: { str (e )} " )
299- # Fallback to no lifespan
300- self .lifespan = None
301- logger .warning ("Could not initialize lifespan - server may not handle startup/shutdown events properly" )
302-
303- await self .startup (sockets = sockets )
354+ # Fallback to NoopLifespan
355+ self .lifespan = NoopLifespan (self .config .app )
356+ logger .warning ("Using NoopLifespan - server may not handle startup/shutdown events properly" )
304357
305- # Call our callback before processing requests
306- # We need to access the on_startup function from the outer scope
307- if hasattr (self , 'on_startup_callback' ) and callable (self .on_startup_callback ):
308- self .on_startup_callback ()
309-
310- await self .main_loop ()
311- await self .shutdown ()
358+ try :
359+ await self .startup (sockets = sockets )
360+
361+ # Call our callback before processing requests
362+ # We need to access the on_startup function from the outer scope
363+ if hasattr (self , 'on_startup_callback' ) and callable (self .on_startup_callback ):
364+ self .on_startup_callback ()
365+
366+ await self .main_loop ()
367+ await self .shutdown ()
368+ except Exception as e :
369+ logger .error (f"Error during server operation: { str (e )} " )
370+ logger .debug (f"Server error details: { traceback .format_exc ()} " )
371+ # Re-raise to allow proper error handling
372+ raise
312373
313374
314375def start_server (use_ngrok : bool = None , port : int = None , ngrok_auth_token : Optional [str ] = None ):
0 commit comments