From 55ed9636cb12f41d1b224fc8aa791df176509313 Mon Sep 17 00:00:00 2001 From: Jib Date: Mon, 5 Feb 2024 08:46:32 -0500 Subject: [PATCH 1/2] PYTHON-4147: Silence noisy thread.start() RuntimeError at shutdown (#1486) (cherry picked from commit 0f7e1b011024b19b5ec755cc3302032bc5a36008) --- pymongo/periodic_executor.py | 11 ++++++++++- test/test_monitor.py | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) 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..8ccec7fd0b 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( + " ".join(command), shell=True, capture_output=True + ) + + self.assertFalse(completed_process.stderr) + self.assertFalse(completed_process.stdout) + if __name__ == "__main__": unittest.main() From ad07b84e16983728d92cb1dacdb554284f697271 Mon Sep 17 00:00:00 2001 From: Jib Date: Wed, 7 Feb 2024 14:24:20 -0500 Subject: [PATCH 2/2] PYTHON-4147-fix: Remove quote wrapping and remove shell=true usage (#1513) (cherry picked from commit fe37841f07d5bdc2a922e51443710ea119fdb35a) --- test/test_monitor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_monitor.py b/test/test_monitor.py index 8ccec7fd0b..92bcdc49ad 100644 --- a/test/test_monitor.py +++ b/test/test_monitor.py @@ -83,9 +83,9 @@ def test_cleanup_executors_on_client_close(self): 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()'"] + command = [sys.executable, "-c", "from pymongo import MongoClient; c = MongoClient()"] completed_process: subprocess.CompletedProcess = subprocess.run( - " ".join(command), shell=True, capture_output=True + command, capture_output=True ) self.assertFalse(completed_process.stderr)