@@ -347,6 +347,12 @@ def shutdown(self):
347347 posthog_client = getattr (self , '_posthog' , None )
348348 if posthog_client :
349349 try :
350+ # Check if Python interpreter is shutting down
351+ if self ._is_interpreter_shutting_down ():
352+ self .logger .debug ("Interpreter shutting down, skipping PostHog operations" )
353+ self ._posthog = None
354+ return
355+
350356 # Use a timeout-based flush to prevent hanging
351357 import threading
352358 import time
@@ -368,27 +374,90 @@ def shutdown(self):
368374 # Cleanup PostHog threads safely
369375 self ._shutdown_posthog_threads (posthog_client )
370376
371- # Standard shutdown
372- posthog_client .shutdown ()
377+ # Standard shutdown - with interpreter shutdown check
378+ if not self ._is_interpreter_shutting_down ():
379+ posthog_client .shutdown ()
380+ else :
381+ self .logger .debug ("Skipping PostHog shutdown call due to interpreter shutdown" )
373382
374383 except Exception as e :
375- # Log the error but don't fail shutdown
376- self .logger .error (f"Error during PostHog shutdown: { e } " )
384+ # Handle specific shutdown-related errors gracefully
385+ error_msg = str (e ).lower ()
386+ if any (phrase in error_msg for phrase in [
387+ 'cannot schedule new futures' ,
388+ 'interpreter shutdown' ,
389+ 'atexit after shutdown' ,
390+ 'event loop closed' ,
391+ 'runtime is shutting down'
392+ ]):
393+ self .logger .debug (f"PostHog shutdown prevented due to interpreter shutdown: { e } " )
394+ else :
395+ self .logger .error (f"Error during PostHog shutdown: { e } " )
377396 finally :
378397 self ._posthog = None
379398
399+ def _is_interpreter_shutting_down (self ) -> bool :
400+ """
401+ Check if the Python interpreter is shutting down.
402+
403+ Returns:
404+ True if interpreter is shutting down, False otherwise
405+ """
406+ try :
407+ import sys
408+ import threading
409+
410+ # Check if the interpreter is in shutdown mode
411+ if hasattr (sys , 'is_finalizing' ) and sys .is_finalizing ():
412+ return True
413+
414+ # Check if we can create new threads (fails during shutdown)
415+ try :
416+ import threading
417+ test_thread = threading .Thread (target = lambda : None )
418+ test_thread .daemon = True
419+ test_thread .start ()
420+ test_thread .join (timeout = 0.001 )
421+ return False
422+ except (RuntimeError , threading .ThreadError ):
423+ return True
424+
425+ except Exception :
426+ # If we can't determine state, assume we're shutting down to be safe
427+ return True
428+
380429 def _safe_flush_posthog (self , posthog_client ):
381430 """Safely flush PostHog data with error handling."""
382431 try :
432+ # Skip flush if interpreter is shutting down
433+ if self ._is_interpreter_shutting_down ():
434+ self .logger .debug ("Skipping PostHog flush due to interpreter shutdown" )
435+ return False
436+
383437 posthog_client .flush ()
384438 return True
385439 except Exception as e :
386- self .logger .debug (f"PostHog flush error: { e } " )
440+ error_msg = str (e ).lower ()
441+ if any (phrase in error_msg for phrase in [
442+ 'cannot schedule new futures' ,
443+ 'interpreter shutdown' ,
444+ 'atexit after shutdown' ,
445+ 'event loop closed' ,
446+ 'runtime is shutting down'
447+ ]):
448+ self .logger .debug (f"PostHog flush prevented due to interpreter shutdown: { e } " )
449+ else :
450+ self .logger .debug (f"PostHog flush error: { e } " )
387451 return False
388452
389453 def _shutdown_posthog_threads (self , posthog_client ):
390454 """Safely shutdown PostHog background threads."""
391455 try :
456+ # Skip thread cleanup if interpreter is shutting down
457+ if self ._is_interpreter_shutting_down ():
458+ self .logger .debug ("Skipping PostHog thread cleanup due to interpreter shutdown" )
459+ return
460+
392461 # Access thread pool safely (fix double shutdown issue)
393462 thread_pool = getattr (posthog_client , '_thread_pool' , None )
394463 if thread_pool :
@@ -400,7 +469,16 @@ def _shutdown_posthog_threads(self, posthog_client):
400469 import time
401470 time .sleep (0.5 )
402471 except Exception as e :
403- self .logger .debug (f"Thread pool shutdown error: { e } " )
472+ error_msg = str (e ).lower ()
473+ if any (phrase in error_msg for phrase in [
474+ 'cannot schedule new futures' ,
475+ 'interpreter shutdown' ,
476+ 'atexit after shutdown' ,
477+ 'event loop closed'
478+ ]):
479+ self .logger .debug (f"Thread pool shutdown prevented due to interpreter shutdown: { e } " )
480+ else :
481+ self .logger .debug (f"Thread pool shutdown error: { e } " )
404482
405483 # Clean up consumer
406484 consumer = getattr (posthog_client , '_consumer' , None )
@@ -411,10 +489,28 @@ def _shutdown_posthog_threads(self, posthog_client):
411489 if hasattr (consumer , 'shutdown' ):
412490 consumer .shutdown ()
413491 except Exception as e :
414- self .logger .debug (f"Consumer shutdown error: { e } " )
492+ error_msg = str (e ).lower ()
493+ if any (phrase in error_msg for phrase in [
494+ 'cannot schedule new futures' ,
495+ 'interpreter shutdown' ,
496+ 'atexit after shutdown' ,
497+ 'event loop closed'
498+ ]):
499+ self .logger .debug (f"Consumer shutdown prevented due to interpreter shutdown: { e } " )
500+ else :
501+ self .logger .debug (f"Consumer shutdown error: { e } " )
415502
416503 except Exception as e :
417- self .logger .debug (f"Error during PostHog thread cleanup: { e } " )
504+ error_msg = str (e ).lower ()
505+ if any (phrase in error_msg for phrase in [
506+ 'cannot schedule new futures' ,
507+ 'interpreter shutdown' ,
508+ 'atexit after shutdown' ,
509+ 'event loop closed'
510+ ]):
511+ self .logger .debug (f"PostHog thread cleanup prevented due to interpreter shutdown: { e } " )
512+ else :
513+ self .logger .debug (f"Error during PostHog thread cleanup: { e } " )
418514
419515
420516# Global telemetry instance
0 commit comments