diff --git a/pymongo/periodic_executor.py b/pymongo/periodic_executor.py index 003b05647c..30fd33ddf4 100644 --- a/pymongo/periodic_executor.py +++ b/pymongo/periodic_executor.py @@ -16,6 +16,7 @@ from __future__ import annotations +import sys import threading import time import weakref @@ -92,7 +93,15 @@ def open(self) -> None: thread.daemon = True self._thread = weakref.proxy(thread) _register_executor(self) - thread.start() + # Mitigation to RuntimeError firing when thread starts on shutdown + # https://github.com/python/cpython/issues/114570 + try: + thread.start() + except RuntimeError as e: + if "interpreter shutdown" in str(e) or sys.is_finalizing(): + self._thread = None + return + raise def close(self, dummy: Any = None) -> None: """Stop. To restart, call open(). diff --git a/test/test_monitor.py b/test/test_monitor.py index 0495a8cbc7..92bcdc49ad 100644 --- a/test/test_monitor.py +++ b/test/test_monitor.py @@ -16,6 +16,7 @@ from __future__ import annotations import gc +import subprocess import sys from functools import partial @@ -79,6 +80,17 @@ def test_cleanup_executors_on_client_close(self): for executor in executors: wait_until(lambda: executor._stopped, f"closed executor: {executor._name}", timeout=5) + def test_no_thread_start_runtime_err_on_shutdown(self): + """Test we silence noisy runtime errors fired when the MongoClient spawns a new thread + on process shutdown.""" + command = [sys.executable, "-c", "from pymongo import MongoClient; c = MongoClient()"] + completed_process: subprocess.CompletedProcess = subprocess.run( + command, capture_output=True + ) + + self.assertFalse(completed_process.stderr) + self.assertFalse(completed_process.stdout) + if __name__ == "__main__": unittest.main()